1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.core.persistence.jpa.entity.user;
20
21 import com.fasterxml.jackson.core.type.TypeReference;
22 import java.time.OffsetDateTime;
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.Optional;
26 import java.util.stream.Collectors;
27 import javax.persistence.Cacheable;
28 import javax.persistence.CascadeType;
29 import javax.persistence.Column;
30 import javax.persistence.Entity;
31 import javax.persistence.EnumType;
32 import javax.persistence.Enumerated;
33 import javax.persistence.FetchType;
34 import javax.persistence.JoinColumn;
35 import javax.persistence.JoinTable;
36 import javax.persistence.Lob;
37 import javax.persistence.ManyToMany;
38 import javax.persistence.ManyToOne;
39 import javax.persistence.OneToMany;
40 import javax.persistence.Table;
41 import javax.persistence.UniqueConstraint;
42 import javax.validation.Valid;
43 import javax.validation.constraints.NotNull;
44 import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
45 import org.apache.syncope.common.lib.types.CipherAlgorithm;
46 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
47 import org.apache.syncope.core.persistence.api.entity.AnyType;
48 import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
49 import org.apache.syncope.core.persistence.api.entity.ExternalResource;
50 import org.apache.syncope.core.persistence.api.entity.RelationshipType;
51 import org.apache.syncope.core.persistence.api.entity.Role;
52 import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
53 import org.apache.syncope.core.persistence.api.entity.user.LinkedAccount;
54 import org.apache.syncope.core.persistence.api.entity.user.SecurityQuestion;
55 import org.apache.syncope.core.persistence.api.entity.user.UMembership;
56 import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr;
57 import org.apache.syncope.core.persistence.api.entity.user.URelationship;
58 import org.apache.syncope.core.persistence.api.entity.user.User;
59 import org.apache.syncope.core.persistence.jpa.entity.AbstractGroupableRelatable;
60 import org.apache.syncope.core.persistence.jpa.entity.JPAAnyTypeClass;
61 import org.apache.syncope.core.persistence.jpa.entity.JPAExternalResource;
62 import org.apache.syncope.core.persistence.jpa.entity.JPARole;
63 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
64 import org.apache.syncope.core.spring.ApplicationContextProvider;
65 import org.apache.syncope.core.spring.security.AuthContextUtils;
66 import org.apache.syncope.core.spring.security.Encryptor;
67 import org.apache.syncope.core.spring.security.SecureRandomUtils;
68
69 @Entity
70 @Table(name = JPAUser.TABLE)
71 @Cacheable
72 public class JPAUser
73 extends AbstractGroupableRelatable<User, UMembership, UPlainAttr, AnyObject, URelationship>
74 implements User {
75
76 private static final long serialVersionUID = -3905046855521446823L;
77
78 public static final String TABLE = "SyncopeUser";
79
80 protected static final Encryptor ENCRYPTOR = Encryptor.getInstance();
81
82 protected static final TypeReference<List<String>> TYPEREF = new TypeReference<List<String>>() {
83 };
84
85 @Column(nullable = true)
86 protected String password;
87
88 @ManyToMany(fetch = FetchType.EAGER)
89 @JoinTable(joinColumns =
90 @JoinColumn(name = "user_id"),
91 inverseJoinColumns =
92 @JoinColumn(name = "role_id"),
93 uniqueConstraints =
94 @UniqueConstraint(columnNames = { "user_id", "role_id" }))
95 protected List<JPARole> roles = new ArrayList<>();
96
97 @OneToMany(cascade = CascadeType.ALL, mappedBy = "owner")
98 @Valid
99 protected List<JPAUPlainAttr> plainAttrs = new ArrayList<>();
100
101 @Column(nullable = true)
102 protected String status;
103
104 @Lob
105 protected String token;
106
107 protected OffsetDateTime tokenExpireTime;
108
109 @Column(nullable = true)
110 @Enumerated(EnumType.STRING)
111 protected CipherAlgorithm cipherAlgorithm;
112
113 @Lob
114 protected String passwordHistory;
115
116
117
118
119 @Column(nullable = true)
120 protected Integer failedLogins;
121
122
123
124
125 @Column(unique = true)
126 @NotNull(message = "Blank username")
127 protected String username;
128
129
130
131
132 protected OffsetDateTime lastLoginDate;
133
134
135
136
137 protected OffsetDateTime changePwdDate;
138
139 protected Boolean suspended = false;
140
141 protected Boolean mustChangePassword = false;
142
143
144
145
146 @ManyToMany(fetch = FetchType.EAGER)
147 @JoinTable(joinColumns =
148 @JoinColumn(name = "user_id"),
149 inverseJoinColumns =
150 @JoinColumn(name = "resource_id"),
151 uniqueConstraints =
152 @UniqueConstraint(columnNames = { "user_id", "resource_id" }))
153 protected List<JPAExternalResource> resources = new ArrayList<>();
154
155 @ManyToMany(fetch = FetchType.LAZY)
156 @JoinTable(joinColumns =
157 @JoinColumn(name = "user_id"),
158 inverseJoinColumns =
159 @JoinColumn(name = "anyTypeClass_id"),
160 uniqueConstraints =
161 @UniqueConstraint(columnNames = { "user_id", "anyTypeClass_id" }))
162 protected List<JPAAnyTypeClass> auxClasses = new ArrayList<>();
163
164 @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "leftEnd")
165 @Valid
166 protected List<JPAURelationship> relationships = new ArrayList<>();
167
168 @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "leftEnd")
169 @Valid
170 protected List<JPAUMembership> memberships = new ArrayList<>();
171
172 @ManyToOne(fetch = FetchType.EAGER)
173 protected JPASecurityQuestion securityQuestion;
174
175 @Column(nullable = true)
176 protected String securityAnswer;
177
178 @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "owner")
179 @Valid
180 protected List<JPALinkedAccount> linkedAccounts = new ArrayList<>();
181
182 @Override
183 public AnyType getType() {
184 return ApplicationContextProvider.getBeanFactory().getBean(AnyTypeDAO.class).findUser();
185 }
186
187 @Override
188 public void setType(final AnyType type) {
189
190 }
191
192 @Override
193 public boolean add(final ExternalResource resource) {
194 checkType(resource, JPAExternalResource.class);
195 return resources.contains((JPAExternalResource) resource) || resources.add((JPAExternalResource) resource);
196 }
197
198 @Override
199 public List<? extends ExternalResource> getResources() {
200 return resources;
201 }
202
203 @Override
204 public boolean add(final Role role) {
205 checkType(role, JPARole.class);
206 return roles.contains((JPARole) role) || roles.add((JPARole) role);
207 }
208
209 @Override
210 public List<? extends Role> getRoles() {
211 return roles;
212 }
213
214 @Override
215 public String getPassword() {
216 return password;
217 }
218
219 @Override
220 public void setEncodedPassword(final String password, final CipherAlgorithm cipherAlgorithm) {
221 this.password = password;
222 this.cipherAlgorithm = cipherAlgorithm;
223 setMustChangePassword(false);
224 }
225
226 @Override
227 public void setPassword(final String password) {
228 try {
229 this.password = ENCRYPTOR.encode(password, cipherAlgorithm == null
230 ? CipherAlgorithm.valueOf(ApplicationContextProvider.getBeanFactory().getBean(ConfParamOps.class).
231 get(AuthContextUtils.getDomain(), "password.cipher.algorithm", CipherAlgorithm.AES.name(),
232 String.class))
233 : cipherAlgorithm);
234 setMustChangePassword(false);
235 } catch (Exception e) {
236 LOG.error("Could not encode password", e);
237 this.password = null;
238 }
239 }
240
241 @Override
242 public CipherAlgorithm getCipherAlgorithm() {
243 return cipherAlgorithm;
244 }
245
246 @Override
247 public void setCipherAlgorithm(final CipherAlgorithm cipherAlgorithm) {
248 if (this.cipherAlgorithm == null || cipherAlgorithm == null) {
249 this.cipherAlgorithm = cipherAlgorithm;
250 } else {
251 throw new IllegalArgumentException("Cannot override existing cipher algorithm");
252 }
253 }
254
255 @Override
256 public boolean canDecodeSecrets() {
257 return this.cipherAlgorithm != null && this.cipherAlgorithm.isInvertible();
258 }
259
260 @Override
261 public boolean add(final UPlainAttr attr) {
262 checkType(attr, JPAUPlainAttr.class);
263 return plainAttrs.add((JPAUPlainAttr) attr);
264 }
265
266 @Override
267 protected List<? extends UPlainAttr> internalGetPlainAttrs() {
268 return plainAttrs;
269 }
270
271 @Override
272 public String getStatus() {
273 return status;
274 }
275
276 @Override
277 public void setStatus(final String status) {
278 this.status = status;
279 }
280
281 @Override
282 public void generateToken(final int tokenLength, final int tokenExpireTime) {
283 this.token = SecureRandomUtils.generateRandomPassword(tokenLength);
284 this.tokenExpireTime = OffsetDateTime.now().plusMinutes(tokenExpireTime);
285 }
286
287 @Override
288 public void removeToken() {
289 this.token = null;
290 this.tokenExpireTime = null;
291 }
292
293 @Override
294 public String getToken() {
295 return token;
296 }
297
298 @Override
299 public OffsetDateTime getTokenExpireTime() {
300 return tokenExpireTime;
301 }
302
303 @Override
304 public boolean checkToken(final String token) {
305 return Optional.ofNullable(this.token).
306 map(s -> s.equals(token) && !hasTokenExpired()).
307 orElseGet(() -> token == null);
308 }
309
310 @Override
311 public boolean hasTokenExpired() {
312 return Optional.ofNullable(tokenExpireTime).
313 filter(expireTime -> expireTime.isBefore(OffsetDateTime.now())).
314 isPresent();
315 }
316
317 @Override
318 public void addToPasswordHistory(final String password) {
319 List<String> ph = getPasswordHistory();
320 ph.add(password);
321 passwordHistory = POJOHelper.serialize(ph);
322 }
323
324 @Override
325 public void removeOldestEntriesFromPasswordHistory(final int n) {
326 List<String> ph = getPasswordHistory();
327 ph.subList(n, ph.size());
328 passwordHistory = POJOHelper.serialize(ph);
329 }
330
331 @Override
332 public List<String> getPasswordHistory() {
333 return passwordHistory == null
334 ? new ArrayList<>(0)
335 : POJOHelper.deserialize(passwordHistory, TYPEREF);
336 }
337
338 @Override
339 public OffsetDateTime getChangePwdDate() {
340 return changePwdDate;
341 }
342
343 @Override
344 public void setChangePwdDate(final OffsetDateTime changePwdDate) {
345 this.changePwdDate = changePwdDate;
346 }
347
348 @Override
349 public Integer getFailedLogins() {
350 return failedLogins == null ? 0 : failedLogins;
351 }
352
353 @Override
354 public void setFailedLogins(final Integer failedLogins) {
355 this.failedLogins = failedLogins;
356 }
357
358 @Override
359 public OffsetDateTime getLastLoginDate() {
360 return lastLoginDate;
361 }
362
363 @Override
364 public void setLastLoginDate(final OffsetDateTime lastLoginDate) {
365 this.lastLoginDate = lastLoginDate;
366 }
367
368 @Override
369 public String getUsername() {
370 return username;
371 }
372
373 @Override
374 public void setUsername(final String username) {
375 this.username = username;
376 }
377
378 @Override
379 public void setSuspended(final Boolean suspended) {
380 this.suspended = suspended;
381 }
382
383 @Override
384 public Boolean isSuspended() {
385 return suspended;
386 }
387
388 @Override
389 public void setMustChangePassword(final boolean mustChangePassword) {
390 this.mustChangePassword = mustChangePassword;
391 }
392
393 @Override
394 public boolean isMustChangePassword() {
395 return mustChangePassword;
396 }
397
398 @Override
399 public SecurityQuestion getSecurityQuestion() {
400 return securityQuestion;
401 }
402
403 @Override
404 public void setSecurityQuestion(final SecurityQuestion securityQuestion) {
405 checkType(securityQuestion, JPASecurityQuestion.class);
406 this.securityQuestion = (JPASecurityQuestion) securityQuestion;
407 }
408
409 @Override
410 public String getSecurityAnswer() {
411 return securityAnswer;
412 }
413
414 @Override
415 public void setSecurityAnswer(final String securityAnswer) {
416 try {
417 this.securityAnswer = ENCRYPTOR.encode(securityAnswer, cipherAlgorithm == null
418 ? CipherAlgorithm.valueOf(ApplicationContextProvider.getBeanFactory().getBean(ConfParamOps.class).
419 get(AuthContextUtils.getDomain(), "password.cipher.algorithm", CipherAlgorithm.AES.name(),
420 String.class))
421 : cipherAlgorithm);
422 } catch (Exception e) {
423 LOG.error("Could not encode security answer", e);
424 this.securityAnswer = null;
425 }
426 }
427
428 @Override
429 public boolean add(final AnyTypeClass auxClass) {
430 checkType(auxClass, JPAAnyTypeClass.class);
431 return auxClasses.contains((JPAAnyTypeClass) auxClass) || auxClasses.add((JPAAnyTypeClass) auxClass);
432 }
433
434 @Override
435 public List<? extends AnyTypeClass> getAuxClasses() {
436 return auxClasses;
437 }
438
439 @Override
440 public boolean add(final URelationship relationship) {
441 checkType(relationship, JPAURelationship.class);
442 return this.relationships.add((JPAURelationship) relationship);
443 }
444
445 @Override
446 public Optional<? extends URelationship> getRelationship(
447 final RelationshipType relationshipType, final String otherEndKey) {
448
449 return getRelationships().stream().filter(relationship -> relationshipType.equals(relationship.getType())
450 && otherEndKey != null && otherEndKey.equals(relationship.getRightEnd().getKey())).
451 findFirst();
452 }
453
454 @Override
455 public List<? extends URelationship> getRelationships() {
456 return relationships;
457 }
458
459 @Override
460 public boolean add(final UMembership membership) {
461 checkType(membership, JPAUMembership.class);
462 return this.memberships.add((JPAUMembership) membership);
463 }
464
465 @Override
466 public boolean remove(final UMembership membership) {
467 checkType(membership, JPAUMembership.class);
468 return this.memberships.remove((JPAUMembership) membership);
469 }
470
471 @Override
472 public List<? extends UMembership> getMemberships() {
473 return memberships;
474 }
475
476 @Override
477 public boolean add(final LinkedAccount account) {
478 checkType(account, JPALinkedAccount.class);
479 return linkedAccounts.contains((JPALinkedAccount) account) || linkedAccounts.add((JPALinkedAccount) account);
480 }
481
482 @Override
483 public Optional<? extends LinkedAccount> getLinkedAccount(final String resource, final String connObjectKeyValue) {
484 return linkedAccounts.stream().
485 filter(account -> account.getResource().getKey().equals(resource)
486 && account.getConnObjectKeyValue().equals(connObjectKeyValue)).
487 findFirst();
488 }
489
490 @Override
491 public List<? extends LinkedAccount> getLinkedAccounts(final String resource) {
492 return linkedAccounts.stream().
493 filter(account -> account.getResource().getKey().equals(resource)).
494 collect(Collectors.toList());
495 }
496
497 @Override
498 public List<? extends LinkedAccount> getLinkedAccounts() {
499 return linkedAccounts;
500 }
501 }