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;
20  
21  import static org.junit.jupiter.api.Assertions.assertEquals;
22  import static org.junit.jupiter.api.Assertions.assertNotEquals;
23  import static org.junit.jupiter.api.Assertions.assertNotNull;
24  import static org.junit.jupiter.api.Assertions.assertNull;
25  import static org.junit.jupiter.api.Assertions.assertTrue;
26  
27  import java.util.List;
28  import java.util.Set;
29  import org.apache.commons.lang3.tuple.Pair;
30  import org.apache.syncope.common.lib.to.Provision;
31  import org.apache.syncope.common.lib.types.AnyTypeKind;
32  import org.apache.syncope.common.lib.types.CipherAlgorithm;
33  import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager;
34  import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO;
35  import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
36  import org.apache.syncope.core.persistence.api.dao.GroupDAO;
37  import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
38  import org.apache.syncope.core.persistence.api.dao.RealmDAO;
39  import org.apache.syncope.core.persistence.api.dao.UserDAO;
40  import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
41  import org.apache.syncope.core.persistence.api.entity.EntityFactory;
42  import org.apache.syncope.core.persistence.api.entity.ExternalResource;
43  import org.apache.syncope.core.persistence.api.entity.group.Group;
44  import org.apache.syncope.core.persistence.api.entity.user.LinkedAccount;
45  import org.apache.syncope.core.persistence.api.entity.user.UDynGroupMembership;
46  import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr;
47  import org.apache.syncope.core.persistence.api.entity.user.User;
48  import org.apache.syncope.core.provisioning.api.MappingManager;
49  import org.identityconnectors.common.security.SecurityUtil;
50  import org.identityconnectors.framework.common.objects.Attribute;
51  import org.identityconnectors.framework.common.objects.AttributeUtil;
52  import org.identityconnectors.framework.common.objects.OperationalAttributes;
53  import org.junit.jupiter.api.Test;
54  import org.springframework.beans.factory.annotation.Autowired;
55  import org.springframework.test.util.ReflectionTestUtils;
56  import org.springframework.transaction.annotation.Transactional;
57  
58  @Transactional("Master")
59  public class DefaultMappingManagerTest extends AbstractTest {
60  
61      @Autowired
62      private MappingManager mappingManager;
63  
64      @Autowired
65      private UserDAO userDAO;
66  
67      @Autowired
68      private ExternalResourceDAO resourceDAO;
69  
70      @Autowired
71      private RealmDAO realmDAO;
72  
73      @Autowired
74      private GroupDAO groupDAO;
75  
76      @Autowired
77      private AnyTypeClassDAO anyTypeClassDAO;
78  
79      @Autowired
80      private PlainSchemaDAO plainSchemaDAO;
81  
82      @Autowired
83      private AnyUtilsFactory anyUtilsFactory;
84  
85      @Autowired
86      private EntityFactory entityFactory;
87  
88      @Autowired
89      private PlainAttrValidationManager validator;
90  
91      @Test
92      public void prepareAttrsForUser() {
93          User bellini = userDAO.findByUsername("bellini");
94          ExternalResource ldap = resourceDAO.find("resource-ldap");
95          Provision provision = ldap.getProvisionByAnyType(AnyTypeKind.USER.name()).get();
96  
97          assertNotEquals(CipherAlgorithm.AES, bellini.getCipherAlgorithm());
98  
99          // 1. with clear-text password
100         Pair<String, Set<Attribute>> attrs = mappingManager.prepareAttrsFromAny(
101                 bellini,
102                 "Password123",
103                 true,
104                 Boolean.TRUE,
105                 ldap,
106                 provision);
107         assertEquals("bellini", attrs.getLeft());
108         assertEquals(
109                 "uid=bellini,ou=people,o=isp",
110                 AttributeUtil.getNameFromAttributes(attrs.getRight()).getNameValue());
111         assertEquals("Password123", SecurityUtil.decrypt(AttributeUtil.getPasswordValue(attrs.getRight())));
112 
113         // 2. with changePwd == false
114         attrs = mappingManager.prepareAttrsFromAny(
115                 bellini,
116                 "Password123",
117                 false,
118                 Boolean.TRUE,
119                 ldap,
120                 provision);
121         assertNull(AttributeUtil.getPasswordValue(attrs.getRight()));
122 
123         // 3. with no clear-text password but random password generation enabled
124         // after SYNCOPE-1751 no password is going to be generated by DefaultMappingManager,
125         // but by AbstractPropagationTaskExecutor
126         ldap = resourceDAO.save(ldap);
127         entityManager().flush();
128 
129         attrs = mappingManager.prepareAttrsFromAny(
130                 bellini,
131                 null,
132                 true,
133                 Boolean.TRUE,
134                 ldap,
135                 provision);
136         assertNull(AttributeUtil.getPasswordValue(attrs.getRight()));
137 
138         // 4. with no clear-text password and random password generation disabled
139         resourceDAO.save(ldap);
140         entityManager().flush();
141 
142         attrs = mappingManager.prepareAttrsFromAny(
143                 bellini,
144                 null,
145                 true,
146                 Boolean.TRUE,
147                 ldap,
148                 provision);
149         assertNull(AttributeUtil.getPasswordValue(attrs.getRight()));
150 
151         // 5. with no clear-text password, random password generation disabled but AES
152         ReflectionTestUtils.setField(bellini, "cipherAlgorithm", CipherAlgorithm.AES);
153         bellini.setPassword("newPassword123");
154         userDAO.save(bellini);
155         entityManager().flush();
156 
157         assertEquals(CipherAlgorithm.AES, bellini.getCipherAlgorithm());
158 
159         attrs = mappingManager.prepareAttrsFromAny(
160                 bellini,
161                 null,
162                 true,
163                 Boolean.TRUE,
164                 ldap,
165                 provision);
166         assertEquals("newPassword123", SecurityUtil.decrypt(AttributeUtil.getPasswordValue(attrs.getRight())));
167     }
168 
169     @Test
170     public void prepareAttrsForLinkedAccount() {
171         User vivaldi = userDAO.findByUsername("vivaldi");
172         ExternalResource ldap = resourceDAO.find("resource-ldap");
173         Provision provision = ldap.getProvisionByAnyType(AnyTypeKind.USER.name()).get();
174 
175         LinkedAccount account = entityFactory.newEntity(LinkedAccount.class);
176         account.setConnObjectKeyValue("admin");
177         account.setResource(ldap);
178         account.setOwner(vivaldi);
179         account.setSuspended(Boolean.FALSE);
180         account.setCipherAlgorithm(CipherAlgorithm.AES);
181         account.setPassword("Password321");
182         vivaldi.add(account);
183 
184         vivaldi = userDAO.save(vivaldi);
185         entityManager().flush();
186 
187         // 1. with account password and clear-text default password
188         Set<Attribute> attrs = mappingManager.prepareAttrsFromLinkedAccount(
189                 vivaldi,
190                 account,
191                 "Password123",
192                 true,
193                 provision);
194         assertEquals("admin", AttributeUtil.getStringValue(AttributeUtil.find("cn", attrs)));
195         assertEquals("Password321", SecurityUtil.decrypt(AttributeUtil.getPasswordValue(attrs)));
196         assertTrue(AttributeUtil.getBooleanValue(AttributeUtil.find(OperationalAttributes.ENABLE_NAME, attrs)));
197 
198         // 2. without account password and clear-text default password
199         account.setEncodedPassword(null, null);
200 
201         attrs = mappingManager.prepareAttrsFromLinkedAccount(
202                 vivaldi,
203                 account,
204                 "Password123",
205                 true,
206                 provision);
207         assertEquals("Password123", SecurityUtil.decrypt(AttributeUtil.getPasswordValue(attrs)));
208 
209         // 3. with changePwd == false
210         attrs = mappingManager.prepareAttrsFromLinkedAccount(
211                 vivaldi,
212                 account,
213                 "Password123",
214                 false,
215                 provision);
216         assertNull(AttributeUtil.getPasswordValue(attrs));
217 
218         // 4. without account password, no clear-text password but random password generation enabled
219         // after SYNCOPE-1751 no password is going to be generated by DefaultMappingManager,
220         // but by AbstractPropagationTaskExecutor
221         ldap = resourceDAO.save(ldap);
222         entityManager().flush();
223 
224         attrs = mappingManager.prepareAttrsFromLinkedAccount(
225                 vivaldi,
226                 account,
227                 null,
228                 true,
229                 provision);
230         assertNull(AttributeUtil.getPasswordValue(attrs));
231 
232         // 5. without account password, no clear-text password and random password generation disabled
233         resourceDAO.save(ldap);
234         entityManager().flush();
235 
236         attrs = mappingManager.prepareAttrsFromLinkedAccount(
237                 vivaldi,
238                 account,
239                 null,
240                 true,
241                 provision);
242         assertNull(AttributeUtil.getPasswordValue(attrs));
243     }
244 
245     @Test
246     public void issueSYNCOPE1583() {
247         // 0. create user matching the condition below
248         User user = entityFactory.newEntity(User.class);
249         user.setUsername("username");
250         user.setRealm(realmDAO.findByFullPath("/even/two"));
251         user.add(anyTypeClassDAO.find("other"));
252 
253         UPlainAttr cool = entityFactory.newEntity(UPlainAttr.class);
254         cool.setOwner(user);
255         cool.setSchema(plainSchemaDAO.find("cool"));
256         cool.add(validator, "true", anyUtilsFactory.getInstance(AnyTypeKind.USER));
257         user.add(cool);
258 
259         user = userDAO.save(user);
260         String newUserKey = user.getKey();
261         assertNotNull(newUserKey);
262 
263         // 1. update group with dynamic membership
264         Group group = groupDAO.findByName("root");
265         assertNotNull(group);
266 
267         UDynGroupMembership dynMembership = entityFactory.newEntity(UDynGroupMembership.class);
268         dynMembership.setFIQLCond("cool==true");
269         dynMembership.setGroup(group);
270         group.setUDynMembership(dynMembership);
271 
272         group = groupDAO.saveAndRefreshDynMemberships(group);
273         assertNotNull(group);
274 
275         entityManager().flush();
276 
277         // 2. verify that dynamic membership is in place
278         assertTrue(userDAO.findAllGroupKeys(user).contains(group.getKey()));
279 
280         // 3. check propagation attrs
281         ExternalResource csv = resourceDAO.find("resource-csv");
282         Provision provision = csv.getProvisionByAnyType(AnyTypeKind.USER.name()).get();
283         assertNotNull(provision);
284 
285         Pair<String, Set<Attribute>> attrs = mappingManager.prepareAttrsFromAny(
286                 user,
287                 null,
288                 false,
289                 Boolean.TRUE,
290                 csv,
291                 provision);
292         assertTrue(attrs.getRight().stream().anyMatch(
293                 attr -> "theirgroup".equals(attr.getName()) && List.of("sx-dx").equals(attr.getValue())));
294     }
295 }