AEM Cloud Service - Multifield with User Roles Autocomplete for Approved and Restricted Assets Delivery


In Asset Metadata Editor extend Multifield widget for adding User Picker selecting DAM Roles (./jcr:content/metadata/dam:roles) searching for a user by email. In Cloud Services Asset Delivery Restricted Assets feature, only the roles added in metadata property dam:roles and status set to Approved have access to the assets on AEM delivery (eg. https://delivery-p99999-e999999.adobeaemcloud.com). In this post Org Users are fetched from Admin Console as json using User Management API (UMAPI) https://adobe-apiplatform.github.io/umapi-documentation/en/ and shown in auto complete added in multifield.

Demo | Package Install | Github



1) As a system administrator, add the User Management API Integration in Org Developer Console - https://developer.adobe.com/console/home 


2) Create a sample project using AEM archetype...

mvn -B org.apache.maven.plugins:maven-archetype-plugin:3.2.1:generate -D archetypeGroupId=com.adobe.aem -D archetypeArtifactId=aem-project-archetype 
-D archetypeVersion=36 -D aemVersion="cloud" -D appTitle="Experience AEM Approved Assets Role Picker" -D appId="eaem-approved-assets-role-picker"
-D groupId="apps.experienceaem.assets" -D frontendModule=none -D includeExamples=n -D includeDispatcherConfig=n


3) Add the necessary UMAPI configuration in Cloud Services as Environment Variables 


4) Add a servlet in aem project to fetch the Admin Console users as json using User Management API

package apps.experienceaem.assets.core.servlets;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.SocketConfig;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.AttributeType;
import org.osgi.service.metatype.annotations.Designate;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.Servlet;
import javax.servlet.ServletException;
import java.io.IOException;

@Component(
immediate = true,
service = Servlet.class,
property = {
"sling.servlet.methods=GET",
"sling.servlet.paths=/bin/eaem/umapi/users"
}
)
@Designate(ocd = GetAdminConsoleUsers.UMAPIConfiguration.class)
public class GetAdminConsoleUsers extends SlingAllMethodsServlet {
private static final Logger LOGGER = LoggerFactory.getLogger(GetAdminConsoleUsers.class);

private static final String UMAPI_END_POINT = "https://usermanagement.adobe.io/v2/usermanagement/users";

private String clientId = "";
private String clientSecret = "";
private String adobeOrg = "";
private String IMS_URL = "https://ims-na1.adobelogin.com/ims/token/v3";
private String UMAPI_URL = "https://usermanagement.adobe.io/v2/usermanagement/users";

@Activate
@Modified
protected void activate(final UMAPIConfiguration config) {
clientId = config.clientId();
clientSecret = config.clientSecret();
adobeOrg = config.adobeOrg();
}

@Override
protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)
throws ServletException, IOException {

try{
response.setContentType("application/json");
if(StringUtils.isEmpty(clientId) || StringUtils.isEmpty(clientSecret) || StringUtils.isEmpty(adobeOrg)){
response.getWriter().println("{ error : 'User API not initialized' }");
}else{
response.getWriter().println(getUsersInOrg());
}
}catch(Exception e){
throw new ServletException("Error getting users in org : " + adobeOrg, e);
}
}

public JsonObject getUsersInOrg(){
JsonObject usersObject = new JsonObject();

try{
String accessToken = getAccessToken();

SocketConfig sc = SocketConfig.custom().setSoTimeout(180000).build();
CloseableHttpClient client = HttpClients.custom().setDefaultSocketConfig(sc).build();

HttpGet userGet = new HttpGet(UMAPI_URL + "/" + adobeOrg + "/0");

userGet.addHeader("X-Api-Key", clientId);
userGet.addHeader("Authorization", "Bearer " + accessToken);
userGet.addHeader("Content-Type", "application/json");

HttpResponse response = client.execute(userGet);

HttpEntity responseEntity = response.getEntity();

String responseBody = IOUtils.toString(responseEntity.getContent(), "UTF-8");

client.close();

Gson gson = new Gson();

JsonObject responseObject = gson.fromJson(responseBody, JsonObject.class);
JsonObject user = null;

JsonArray users = (JsonArray)responseObject.get("users");

for(JsonElement element : users){
user = element.getAsJsonObject();
usersObject.addProperty(user.get("email").getAsString(), user.get("id").getAsString());
}
}catch(Exception e){
LOGGER.error("Error getting users in org : " + adobeOrg,e);
throw new RuntimeException("Error getting users in org : " + adobeOrg, e);
}

return usersObject;
}

private String getAccessTokenRequestBody(){
return "grant_type=client_credentials&client_id=" + clientId + "&client_secret=" + clientSecret + "&scope=openid,AdobeID,user_management_sdk";
}

