You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2021/07/13 15:16:42 UTC

[syncope] branch 2_1_X updated: LDAP password pull and propagation improvements

This is an automated email from the ASF dual-hosted git repository.

ilgrosso pushed a commit to branch 2_1_X
in repository https://gitbox.apache.org/repos/asf/syncope.git


The following commit(s) were added to refs/heads/2_1_X by this push:
     new 874fd6e  LDAP password pull and propagation improvements
874fd6e is described below

commit 874fd6e770946e6fdd2dfab0714b1f30ebecb1e0
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Tue Jul 13 15:37:25 2021 +0200

    LDAP password pull and propagation improvements
---
 .../LDAPPasswordPropagationActions.java            | 29 +++----
 .../java/pushpull/LDAPPasswordPullActions.java     | 71 ++++++++---------
 .../java/pushpull/LDAPPasswordPullActionsTest.java | 90 ++++++++--------------
 3 files changed, 73 insertions(+), 117 deletions(-)

diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPPasswordPropagationActions.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPPasswordPropagationActions.java
index 8d79c7c..21e5e0c 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPPasswordPropagationActions.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPPasswordPropagationActions.java
@@ -20,12 +20,10 @@ package org.apache.syncope.core.provisioning.java.propagation;
 
 import java.util.Base64;
 import java.util.HashSet;
-import java.util.Optional;
 import java.util.Set;
 import javax.xml.bind.DatatypeConverter;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.CipherAlgorithm;
-import org.apache.syncope.common.lib.types.ConnConfProperty;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.entity.ConnInstance;
 import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
@@ -89,33 +87,26 @@ public class LDAPPasswordPropagationActions implements PropagationActions {
         }
     }
 
-    private String getCipherAlgorithm(final ConnInstance connInstance) {
-        Optional<ConnConfProperty> cipherAlgorithm = connInstance.getConf().stream().
+    private static String getCipherAlgorithm(final ConnInstance connInstance) {
+        return connInstance.getConf().stream().
                 filter(property -> "passwordHashAlgorithm".equals(property.getSchema().getName())
-                && property.getValues() != null && !property.getValues().isEmpty()).findFirst();
-
-        return cipherAlgorithm.isPresent()
-                ? (String) cipherAlgorithm.get().getValues().get(0)
-                : CLEARTEXT;
+                && property.getValues() != null && !property.getValues().isEmpty()).findFirst().
+                map(cipherAlgorithm -> (String) cipherAlgorithm.getValues().get(0)).
+                orElse(CLEARTEXT);
     }
 
