Skip to content

Commit

Permalink
Allow differential IMS updates
Browse files Browse the repository at this point in the history
It is opt-in though, because it doesn't always speed up performance.
This closes #757
  • Loading branch information
kwin committed Aug 2, 2024
1 parent a39360f commit 647d336
Show file tree
Hide file tree
Showing 9 changed files with 425 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,8 @@ private void syncWithExternalGroupManagement(Collection<AuthorizableConfigBean>
return;
}
for (ExternalGroupManagement externalGroupManagement : externalGroupManagementServices) {
externalGroupManagement.updateGroups(groupConfigBeans);
installLog.addMessage(LOG, "Synchronized " + groupConfigBeans.size() + " groups with external user management " + externalGroupManagement.getLabel());
int numGroupsSynced = externalGroupManagement.updateGroups(groupConfigBeans);
installLog.addMessage(LOG, "Synchronized " + numGroupsSynced + " groups with external user management " + externalGroupManagement.getLabel());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@
* Implementations of this service synchronize (i.e. create/update/delete) groups in an external directory (outside AEM).
*/
public interface ExternalGroupManagement {
void updateGroups(Collection<AuthorizableConfigBean> groupConfigs) throws IOException;
/**
* Updates the groups in the external directory.
* @param groupConfigs the groups to be updated
* @return the effective number of groups updated (may be less than the number of groups in {@code groupConfigs}) if some are considered up to date
* @throws IOException
*/
int updateGroups(Collection<AuthorizableConfigBean> groupConfigs) throws IOException;

/**
*
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

/** General response format for UMAPI requests */
/** General response format for UMAPI action requests */
@JsonIgnoreProperties(ignoreUnknown = true)
public class ActionCommandResponse {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package biz.netcentric.cq.tools.actool.ims.response;

import java.util.List;

import org.apache.http.client.methods.HttpRequestBase;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown = true)
public class GroupResponse {

@JsonProperty("lastPage")
public boolean isLastPage;

@JsonProperty("result")
public String result;

@JsonProperty("groups")
public List<IMSGroup> groups;

@JsonIgnore
public HttpRequestBase associatedRequest;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package biz.netcentric.cq.tools.actool.ims.response;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

/** Represents either a user group or product profile in IMS. */
@JsonIgnoreProperties(ignoreUnknown = true)
public class IMSGroup {

@JsonProperty("type")
public String type;

@JsonProperty("memberCount")
public int memberCount;

@JsonProperty("adminGroupName")
public String adminGroupName;

@JsonProperty("groupName")
public String groupName;

@JsonProperty("groupId")
public long groupId;

@JsonProperty("userGroupName")
public String userGroupName;

@Override
public String toString() {
return "IMSGroup [type=" + type + ", memberCount=" + memberCount + ", adminGroupName=" + adminGroupName + ", groupName=" + groupName
+ ", groupId=" + groupId + ", userGroupName=" + userGroupName + "]";
}

public String getType() {
return type;
}

public int getMemberCount() {
return memberCount;
}

public String getAdminGroupName() {
return adminGroupName;
}

public String getGroupName() {
return groupName;
}

public long getGroupId() {
return groupId;
}

public String getUserGroupName() {
return userGroupName;
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package biz.netcentric.cq.tools.actool.ims.response;

import java.util.List;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown = true)
public class IMSUser {

@JsonProperty("type")
public String type;

@JsonProperty("email")
public String email;

@JsonProperty("status")
public String status;

@JsonProperty("groups")
public List<String> groups;

@JsonProperty("domain")
public String domain;

@JsonProperty("country")
public String country;

@JsonProperty("tags")
public List<String> tags;

@JsonProperty("username")
public String username;

@Override
public String toString() {
return "IMSUser [type=" + type + ", email=" + email + ", status=" + status + ", groups=" + groups + ", domain=" + domain
+ ", country=" + country + ", tags=" + tags + ", username=" + username + "]";
}

public String getType() {
return type;
}

public String getEmail() {
return email;
}

public String getStatus() {
return status;
}

public List<String> getGroups() {
return groups;
}

public String getDomain() {
return domain;
}

public String getCountry() {
return country;
}

public List<String> getTags() {
return tags;
}

public String getUsername() {
return username;
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package biz.netcentric.cq.tools.actool.ims.response;

import java.util.List;

import org.apache.http.client.methods.HttpRequestBase;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown = true)
public class UsersInGroupResponse {

@JsonProperty("lastPage")
public boolean isLastPage;

@JsonProperty("result")
public String result;

@JsonProperty("groupName")
public String groupName;

@JsonProperty("users")
public List<IMSUser> users;

@JsonIgnore
public HttpRequestBase associatedRequest;
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@

import biz.netcentric.cq.tools.actool.configmodel.AuthorizableConfigBean;
import biz.netcentric.cq.tools.actool.ims.IMSUserManagement.Configuration;
import biz.netcentric.cq.tools.actool.ims.response.IMSGroup;
import biz.netcentric.cq.tools.actool.ims.response.IMSUser;

/**
* Example Adobe Developer Console Project: https://developer.adobe.com/console/projects/25605/4566206088345177434/overview (Organization: Netcentric).
Expand All @@ -56,10 +58,11 @@ void setUp() {
properties.put("clientId", getMandatoryEnvironmentVariable("ACTOOL_IMS_IT_CLIENTID"));
properties.put("clientSecret", getMandatoryEnvironmentVariable("ACTOOL_IMS_IT_CLIENTSECRET"));
properties.put("isTestOnly", Boolean.TRUE);
properties.put("socketTimeout", "60000");
}

@Test
void testSimpleGroup() throws IOException {
void testAddSimpleGroup() throws IOException {
Configuration config = Converters.standardConverter().convert(properties).to(Configuration.class);
IMSUserManagement imsUserManagement = new IMSUserManagement(config, new HttpClientBuilderFactory() {
@Override
Expand All @@ -71,22 +74,37 @@ public HttpClientBuilder newBuilder() {
AuthorizableConfigBean group = new AuthorizableConfigBean();
group.setAuthorizableId("testGroup");
group.setDescription("my description");
imsUserManagement.updateGroups(Collections.singleton(group));
assertEquals(1, imsUserManagement.updateGroups(Collections.singleton(group)));

// test without description
AuthorizableConfigBean group2 = new AuthorizableConfigBean();
group2.setAuthorizableId("testGroup");
imsUserManagement.updateGroups(Collections.singleton(group2));
assertEquals(1, imsUserManagement.updateGroups(Collections.singleton(group2)));

// test with empty description
AuthorizableConfigBean group3 = new AuthorizableConfigBean();
group3.setAuthorizableId("testGroup");
group3.setDescription("");
imsUserManagement.updateGroups(Collections.singleton(group3));
assertEquals(1, imsUserManagement.updateGroups(Collections.singleton(group3)));
}

@Test
void testGroupWithProductProfileMembership() throws IOException {
void testAddAlreadyExistingGroupInDifferentialUpdatesMode() throws IOException {
properties.put("isDifferentialUpdates", Boolean.TRUE);
Configuration config = Converters.standardConverter().convert(properties).to(Configuration.class);
IMSUserManagement imsUserManagement = new IMSUserManagement(config, new HttpClientBuilderFactory() {
@Override
public HttpClientBuilder newBuilder() {
return HttpClientBuilder.create();
}
});
AuthorizableConfigBean group = new AuthorizableConfigBean();
group.setAuthorizableId("testGroup"); // this group is already there
assertEquals(0, imsUserManagement.updateGroups(Collections.singleton(group)));
}

@Test
void testAddGroupWithProductProfileMembership() throws IOException {
properties.put("productProfiles", getMandatoryEnvironmentVariable("ACTOOL_IMS_IT_PRODUCTPROFILE"));
Configuration config = Converters.standardConverter().convert(properties).to(Configuration.class);
IMSUserManagement imsUserManagement = new IMSUserManagement(config, new HttpClientBuilderFactory() {
Expand All @@ -102,7 +120,7 @@ public HttpClientBuilder newBuilder() {
}

@Test
void testGroupWithInvalidProductProfileMembership() throws IOException {
void testAddGroupWithInvalidProductProfileMembership() throws IOException {
properties.put("productProfiles", "invalid");
Configuration config = Converters.standardConverter().convert(properties).to(Configuration.class);
IMSUserManagement imsUserManagement = new IMSUserManagement(config, new HttpClientBuilderFactory() {
Expand All @@ -119,7 +137,7 @@ public HttpClientBuilder newBuilder() {
}

@Test
void testGroupWithAdmin() throws IOException {
void testAddGroupWithAdmin() throws IOException {
properties.put("groupAdmins", getMandatoryEnvironmentVariable("ACTOOL_IMS_IT_USERID"));
Configuration config = Converters.standardConverter().convert(properties).to(Configuration.class);
IMSUserManagement imsUserManagement = new IMSUserManagement(config, new HttpClientBuilderFactory() {
Expand All @@ -131,11 +149,11 @@ public HttpClientBuilder newBuilder() {
AuthorizableConfigBean group = new AuthorizableConfigBean();
group.setAuthorizableId("testGroup");
group.setDescription("my description");
imsUserManagement.updateGroups(Collections.singleton(group));
assertEquals(1, imsUserManagement.updateGroups(Collections.singleton(group)));
}

@Test
void test25GroupsWithAdmin() throws IOException {
void testAdd25GroupsWithAdmin() throws IOException {
properties.put("groupAdmins", getMandatoryEnvironmentVariable("ACTOOL_IMS_IT_USERID"));
Configuration config = Converters.standardConverter().convert(properties).to(Configuration.class);
IMSUserManagement imsUserManagement = new IMSUserManagement(config, new HttpClientBuilderFactory() {
Expand All @@ -151,7 +169,39 @@ public HttpClientBuilder newBuilder() {
group.setDescription("my description" + n);
groups.add(group);
}
imsUserManagement.updateGroups(groups);
assertEquals(25, imsUserManagement.updateGroups(groups));
}

@Test
void testGetGroups() throws IOException {
Configuration config = Converters.standardConverter().convert(properties).to(Configuration.class);
IMSUserManagement imsUserManagement = new IMSUserManagement(config, new HttpClientBuilderFactory() {
@Override
public HttpClientBuilder newBuilder() {
return HttpClientBuilder.create();
}
});
String token = imsUserManagement.getOAuthServer2ServerToken();
Map<String, IMSGroup> groups = imsUserManagement.getGroups(token);
for (Map.Entry<String,IMSGroup> imsGroup : groups.entrySet()) {
System.out.println(imsGroup);
}
}

@Test
void testGetUserInGroup() throws IOException {
Configuration config = Converters.standardConverter().convert(properties).to(Configuration.class);
IMSUserManagement imsUserManagement = new IMSUserManagement(config, new HttpClientBuilderFactory() {
@Override
public HttpClientBuilder newBuilder() {
return HttpClientBuilder.create();
}
});
String token = imsUserManagement.getOAuthServer2ServerToken();
Map<String, IMSUser> users = imsUserManagement.getUsersInGroup(token, "AEM Users-3e6e8bd0a05f39bc82d788bb27ac83b4");
for (Map.Entry<String,IMSUser> imsUser : users.entrySet()) {
System.out.println(imsUser);
}
}

private static String getMandatoryEnvironmentVariable(String name) {
Expand Down

0 comments on commit 647d336

Please sign in to comment.