View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.syncope.core.provisioning.java.pushpull;
20  
21  import java.util.Optional;
22  import org.apache.syncope.common.lib.request.AbstractPatchItem;
23  import org.apache.syncope.common.lib.request.AnyCR;
24  import org.apache.syncope.common.lib.request.AnyUR;
25  import org.apache.syncope.common.lib.request.PasswordPatch;
26  import org.apache.syncope.common.lib.request.UserCR;
27  import org.apache.syncope.common.lib.request.UserUR;
28  import org.apache.syncope.common.lib.to.EntityTO;
29  import org.apache.syncope.common.lib.to.ProvisioningReport;
30  import org.apache.syncope.common.lib.to.UserTO;
31  import org.apache.syncope.common.lib.types.CipherAlgorithm;
32  import org.apache.syncope.core.persistence.api.dao.UserDAO;
33  import org.apache.syncope.core.persistence.api.entity.ConnInstance;
34  import org.apache.syncope.core.persistence.api.entity.user.User;
35  import org.apache.syncope.core.provisioning.api.Connector;
36  import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile;
37  import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
38  import org.identityconnectors.framework.common.objects.SyncDelta;
39  import org.quartz.JobExecutionException;
40  import org.slf4j.Logger;
41  import org.slf4j.LoggerFactory;
42  import org.springframework.beans.factory.annotation.Autowired;
43  import org.springframework.transaction.annotation.Transactional;
44  
45  /**
46   * A {@link org.apache.syncope.core.provisioning.api.pushpull.PullActions} implementation which allows the ability to
47   * import passwords from a Database backend, where the passwords are hashed according to the password cipher algorithm
48   * property of the (DB) Connector and HEX-encoded.
49   */
50  public class DBPasswordPullActions implements PullActions {
51  
52      protected static final Logger LOG = LoggerFactory.getLogger(DBPasswordPullActions.class);
53  
54      protected static final String CLEARTEXT = "CLEARTEXT";
55  
56      @Autowired
57      protected UserDAO userDAO;
58  
59      protected String encodedPassword;
60  
61      protected CipherAlgorithm cipher;
62  
63      @Transactional(readOnly = true)
64      @Override
65      public void beforeProvision(
66              final ProvisioningProfile<?, ?> profile,
67              final SyncDelta delta,
68              final AnyCR anyCR) throws JobExecutionException {
69  
70          if (anyCR instanceof UserCR) {
71              String password = ((UserCR) anyCR).getPassword();
72              parseEncodedPassword(password, profile.getConnector());
73          }
74      }
75  
76      @Transactional(readOnly = true)
77      @Override
78      public void beforeUpdate(
79              final ProvisioningProfile<?, ?> profile,
80              final SyncDelta delta,
81              final EntityTO entityTO,
82              final AnyUR anyUR) throws JobExecutionException {
83  
84          if (anyUR instanceof UserUR) {
85              PasswordPatch modPassword = ((UserUR) anyUR).getPassword();
86              parseEncodedPassword(Optional.ofNullable(modPassword)
87                      .map(AbstractPatchItem::getValue).orElse(null), profile.getConnector());
88          }
89      }
90  
91      protected void parseEncodedPassword(final String password, final Connector connector) {
92          if (password != null) {
93              ConnInstance connInstance = connector.getConnInstance();
94  
95              String cipherAlgorithm = getCipherAlgorithm(connInstance);
96              if (!CLEARTEXT.equals(cipherAlgorithm)) {
97                  try {
98                      encodedPassword = password;
99                      cipher = CipherAlgorithm.valueOf(cipherAlgorithm);
100                 } catch (IllegalArgumentException e) {
101                     LOG.error("Cipher algorithm not allowed: {}", cipherAlgorithm, e);
102                     encodedPassword = null;
103                 }
104             }
105         }
106     }
107 
108     protected String getCipherAlgorithm(final ConnInstance connInstance) {
109         return connInstance.getConf().stream().
110                 filter(property -> "cipherAlgorithm".equals(property.getSchema().getName())
111                 && property.getValues() != null && !property.getValues().isEmpty()).findFirst().
112                 map(cipherAlgorithm -> cipherAlgorithm.getValues().get(0).toString()).
113                 orElse(CLEARTEXT);
114     }
115 
116     @Transactional
117     @Override
118     public void after(
119             final ProvisioningProfile<?, ?> profile,
120             final SyncDelta delta,
121             final EntityTO any,
122             final ProvisioningReport result) throws JobExecutionException {
123 
124         if (any instanceof UserTO && encodedPassword != null && cipher != null) {
125             User user = userDAO.find(any.getKey());
126             if (user != null) {
127                 user.setEncodedPassword(encodedPassword.toUpperCase(), cipher);
128             }
129             encodedPassword = null;
130             cipher = null;
131         }
132     }
133 }