-    private boolean cipherAlgorithmMatches(final String connectorAlgorithm, final CipherAlgorithm userAlgorithm) {
-        if (userAlgorithm == null) {
+    private static boolean cipherAlgorithmMatches(final String connectorAlgo, final CipherAlgorithm userAlgo) {
+        if (userAlgo == null) {
             return false;
         }
 
-        if (connectorAlgorithm.equals(userAlgorithm.name())) {
+        if (connectorAlgo.equals(userAlgo.name())) {
             return true;
         }
 
         // Special check for "SHA" and "SSHA" (user pulled from LDAP)
-        if (("SHA".equals(connectorAlgorithm) && userAlgorithm.name().startsWith("SHA"))
-                || ("SSHA".equals(connectorAlgorithm) && userAlgorithm.name().startsWith("SSHA"))) {
-
-            return true;
-        }
-
-        return false;
+        return ("SHA".equals(connectorAlgo) && userAlgo.name().startsWith("SHA"))
+                || ("SSHA".equals(connectorAlgo) && userAlgo.name().startsWith("SSHA"));
     }
 
 }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActions.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActions.java
index a3d6818..85236f5 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActions.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActions.java
@@ -19,10 +19,11 @@
 package org.apache.syncope.core.provisioning.java.pushpull;
 
 import java.util.Base64;
+import java.util.Collections;
+import java.util.Optional;
+import java.util.Set;
 import javax.xml.bind.DatatypeConverter;
-import org.apache.syncope.common.lib.patch.AnyPatch;
-import org.apache.syncope.common.lib.patch.PasswordPatch;
-import org.apache.syncope.common.lib.patch.UserPatch;
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.to.EntityTO;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.CipherAlgorithm;
@@ -30,7 +31,13 @@ import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile;
 import org.apache.syncope.common.lib.to.ProvisioningReport;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.entity.resource.Provision;
 import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
+import org.identityconnectors.common.security.GuardedString;
+import org.identityconnectors.common.security.SecurityUtil;
+import org.identityconnectors.framework.common.objects.AttributeUtil;
+import org.identityconnectors.framework.common.objects.OperationalAttributes;
 import org.identityconnectors.framework.common.objects.SyncDelta;
 import org.quartz.JobExecutionException;
 import org.slf4j.Logger;
@@ -49,49 +56,28 @@ public class LDAPPasswordPullActions implements PullActions {
     @Autowired
     protected UserDAO userDAO;
 
-    protected String encodedPassword;
-
-    protected CipherAlgorithm cipher;
-
-    @Transactional(readOnly = true)
-    @Override
-    public void beforeProvision(
-            final ProvisioningProfile<?, ?> profile,
-            final SyncDelta delta,
-            final EntityTO entity) throws JobExecutionException {
-
-        if (entity instanceof UserTO) {
-            String password = ((UserTO) entity).getPassword();
-            parseEncodedPassword(password);
-        }
-    }
-
-    @Transactional(readOnly = true)
     @Override
-    public <M extends AnyPatch> void beforeUpdate(
-            final ProvisioningProfile<?, ?> profile,
-            final SyncDelta delta,
-            final EntityTO entityTO,
-            final M anyPatch) throws JobExecutionException {
-
-        if (anyPatch instanceof UserPatch) {
-            PasswordPatch modPassword = ((UserPatch) anyPatch).getPassword();
-            parseEncodedPassword(modPassword == null ? null : modPassword.getValue());
+    public Set<String> moreAttrsToGet(final ProvisioningProfile<?, ?> profile, final Provision provision) {
+        if (AnyTypeKind.USER == provision.getAnyType().getKind()) {
+            return Collections.singleton(OperationalAttributes.PASSWORD_NAME);
         }
+        return PullActions.super.moreAttrsToGet(profile, provision);
     }
 
-    protected void parseEncodedPassword(final String password) {
+    private static Optional<Pair<String, CipherAlgorithm>> parseEncodedPassword(final String password) {
         if (password != null && password.startsWith("{")) {
+            String digest = Optional.ofNullable(
+                    password.substring(1, password.indexOf('}'))).map(String::toUpperCase).
+                    orElse(null);
             int closingBracketIndex = password.indexOf('}');
-            String digest = password.substring(1, closingBracketIndex).toUpperCase();
             try {
-                encodedPassword = password.substring(closingBracketIndex + 1);
-                cipher = CipherAlgorithm.valueOf(digest);
+                return Optional.of(
+                        Pair.of(password.substring(closingBracketIndex + 1), CipherAlgorithm.valueOf(digest)));
             } catch (IllegalArgumentException e) {
                 LOG.error("Cipher algorithm not allowed: {}", digest, e);
-                encodedPassword = null;
             }
         }
+        return Optional.empty();
     }
 
     @Transactional
@@ -102,16 +88,19 @@ public class LDAPPasswordPullActions implements PullActions {
             final EntityTO entity,
             final ProvisioningReport result) throws JobExecutionException {
 
-        if (entity instanceof UserTO && encodedPassword != null && cipher != null) {
+        if (entity instanceof UserTO) {
             User user = userDAO.find(entity.getKey());
             if (user != null) {
-                byte[] encodedPasswordBytes = Base64.getDecoder().decode(encodedPassword.getBytes());
-                String encodedHexStr = DatatypeConverter.printHexBinary(encodedPasswordBytes).toUpperCase();
+                GuardedString passwordAttr = AttributeUtil.getPasswordValue(delta.getObject().getAttributes());
+                if (passwordAttr != null) {
+                    parseEncodedPassword(SecurityUtil.decrypt(passwordAttr)).ifPresent(encoded -> {
+                        byte[] encodedPasswordBytes = Base64.getDecoder().decode(encoded.getLeft().getBytes());
+                        String encodedHexStr = DatatypeConverter.printHexBinary(encodedPasswordBytes).toUpperCase();
 
-                user.setEncodedPassword(encodedHexStr, cipher);
+                        user.setEncodedPassword(encodedHexStr, encoded.getRight());
+                    });
+                }
             }
-            encodedPassword = null;
-            cipher = null;
         }
     }
 }
diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActionsTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActionsTest.java
index 5754603..02ae4bb 100644
--- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActionsTest.java
+++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActionsTest.java
@@ -18,15 +18,15 @@
  */
 package org.apache.syncope.core.provisioning.java.pushpull;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.verify;
 
-import org.apache.syncope.common.lib.patch.PasswordPatch;
-import org.apache.syncope.common.lib.patch.UserPatch;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
 import org.apache.syncope.common.lib.to.ProvisioningReport;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.CipherAlgorithm;
@@ -34,20 +34,25 @@ import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile;
 import org.apache.syncope.core.provisioning.java.AbstractTest;
+import org.identityconnectors.common.security.GuardedString;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.identityconnectors.framework.common.objects.AttributeBuilder;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.identityconnectors.framework.common.objects.Name;
+import org.identityconnectors.framework.common.objects.ObjectClass;
 import org.identityconnectors.framework.common.objects.SyncDelta;
-import org.junit.jupiter.api.BeforeEach;
+import org.identityconnectors.framework.common.objects.SyncDeltaBuilder;
+import org.identityconnectors.framework.common.objects.SyncDeltaType;
+import org.identityconnectors.framework.common.objects.SyncToken;
+import org.identityconnectors.framework.common.objects.Uid;
 import org.junit.jupiter.api.Test;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.quartz.JobExecutionException;
-import org.springframework.test.util.ReflectionTestUtils;
 
 public class LDAPPasswordPullActionsTest extends AbstractTest {
 
     @Mock
-    private SyncDelta syncDelta;
-
-    @Mock
     private ProvisioningProfile<?, ?> profile;
 
     @Mock
@@ -57,66 +62,37 @@ public class LDAPPasswordPullActionsTest extends AbstractTest {
     private ProvisioningReport result;
 
     @InjectMocks
-    private LDAPPasswordPullActions ldapPasswordPullActions;
-
-    private UserTO userTO;
-
-    private UserPatch userPatch;
-
-    private String encodedPassword;
-
-    private CipherAlgorithm cipher;
-
-    @BeforeEach
-    public void initTest() {
-        userTO = new UserTO();
-        encodedPassword = "s3cureP4ssw0rd";
-        cipher = CipherAlgorithm.SHA512;
-
-        ReflectionTestUtils.setField(ldapPasswordPullActions, "encodedPassword", encodedPassword);
-        ReflectionTestUtils.setField(ldapPasswordPullActions, "cipher", cipher);
-    }
-
-    @Test
-    public void beforeProvision() throws JobExecutionException {
-        String digest = "SHA256";
-        String password = "t3stPassw0rd";
-        userTO.setPassword(String.format("{%s}%s", digest, password));
-
-        ldapPasswordPullActions.beforeProvision(profile, syncDelta, userTO);
-
-        assertEquals(CipherAlgorithm.valueOf(digest), ReflectionTestUtils.getField(ldapPasswordPullActions, "cipher"));
-        assertEquals(password, ReflectionTestUtils.getField(ldapPasswordPullActions, "encodedPassword"));
-    }
-
-    @Test
-    public void beforeUpdate() throws JobExecutionException {
-        userPatch = new UserPatch();
-        userPatch.setPassword(new PasswordPatch.Builder().value("{MD5}an0therTestP4ss").build());
-
-        ldapPasswordPullActions.beforeUpdate(profile, syncDelta, userTO, userPatch);
-
-        assertNull(ReflectionTestUtils.getField(ldapPasswordPullActions, "encodedPassword"));
-    }
+    private LDAPPasswordPullActions actions;
 
     @Test
     public void afterWithNullUser() throws JobExecutionException {
+        UserTO userTO = new UserTO();
+        userTO.setKey(UUID.randomUUID().toString());
         when(userDAO.find(userTO.getKey())).thenReturn(null);
 
-        ldapPasswordPullActions.after(profile, syncDelta, userTO, result);
-
-        assertNull(ReflectionTestUtils.getField(ldapPasswordPullActions, "encodedPassword"));
-        assertNull(ReflectionTestUtils.getField(ldapPasswordPullActions, "cipher"));
+        assertDoesNotThrow(() -> actions.after(profile, null, userTO, result));
     }
 
     @Test
     public void after(@Mock User user) throws JobExecutionException {
+        UserTO userTO = new UserTO();
+        userTO.setKey(UUID.randomUUID().toString());
         when(userDAO.find(userTO.getKey())).thenReturn(user);
 
-        ldapPasswordPullActions.after(profile, syncDelta, userTO, result);
+        Set<Attribute> attributes = new HashSet<>();
+        attributes.add(new Uid(UUID.randomUUID().toString()));
+        attributes.add(new Name(UUID.randomUUID().toString()));
+        attributes.add(AttributeBuilder.buildPassword(
+                new GuardedString("{SSHA}4AwQq1UVDwubSXmR4pnmLsoVR6U2Z7R55kwxRA==".toCharArray())));
+        SyncDelta delta = new SyncDeltaBuilder().
+                setToken(new SyncToken("sample-token")).
+                setDeltaType(SyncDeltaType.CREATE_OR_UPDATE).
+                setUid(new Uid(UUID.randomUUID().toString())).
+                setObject(new ConnectorObject(ObjectClass.ACCOUNT, attributes)).
+                build();
+
+        actions.after(profile, delta, userTO, result);
 
         verify(user).setEncodedPassword(anyString(), any(CipherAlgorithm.class));
-        assertNull(ReflectionTestUtils.getField(ldapPasswordPullActions, "encodedPassword"));
-        assertNull(ReflectionTestUtils.getField(ldapPasswordPullActions, "cipher"));
     }
 }