Skip to content

Commit

Permalink
Optionally create/update managed groups in Adobe IMS
Browse files Browse the repository at this point in the history
This leverages the UMAPI

This closes #698
  • Loading branch information
kwin committed Apr 25, 2024
1 parent ba3c803 commit daaa506
Show file tree
Hide file tree
Showing 23 changed files with 568 additions and 9 deletions.
13 changes: 13 additions & 0 deletions .github/workflows/maven.yml
Expand Up @@ -35,6 +35,11 @@ jobs:

- name: Build with Maven
if: '!matrix.isMainBuildEnv'
env:
# Necessary for IMS IT
ACTOOL_IMS_IT_ORGANIZATIONID: ${{ vars.ACTOOL_IMS_IT_ORGANIZATIONID }}
ACTOOL_IMS_IT_CLIENTID: ${{ vars.ACTOOL_IMS_IT_CLIENTID }}
ACTOOL_IMS_IT_CLIENTSECRET: ${{ secrets.ACTOOL_IMS_IT_CLIENTSECRET }}
run: mvn -e -B -V -Pintegration-tests clean verify
- name: Build and Analyse with Maven
if: github.ref != 'refs/heads/develop' && matrix.isMainBuildEnv
Expand All @@ -45,6 +50,10 @@ jobs:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
# Needed to get some information about the pull request, if any
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Necessary for IMS IT
ACTOOL_IMS_IT_ORGANIZATIONID: ${{ vars.ACTOOL_IMS_IT_ORGANIZATIONID }}
ACTOOL_IMS_IT_CLIENTID: ${{ vars.ACTOOL_IMS_IT_CLIENTID }}
ACTOOL_IMS_IT_CLIENTSECRET: ${{ secrets.ACTOOL_IMS_IT_CLIENTSECRET }}
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 @@ -58,4 +67,8 @@ jobs:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
# Needed to get some information about the pull request, if any
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Necessary for IMS IT
ACTOOL_IMS_IT_ORGANIZATIONID: ${{ vars.ACTOOL_IMS_IT_ORGANIZATIONID }}
ACTOOL_IMS_IT_CLIENTID: ${{ vars.ACTOOL_IMS_IT_CLIENTID }}
ACTOOL_IMS_IT_CLIENTSECRET: ${{ secrets.ACTOOL_IMS_IT_CLIENTSECRET }}
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
2 changes: 2 additions & 0 deletions accesscontroltool-bundle/bnd.bnd
Expand Up @@ -11,6 +11,8 @@ Import-Package: \
com.adobe.granite.crypto;resolution:=optional,\
com.adobe.granite.keystore;resolution:=optional,\
com.adobe.granite.jmx.annotation;resolution:=optional,\
com.fasterxml.jackson.databind;resolution:=optional,\
org.apache.http.*;resolution:=optional,\
org.bouncycastle.*;resolution:=optional,\
org.apache.sling.commons.scheduler.*;resolution:=optional,\
org.apache.jackrabbit.oak.spi.security.principal;version="[1.5.0,3)",\
Expand Down
18 changes: 17 additions & 1 deletion accesscontroltool-bundle/pom.xml
Expand Up @@ -150,6 +150,12 @@
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.util.converter</artifactId>
<version>1.0.9</version>
<scope>test</scope>
</dependency>
<!-- IT dependencies -->
<dependency>
<groupId>org.apache.jackrabbit</groupId>
Expand Down Expand Up @@ -183,7 +189,17 @@
<version>3.6.8</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient-osgi</artifactId>
<version>4.5.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<scope>test</scope>
</dependency>
<!-- END: Test Dependencies -->

<!-- use the uber-jar always as last dependency because a lot of classes are provided also by other artifacts
Expand Down
Expand Up @@ -18,6 +18,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
Expand Down Expand Up @@ -65,6 +66,7 @@
import biz.netcentric.cq.tools.actool.configmodel.pkcs.Key;
import biz.netcentric.cq.tools.actool.configmodel.pkcs.RandomPassword;
import biz.netcentric.cq.tools.actool.crypto.DecryptionService;
import biz.netcentric.cq.tools.actool.externalusermanagement.ExternalGroupManagement;
import biz.netcentric.cq.tools.actool.helper.AcHelper;
import biz.netcentric.cq.tools.actool.helper.AccessControlUtils;
import biz.netcentric.cq.tools.actool.helper.Constants;
Expand Down Expand Up @@ -102,7 +104,10 @@ public class AuthorizableInstallerServiceImpl implements

@Reference(policyOption = ReferencePolicyOption.GREEDY)
SlingRepository repository;


@Reference(policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MULTIPLE)
List<ExternalGroupManagement> externalGroupManagementServices;

