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.persistence.jpa.inner;
20  
21  import static org.junit.jupiter.api.Assertions.assertEquals;
22  import static org.junit.jupiter.api.Assertions.assertFalse;
23  import static org.junit.jupiter.api.Assertions.assertTrue;
24  
25  import java.time.LocalDateTime;
26  import java.time.OffsetDateTime;
27  import java.util.List;
28  import java.util.Optional;
29  import java.util.stream.Collectors;
30  import java.util.stream.IntStream;
31  import org.apache.syncope.common.lib.wa.GoogleMfaAuthAccount;
32  import org.apache.syncope.common.lib.wa.GoogleMfaAuthToken;
33  import org.apache.syncope.common.lib.wa.ImpersonationAccount;
34  import org.apache.syncope.common.lib.wa.U2FDevice;
35  import org.apache.syncope.common.lib.wa.WebAuthnDeviceCredential;
36  import org.apache.syncope.core.persistence.api.dao.AuthProfileDAO;
37  import org.apache.syncope.core.persistence.api.entity.am.AuthProfile;
38  import org.apache.syncope.core.persistence.jpa.AbstractTest;
39  import org.apache.syncope.core.persistence.jpa.entity.am.JPAAuthProfile;
40  import org.apache.syncope.core.spring.security.SecureRandomUtils;
41  import org.junit.jupiter.api.BeforeEach;
42  import org.junit.jupiter.api.Test;
43  import org.springframework.beans.factory.annotation.Autowired;
44  import org.springframework.transaction.annotation.Transactional;
45  
46  @Transactional("Master")
47  public class AuthProfileTest extends AbstractTest {
48  
49      @Autowired
50      private AuthProfileDAO authProfileDAO;
51  
52      @BeforeEach
53      public void beforeEach() {
54          entityManager().createQuery("DELETE FROM " + JPAAuthProfile.class.getSimpleName()).executeUpdate();
55      }
56  
57      @Test
58      public void googleMfaToken() {
59          String id = SecureRandomUtils.generateRandomUUID().toString();
60  
61          createAuthProfileWithToken(id, 123456);
62  
63          Optional<AuthProfile> result = authProfileDAO.findByOwner(id);
64          assertTrue(result.isPresent());
65  
66          assertFalse(authProfileDAO.findAll(-1, -1).isEmpty());
67  
68          AuthProfile authProfile = result.get();
69          result = Optional.ofNullable(authProfileDAO.find(authProfile.getKey()));
70          assertTrue(result.isPresent());
71  
72          authProfile.setOwner("SyncopeCreate-New");
73          authProfile.setGoogleMfaAuthTokens(List.of());
74          authProfileDAO.save(authProfile);
75  
76          assertFalse(authProfileDAO.findByOwner(id).isPresent());
77      }
78  
79      @Test
80      public void u2fRegisteredDevice() {
81          String id = SecureRandomUtils.generateRandomUUID().toString();
82          createAuthProfileWithU2FDevice(id, "{ 'record': 1 }");
83  
84          Optional<AuthProfile> result = authProfileDAO.findByOwner(id);
85          assertTrue(result.isPresent());
86  
87          assertFalse(authProfileDAO.findAll(-1, -1).isEmpty());
88  
89          AuthProfile authProfile = result.get();
90          result = Optional.ofNullable(authProfileDAO.find(authProfile.getKey()));
91          assertTrue(result.isPresent());
92  
93          authProfile.setOwner("SyncopeCreate-NewU2F");
94          authProfile.setU2FRegisteredDevices(List.of());
95          authProfileDAO.save(authProfile);
96  
97          assertFalse(authProfileDAO.findByOwner(id).isPresent());
98      }
99  
100     @Test
101     public void webAuthnRegisteredDevice() {
102         String id = SecureRandomUtils.generateRandomUUID().toString();
103         String record = "[ {"
104                 + "    \"userIdentity\" : {"
105                 + "      \"name\" : \"casuser\","
106                 + "      \"displayName\" : \"casuser\""
107                 + "    },"
108                 + "    \"credential\" : {"
109                 + "      \"credentialId\" : \"fFGyV3K5x1\""
110                 + "    },"
111                 + "    \"username\" : \"casuser\""
112                 + "  } ]";
113 
114         WebAuthnDeviceCredential credential = new WebAuthnDeviceCredential.Builder().
115                 json(record).
116                 identifier("fFGyV3K5x1").
117                 build();
118 
119         createAuthProfileWithWebAuthnDevice(id, List.of(credential));
120 
121         Optional<AuthProfile> result = authProfileDAO.findByOwner(id);
122         assertTrue(result.isPresent());
123 
124         assertFalse(authProfileDAO.findAll(-1, -1).isEmpty());
125 
126         AuthProfile authProfile = result.get();
127         result = Optional.ofNullable(authProfileDAO.find(authProfile.getKey()));
128         assertTrue(result.isPresent());
129 
130         authProfile.setOwner("SyncopeCreate-NewU2F");
131         authProfile.setWebAuthnDeviceCredentials(List.of());
132         authProfileDAO.save(authProfile);
133 
134         assertFalse(authProfileDAO.findByOwner(id).isPresent());
135     }
136 
137     @Test
138     public void googleMfaAccount() {
139         String id = SecureRandomUtils.generateRandomUUID().toString();
140 
141         createAuthProfileWithAccount(id);
142 
143         Optional<AuthProfile> result = authProfileDAO.findByOwner(id);
144         assertTrue(result.isPresent());
145 
146         assertFalse(authProfileDAO.findAll(-1, -1).isEmpty());
147 
148         AuthProfile authProfile = result.get();
149         result = Optional.ofNullable(authProfileDAO.find(authProfile.getKey()));
150         assertTrue(result.isPresent());
151 
152         String secret = SecureRandomUtils.generateRandomUUID().toString();
153         List<GoogleMfaAuthAccount> googleMfaAuthAccounts = authProfile.getGoogleMfaAuthAccounts();
154         assertFalse(googleMfaAuthAccounts.isEmpty());
155         GoogleMfaAuthAccount googleMfaAuthAccount = googleMfaAuthAccounts.get(0);
156         googleMfaAuthAccount.setSecretKey(secret);
157 
158         authProfile.setGoogleMfaAuthAccounts(googleMfaAuthAccounts);
159         authProfile = authProfileDAO.save(authProfile);
160         assertEquals(secret, authProfile.getGoogleMfaAuthAccounts().get(0).getSecretKey());
161     }
162 
163     @Test
164     public void impersonationAccounts() {
165         String id = SecureRandomUtils.generateRandomUUID().toString();
166 
167         createAuthProfileWithAccount(id);
168 
169         Optional<AuthProfile> result = authProfileDAO.findByOwner(id);
170         assertTrue(result.isPresent());
171 
172         AuthProfile authProfile = result.get();
173         result = Optional.ofNullable(authProfileDAO.find(authProfile.getKey()));
174         assertTrue(result.isPresent());
175 
176         List<ImpersonationAccount> accounts = IntStream.range(1, 10).
177                 mapToObj(i -> new ImpersonationAccount.Builder().impersonated("impersonatee" + i).build()).
178                 collect(Collectors.toList());
179 
180         authProfile.setImpersonationAccounts(accounts);
181         authProfile = authProfileDAO.save(authProfile);
182         assertEquals(accounts.size(), authProfile.getImpersonationAccounts().size());
183     }
184 
185     private AuthProfile createAuthProfileWithToken(final String owner, final Integer otp) {
186         AuthProfile profile = entityFactory.newEntity(AuthProfile.class);
187         profile.setOwner(owner);
188         GoogleMfaAuthToken token = new GoogleMfaAuthToken.Builder().issueDate(LocalDateTime.now()).token(otp).build();
189         profile.setGoogleMfaAuthTokens(List.of(token));
190         return authProfileDAO.save(profile);
191     }
192 
193     private AuthProfile createAuthProfileWithU2FDevice(final String owner, final String record) {
194         AuthProfile profile = entityFactory.newEntity(AuthProfile.class);
195         profile.setOwner(owner);
196         U2FDevice device = new U2FDevice.Builder().issueDate(OffsetDateTime.now()).record(record).build();
197         profile.setU2FRegisteredDevices(List.of(device));
198         return authProfileDAO.save(profile);
199     }
200 
201     private AuthProfile createAuthProfileWithWebAuthnDevice(
202             final String owner,
203             final List<WebAuthnDeviceCredential> credentials) {
204 
205         AuthProfile profile = entityFactory.newEntity(AuthProfile.class);
206         profile.setOwner(owner);
207         profile.setWebAuthnDeviceCredentials(credentials);
208         return authProfileDAO.save(profile);
209     }
210 
211     private AuthProfile createAuthProfileWithAccount(final String owner) {
212         AuthProfile profile = entityFactory.newEntity(AuthProfile.class);
213         profile.setOwner(owner);
214         GoogleMfaAuthAccount account = new GoogleMfaAuthAccount.Builder()
215                 .registrationDate(OffsetDateTime.now())
216                 .scratchCodes(List.of(1, 2, 3, 4, 5))
217                 .secretKey(SecureRandomUtils.generateRandomUUID().toString())
218                 .validationCode(123456)
219                 .name(SecureRandomUtils.generateRandomUUID().toString())
220                 .build();
221         profile.setGoogleMfaAuthAccounts(List.of(account));
222         return authProfileDAO.save(profile);
223     }
224 }