Skip to content

Commit

Permalink
Optionally assign IMS group administrators
Browse files Browse the repository at this point in the history
This closes #749
  • Loading branch information
kwin committed Jul 11, 2024
1 parent 63567d0 commit b733886
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 7 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/maven.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ jobs:
ACTOOL_IMS_IT_CLIENTID: ${{ vars.ACTOOL_IMS_IT_CLIENTID }}
ACTOOL_IMS_IT_CLIENTSECRET: ${{ secrets.ACTOOL_IMS_IT_CLIENTSECRET }}
ACTOOL_IMS_IT_PRODUCTPROFILE: ${{ vars.ACTOOL_IMS_IT_PRODUCTPROFILE }}
ACTOOL_IMS_IT_USERID: ${{ vars.ACTOOL_IMS_IT_USERID }}
run: mvn -e -B -V clean verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=Netcentric_accesscontroltool -Dsonar.organization=netcentric -Dsonar.host.url=https://sonarcloud.io -DnvdApiKeyEnvironmentVariable=NVD_API_KEY -Pdependency-check,coverage-report,integration-tests

- name: Build, Analyse and Deploy with Maven
Expand All @@ -68,4 +69,5 @@ jobs:
ACTOOL_IMS_IT_CLIENTID: ${{ vars.ACTOOL_IMS_IT_CLIENTID }}
ACTOOL_IMS_IT_CLIENTSECRET: ${{ secrets.ACTOOL_IMS_IT_CLIENTSECRET }}
ACTOOL_IMS_IT_PRODUCTPROFILE: ${{ vars.ACTOOL_IMS_IT_PRODUCTPROFILE }}
ACTOOL_IMS_IT_USERID: ${{ vars.ACTOOL_IMS_IT_USERID }}
run: mvn -e -B -V clean deploy org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=Netcentric_accesscontroltool -Dsonar.organization=netcentric -Dsonar.host.url=https://sonarcloud.io -DnvdApiKeyEnvironmentVariable=NVD_API_KEY -Pdependency-check,coverage-report,integration-tests
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -70,8 +71,10 @@
import biz.netcentric.cq.tools.actool.externalusermanagement.ExternalGroupManagement;
import biz.netcentric.cq.tools.actool.ims.IMSUserManagement.Configuration;
import biz.netcentric.cq.tools.actool.ims.request.ActionCommand;
import biz.netcentric.cq.tools.actool.ims.request.AddMembershipStep;
import biz.netcentric.cq.tools.actool.ims.request.AddGroupMembers;
import biz.netcentric.cq.tools.actool.ims.request.AddGroupMembership;
import biz.netcentric.cq.tools.actool.ims.request.CreateGroupStep;
import biz.netcentric.cq.tools.actool.ims.request.UserActionCommand;
import biz.netcentric.cq.tools.actool.ims.request.UserGroupActionCommand;
import biz.netcentric.cq.tools.actool.ims.response.AccessToken;
import biz.netcentric.cq.tools.actool.ims.response.ActionCommandResponse;
Expand Down Expand Up @@ -107,6 +110,8 @@ public class IMSUserManagement implements ExternalGroupManagement {
int socketTimeout() default 10000;
@AttributeDefinition(name = "AEM Product Profiles", description = "The given product profile names are automatically added to each synchronized IMS group. The given product profile names must exist for an AEM product!")
String[] productProfiles() default {};
@AttributeDefinition(name = "Group Administrators", description = "The given users are automatically added to each synchronized IMS group as administrator. The given user ids must already exist!")
String[] groupAdmins() default {};
}