private String getAccessToken() throws IOException {
CloseableHttpClient client = null;
String responseBody = "";

SocketConfig sc = SocketConfig.custom().setSoTimeout(180000).build();
client = HttpClients.custom().setDefaultSocketConfig(sc).build();

HttpPost post = new HttpPost(IMS_URL);
StringEntity entity = new StringEntity(getAccessTokenRequestBody(), "UTF-8");

post.addHeader("Content-Type", "application/x-www-form-urlencoded");
post.setEntity(entity);

HttpResponse response = client.execute(post);

HttpEntity responseEntity = response.getEntity();

responseBody = IOUtils.toString(responseEntity.getContent(), "UTF-8");

client.close();

Gson gson = new Gson();

JsonObject responseObject = gson.fromJson(responseBody, JsonObject.class);

return responseObject.get("access_token").getAsString();
}

@ObjectClassDefinition(name = "Experience AEM User Management API Configuration")
public @interface UMAPIConfiguration {
@AttributeDefinition(
name = "Client Id",
description = "UMAPI account clientId",
type = AttributeType.STRING
)
String clientId() default "";

@AttributeDefinition(
name = "Client Secret",
description = "UMAPI account clientSecret",
type = AttributeType.STRING
)
String clientSecret() default "";

@AttributeDefinition(
name = "Adobe Org",
description = "Adobe Org",
type = AttributeType.STRING
)
String adobeOrg() default "";
}
}

            Users returned as json with the url https://author-p99999-e999999.adobeaemcloud.com/bin/eaem/umapi/users


5) The configuration for reading UMAPI configuration in env variables eaem-approved-assets-role-picker\ui.config\src\main\content\jcr_root\apps\eaem-approved-assets-role-picker\osgiconfig\config.author\apps.experienceaem.assets.core.servlets.GetAdminConsoleUsers~eaem.cfg.json

{
"clientId": "$[env:EAEM_UMPAI_CLIENT_ID]",
"clientSecret": "$[env:EAEM_UMPAI_CLIENT_SECRET]",
"adobeOrg": "$[env:EAEM_ADOBE_ORG]"
}

6) For adding the necessary logic to extend multifield for property ./jcr:content/metadata/dam:roles, create a clientlib /apps/eaem-approved-assets-role-picker/clientlibs/pick-dam-roles with categories=dam.gui.actions.coral, add the following code in /apps/eaem-approved-assets-role-picker/clientlibs/pick-dam-roles/pick-dam-roles.js

(function ($, $document) {
"use strict";

const METADATA_EDITOR_PAGE = "/mnt/overlay/dam/gui/content/assets/metadataeditor.external.html",
UMAPI_USERS_URL = "/bin/eaem/umapi/users",
DAM_ROLES_NAME = "./jcr:content/metadata/dam:roles",
DAM_ROLES_SELECTOR = '[data-granite-coral-multifield-name="./jcr:content/metadata/dam:roles"]';

let usersJson = {};

if (isMetadataEditPage()) {
$document.on("foundation-contentloaded", addPickerInDamRolesWidget);
}

function addPickerInDamRolesWidget(){
$.ajax(UMAPI_USERS_URL).done( json => {
usersJson = json;
$(DAM_ROLES_SELECTOR)[0].items.getAll().forEach((mfItem) => addRolesDropdown(mfItem));
});

$(DAM_ROLES_SELECTOR).on("coral-collection:add", (event) => addRolesDropdown(event.detail.item));
}

function addRolesDropdown(item){
Coral.commons.ready(item, (mfItem) => {
const selectedVal = mfItem.content.querySelector("input").value,
$autoComplete = $(mfItem).find("coral-multifield-item-content").html(getUsersAutoComplete()).find("coral-autocomplete");
Coral.commons.ready($autoComplete[0], (autoComplete) => {
autoComplete.value = selectedVal || '';
autoComplete._elements["inputGroup"].style.width = "100%";
autoComplete._elements["trigger"].style.marginRight = "15px"
});
});
}

function getUsersAutoComplete(){
let userWidget = `<coral-autocomplete placeholder="Select user" match="startswith" name="${DAM_ROLES_NAME}" style="width:50px">`;

for (const email in usersJson) {
userWidget = userWidget + `<coral-autocomplete-item value='${usersJson[email]}'>${email}</coral-autocomplete-item>`;
}

userWidget = userWidget + '</coral-autocomplete>';

return userWidget;
}

function isMetadataEditPage() {
return (window.location.pathname.indexOf(METADATA_EDITOR_PAGE) >= 0);
}

}(jQuery, jQuery(document)));


7) You can search for Restricted Assets using the Delivery API eg. https://delivery-p99999-e99999.adobeaemcloud.com/adobe/assets/search


8) Pick the Asset URN from search results and make the call to get actual asset binary (setting the Authorization header token for user) eg. https://delivery-p99999-e999999.adobeaemcloud.com/adobe/assets/deliver/urn:aaid:aem:274a942b-0c1f-425c-a878-ec87c4fea99f/AdobeStock_151208034.jpeg 



No comments:

Post a Comment