1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.core.spring.security;
20
21 import java.util.ArrayList;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Optional;
25 import java.util.concurrent.ConcurrentHashMap;
26 import java.util.stream.Collectors;
27 import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf;
28 import org.apache.syncope.core.persistence.api.entity.ExternalResource;
29 import org.apache.syncope.core.persistence.api.entity.Implementation;
30 import org.apache.syncope.core.persistence.api.entity.Realm;
31 import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
32 import org.apache.syncope.core.provisioning.api.rules.PasswordRule;
33 import org.apache.syncope.core.spring.implementation.ImplementationManager;
34 import org.apache.syncope.core.spring.policy.DefaultPasswordRule;
35 import org.passay.CharacterRule;
36 import org.passay.EnglishCharacterData;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39 import org.springframework.transaction.annotation.Transactional;
40
41
42
43
44
45
46
47 public class DefaultPasswordGenerator implements PasswordGenerator {
48
49 protected static final Logger LOG = LoggerFactory.getLogger(PasswordGenerator.class);
50
51 protected static final int VERY_MIN_LENGTH = 0;
52
53 protected static final int VERY_MAX_LENGTH = 64;
54
55 protected static final int MIN_LENGTH_IF_ZERO = 8;
56
57 protected final Map<String, PasswordRule> perContextPasswordRules = new ConcurrentHashMap<>();
58
59 @Transactional(readOnly = true)
60 @Override
61 public String generate(final ExternalResource resource, final List<Realm> realms) {
62 List<PasswordPolicy> policies = new ArrayList<>();
63
64
65 Optional.ofNullable(resource.getPasswordPolicy()).ifPresent(policies::add);
66
67
68 realms.forEach(r -> Optional.ofNullable(r.getPasswordPolicy()).
69 filter(p -> !policies.contains(p)).
70 ifPresent(policies::add));
71
72 return generate(policies);
73 }
74
75 protected List<PasswordRule> getPasswordRules(final PasswordPolicy policy) {
76 List<PasswordRule> result = new ArrayList<>();
77
78 for (Implementation impl : policy.getRules()) {
79 try {
80 ImplementationManager.buildPasswordRule(
81 impl,
82 () -> perContextPasswordRules.get(impl.getKey()),
83 instance -> perContextPasswordRules.put(impl.getKey(), instance)).
84 ifPresent(result::add);
85 } catch (Exception e) {
86 LOG.warn("While building {}", impl, e);
87 }
88 }
89
90 return result;
91 }
92
93 @Override
94 public String generate(final List<PasswordPolicy> policies) {
95 List<DefaultPasswordRuleConf> ruleConfs = new ArrayList<>();
96
97 policies.stream().forEach(policy -> getPasswordRules(policy).stream().
98 filter(rule -> rule.getConf() instanceof DefaultPasswordRuleConf).
99 forEach(rule -> ruleConfs.add((DefaultPasswordRuleConf) rule.getConf())));
100
101 return generate(merge(ruleConfs));
102 }
103
104 protected DefaultPasswordRuleConf merge(final List<DefaultPasswordRuleConf> defaultRuleConfs) {
105 DefaultPasswordRuleConf result = new DefaultPasswordRuleConf();
106 result.setMinLength(VERY_MIN_LENGTH);
107 result.setMaxLength(VERY_MAX_LENGTH);
108
109 defaultRuleConfs.forEach(ruleConf -> {
110 if (ruleConf.getMinLength() > result.getMinLength()) {
111 result.setMinLength(ruleConf.getMinLength());
112 }
113
114 if (ruleConf.getMaxLength() > 0 && ruleConf.getMaxLength() < result.getMaxLength()) {
115 result.setMaxLength(ruleConf.getMaxLength());
116 }
117
118 if (ruleConf.getAlphabetical() > result.getAlphabetical()) {
119 result.setAlphabetical(ruleConf.getAlphabetical());
120 }
121
122 if (ruleConf.getUppercase() > result.getUppercase()) {
123 result.setUppercase(ruleConf.getUppercase());
124 }
125
126 if (ruleConf.getLowercase() > result.getLowercase()) {
127 result.setLowercase(ruleConf.getLowercase());
128 }
129
130 if (ruleConf.getDigit() > result.getDigit()) {
131 result.setDigit(ruleConf.getDigit());
132 }
133
134 if (ruleConf.getSpecial() > result.getSpecial()) {
135 result.setSpecial(ruleConf.getSpecial());
136 }
137
138 if (!ruleConf.getSpecialChars().isEmpty()) {
139 result.getSpecialChars().addAll(ruleConf.getSpecialChars().stream().
140 filter(c -> !result.getSpecialChars().contains(c)).collect(Collectors.toList()));
141 }
142
143 if (!ruleConf.getIllegalChars().isEmpty()) {
144 result.getIllegalChars().addAll(ruleConf.getIllegalChars().stream().
145 filter(c -> !result.getIllegalChars().contains(c)).collect(Collectors.toList()));
146 }
147
148 if (ruleConf.getRepeatSame() > result.getRepeatSame()) {
149 result.setRepeatSame(ruleConf.getRepeatSame());
150 }
151
152 if (!result.isUsernameAllowed()) {
153 result.setUsernameAllowed(ruleConf.isUsernameAllowed());
154 }
155
156 if (!ruleConf.getWordsNotPermitted().isEmpty()) {
157 result.getWordsNotPermitted().addAll(ruleConf.getWordsNotPermitted().stream().
158 filter(w -> !result.getWordsNotPermitted().contains(w)).collect(Collectors.toList()));
159 }
160 });
161
162 if (result.getMinLength() == 0) {
163 result.setMinLength(
164 result.getMaxLength() < MIN_LENGTH_IF_ZERO ? result.getMaxLength() : MIN_LENGTH_IF_ZERO);
165 }
166 if (result.getMinLength() > result.getMaxLength()) {
167 result.setMaxLength(result.getMinLength());
168 }
169
170 return result;
171 }
172
173 protected String generate(final DefaultPasswordRuleConf ruleConf) {
174 List<CharacterRule> characterRules = DefaultPasswordRule.conf2Rules(ruleConf).stream().
175 filter(CharacterRule.class::isInstance).map(CharacterRule.class::cast).
176 collect(Collectors.toList());
177 if (characterRules.isEmpty()) {
178 int halfMinLength = ruleConf.getMinLength() / 2;
179 characterRules = List.of(
180 new CharacterRule(EnglishCharacterData.Alphabetical, halfMinLength),
181 new CharacterRule(EnglishCharacterData.Digit, halfMinLength));
182 }
183 int min = Math.max(ruleConf.getMinLength(),
184 characterRules.stream().mapToInt(CharacterRule::getNumberOfCharacters).sum());
185 return SecureRandomUtils.passwordGenerator().generatePassword(min, characterRules);
186 }
187 }