public static final Logger LOG = LoggerFactory.getLogger(IMSUserManagement.class);
Expand Down Expand Up @@ -199,18 +204,31 @@ public String getLabel() {
public void updateGroups(Collection<AuthorizableConfigBean> groupConfigs) throws IOException {
List<ActionCommand> actionCommands = new LinkedList<>();
for (AuthorizableConfigBean groupConfig : groupConfigs) {
UserGroupActionCommand actionCommand = new UserGroupActionCommand(groupConfig.getAuthorizableId());
ActionCommand actionCommand = new UserGroupActionCommand(groupConfig.getAuthorizableId());
CreateGroupStep createGroupStep = new CreateGroupStep();
createGroupStep.description = groupConfig.getDescription();
actionCommand.addStep(createGroupStep);
// optionally maintain product profile memberships in the group as well
if (config.productProfiles() != null && config.productProfiles().length > 0) {
AddMembershipStep addMembershipStep = new AddMembershipStep();
addMembershipStep.productProfileIds = new HashSet<>(Arrays.asList(config.productProfiles()));
actionCommand.addStep(addMembershipStep);
AddGroupMembers addMembers = new AddGroupMembers();
addMembers.productProfileIds = new HashSet<>(Arrays.asList(config.productProfiles()));
actionCommand.addStep(addMembers);
}
actionCommands.add(actionCommand);
}
// optionally make users group administrators
if (config.groupAdmins() != null && config.groupAdmins().length > 0) {
Set<String> adminGroupNames = groupConfigs.stream()
.map(AuthorizableConfigBean::getAuthorizableId)
.map(id -> "_admin_" + id) // https://adobe-apiplatform.github.io/umapi-documentation/en/api/ActionsCmds.html#addRemoveAttr
.collect(Collectors.toSet());
for (String groupAdmin : config.groupAdmins()) {
ActionCommand actionCommand = new UserActionCommand(groupAdmin);
AddGroupMembership addGroupMembership = new AddGroupMembership(adminGroupNames);
actionCommand.addStep(addGroupMembership);
actionCommands.add(actionCommand);
}
}
// update in batches of 10 commands
AtomicInteger counter = new AtomicInteger();
final Collection<List<ActionCommand>> actionCommandsBatches = actionCommands.stream().collect(Collectors.groupingBy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;

/** Maintains members of groups, to be used with {@link UserGroupActionCommand}.
* For maintaining group administrators use {@link AddGroupMembership}.
*/
@JsonTypeName("add")
@JsonInclude(Include.NON_EMPTY) // neither empty strings nor null values are allowed for the fields
public class AddMembershipStep implements Step {
public class AddGroupMembers implements Step {

@JsonProperty("user")
public Set<String> userIds;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package biz.netcentric.cq.tools.actool.ims.request;

/*-
* #%L
* Access Control Tool Bundle
* %%
* Copyright (C) 2015 - 2024 Cognizant Netcentric
* %%
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
* #L%
*/

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;

/** Maintains memberships of users in (admin) groups, to be used with {@link UserActionCommand}.
* @see AddGroupMembers
*/
@JsonTypeName("add")
@JsonInclude(Include.NON_EMPTY) // neither empty strings nor null values are allowed for the fields
public class AddGroupMembership implements Step {

public AddGroupMembership(Collection<String> group) {
this.group = new HashSet<>(group);
}

@JsonProperty("group")
public Set<String> group;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package biz.netcentric.cq.tools.actool.ims.request;

/*-
* #%L
* Access Control Tool Bundle
* %%
* Copyright (C) 2015 - 2024 Cognizant Netcentric
* %%
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
* #L%
*/

import com.fasterxml.jackson.annotation.JsonProperty;

public class UserActionCommand extends ActionCommand {

public UserActionCommand(String user) {
this.user = user;
}

@JsonProperty("user")
String user;
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public HttpClientBuilder newBuilder() {

@Test
void testGroupWithInvalidProductProfileMembership() throws IOException {
properties.put("productProfiles", "Invalid name");
properties.put("productProfiles", "invalid");
Configuration config = Converters.standardConverter().convert(properties).to(Configuration.class);
IMSUserManagement imsUserManagement = new IMSUserManagement(config, new HttpClientBuilderFactory() {
@Override
Expand All @@ -116,6 +116,22 @@ public HttpClientBuilder newBuilder() {
assertTrue(t.getMessage().contains("error.plc.not_found"), "Exceptions message is supposed to contain 'error.plc.not_found' but was " + t.getMessage());
}

@Test
void testGroupWithAdmin() 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() {
@Override
public HttpClientBuilder newBuilder() {
return HttpClientBuilder.create();
}
});
AuthorizableConfigBean group = new AuthorizableConfigBean();
group.setAuthorizableId("testGroup");
group.setDescription("my description");
imsUserManagement.updateGroups(Collections.singleton(group));
}

private static String getMandatoryEnvironmentVariable(String name) {
String value = System.getenv(name);
if (value == null) {
Expand Down

0 comments on commit b733886

Please sign in to comment.