@Override
public void installAuthorizables(
AcConfiguration acConfiguration,
Expand All @@ -113,16 +118,32 @@ public void installAuthorizables(
AuthInstallerUserManager userManager = new AuthInstallerUserManagerPrefetchingImpl(AccessControlUtils.getUserManagerAutoSaveDisabled(session), session.getValueFactory(), installLog);

Set<String> authorizablesFromConfigurations = authorizablesConfigBeans.getAuthorizableIds();
Collection<AuthorizableConfigBean> groupsToSyncWithExternalUserMgmt = new LinkedList<>();
for (AuthorizableConfigBean authorizableConfigBean : authorizablesConfigBeans) {

installAuthorizableConfigurationBean(session, userManager, acConfiguration,
authorizableConfigBean, installLog, authorizablesFromConfigurations);

if (authorizableConfigBean.isSyncWithExternalUserManagement() && authorizableConfigBean.isGroup() && !externalGroupManagementServices.isEmpty()) {
groupsToSyncWithExternalUserMgmt.add(authorizableConfigBean);
}
}

installLog.addMessage(LOG, "Created "+installLog.getCountAuthorizablesCreated() + " authorizables (moved "+installLog.getCountAuthorizablesMoved() + " authorizables)");
syncWithExternalGroupManagement(groupsToSyncWithExternalUserMgmt, installLog);

}

private void syncWithExternalGroupManagement(Collection<AuthorizableConfigBean> groupConfigBeans, InstallationLogger installLog) throws IOException {
if (groupConfigBeans.isEmpty()) {
return;
}
for (ExternalGroupManagement externalGroupManagement : externalGroupManagementServices) {
externalGroupManagement.updateGroups(groupConfigBeans);
installLog.addMessage(LOG, "Synchronized " + groupConfigBeans.size() + " groups with external user management " + externalGroupManagement.getLabel());
}
}

private void installAuthorizableConfigurationBean(final Session session,
AuthInstallerUserManager userManager,
AcConfiguration acConfiguration,
Expand Down
Expand Up @@ -34,7 +34,7 @@
import biz.netcentric.cq.tools.actool.helper.Constants;
import biz.netcentric.cq.tools.actool.history.InstallationLogger;

/** SCR component to create external groups (as configured using "externalId"). Only available if package
/** SCR component to create groups in AEM which are linked to external directory entries (as configured using {@code externalId}). Only available if package
* o.a.j.oak.spi.security.authentication.external.basic (optional OSGi import) is available (this is the case starting from AEM 6.1+SP1, the
* functionality is crucial since the change in AEM 6.2 + oak 1.4.7 (see #140).
*
Expand Down
Expand Up @@ -19,6 +19,7 @@
import biz.netcentric.cq.tools.actool.configmodel.pkcs.Key;
import biz.netcentric.cq.tools.actool.dumpservice.AcDumpElement;
import biz.netcentric.cq.tools.actool.dumpservice.AcDumpElementVisitor;
import biz.netcentric.cq.tools.actool.externalusermanagement.ExternalGroupManagement;

public class AuthorizableConfigBean implements AcDumpElement {

Expand Down Expand Up @@ -59,6 +60,11 @@ public class AuthorizableConfigBean implements AcDumpElement {
private Map<String, Key> keys;
private List<String> impersonationAllowedFor;

/**
* if {@true} synchronized with external user managed. Synchronization happens via all active services of type {@link ExternalGroupManagement}
*/
private boolean syncWithExternalUserManagement;

public String getAuthorizableId() {
return authorizableId;
}
Expand Down Expand Up @@ -320,8 +326,9 @@ public String toString() {
sb.append("isMemberOf: " + getIsMemberOfString() + "\n");
sb.append("members: " + getMembersString() + "\n");
sb.append("appendToKeyStore: " + isAppendToKeyStore() + "\n");
sb.append("keys:" + getKeys());
sb.append("impersonationAllowedFor:" + getImpersonationAllowedFor());
sb.append("keys:" + getKeys() + "\n");
sb.append("impersonationAllowedFor:" + getImpersonationAllowedFor() + "\n");
sb.append("syncWithExternalUserManagement: " + isSyncWithExternalUserManagement());
return sb.toString();
}

Expand All @@ -341,4 +348,12 @@ public void accept(final AcDumpElementVisitor acDumpElementVisitor) {
acDumpElementVisitor.visit(this);
}

public boolean isSyncWithExternalUserManagement() {
return syncWithExternalUserManagement;
}

public void setSyncWithExternalUserManagement(boolean syncWithExternalUserManagement) {
this.syncWithExternalUserManagement = syncWithExternalUserManagement;
}

}
Expand Up @@ -79,6 +79,7 @@ public class YamlConfigReader implements ConfigReader {
private static final String GROUP_CONFIG_PROPERTY_UNMANAGED_EXTERNAL_MEMBERS_REGEX = "unmanagedExternalMembersRegex";

private static final String GROUP_CONFIG_IS_VIRTUAL = "virtual";
private static final String GROUP_CONFIG_SYNC_WITH_EXTERNAL_USERMANAGEMENT = "syncWithExternalUserManagement";

private static final String USER_CONFIG_PROPERTY_IS_SYSTEM_USER = "isSystemUser";

Expand Down Expand Up @@ -413,6 +414,9 @@ protected void setupAuthorizableBean(
authorizableConfigBean.setVirtual(Boolean.valueOf(getMapValueAsString(currentPrincipalDataMap,
GROUP_CONFIG_IS_VIRTUAL)));

authorizableConfigBean.setSyncWithExternalUserManagement(Boolean.valueOf(getMapValueAsString(currentPrincipalDataMap,
GROUP_CONFIG_SYNC_WITH_EXTERNAL_USERMANAGEMENT)));

authorizableConfigBean.setIsGroup(isGroupSection);
authorizableConfigBean.setIsSystemUser(Boolean.valueOf(getMapValueAsString(currentPrincipalDataMap,
USER_CONFIG_PROPERTY_IS_SYSTEM_USER)));
Expand Down
@@ -0,0 +1,19 @@
package biz.netcentric.cq.tools.actool.externalusermanagement;

import java.io.IOException;
import java.util.Collection;

import biz.netcentric.cq.tools.actool.configmodel.AuthorizableConfigBean;

/**
* 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;

/**
*
* @return a label for the external group management tool (e.g. IMS)
*/
String getLabel();
}

0 comments on commit daaa506

Please sign in to comment.