1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.core.provisioning.java.propagation;
20
21 import java.util.Base64;
22 import java.util.Optional;
23 import java.util.Set;
24 import javax.xml.bind.DatatypeConverter;
25 import org.apache.syncope.common.lib.types.AnyTypeKind;
26 import org.apache.syncope.common.lib.types.CipherAlgorithm;
27 import org.apache.syncope.core.persistence.api.dao.UserDAO;
28 import org.apache.syncope.core.persistence.api.entity.ConnInstance;
29 import org.apache.syncope.core.persistence.api.entity.user.User;
30 import org.apache.syncope.core.provisioning.api.propagation.PropagationActions;
31 import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
32 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
33 import org.apache.syncope.core.spring.implementation.InstanceScope;
34 import org.apache.syncope.core.spring.implementation.SyncopeImplementation;
35 import org.identityconnectors.common.security.GuardedString;
36 import org.identityconnectors.framework.common.objects.Attribute;
37 import org.identityconnectors.framework.common.objects.AttributeBuilder;
38 import org.identityconnectors.framework.common.objects.AttributeUtil;
39 import org.identityconnectors.framework.common.objects.OperationalAttributes;
40 import org.springframework.beans.factory.annotation.Autowired;
41 import org.springframework.transaction.annotation.Transactional;
42 import org.springframework.util.CollectionUtils;
43
44
45
46
47
48
49 @SyncopeImplementation(scope = InstanceScope.PER_CONTEXT)
50 public class LDAPPasswordPropagationActions implements PropagationActions {
51
52 protected static final String CLEARTEXT = "CLEARTEXT";
53
54 @Autowired
55 protected UserDAO userDAO;
56
57 @Transactional(readOnly = true)
58 @Override
59 public void before(final PropagationTaskInfo taskInfo) {
60 if (AnyTypeKind.USER == taskInfo.getAnyTypeKind()) {
61 User user = userDAO.find(taskInfo.getEntityKey());
62 if (user == null || user.getPassword() == null) {
63 return;
64 }
65
66 Set<Attribute> attrs = taskInfo.getPropagationData().getAttributes();
67
68 String cipherAlgorithm = getCipherAlgorithm(taskInfo.getResource().getConnector());
69 Optional.ofNullable(AttributeUtil.find(PropagationManager.MANDATORY_MISSING_ATTR_NAME, attrs)).
70 filter(missing -> !CollectionUtils.isEmpty(missing.getValue())
71 && OperationalAttributes.PASSWORD_NAME.equals(missing.getValue().get(0))
72 && cipherAlgorithmMatches(cipherAlgorithm, user.getCipherAlgorithm())).
73 ifPresent(missing -> {
74 attrs.remove(missing);
75
76 byte[] decodedPassword = DatatypeConverter.parseHexBinary(user.getPassword().toLowerCase());
77 String base64EncodedPassword = Base64.getEncoder().encodeToString(decodedPassword);
78
79 String cipherPlusPassword = '{' + cipherAlgorithm + '}' + base64EncodedPassword;
80
81 attrs.add(AttributeBuilder.buildPassword(new GuardedString(cipherPlusPassword.toCharArray())));
82 });
83 }
84 }
85
86 protected String getCipherAlgorithm(final ConnInstance connInstance) {
87 return connInstance.getConf().stream().
88 filter(property -> "passwordHashAlgorithm".equals(property.getSchema().getName())
89 && !property.getValues().isEmpty()).findFirst().
90 map(cipherAlgorithm -> cipherAlgorithm.getValues().get(0).toString()).
91 orElse(CLEARTEXT);
92 }
93
94 protected boolean cipherAlgorithmMatches(final String connectorAlgo, final CipherAlgorithm userAlgo) {
95 if (userAlgo == null) {
96 return false;
97 }
98
99 if (connectorAlgo.equals(userAlgo.name())) {
100 return true;
101 }
102
103
104 return ("SHA".equals(connectorAlgo) && userAlgo.name().startsWith("SHA"))
105 || ("SSHA".equals(connectorAlgo) && userAlgo.name().startsWith("SSHA"));
106 }
107 }