You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2022/04/13 09:11:43 UTC
[syncope] branch master updated: [SYNCOPE-1673] DefaultPasswordRule and DefaultPasswordGenerator now b… (#338)
This is an automated email from the ASF dual-hosted git repository.
ilgrosso pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git
The following commit(s) were added to refs/heads/master by this push:
new 4d9dcb63ce [SYNCOPE-1673] DefaultPasswordRule and DefaultPasswordGenerator now b… (#338)
4d9dcb63ce is described below
commit 4d9dcb63ce89ecdc33f544f7e5e937786e354599
Author: Francesco Chicchiriccò <il...@users.noreply.github.com>
AuthorDate: Wed Apr 13 11:11:38 2022 +0200
[SYNCOPE-1673] DefaultPasswordRule and DefaultPasswordGenerator now b… (#338)
---
.../console/panels/ProvisionAuxClassesPanel.java | 2 +-
.../syncope/client/console/topology/Topology.java | 2 +-
.../client/console/SyncopeConsoleSession.java | 2 +-
.../notifications/NotificationWizardBuilder.java | 8 +-
.../panels/search/AnyObjectSearchPanel.java | 16 +-
.../console/panels/search/GroupSearchPanel.java | 4 +-
.../client/console/rest/SchemaRestClient.java | 2 +-
.../wizards/any/AbstractAttrsWizardStep.java | 2 +-
.../client/console/wizards/any/Relationships.java | 2 +-
.../client/enduser/panels/any/AbstractAttrs.java | 2 +-
.../client/enduser/rest/SchemaRestClient.java | 2 +-
.../common/lib/policy/DefaultPasswordRuleConf.java | 253 +++----------------
.../syncope/core/logic/ReconciliationLogic.java | 4 +-
.../apache/syncope/core/logic/ResourceLogic.java | 4 +-
.../apache/syncope/core/rest/cxf/JavaDocUtils.java | 2 +-
.../src/test/resources/domains/MasterContent.xml | 8 +-
.../persistence/jpa/PersistenceTestContext.java | 2 +-
.../core/persistence/jpa/inner/UserTest.java | 15 +-
.../src/test/resources/domains/MasterContent.xml | 6 +-
.../provisioning/java/ConnectorFacadeProxy.java | 2 +-
.../java/DefaultConnIdBundleManager.java | 4 +-
.../provisioning/java/DefaultMappingManager.java | 7 +-
.../AbstractPropagationTaskExecutor.java | 2 +-
.../java/pushpull/AbstractPushResultHandler.java | 4 +-
.../java/pushpull/OutboundMatcher.java | 4 +-
.../java/pushpull/PullJobDelegate.java | 4 +-
.../java/pushpull/SinglePullJobDelegate.java | 2 +-
.../pushpull/stream/StreamPullJobDelegate.java | 2 +-
.../provisioning/java/utils/ConnObjectUtils.java | 12 +-
core/spring/pom.xml | 5 +
.../core/spring/policy/DefaultPasswordRule.java | 189 +++++++-------
.../spring/policy/InvalidPasswordRuleConf.java | 37 ---
.../spring/security/DefaultPasswordGenerator.java | 272 ++++-----------------
.../core/spring/security/PasswordGenerator.java | 6 +-
.../core/spring/security/SecureRandomUtils.java | 7 +
.../core/spring/ImplementationManagerTest.java | 21 +-
.../spring/security/PasswordGeneratorTest.java | 186 +++++---------
.../core/spring/security/TestPasswordPolicy.java | 5 +-
.../fit/core/reference/ITImplementationLookup.java | 83 ++-----
.../org/apache/syncope/fit/AbstractITCase.java | 2 +-
pom.xml | 6 +
.../org/apache/syncope/sra/SecurityConfig.java | 4 +-
.../security/pac4j/ServerWebExchangeContext.java | 2 +-
.../reference-guide/concepts/policies.adoc | 41 ++--
44 files changed, 360 insertions(+), 887 deletions(-)
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ProvisionAuxClassesPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ProvisionAuxClassesPanel.java
index 377a8c4b4e..d2844bb8b4 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ProvisionAuxClassesPanel.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ProvisionAuxClassesPanel.java
@@ -102,7 +102,7 @@ public class ProvisionAuxClassesPanel extends Panel {
classes.addAll(anyTypeClasses);
return SchemaRestClient.<PlainSchemaTO>getSchemas(
- SchemaType.PLAIN, null, classes.toArray(new String[] {})).
+ SchemaType.PLAIN, null, classes.toArray(String[]::new)).
stream().map(EntityTO::getKey).collect(Collectors.toList());
}
}
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java
index b152db8d04..96377e0441 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java
@@ -548,7 +548,7 @@ public class Topology extends BasePage {
}
});
- panel.add(behaviors.toArray(new Behavior[] {}));
+ panel.add(behaviors.toArray(Behavior[]::new));
return panel;
}
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java
index 35045f0d83..3292c2c667 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java
@@ -338,7 +338,7 @@ public class SyncopeConsoleSession extends AuthenticatedWebSession implements Ba
@Override
public Roles getRoles() {
if (isSignedIn() && roles == null && auth != null) {
- roles = new Roles(auth.keySet().toArray(new String[] {}));
+ roles = new Roles(auth.keySet().toArray(String[]::new));
roles.add(Constants.ROLE_AUTHENTICATED);
}
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java
index 98a276204a..ecc23d1cd5 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java
@@ -166,7 +166,7 @@ public class NotificationWizardBuilder extends BaseAjaxWizardBuilder<Notificatio
add(new EventCategoryPanel(
"eventSelection",
AuditRestClient.listEvents(),
- new PropertyModel<>(modelObject.getInnerObject(), "events")) {
+ new PropertyModel<>(modelObject.getInnerObject(), "events")) {
private static final long serialVersionUID = 6429053774964787735L;
@@ -332,7 +332,7 @@ public class NotificationWizardBuilder extends BaseAjaxWizardBuilder<Notificatio
@Override
protected List<String> load() {
return ImplementationRestClient.list(IdRepoImplementationType.RECIPIENTS_PROVIDER).stream().
- map(EntityTO::getKey).sorted().collect(Collectors.toList());
+ map(EntityTO::getKey).sorted().collect(Collectors.toList());
}
};
@@ -389,8 +389,8 @@ public class NotificationWizardBuilder extends BaseAjaxWizardBuilder<Notificatio
LOG.error("While reading all any types", e);
}
- String[] anyTypeClasses = Optional.ofNullable(type)
- .map(anyTypeTO -> anyTypeTO.getClasses().toArray(new String[] {})).orElseGet(() -> new String[0]);
+ String[] anyTypeClasses = Optional.ofNullable(type).
+ map(anyTypeTO -> anyTypeTO.getClasses().toArray(String[]::new)).orElseGet(() -> new String[0]);
List<String> result = new ArrayList<>();
result.add(Constants.USERNAME_FIELD_NAME);
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/AnyObjectSearchPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/AnyObjectSearchPanel.java
index d1c22c6de0..80235038aa 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/AnyObjectSearchPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/AnyObjectSearchPanel.java
@@ -86,12 +86,12 @@ public class AnyObjectSearchPanel extends AbstractSearchPanel {
@Override
protected List<String> load() {
return groupRestClient.search(
- SyncopeConstants.ROOT_REALM,
- null,
- 1,
- Constants.MAX_GROUP_LIST_SIZE,
- new SortParam<>(Constants.NAME_FIELD_NAME, true),
- null).stream().map(GroupTO::getName).collect(Collectors.toList());
+ SyncopeConstants.ROOT_REALM,
+ null,
+ 1,
+ Constants.MAX_GROUP_LIST_SIZE,
+ new SortParam<>(Constants.NAME_FIELD_NAME, true),
+ null).stream().map(GroupTO::getName).collect(Collectors.toList());
}
};
@@ -102,8 +102,8 @@ public class AnyObjectSearchPanel extends AbstractSearchPanel {
@Override
protected Map<String, PlainSchemaTO> load() {
return SchemaRestClient.<PlainSchemaTO>getSchemas(
- SchemaType.PLAIN, null, AnyTypeRestClient.read(type).getClasses().toArray(new String[]{})).
- stream().collect(Collectors.toMap(SchemaTO::getKey, Function.identity()));
+ SchemaType.PLAIN, null, AnyTypeRestClient.read(type).getClasses().toArray(String[]::new)).
+ stream().collect(Collectors.toMap(SchemaTO::getKey, Function.identity()));
}
};
}
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/GroupSearchPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/GroupSearchPanel.java
index 80ae8e439e..b912f238b7 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/GroupSearchPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/GroupSearchPanel.java
@@ -89,8 +89,8 @@ public class GroupSearchPanel extends AbstractSearchPanel {
@Override
protected Map<String, PlainSchemaTO> load() {
return SchemaRestClient.<PlainSchemaTO>getSchemas(
- SchemaType.PLAIN, null, AnyTypeRestClient.read(type).getClasses().toArray(new String[]{})).
- stream().collect(Collectors.toMap(SchemaTO::getKey, Function.identity()));
+ SchemaType.PLAIN, null, AnyTypeRestClient.read(type).getClasses().toArray(String[]::new)).
+ stream().collect(Collectors.toMap(SchemaTO::getKey, Function.identity()));
}
};
}
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/SchemaRestClient.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/SchemaRestClient.java
index fbf96815d7..adfe707cfa 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/SchemaRestClient.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/SchemaRestClient.java
@@ -62,7 +62,7 @@ public class SchemaRestClient extends BaseRestClient {
&& anyTypeTO.getKind() != AnyTypeKind.GROUP)).
forEach((anyTypeTO) -> classes.addAll(anyTypeTO.getClasses()));
}
- return getSchemas(schemaType, null, classes.toArray(new String[] {}));
+ return getSchemas(schemaType, null, classes.toArray(String[]::new));
}
public static <T extends SchemaTO> List<T> getSchemas(
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrsWizardStep.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrsWizardStep.java
index fd6a0617da..64e551e525 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrsWizardStep.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrsWizardStep.java
@@ -131,7 +131,7 @@ public abstract class AbstractAttrsWizardStep<S extends SchemaTO> extends Wizard
protected void setSchemas(final List<String> anyTypeClasses, final Map<String, S> scs) {
List<S> allSchemas = anyTypeClasses.isEmpty()
? List.of()
- : SchemaRestClient.getSchemas(getSchemaType(), null, anyTypeClasses.toArray(new String[] {}));
+ : SchemaRestClient.getSchemas(getSchemaType(), null, anyTypeClasses.toArray(String[]::new));
scs.clear();
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Relationships.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Relationships.java
index 1813cc73f7..159d3a9296 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Relationships.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Relationships.java
@@ -117,7 +117,7 @@ public class Relationships extends WizardStep implements ICondition {
private Fragment getViewFragment() {
final Map<String, List<RelationshipTO>> relationships = new HashMap<>();
- addRelationship(relationships, getCurrentRelationships().toArray(new RelationshipTO[] {}));
+ addRelationship(relationships, getCurrentRelationships().toArray(RelationshipTO[]::new));
final Fragment viewFragment = new Fragment("relationships", "viewFragment", this);
viewFragment.setOutputMarkupId(true);
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/AbstractAttrs.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/AbstractAttrs.java
index 0acd3bde1c..f49f68f4d8 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/AbstractAttrs.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/AbstractAttrs.java
@@ -171,7 +171,7 @@ public abstract class AbstractAttrs<S extends SchemaTO> extends Panel {
if (anyTypeClasses.isEmpty()) {
allSchemas = new ArrayList<>();
} else {
- allSchemas = SchemaRestClient.getSchemas(getSchemaType(), null, anyTypeClasses.toArray(new String[] {}));
+ allSchemas = SchemaRestClient.getSchemas(getSchemaType(), null, anyTypeClasses.toArray(String[]::new));
}
scs.clear();
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/SchemaRestClient.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/SchemaRestClient.java
index e897bf7dc7..6391f0a76a 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/SchemaRestClient.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/SchemaRestClient.java
@@ -62,7 +62,7 @@ public class SchemaRestClient extends BaseRestClient {
&& anyTypeTO.getKind() != AnyTypeKind.GROUP)).
forEach((anyTypeTO) -> classes.addAll(anyTypeTO.getClasses()));
}
- return getSchemas(schemaType, null, classes.toArray(new String[] {}));
+ return getSchemas(schemaType, null, classes.toArray(String[]::new));
}
public static <T extends SchemaTO> List<T> getSchemas(
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultPasswordRuleConf.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultPasswordRuleConf.java
index b56d7ef992..16b501f24c 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultPasswordRuleConf.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultPasswordRuleConf.java
@@ -30,100 +30,25 @@ public class DefaultPasswordRuleConf extends AbstractPasswordRuleConf {
private static final long serialVersionUID = -7988778083915548547L;
- /**
- * Minimum length.
- */
private int maxLength;
- /**
- * Maximum length.
- */
private int minLength;
- /**
- * Specify if one or more non alphanumeric characters are required.
- */
- private boolean nonAlphanumericRequired;
+ private int alphabetical;
- /**
- * Specify if one or more alphanumeric characters are required.
- */
- private boolean alphanumericRequired;
+ private int uppercase;
- /**
- * Specify if one or more digits are required.
- */
- private boolean digitRequired;
+ private int lowercase;
- /**
- * Specify if one or more lowercase alphabetic characters are required.
- */
- private boolean lowercaseRequired;
+ private int digit;
- /**
- * Specify if one or more uppercase alphabetic characters are required.
- */
- private boolean uppercaseRequired;
-
- /**
- * Specify if must start with a digit.
- */
- private boolean mustStartWithDigit;
+ private int special;
- /**
- * Specify if mustn't start with a digit.
- */
- private boolean mustntStartWithDigit;
+ private final List<Character> specialChars = new ArrayList<>();
- /**
- * Specify if must end with a digit.
- */
- private boolean mustEndWithDigit;
+ private final List<Character> illegalChars = new ArrayList<>();
- /**
- * Specify if mustn't end with a digit.
- */
- private boolean mustntEndWithDigit;
-
- /**
- * Specify if must start with a non alphanumeric character.
- */
- private boolean mustStartWithNonAlpha;
-
- /**
- * Specify if must start with a alphanumeric character.
- */
- private boolean mustStartWithAlpha;
-
- /**
- * Specify if mustn't start with a non alphanumeric character.
- */
- private boolean mustntStartWithNonAlpha;
-
- /**
- * Specify if mustn't start with a alphanumeric character.
- */
- private boolean mustntStartWithAlpha;
-
- /**
- * Specify if must end with a non alphanumeric character.
- */
- private boolean mustEndWithNonAlpha;
-
- /**
- * Specify if must end with a alphanumeric character.
- */
- private boolean mustEndWithAlpha;
-
- /**
- * Specify if mustn't end with a non alphanumeric character.
- */
- private boolean mustntEndWithNonAlpha;
-
- /**
- * Specify if mustn't end with a alphanumeric character.
- */
- private boolean mustntEndWithAlpha;
+ private int repeatSame;
/**
* Specify if using username as password is allowed.
@@ -142,32 +67,6 @@ public class DefaultPasswordRuleConf extends AbstractPasswordRuleConf {
type = { SchemaType.PLAIN, SchemaType.DERIVED, SchemaType.VIRTUAL })
private final List<String> schemasNotPermitted = new ArrayList<>();
- /**
- * Substrings not permitted as prefix.
- */
- private final List<String> prefixesNotPermitted = new ArrayList<>();
-
- /**
- * Substrings not permitted as suffix.
- */
- private final List<String> suffixesNotPermitted = new ArrayList<>();
-
- public boolean isDigitRequired() {
- return digitRequired;
- }
-
- public void setDigitRequired(final boolean digitRequired) {
- this.digitRequired = digitRequired;
- }
-
- public boolean isLowercaseRequired() {
- return lowercaseRequired;
- }
-
- public void setLowercaseRequired(final boolean lowercaseRequired) {
- this.lowercaseRequired = lowercaseRequired;
- }
-
public int getMaxLength() {
return maxLength;
}
@@ -184,124 +83,64 @@ public class DefaultPasswordRuleConf extends AbstractPasswordRuleConf {
this.minLength = minLength;
}
- public boolean isMustEndWithDigit() {
- return mustEndWithDigit;
- }
-
- public void setMustEndWithDigit(final boolean mustEndWithDigit) {
- this.mustEndWithDigit = mustEndWithDigit;
- }
-
- public boolean isMustEndWithNonAlpha() {
- return mustEndWithNonAlpha;
- }
-
- public void setMustEndWithNonAlpha(final boolean mustEndWithNonAlpha) {
- this.mustEndWithNonAlpha = mustEndWithNonAlpha;
- }
-
- public boolean isMustStartWithDigit() {
- return mustStartWithDigit;
- }
-
- public void setMustStartWithDigit(final boolean mustStartWithDigit) {
- this.mustStartWithDigit = mustStartWithDigit;
- }
-
- public boolean isMustStartWithNonAlpha() {
- return mustStartWithNonAlpha;
+ public int getAlphabetical() {
+ return alphabetical;
}
- public void setMustStartWithNonAlpha(final boolean mustStartWithNonAlpha) {
- this.mustStartWithNonAlpha = mustStartWithNonAlpha;
+ public void setAlphabetical(final int alphabetical) {
+ this.alphabetical = alphabetical;
}
- public boolean isMustntEndWithDigit() {
- return mustntEndWithDigit;
+ public int getUppercase() {
+ return uppercase;
}
- public void setMustntEndWithDigit(final boolean mustntEndWithDigit) {
- this.mustntEndWithDigit = mustntEndWithDigit;
+ public void setUppercase(final int uppercase) {
+ this.uppercase = uppercase;
}
- public boolean isMustntEndWithNonAlpha() {
- return mustntEndWithNonAlpha;
+ public int getLowercase() {
+ return lowercase;
}
- public void setMustntEndWithNonAlpha(final boolean mustntEndWithNonAlpha) {
- this.mustntEndWithNonAlpha = mustntEndWithNonAlpha;
+ public void setLowercase(final int lowercase) {
+ this.lowercase = lowercase;
}
- public boolean isMustntStartWithDigit() {
- return mustntStartWithDigit;
+ public int getDigit() {
+ return digit;
}
- public void setMustntStartWithDigit(final boolean mustntStartWithDigit) {
- this.mustntStartWithDigit = mustntStartWithDigit;
+ public void setDigit(final int digit) {
+ this.digit = digit;
}
- public boolean isMustntStartWithNonAlpha() {
- return mustntStartWithNonAlpha;
+ public int getSpecial() {
+ return special;
}
- public void setMustntStartWithNonAlpha(final boolean mustntStartWithNonAlpha) {
- this.mustntStartWithNonAlpha = mustntStartWithNonAlpha;
+ public void setSpecial(final int special) {
+ this.special = special;
}
- public boolean isNonAlphanumericRequired() {
- return nonAlphanumericRequired;
+ @JacksonXmlElementWrapper(localName = "specialChars")
+ @JacksonXmlProperty(localName = "char")
+ public List<Character> getSpecialChars() {
+ return specialChars;
}
- public void setNonAlphanumericRequired(final boolean nonAlphanumericRequired) {
- this.nonAlphanumericRequired = nonAlphanumericRequired;
+ @JacksonXmlElementWrapper(localName = "illegalChars")
+ @JacksonXmlProperty(localName = "char")
+ public List<Character> getIllegalChars() {
+ return illegalChars;
}
- public boolean isUppercaseRequired() {
- return uppercaseRequired;
+ public int getRepeatSame() {
+ return repeatSame;
}
- public void setUppercaseRequired(final boolean uppercaseRequired) {
- this.uppercaseRequired = uppercaseRequired;
- }
-
- public boolean isAlphanumericRequired() {
- return alphanumericRequired;
- }
-
- public void setAlphanumericRequired(final boolean alphanumericRequired) {
- this.alphanumericRequired = alphanumericRequired;
- }
-
- public boolean isMustEndWithAlpha() {
- return mustEndWithAlpha;
- }
-
- public void setMustEndWithAlpha(final boolean mustEndWithAlpha) {
- this.mustEndWithAlpha = mustEndWithAlpha;
- }
-
- public boolean isMustStartWithAlpha() {
- return mustStartWithAlpha;
- }
-
- public void setMustStartWithAlpha(final boolean mustStartWithAlpha) {
- this.mustStartWithAlpha = mustStartWithAlpha;
- }
-
- public boolean isMustntEndWithAlpha() {
- return mustntEndWithAlpha;
- }
-
- public void setMustntEndWithAlpha(final boolean mustntEndWithAlpha) {
- this.mustntEndWithAlpha = mustntEndWithAlpha;
- }
-
- public boolean isMustntStartWithAlpha() {
- return mustntStartWithAlpha;
- }
-
- public void setMustntStartWithAlpha(final boolean mustntStartWithAlpha) {
- this.mustntStartWithAlpha = mustntStartWithAlpha;
+ public void setRepeatSame(final int repeatSame) {
+ this.repeatSame = repeatSame;
}
public boolean isUsernameAllowed() {
@@ -318,21 +157,9 @@ public class DefaultPasswordRuleConf extends AbstractPasswordRuleConf {
return wordsNotPermitted;
}
- @JacksonXmlElementWrapper(localName = "prefixesNotPermitted")
- @JacksonXmlProperty(localName = "prefix")
- public List<String> getPrefixesNotPermitted() {
- return prefixesNotPermitted;
- }
-
@JacksonXmlElementWrapper(localName = "schemasNotPermitted")
@JacksonXmlProperty(localName = "schema")
public List<String> getSchemasNotPermitted() {
return schemasNotPermitted;
}
-
- @JacksonXmlElementWrapper(localName = "suffixesNotPermitted")
- @JacksonXmlProperty(localName = "suffix")
- public List<String> getSuffixesNotPermitted() {
- return suffixesNotPermitted;
- }
}
diff --git a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java
index ef107f7820..34fa534d45 100644
--- a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java
+++ b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java
@@ -251,7 +251,7 @@ public class ReconciliationLogic extends AbstractTransactionalLogic<EntityTO> {
status.setOnSyncope(getOnSyncope(any, connObjectKeyItem, provision));
List<ConnectorObject> connObjs = outboundMatcher.match(connectorManager.getConnector(
- provision.getResource()), any, provision, Optional.of(moreAttrsToGet.toArray(new String[] {})));
+ provision.getResource()), any, provision, Optional.of(moreAttrsToGet.toArray(String[]::new)));
if (!connObjs.isEmpty()) {
status.setOnResource(ConnObjectUtils.getConnObjectTO(
outboundMatcher.getFIQL(connObjs.get(0), provision), connObjs.get(0).getAttributes()));
@@ -274,7 +274,7 @@ public class ReconciliationLogic extends AbstractTransactionalLogic<EntityTO> {
Stream<MappingItem> mapItems = Stream.concat(
provision.getMapping().getItems().stream(),
virSchemaDAO.findByProvision(provision).stream().map(VirSchema::asLinkingMappingItem));
- OperationOptions options = MappingUtils.buildOperationOptions(mapItems, moreAttrsToGet.toArray(new String[0]));
+ OperationOptions options = MappingUtils.buildOperationOptions(mapItems, moreAttrsToGet.toArray(String[]::new));
SyncDeltaBuilder syncDeltaBuilder = new SyncDeltaBuilder().
setToken(new SyncToken("")).
diff --git a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
index 6f7dcb09c0..ed35249621 100644
--- a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
+++ b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
@@ -417,7 +417,7 @@ public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> {
provision = null;
objectClass = resource.getOrgUnit().getObjectClass();
options = MappingUtils.buildOperationOptions(
- resource.getOrgUnit().getItems().stream(), moreAttrsToGet.toArray(new String[0]));
+ resource.getOrgUnit().getItems().stream(), moreAttrsToGet.toArray(String[]::new));
} else {
provision = getProvision(key, anyTypeKey);
resource = provision.getResource();
@@ -426,7 +426,7 @@ public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> {
Stream<MappingItem> mapItems = Stream.concat(
provision.getMapping().getItems().stream(),
virSchemaDAO.findByProvision(provision).stream().map(VirSchema::asLinkingMappingItem));
- options = MappingUtils.buildOperationOptions(mapItems, moreAttrsToGet.toArray(new String[0]));
+ options = MappingUtils.buildOperationOptions(mapItems, moreAttrsToGet.toArray(String[]::new));
}
List<ConnObjectTO> connObjects = new ArrayList<>();
diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/JavaDocUtils.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/JavaDocUtils.java
index 2a76632dc8..43b14f4977 100644
--- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/JavaDocUtils.java
+++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/JavaDocUtils.java
@@ -43,7 +43,7 @@ public final class JavaDocUtils {
}
}
if (!javaDocURLs.isEmpty()) {
- result = javaDocURLs.toArray(new URL[javaDocURLs.size()]);
+ result = javaDocURLs.toArray(URL[]::new);
}
}
diff --git a/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
index 1fad8310e4..05b0b89db3 100644
--- a/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
@@ -22,12 +22,12 @@ under the License.
<PasswordPolicy id="ce93fcda-dc3a-4369-a7b0-a6108c261c85" name="a password policy"
historyLength="1" allowNullPassword="1"/>
<Implementation id="DefaultPasswordRuleConf1" type="PASSWORD_RULE" engine="JAVA"
- body='{"_class":"org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf","maxLength":0,"minLength":8,"nonAlphanumericRequired":false,"alphanumericRequired":false,"digitRequired":false,"lowercaseRequired":false,"uppercaseRequired":false,"mustStartWithDigit":false,"mustntStartWithDigit":false,"mustEndWithDigit":false,"mustntEndWithDigit":false,"mustStartWithNonAlpha":false,"mustStartWithAlpha":false,"mustntStartWithNonAlpha":false,"mustntStartWithAlpha":false,"mustE [...]
+ body='{"_class":"org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf","maxLength":0,"minLength":8,"wordsNotPermitted":["notpermitted1","notpermitted2"]}'/>
<PasswordPolicyRule policy_id="ce93fcda-dc3a-4369-a7b0-a6108c261c85" implementation_id="DefaultPasswordRuleConf1"/>
<PasswordPolicy id="986d1236-3ac5-4a19-810c-5ab21d79cba1"
name="sample password policy" historyLength="0" allowNullPassword="1"/>
<Implementation id="DefaultPasswordRuleConf2" type="PASSWORD_RULE" engine="JAVA"
- body='{"_class":"org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf","maxLength":0,"minLength":10,"nonAlphanumericRequired":false,"alphanumericRequired":false,"digitRequired":true,"lowercaseRequired":false,"uppercaseRequired":false,"mustStartWithDigit":false,"mustntStartWithDigit":false,"mustEndWithDigit":false,"mustntEndWithDigit":false,"mustStartWithNonAlpha":false,"mustStartWithAlpha":false,"mustntStartWithNonAlpha":false,"mustntStartWithAlpha":false,"mustE [...]
+ body='{"_class":"org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf","maxLength":0,"minLength":10,"digit":1,"wordsNotPermitted":["notpermitted1","notpermitted2"]}'/>
<PasswordPolicyRule policy_id="986d1236-3ac5-4a19-810c-5ab21d79cba1" implementation_id="DefaultPasswordRuleConf2"/>
<AccountPolicy id="20ab5a8c-4b0c-432c-b957-f7fb9784d9f7" name="an account policy"
propagateSuspension="0" maxAuthenticationAttempts="0"/>
@@ -41,11 +41,11 @@ under the License.
<AccountPolicyRule policy_id="06e2ed52-6966-44aa-a177-a0ca7434201f" implementation_id="DefaultAccountRuleConf2"/>
<PasswordPolicy id="55e5de0b-c79c-4e66-adda-251b6fb8579a" name="sample password policy" historyLength="0" allowNullPassword="0"/>
<Implementation id="DefaultPasswordRuleConf3" type="PASSWORD_RULE" engine="JAVA"
- body='{"_class":"org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf","maxLength":0,"minLength":10,"nonAlphanumericRequired":true,"alphanumericRequired":false,"digitRequired":true,"lowercaseRequired":true,"uppercaseRequired":true,"mustStartWithDigit":true,"mustntStartWithDigit":false,"mustEndWithDigit":true,"mustntEndWithDigit":false,"mustStartWithNonAlpha":false,"mustStartWithAlpha":false,"mustntStartWithNonAlpha":false,"mustntStartWithAlpha":false,"mustEndWit [...]
+ body='{"_class":"org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf","maxLength":0,"minLength":10,"special":1,"specialChars":["@","!"],"digit":1,"lowercase":1,"uppercase":1,"wordsNotPermitted":["notpermitted1","notpermitted2"]}'/>
<PasswordPolicyRule policy_id="55e5de0b-c79c-4e66-adda-251b6fb8579a" implementation_id="DefaultPasswordRuleConf3"/>
<PropagationPolicy id="89d322db-9878-420c-b49c-67be13df9a12" name="sample propagation policy"
maxAttempts="5" backOffStrategy="FIXED" backOffParams="10000"/>
-
+
<!-- Authentication policies -->
<AuthPolicy id="659b9906-4b6e-4bc0-aca0-6809dff346d4" name="MyDefaultAuthPolicyConf"
jsonConf='{"_class":"org.apache.syncope.common.lib.policy.DefaultAuthPolicyConf","authModules":["LdapAuthenticationTest"]}'/>
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java
index c7048c7424..8d5de57869 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java
@@ -53,7 +53,7 @@ public class PersistenceTestContext {
for (String location : System.getProperty("CORE_PROPERTIES").split(",")) {
locations.add(resourceLoader.getResource(location));
}
- ppc.setLocations(locations.toArray(new Resource[0]));
+ ppc.setLocations(locations.toArray(Resource[]::new));
return ppc;
}
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java
index a7f568430b..b69f426f36 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java
@@ -40,7 +40,6 @@ import org.apache.syncope.core.persistence.api.dao.UserDAO;
import org.apache.syncope.core.persistence.api.entity.user.UPlainAttrValue;
import org.apache.syncope.core.persistence.api.entity.user.User;
import org.apache.syncope.core.persistence.jpa.AbstractTest;
-import org.apache.syncope.core.spring.policy.InvalidPasswordRuleConf;
import org.apache.syncope.core.spring.security.Encryptor;
import org.apache.syncope.core.spring.security.PasswordGenerator;
import org.apache.syncope.core.persistence.api.dao.RealmDAO;
@@ -296,12 +295,7 @@ public class UserTest extends AbstractTest {
@Test
public void testPasswordGenerator() {
- String password = "";
- try {
- password = passwordGenerator.generate(resourceDAO.find("ws-target-resource-nopropagation"));
- } catch (InvalidPasswordRuleConf e) {
- fail(e::getMessage);
- }
+ String password = passwordGenerator.generate(resourceDAO.find("ws-target-resource-nopropagation"));
assertNotNull(password);
User user = userDAO.find("c9b2dec2-00a7-4855-97c0-d854842b4b24");
@@ -312,12 +306,7 @@ public class UserTest extends AbstractTest {
@Test
public void passwordGeneratorFailing() {
assertThrows(IllegalArgumentException.class, () -> {
- String password = "";
- try {
- password = passwordGenerator.generate(resourceDAO.find("ws-target-resource-nopropagation"));
- } catch (InvalidPasswordRuleConf e) {
- fail(e.getMessage());
- }
+ String password = passwordGenerator.generate(resourceDAO.find("ws-target-resource-nopropagation"));
assertNotNull(password);
User user = userDAO.find("c9b2dec2-00a7-4855-97c0-d854842b4b24");
diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
index 05caca9153..d11af422b7 100644
--- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
@@ -22,12 +22,12 @@ under the License.
<PasswordPolicy id="ce93fcda-dc3a-4369-a7b0-a6108c261c85" name="a password policy"
historyLength="1" allowNullPassword="1"/>
<Implementation id="DefaultPasswordRuleConf1" type="PASSWORD_RULE" engine="JAVA"
- body='{"_class":"org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf","maxLength":0,"minLength":8,"nonAlphanumericRequired":false,"alphanumericRequired":false,"digitRequired":false,"lowercaseRequired":false,"uppercaseRequired":false,"mustStartWithDigit":false,"mustntStartWithDigit":false,"mustEndWithDigit":false,"mustntEndWithDigit":false,"mustStartWithNonAlpha":false,"mustStartWithAlpha":false,"mustntStartWithNonAlpha":false,"mustntStartWithAlpha":false,"mustE [...]
+ body='{"_class":"org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf","maxLength":0,"minLength":8,"wordsNotPermitted":["notpermitted1","notpermitted2"]}'/>
<PasswordPolicyRule policy_id="ce93fcda-dc3a-4369-a7b0-a6108c261c85" implementation_id="DefaultPasswordRuleConf1"/>
<PasswordPolicy id="986d1236-3ac5-4a19-810c-5ab21d79cba1"
name="sample password policy" historyLength="0" allowNullPassword="1"/>
<Implementation id="DefaultPasswordRuleConf2" type="PASSWORD_RULE" engine="JAVA"
- body='{"_class":"org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf","maxLength":0,"minLength":10,"nonAlphanumericRequired":false,"alphanumericRequired":false,"digitRequired":true,"lowercaseRequired":false,"uppercaseRequired":false,"mustStartWithDigit":false,"mustntStartWithDigit":false,"mustEndWithDigit":false,"mustntEndWithDigit":false,"mustStartWithNonAlpha":false,"mustStartWithAlpha":false,"mustntStartWithNonAlpha":false,"mustntStartWithAlpha":false,"mustE [...]
+ body='{"_class":"org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf","maxLength":0,"minLength":10,"digit":1,"wordsNotPermitted":["notpermitted1","notpermitted2"]}'/>
<PasswordPolicyRule policy_id="986d1236-3ac5-4a19-810c-5ab21d79cba1" implementation_id="DefaultPasswordRuleConf2"/>
<AccountPolicy id="20ab5a8c-4b0c-432c-b957-f7fb9784d9f7" name="an account policy"
propagateSuspension="0" maxAuthenticationAttempts="0"/>
@@ -41,7 +41,7 @@ under the License.
<AccountPolicyRule policy_id="06e2ed52-6966-44aa-a177-a0ca7434201f" implementation_id="DefaultAccountRuleConf2"/>
<PasswordPolicy id="55e5de0b-c79c-4e66-adda-251b6fb8579a" name="sample password policy" historyLength="0" allowNullPassword="0"/>
<Implementation id="DefaultPasswordRuleConf3" type="PASSWORD_RULE" engine="JAVA"
- body='{"_class":"org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf","maxLength":0,"minLength":10,"nonAlphanumericRequired":true,"alphanumericRequired":false,"digitRequired":true,"lowercaseRequired":true,"uppercaseRequired":true,"mustStartWithDigit":true,"mustntStartWithDigit":false,"mustEndWithDigit":true,"mustntEndWithDigit":false,"mustStartWithNonAlpha":false,"mustStartWithAlpha":false,"mustntStartWithNonAlpha":false,"mustntStartWithAlpha":false,"mustEndWit [...]
+ body='{"_class":"org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf","maxLength":0,"minLength":10,"special":1,"specialChars":["@","!"],"digit":1,"lowercase":1,"uppercase":1,"wordsNotPermitted":["notpermitted1","notpermitted2"]}'/>
<PasswordPolicyRule policy_id="55e5de0b-c79c-4e66-adda-251b6fb8579a" implementation_id="DefaultPasswordRuleConf3"/>
<PropagationPolicy id="89d322db-9878-420c-b49c-67be13df9a12" name="sample propagation policy"
maxAttempts="5" backOffStrategy="FIXED" backOffParams="10000"/>
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java
index 8a1f7d16ba..d6ba13da05 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java
@@ -483,7 +483,7 @@ public class ConnectorFacadeProxy implements Connector {
} else if (File.class.equals(propertySchemaClass)) {
value = new File(values.get(0).toString());
} else if (String[].class.equals(propertySchemaClass)) {
- value = values.toArray(new String[] {});
+ value = values.toArray(String[]::new);
} else {
value = values.get(0) == null ? null : values.get(0).toString();
}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnIdBundleManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnIdBundleManager.java
index 3c6d875918..9e18cfdb9c 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnIdBundleManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnIdBundleManager.java
@@ -106,8 +106,8 @@ public class DefaultConnIdBundleManager implements ConnIdBundleManager {
+ "\n\tFiles: {}", bundleFileURLs);
// 2. Get connector info manager
- ConnectorInfoManager manager = ConnectorInfoManagerFactory.getInstance().getLocalManager(
- bundleFileURLs.toArray(new URL[bundleFileURLs.size()]));
+ ConnectorInfoManager manager =
+ ConnectorInfoManagerFactory.getInstance().getLocalManager(bundleFileURLs.toArray(URL[]::new));
if (manager == null) {
throw new NotFoundException("Local ConnectorInfoManager");
}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java
index a4c90bd90f..30d509256d 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java
@@ -90,7 +90,6 @@ import org.apache.syncope.core.provisioning.api.cache.VirAttrCache;
import org.apache.syncope.core.provisioning.api.cache.VirAttrCacheKey;
import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils;
import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
-import org.apache.syncope.core.spring.policy.InvalidPasswordRuleConf;
import org.apache.syncope.core.spring.security.Encryptor;
import org.apache.syncope.core.spring.security.PasswordGenerator;
import org.identityconnectors.framework.common.FrameworkUtil;
@@ -517,11 +516,7 @@ public class DefaultMappingManager implements MappingManager {
}
if (passwordAttrValue == null && provision.getResource().isRandomPwdIfNotProvided()) {
- try {
- passwordAttrValue = passwordGenerator.generate(provision.getResource());
- } catch (InvalidPasswordRuleConf e) {
- LOG.error("Could not generate policy-compliant random password for {}", account, e);
- }
+ passwordAttrValue = passwordGenerator.generate(provision.getResource());
}
return passwordAttrValue;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
index e6daff8a3d..31e95b8adf 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
@@ -749,7 +749,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
orgUnit.isIgnoreCaseMatch(),
MappingUtils.buildOperationOptions(
MappingUtils.getPropagationItems(orgUnit.getItems().stream()),
- moreAttrsToGet.toArray(new String[0])));
+ moreAttrsToGet.toArray(String[]::new)));
} catch (TimeoutException toe) {
LOG.debug("Request timeout", toe);
throw toe;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
index 046ab729b0..2f9445f0ef 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
@@ -260,7 +260,7 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
Set<String> moreAttrsToGet = new HashSet<>();
profile.getActions().forEach(action -> moreAttrsToGet.addAll(action.moreAttrsToGet(profile, provision)));
List<ConnectorObject> connObjs = outboundMatcher.match(
- profile.getConnector(), any, provision, Optional.of(moreAttrsToGet.toArray(new String[0])));
+ profile.getConnector(), any, provision, Optional.of(moreAttrsToGet.toArray(String[]::new)));
LOG.debug("Match(es) found for {} as {}: {}", any, provision.getObjectClass(), connObjs);
if (connObjs.size() > 1) {
@@ -454,7 +454,7 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
if (notificationsAvailable || auditRequested) {
resultStatus = AuditElements.Result.SUCCESS;
output = outboundMatcher.match(
- profile.getConnector(), any, provision, Optional.of(moreAttrsToGet.toArray(new String[0])));
+ profile.getConnector(), any, provision, Optional.of(moreAttrsToGet.toArray(String[]::new)));
}
} catch (IgnoreProvisionException e) {
throw e;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/OutboundMatcher.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/OutboundMatcher.java
index 33d2d6493c..da3b84aae8 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/OutboundMatcher.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/OutboundMatcher.java
@@ -131,7 +131,7 @@ public class OutboundMatcher {
connector,
rule.get().getFilter(any, provision),
provision,
- Optional.of(moreAttrsToGet.toArray(new String[0])),
+ Optional.of(moreAttrsToGet.toArray(String[]::new)),
Optional.empty()));
} else {
MappingUtils.getConnObjectKeyItem(provision).flatMap(connObjectKeyItem -> matchByConnObjectKeyValue(
@@ -139,7 +139,7 @@ public class OutboundMatcher {
connObjectKeyItem,
connObjectKeyValue,
provision,
- Optional.of(moreAttrsToGet.toArray(new String[0])),
+ Optional.of(moreAttrsToGet.toArray(String[]::new)),
Optional.empty())).ifPresent(result::add);
}
} catch (RuntimeException e) {
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java
index 62ceee8aa4..b67aa53f0f 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java
@@ -240,7 +240,7 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i
Set<String> moreAttrsToGet = new HashSet<>();
actions.forEach(action -> moreAttrsToGet.addAll(action.moreAttrsToGet(profile, orgUnit)));
OperationOptions options = MappingUtils.buildOperationOptions(
- MappingUtils.getPullItems(orgUnit.getItems().stream()), moreAttrsToGet.toArray(new String[0]));
+ MappingUtils.getPullItems(orgUnit.getItems().stream()), moreAttrsToGet.toArray(String[]::new));
RealmPullResultHandler handler = buildRealmHandler();
handler.setProfile(profile);
@@ -326,7 +326,7 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i
MappingUtils.getPullItems(provision.getMapping().getItems().stream()),
virSchemaDAO.findByProvision(provision).stream().map(VirSchema::asLinkingMappingItem));
OperationOptions options = MappingUtils.buildOperationOptions(
- mapItems, moreAttrsToGet.toArray(new String[0]));
+ mapItems, moreAttrsToGet.toArray(String[]::new));
switch (pullTask.getPullMode()) {
case INCREMENTAL:
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java
index 0b89fad338..12086e606f 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java
@@ -157,7 +157,7 @@ public class SinglePullJobDelegate extends PullJobDelegate implements SyncopeSin
provision.getObjectClass(),
reconFilterBuilder,
handler,
- MappingUtils.buildOperationOptions(mapItems, matg.toArray(new String[0])));
+ MappingUtils.buildOperationOptions(mapItems, matg.toArray(String[]::new)));
try {
setGroupOwners(ghandler);
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java
index 1afe25440c..67004b1b88 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java
@@ -240,7 +240,7 @@ public class StreamPullJobDelegate extends PullJobDelegate implements SyncopeStr
connector.fullReconciliation(
provision.getObjectClass(),
handler,
- MappingUtils.buildOperationOptions(mapItems, moreAttrsToGet.toArray(new String[0])));
+ MappingUtils.buildOperationOptions(mapItems, moreAttrsToGet.toArray(String[]::new)));
try {
setGroupOwners(ghandler);
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java
index bbf2614eae..0582f89470 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java
@@ -50,10 +50,8 @@ import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit;
import org.apache.syncope.core.persistence.api.entity.resource.Provision;
import org.apache.syncope.core.persistence.api.entity.task.PullTask;
import org.apache.syncope.core.provisioning.api.MappingManager;
-import org.apache.syncope.core.spring.policy.InvalidPasswordRuleConf;
import org.apache.syncope.core.spring.security.Encryptor;
import org.apache.syncope.core.spring.security.PasswordGenerator;
-import org.apache.syncope.core.spring.security.SecureRandomUtils;
import org.identityconnectors.common.security.GuardedByteArray;
import org.identityconnectors.common.security.GuardedString;
import org.identityconnectors.common.security.SecurityUtil;
@@ -201,15 +199,7 @@ public class ConnObjectUtils {
filter(resource -> resource != null && resource.getPasswordPolicy() != null).
forEach(resource -> passwordPolicies.add(resource.getPasswordPolicy()));
- String password;
- try {
- password = passwordGenerator.generate(passwordPolicies);
- } catch (InvalidPasswordRuleConf e) {
- LOG.error("Could not generate policy-compliant random password for {}", userCR, e);
-
- password = SecureRandomUtils.generateRandomPassword(16);
- }
- userCR.setPassword(password);
+ userCR.setPassword(passwordGenerator.generate(passwordPolicies));
}
return anyCR;
diff --git a/core/spring/pom.xml b/core/spring/pom.xml
index 56c7b367ea..f7a230b578 100644
--- a/core/spring/pom.xml
+++ b/core/spring/pom.xml
@@ -59,6 +59,11 @@ under the License.
<artifactId>nimbus-jose-jwt</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.passay</groupId>
+ <artifactId>passay</artifactId>
+ </dependency>
+
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/policy/DefaultPasswordRule.java b/core/spring/src/main/java/org/apache/syncope/core/spring/policy/DefaultPasswordRule.java
index 1bc5af62ba..b8c2de24fe 100644
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/policy/DefaultPasswordRule.java
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/policy/DefaultPasswordRule.java
@@ -18,11 +18,16 @@
*/
package org.apache.syncope.core.spring.policy;
+import java.io.InputStream;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
+import java.util.List;
import java.util.Optional;
+import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
+import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf;
import org.apache.syncope.common.lib.policy.PasswordRuleConf;
@@ -31,6 +36,18 @@ import org.apache.syncope.core.persistence.api.dao.PasswordRuleConfClass;
import org.apache.syncope.core.persistence.api.entity.user.LinkedAccount;
import org.apache.syncope.core.persistence.api.entity.user.User;
import org.apache.syncope.core.spring.security.Encryptor;
+import org.passay.CharacterData;
+import org.passay.CharacterRule;
+import org.passay.EnglishCharacterData;
+import org.passay.IllegalCharacterRule;
+import org.passay.LengthRule;
+import org.passay.PasswordData;
+import org.passay.PasswordValidator;
+import org.passay.PropertiesMessageResolver;
+import org.passay.RepeatCharactersRule;
+import org.passay.Rule;
+import org.passay.RuleResult;
+import org.passay.UsernameRule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
@@ -39,140 +56,110 @@ import org.springframework.util.CollectionUtils;
@PasswordRuleConfClass(DefaultPasswordRuleConf.class)
public class DefaultPasswordRule implements PasswordRule {
- private static final Logger LOG = LoggerFactory.getLogger(DefaultPasswordRule.class);
+ protected static final Logger LOG = LoggerFactory.getLogger(DefaultPasswordRule.class);
- private static final Encryptor ENCRYPTOR = Encryptor.getInstance();
+ protected static final Encryptor ENCRYPTOR = Encryptor.getInstance();
- private DefaultPasswordRuleConf conf;
+ public static List<Rule> conf2Rules(final DefaultPasswordRuleConf conf) {
+ List<Rule> rules = new ArrayList<>();
- @Override
- public PasswordRuleConf getConf() {
- return conf;
- }
-
- @Override
- public void setConf(final PasswordRuleConf conf) {
- if (conf instanceof DefaultPasswordRuleConf) {
- this.conf = (DefaultPasswordRuleConf) conf;
- } else {
- throw new IllegalArgumentException(
- DefaultPasswordRuleConf.class.getName() + " expected, got " + conf.getClass().getName());
- }
- }
-
- protected void enforce(final String clear, final String username, final Set<String> wordsNotPermitted) {
- // check length
- if (conf.getMinLength() > 0 && conf.getMinLength() > clear.length()) {
- throw new PasswordPolicyException("Password too short");
+ LengthRule lengthRule = new LengthRule();
+ if (conf.getMinLength() > 0) {
+ lengthRule.setMinimumLength(conf.getMinLength());
}
-
- if (conf.getMaxLength() > 0 && conf.getMaxLength() < clear.length()) {
- throw new PasswordPolicyException("Password too long");
+ if (conf.getMaxLength() > 0) {
+ lengthRule.setMaximumLength(conf.getMaxLength());
}
+ rules.add(lengthRule);
- // check words not permitted
- if (!conf.isUsernameAllowed() && username != null && username.equals(clear)) {
- throw new PasswordPolicyException("Password mustn't be equal to username");
+ if (conf.getAlphabetical() > 0) {
+ rules.add(new CharacterRule(EnglishCharacterData.Alphabetical, conf.getAlphabetical()));
}
- wordsNotPermitted.stream().
- filter(word -> StringUtils.containsIgnoreCase(clear, word)).
- forEach(item -> {
- throw new PasswordPolicyException("Used word(s) not permitted");
- });
-
- // check digits occurrence
- if (conf.isDigitRequired() && !PolicyPattern.DIGIT.matcher(clear).matches()) {
- throw new PasswordPolicyException("Password must contain digit(s)");
+ if (conf.getUppercase() > 0) {
+ rules.add(new CharacterRule(EnglishCharacterData.UpperCase, conf.getUppercase()));
}
- // check lowercase alphabetic characters occurrence
- if (conf.isLowercaseRequired() && !PolicyPattern.ALPHA_LOWERCASE.matcher(clear).matches()) {
- throw new PasswordPolicyException("Password must contain lowercase alphabetic character(s)");
+ if (conf.getLowercase() > 0) {
+ rules.add(new CharacterRule(EnglishCharacterData.LowerCase, conf.getLowercase()));
}
- // check uppercase alphabetic characters occurrence
- if (conf.isUppercaseRequired() && !PolicyPattern.ALPHA_UPPERCASE.matcher(clear).matches()) {
- throw new PasswordPolicyException("Password must contain uppercase alphabetic character(s)");
+ if (conf.getDigit() > 0) {
+ rules.add(new CharacterRule(EnglishCharacterData.Digit, conf.getDigit()));
}
- // check prefix
- conf.getPrefixesNotPermitted().stream().
- filter(clear::startsWith).
- forEach(item -> {
- throw new PasswordPolicyException("Prefix not permitted");
- });
-
- // check suffix
- conf.getSuffixesNotPermitted().stream().
- filter(clear::endsWith).
- forEach(item -> {
- throw new PasswordPolicyException("Suffix not permitted");
- });
+ if (conf.getSpecial() > 0) {
+ rules.add(new CharacterRule(new CharacterData() {
- // check digit first occurrence
- if (conf.isMustStartWithDigit() && !PolicyPattern.FIRST_DIGIT.matcher(clear).matches()) {
- throw new PasswordPolicyException("Password must start with a digit");
- }
-
- if (conf.isMustntStartWithDigit() && PolicyPattern.FIRST_DIGIT.matcher(clear).matches()) {
- throw new PasswordPolicyException("Password mustn't start with a digit");
- }
+ @Override
+ public String getErrorCode() {
+ return "INSUFFICIENT_SPECIAL";
+ }
- // check digit last occurrence
- if (conf.isMustEndWithDigit() && !PolicyPattern.LAST_DIGIT.matcher(clear).matches()) {
- throw new PasswordPolicyException("Password must end with a digit");
+ @Override
+ public String getCharacters() {
+ return new String(ArrayUtils.toPrimitive(conf.getSpecialChars().toArray(Character[]::new)));
+ }
+ }, conf.getSpecial()));
}
- if (conf.isMustntEndWithDigit() && PolicyPattern.LAST_DIGIT.matcher(clear).matches()) {
- throw new PasswordPolicyException("Password mustn't end with a digit");
+ if (!conf.getIllegalChars().isEmpty()) {
+ rules.add(new IllegalCharacterRule(
+ ArrayUtils.toPrimitive(conf.getIllegalChars().toArray(Character[]::new))));
}
- // check alphanumeric characters occurence
- if (conf.isAlphanumericRequired() && !PolicyPattern.ALPHANUMERIC.matcher(clear).matches()) {
- throw new PasswordPolicyException("Password must contain alphanumeric character(s)");
+ if (conf.getRepeatSame() > 0) {
+ rules.add(new RepeatCharactersRule(conf.getRepeatSame()));
}
- // check non alphanumeric characters occurence
- if (conf.isNonAlphanumericRequired() && !PolicyPattern.NON_ALPHANUMERIC.matcher(clear).matches()) {
- throw new PasswordPolicyException("Password must contain non-alphanumeric character(s)");
+ if (!conf.isUsernameAllowed()) {
+ rules.add(new UsernameRule(true, true));
}
- // check alphanumeric character first occurrence
- if (conf.isMustStartWithAlpha() && !PolicyPattern.FIRST_ALPHANUMERIC.matcher(clear).matches()) {
- throw new PasswordPolicyException("Password must start with an alphanumeric character");
- }
+ return rules;
+ }
- if (conf.isMustntStartWithAlpha() && PolicyPattern.FIRST_ALPHANUMERIC.matcher(clear).matches()) {
- throw new PasswordPolicyException("Password mustn't start with an alphanumeric character");
- }
+ protected DefaultPasswordRuleConf conf;
- // check alphanumeric character last occurrence
- if (conf.isMustEndWithAlpha() && !PolicyPattern.LAST_ALPHANUMERIC.matcher(clear).matches()) {
- throw new PasswordPolicyException("Password must end with an alphanumeric character");
- }
+ protected PasswordValidator passwordValidator;
- if (conf.isMustntEndWithAlpha() && PolicyPattern.LAST_ALPHANUMERIC.matcher(clear).matches()) {
- throw new PasswordPolicyException("Password mustn't end with an alphanumeric character");
- }
+ @Override
+ public PasswordRuleConf getConf() {
+ return conf;
+ }
- // check non alphanumeric character first occurrence
- if (conf.isMustStartWithNonAlpha() && !PolicyPattern.FIRST_NON_ALPHANUMERIC.matcher(clear).matches()) {
- throw new PasswordPolicyException("Password must start with a non-alphanumeric character");
- }
+ @Override
+ public void setConf(final PasswordRuleConf conf) {
+ if (conf instanceof DefaultPasswordRuleConf) {
+ this.conf = (DefaultPasswordRuleConf) conf;
- if (conf.isMustntStartWithNonAlpha() && PolicyPattern.FIRST_NON_ALPHANUMERIC.matcher(clear).matches()) {
- throw new PasswordPolicyException("Password mustn't start with a non-alphanumeric character");
+ Properties passay = new Properties();
+ try (InputStream in = getClass().getResourceAsStream("/passay.properties")) {
+ passay.load(in);
+ passwordValidator = new PasswordValidator(new PropertiesMessageResolver(passay), conf2Rules(this.conf));
+ } catch (Exception e) {
+ throw new IllegalStateException("Could not initialize Passay", e);
+ }
+ } else {
+ throw new IllegalArgumentException(
+ DefaultPasswordRuleConf.class.getName() + " expected, got " + conf.getClass().getName());
}
+ }
- // check non alphanumeric character last occurrence
- if (conf.isMustEndWithNonAlpha() && !PolicyPattern.LAST_NON_ALPHANUMERIC.matcher(clear).matches()) {
- throw new PasswordPolicyException("Password must end with a non-alphanumeric character");
+ protected void enforce(final String clear, final String username, final Set<String> wordsNotPermitted) {
+ RuleResult result = passwordValidator.validate(
+ username == null ? new PasswordData(clear) : new PasswordData(username, clear));
+ if (!result.isValid()) {
+ throw new PasswordPolicyException(passwordValidator.getMessages(result).
+ stream().collect(Collectors.joining(",")));
}
- if (conf.isMustntEndWithNonAlpha() && PolicyPattern.LAST_NON_ALPHANUMERIC.matcher(clear).matches()) {
- throw new PasswordPolicyException("Password mustn't end with a non-alphanumeric character");
- }
+ // check words not permitted
+ wordsNotPermitted.stream().
+ filter(word -> StringUtils.containsIgnoreCase(clear, word)).findFirst().
+ ifPresent(word -> {
+ throw new PasswordPolicyException("Used word(s) not permitted");
+ });
}
@Transactional(readOnly = true)
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/policy/InvalidPasswordRuleConf.java b/core/spring/src/main/java/org/apache/syncope/core/spring/policy/InvalidPasswordRuleConf.java
deleted file mode 100644
index e016a25ca2..0000000000
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/policy/InvalidPasswordRuleConf.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.spring.policy;
-
-/**
- * Raise when the merge of two or more PasswordRuleconf instances led to an inconsistent condition.
- *
- * @see org.apache.syncope.common.lib.policy.PasswordRuleConf
- */
-public class InvalidPasswordRuleConf extends Exception {
-
- private static final long serialVersionUID = 4810651743226663580L;
-
- public InvalidPasswordRuleConf(final String msg) {
- super(msg);
- }
-
- public InvalidPasswordRuleConf(final String msg, final Exception e) {
- super(msg, e);
- }
-}
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultPasswordGenerator.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultPasswordGenerator.java
index f3bf0142ce..b82e80c50b 100644
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultPasswordGenerator.java
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultPasswordGenerator.java
@@ -20,13 +20,14 @@ package org.apache.syncope.core.spring.security;
import java.util.ArrayList;
import java.util.List;
-import org.apache.commons.lang3.StringUtils;
+import java.util.stream.Collectors;
import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf;
import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
import org.apache.syncope.core.spring.ImplementationManager;
-import org.apache.syncope.core.spring.policy.InvalidPasswordRuleConf;
-import org.apache.syncope.core.spring.policy.PolicyPattern;
+import org.apache.syncope.core.spring.policy.DefaultPasswordRule;
+import org.passay.CharacterRule;
+import org.passay.EnglishCharacterData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
@@ -49,7 +50,7 @@ public class DefaultPasswordGenerator implements PasswordGenerator {
@Transactional(readOnly = true)
@Override
- public String generate(final ExternalResource resource) throws InvalidPasswordRuleConf {
+ public String generate(final ExternalResource resource) {
List<PasswordPolicy> policies = new ArrayList<>();
if (resource.getPasswordPolicy() != null) {
@@ -60,14 +61,14 @@ public class DefaultPasswordGenerator implements PasswordGenerator {
}
@Override
- public String generate(final List<PasswordPolicy> policies) throws InvalidPasswordRuleConf {
- List<DefaultPasswordRuleConf> defaultRuleConfs = new ArrayList<>();
+ public String generate(final List<PasswordPolicy> policies) {
+ List<DefaultPasswordRuleConf> ruleConfs = new ArrayList<>();
policies.stream().forEach(policy -> policy.getRules().forEach(impl -> {
try {
ImplementationManager.buildPasswordRule(impl).ifPresent(rule -> {
if (rule.getConf() instanceof DefaultPasswordRuleConf) {
- defaultRuleConfs.add((DefaultPasswordRuleConf) rule.getConf());
+ ruleConfs.add((DefaultPasswordRuleConf) rule.getConf());
}
});
} catch (Exception e) {
@@ -75,12 +76,10 @@ public class DefaultPasswordGenerator implements PasswordGenerator {
}
}));
- DefaultPasswordRuleConf ruleConf = merge(defaultRuleConfs);
- check(ruleConf);
- return generate(ruleConf);
+ return generate(merge(ruleConfs));
}
- protected static DefaultPasswordRuleConf merge(final List<DefaultPasswordRuleConf> defaultRuleConfs) {
+ protected DefaultPasswordRuleConf merge(final List<DefaultPasswordRuleConf> defaultRuleConfs) {
DefaultPasswordRuleConf result = new DefaultPasswordRuleConf();
result.setMinLength(VERY_MIN_LENGTH);
result.setMaxLength(VERY_MAX_LENGTH);
@@ -90,240 +89,71 @@ public class DefaultPasswordGenerator implements PasswordGenerator {
result.setMinLength(ruleConf.getMinLength());
}
- if ((ruleConf.getMaxLength() != 0) && ((ruleConf.getMaxLength() < result.getMaxLength()))) {
+ if (ruleConf.getMaxLength() > 0 && ruleConf.getMaxLength() < result.getMaxLength()) {
result.setMaxLength(ruleConf.getMaxLength());
}
- result.getPrefixesNotPermitted().addAll(ruleConf.getPrefixesNotPermitted());
- result.getSuffixesNotPermitted().addAll(ruleConf.getSuffixesNotPermitted());
- if (!result.isNonAlphanumericRequired()) {
- result.setNonAlphanumericRequired(ruleConf.isNonAlphanumericRequired());
+ if (ruleConf.getUppercase() > result.getUppercase()) {
+ result.setUppercase(ruleConf.getUppercase());
}
- if (!result.isAlphanumericRequired()) {
- result.setAlphanumericRequired(ruleConf.isAlphanumericRequired());
- }
- if (!result.isDigitRequired()) {
- result.setDigitRequired(ruleConf.isDigitRequired());
+ if (ruleConf.getLowercase() > result.getLowercase()) {
+ result.setLowercase(ruleConf.getLowercase());
}
- if (!result.isLowercaseRequired()) {
- result.setLowercaseRequired(ruleConf.isLowercaseRequired());
- }
- if (!result.isUppercaseRequired()) {
- result.setUppercaseRequired(ruleConf.isUppercaseRequired());
- }
- if (!result.isMustStartWithDigit()) {
- result.setMustStartWithDigit(ruleConf.isMustStartWithDigit());
- }
- if (!result.isMustntStartWithDigit()) {
- result.setMustntStartWithDigit(ruleConf.isMustntStartWithDigit());
- }
- if (!result.isMustEndWithDigit()) {
- result.setMustEndWithDigit(ruleConf.isMustEndWithDigit());
- }
- if (result.isMustntEndWithDigit()) {
- result.setMustntEndWithDigit(ruleConf.isMustntEndWithDigit());
+ if (ruleConf.getDigit() > result.getDigit()) {
+ result.setDigit(ruleConf.getDigit());
}
- if (!result.isMustStartWithAlpha()) {
- result.setMustStartWithAlpha(ruleConf.isMustStartWithAlpha());
- }
- if (!result.isMustntStartWithAlpha()) {
- result.setMustntStartWithAlpha(ruleConf.isMustntStartWithAlpha());
- }
- if (!result.isMustStartWithNonAlpha()) {
- result.setMustStartWithNonAlpha(ruleConf.isMustStartWithNonAlpha());
- }
- if (!result.isMustntStartWithNonAlpha()) {
- result.setMustntStartWithNonAlpha(ruleConf.isMustntStartWithNonAlpha());
- }
- if (!result.isMustEndWithNonAlpha()) {
- result.setMustEndWithNonAlpha(ruleConf.isMustEndWithNonAlpha());
+
+ if (ruleConf.getSpecial() > result.getSpecial()) {
+ result.setSpecial(ruleConf.getSpecial());
}
- if (!result.isMustntEndWithNonAlpha()) {
- result.setMustntEndWithNonAlpha(ruleConf.isMustntEndWithNonAlpha());
+
+ if (!ruleConf.getSpecialChars().isEmpty()) {
+ result.getSpecialChars().addAll(ruleConf.getSpecialChars().stream().
+ filter(c -> !result.getSpecialChars().contains(c)).collect(Collectors.toList()));
}
- if (!result.isMustEndWithAlpha()) {
- result.setMustEndWithAlpha(ruleConf.isMustEndWithAlpha());
+
+ if (!ruleConf.getIllegalChars().isEmpty()) {
+ result.getIllegalChars().addAll(ruleConf.getIllegalChars().stream().
+ filter(c -> !result.getIllegalChars().contains(c)).collect(Collectors.toList()));
}
- if (!result.isMustntEndWithAlpha()) {
- result.setMustntEndWithAlpha(ruleConf.isMustntEndWithAlpha());
+
+ if (ruleConf.getRepeatSame() > result.getRepeatSame()) {
+ result.setRepeatSame(ruleConf.getRepeatSame());
}
+
if (!result.isUsernameAllowed()) {
result.setUsernameAllowed(ruleConf.isUsernameAllowed());
}
+
+ if (!ruleConf.getWordsNotPermitted().isEmpty()) {
+ result.getWordsNotPermitted().addAll(ruleConf.getWordsNotPermitted().stream().
+ filter(w -> !result.getWordsNotPermitted().contains(w)).collect(Collectors.toList()));
+ }
});
if (result.getMinLength() == 0) {
result.setMinLength(
result.getMaxLength() < MIN_LENGTH_IF_ZERO ? result.getMaxLength() : MIN_LENGTH_IF_ZERO);
}
-
- return result;
- }
-
- protected static void check(final DefaultPasswordRuleConf defaultPasswordRuleConf)
- throws InvalidPasswordRuleConf {
-
- if (defaultPasswordRuleConf.isMustEndWithAlpha() && defaultPasswordRuleConf.isMustntEndWithAlpha()) {
- throw new InvalidPasswordRuleConf(
- "mustEndWithAlpha and mustntEndWithAlpha are both true");
- }
- if (defaultPasswordRuleConf.isMustEndWithAlpha() && defaultPasswordRuleConf.isMustEndWithDigit()) {
- throw new InvalidPasswordRuleConf(
- "mustEndWithAlpha and mustEndWithDigit are both true");
- }
- if (defaultPasswordRuleConf.isMustEndWithDigit() && defaultPasswordRuleConf.isMustntEndWithDigit()) {
- throw new InvalidPasswordRuleConf(
- "mustEndWithDigit and mustntEndWithDigit are both true");
- }
- if (defaultPasswordRuleConf.isMustEndWithNonAlpha() && defaultPasswordRuleConf.isMustntEndWithNonAlpha()) {
- throw new InvalidPasswordRuleConf(
- "mustEndWithNonAlpha and mustntEndWithNonAlpha are both true");
- }
- if (defaultPasswordRuleConf.isMustStartWithAlpha() && defaultPasswordRuleConf.isMustntStartWithAlpha()) {
- throw new InvalidPasswordRuleConf(
- "mustStartWithAlpha and mustntStartWithAlpha are both true");
- }
- if (defaultPasswordRuleConf.isMustStartWithAlpha() && defaultPasswordRuleConf.isMustStartWithDigit()) {
- throw new InvalidPasswordRuleConf(
- "mustStartWithAlpha and mustStartWithDigit are both true");
+ if (result.getMinLength() > result.getMaxLength()) {
+ result.setMaxLength(result.getMinLength());
}
- if (defaultPasswordRuleConf.isMustStartWithDigit() && defaultPasswordRuleConf.isMustntStartWithDigit()) {
- throw new InvalidPasswordRuleConf(
- "mustStartWithDigit and mustntStartWithDigit are both true");
- }
- if (defaultPasswordRuleConf.isMustStartWithNonAlpha() && defaultPasswordRuleConf.isMustntStartWithNonAlpha()) {
- throw new InvalidPasswordRuleConf(
- "mustStartWithNonAlpha and mustntStartWithNonAlpha are both true");
- }
- if (defaultPasswordRuleConf.getMinLength() > defaultPasswordRuleConf.getMaxLength()) {
- throw new InvalidPasswordRuleConf(
- "Minimun length (" + defaultPasswordRuleConf.getMinLength() + ')'
- + "is greater than maximum length (" + defaultPasswordRuleConf.getMaxLength() + ')');
- }
- }
-
- protected static String generate(final DefaultPasswordRuleConf ruleConf) {
- String[] generatedPassword = new String[ruleConf.getMinLength()];
- for (int i = 0; i < generatedPassword.length; i++) {
- generatedPassword[i] = StringUtils.EMPTY;
- }
-
- checkStartChar(generatedPassword, ruleConf);
-
- checkEndChar(generatedPassword, ruleConf);
-
- checkRequired(generatedPassword, ruleConf);
-
- for (int firstEmptyChar = firstEmptyChar(generatedPassword);
- firstEmptyChar < generatedPassword.length - 1; firstEmptyChar++) {
-
- generatedPassword[firstEmptyChar] = SecureRandomUtils.generateRandomLetter();
- }
-
- checkPrefixAndSuffix(generatedPassword, ruleConf);
-
- return StringUtils.join(generatedPassword);
- }
-
- protected static void checkStartChar(final String[] generatedPassword, final DefaultPasswordRuleConf ruleConf) {
- if (ruleConf.isMustStartWithAlpha()) {
- generatedPassword[0] = SecureRandomUtils.generateRandomLetter();
- }
- if (ruleConf.isMustStartWithNonAlpha() || ruleConf.isMustStartWithDigit()) {
- generatedPassword[0] = SecureRandomUtils.generateRandomNumber();
- }
- if (ruleConf.isMustntStartWithAlpha()) {
- generatedPassword[0] = SecureRandomUtils.generateRandomNumber();
- }
- if (ruleConf.isMustntStartWithDigit()) {
- generatedPassword[0] = SecureRandomUtils.generateRandomLetter();
- }
- if (ruleConf.isMustntStartWithNonAlpha()) {
- generatedPassword[0] = SecureRandomUtils.generateRandomLetter();
- }
-
- if (StringUtils.EMPTY.equals(generatedPassword[0])) {
- generatedPassword[0] = SecureRandomUtils.generateRandomLetter();
- }
- }
-
- protected static void checkEndChar(final String[] generatedPassword, final DefaultPasswordRuleConf ruleConf) {
- if (ruleConf.isMustEndWithAlpha()) {
- generatedPassword[ruleConf.getMinLength() - 1] = SecureRandomUtils.generateRandomLetter();
- }
- if (ruleConf.isMustEndWithNonAlpha() || ruleConf.isMustEndWithDigit()) {
- generatedPassword[ruleConf.getMinLength() - 1] = SecureRandomUtils.generateRandomNumber();
- }
-
- if (ruleConf.isMustntEndWithAlpha()) {
- generatedPassword[ruleConf.getMinLength() - 1] = SecureRandomUtils.generateRandomNumber();
- }
- if (ruleConf.isMustntEndWithDigit()) {
- generatedPassword[ruleConf.getMinLength() - 1] = SecureRandomUtils.generateRandomLetter();
- }
- if (ruleConf.isMustntEndWithNonAlpha()) {
- generatedPassword[ruleConf.getMinLength() - 1] = SecureRandomUtils.generateRandomLetter();
- }
-
- if (StringUtils.EMPTY.equals(generatedPassword[ruleConf.getMinLength() - 1])) {
- generatedPassword[ruleConf.getMinLength() - 1] = SecureRandomUtils.generateRandomLetter();
- }
- }
-
- protected static int firstEmptyChar(final String[] generatedPStrings) {
- int index = 0;
- while (!generatedPStrings[index].isEmpty()) {
- index++;
- }
- return index;
- }
-
- protected static void checkRequired(final String[] generatedPassword, final DefaultPasswordRuleConf ruleConf) {
- if (ruleConf.isDigitRequired()
- && !PolicyPattern.DIGIT.matcher(StringUtils.join(generatedPassword)).matches()) {
-
- generatedPassword[firstEmptyChar(generatedPassword)] = SecureRandomUtils.generateRandomNumber();
- }
-
- if (ruleConf.isUppercaseRequired()
- && !PolicyPattern.ALPHA_UPPERCASE.matcher(StringUtils.join(generatedPassword)).matches()) {
-
- generatedPassword[firstEmptyChar(generatedPassword)] =
- SecureRandomUtils.generateRandomLetter().toUpperCase();
- }
-
- if (ruleConf.isLowercaseRequired()
- && !PolicyPattern.ALPHA_LOWERCASE.matcher(StringUtils.join(generatedPassword)).matches()) {
-
- generatedPassword[firstEmptyChar(generatedPassword)] =
- SecureRandomUtils.generateRandomLetter().toLowerCase();
- }
-
- if (ruleConf.isNonAlphanumericRequired()
- && !PolicyPattern.NON_ALPHANUMERIC.matcher(StringUtils.join(generatedPassword)).matches()) {
-
- generatedPassword[firstEmptyChar(generatedPassword)] =
- SecureRandomUtils.generateRandomNonAlphanumericChar(
- PolicyPattern.NON_ALPHANUMERIC_CHARS_FOR_PASSWORD_VALUES);
- }
+ return result;
}
- protected static void checkPrefixAndSuffix(
- final String[] generatedPassword, final DefaultPasswordRuleConf ruleConf) {
-
- ruleConf.getPrefixesNotPermitted().forEach(prefix -> {
- if (StringUtils.join(generatedPassword).startsWith(prefix)) {
- checkStartChar(generatedPassword, ruleConf);
- }
- });
-
- ruleConf.getSuffixesNotPermitted().forEach(suffix -> {
- if (StringUtils.join(generatedPassword).endsWith(suffix)) {
- checkEndChar(generatedPassword, ruleConf);
- }
- });
+ protected String generate(final DefaultPasswordRuleConf ruleConf) {
+ List<CharacterRule> characterRules = DefaultPasswordRule.conf2Rules(ruleConf).stream().
+ filter(CharacterRule.class::isInstance).map(CharacterRule.class::cast).
+ collect(Collectors.toList());
+ if (characterRules.isEmpty()) {
+ int halfMinLength = ruleConf.getMinLength() / 2;
+ characterRules = List.of(
+ new CharacterRule(EnglishCharacterData.Alphabetical, halfMinLength),
+ new CharacterRule(EnglishCharacterData.Digit, halfMinLength));
+ }
+ return SecureRandomUtils.passwordGenerator().generatePassword(ruleConf.getMinLength(), characterRules);
}
}
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/PasswordGenerator.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/PasswordGenerator.java
index a73a8f2092..17518b1ed2 100644
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/PasswordGenerator.java
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/PasswordGenerator.java
@@ -21,12 +21,10 @@ package org.apache.syncope.core.spring.security;
import java.util.List;
import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
-import org.apache.syncope.core.spring.policy.InvalidPasswordRuleConf;
public interface PasswordGenerator {
- String generate(ExternalResource resource) throws InvalidPasswordRuleConf;
-
- String generate(List<PasswordPolicy> policies) throws InvalidPasswordRuleConf;
+ String generate(ExternalResource resource);
+ String generate(List<PasswordPolicy> policies);
}
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecureRandomUtils.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecureRandomUtils.java
index 9d7be34991..d27e37240a 100644
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecureRandomUtils.java
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecureRandomUtils.java
@@ -23,11 +23,14 @@ import com.fasterxml.uuid.impl.RandomBasedGenerator;
import java.security.SecureRandom;
import java.util.UUID;
import org.apache.commons.text.RandomStringGenerator;
+import org.passay.PasswordGenerator;
public final class SecureRandomUtils {
private static final SecureRandom RANDOM = new SecureRandom();
+ private static final PasswordGenerator PASSWORD_GENERATOR = new PasswordGenerator(RANDOM);
+
private static final RandomStringGenerator FOR_PASSWORD = new RandomStringGenerator.Builder().
usingRandom(RANDOM::nextInt).
withinRange('0', 'z').
@@ -79,6 +82,10 @@ public final class SecureRandomUtils {
return UUID_GENERATOR.generate();
}
+ public static PasswordGenerator passwordGenerator() {
+ return PASSWORD_GENERATOR;
+ }
+
private SecureRandomUtils() {
// private constructor for static utility class
}
diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/ImplementationManagerTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/ImplementationManagerTest.java
index 4e32e2fb31..fa292318ac 100644
--- a/core/spring/src/test/java/org/apache/syncope/core/spring/ImplementationManagerTest.java
+++ b/core/spring/src/test/java/org/apache/syncope/core/spring/ImplementationManagerTest.java
@@ -37,27 +37,12 @@ import java.util.stream.Collectors;
@SpringJUnitConfig(classes = { SpringTestConfiguration.class })
public class ImplementationManagerTest {
- private static DefaultPasswordRuleConf createBaseDefaultPasswordRuleConf() {
+ public static DefaultPasswordRuleConf createBaseDefaultPasswordRuleConf() {
DefaultPasswordRuleConf baseDefaultPasswordRuleConf = new DefaultPasswordRuleConf();
- baseDefaultPasswordRuleConf.setAlphanumericRequired(false);
- baseDefaultPasswordRuleConf.setDigitRequired(false);
- baseDefaultPasswordRuleConf.setLowercaseRequired(false);
+ baseDefaultPasswordRuleConf.setUppercase(1);
+ baseDefaultPasswordRuleConf.setDigit(1);
baseDefaultPasswordRuleConf.setMaxLength(1000);
baseDefaultPasswordRuleConf.setMinLength(8);
- baseDefaultPasswordRuleConf.setMustEndWithAlpha(false);
- baseDefaultPasswordRuleConf.setMustEndWithDigit(false);
- baseDefaultPasswordRuleConf.setMustEndWithNonAlpha(false);
- baseDefaultPasswordRuleConf.setMustStartWithAlpha(false);
- baseDefaultPasswordRuleConf.setMustStartWithDigit(false);
- baseDefaultPasswordRuleConf.setMustStartWithNonAlpha(false);
- baseDefaultPasswordRuleConf.setMustntEndWithAlpha(false);
- baseDefaultPasswordRuleConf.setMustntEndWithDigit(false);
- baseDefaultPasswordRuleConf.setMustntEndWithNonAlpha(false);
- baseDefaultPasswordRuleConf.setMustntStartWithAlpha(false);
- baseDefaultPasswordRuleConf.setMustntStartWithDigit(false);
- baseDefaultPasswordRuleConf.setMustntStartWithNonAlpha(false);
- baseDefaultPasswordRuleConf.setNonAlphanumericRequired(false);
- baseDefaultPasswordRuleConf.setUppercaseRequired(false);
return baseDefaultPasswordRuleConf;
}
diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/PasswordGeneratorTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/PasswordGeneratorTest.java
index 0cf68d9f4a..fd9696725d 100644
--- a/core/spring/src/test/java/org/apache/syncope/core/spring/security/PasswordGeneratorTest.java
+++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/PasswordGeneratorTest.java
@@ -20,18 +20,12 @@ package org.apache.syncope.core.spring.security;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.fail;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf;
-import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+import org.apache.syncope.core.spring.ImplementationManagerTest;
import org.apache.syncope.core.spring.SpringTestConfiguration;
-import org.apache.syncope.core.spring.policy.InvalidPasswordRuleConf;
-import org.apache.syncope.core.spring.policy.PolicyPattern;
import org.junit.jupiter.api.Test;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
@@ -40,148 +34,80 @@ public class PasswordGeneratorTest {
private final DefaultPasswordGenerator passwordGenerator = new DefaultPasswordGenerator();
- private static DefaultPasswordRuleConf createBaseDefaultPasswordRuleConf() {
- DefaultPasswordRuleConf baseDefaultPasswordRuleConf = new DefaultPasswordRuleConf();
- baseDefaultPasswordRuleConf.setAlphanumericRequired(false);
- baseDefaultPasswordRuleConf.setDigitRequired(false);
- baseDefaultPasswordRuleConf.setLowercaseRequired(false);
- baseDefaultPasswordRuleConf.setMaxLength(1000);
- baseDefaultPasswordRuleConf.setMinLength(8);
- baseDefaultPasswordRuleConf.setMustEndWithAlpha(false);
- baseDefaultPasswordRuleConf.setMustEndWithDigit(false);
- baseDefaultPasswordRuleConf.setMustEndWithNonAlpha(false);
- baseDefaultPasswordRuleConf.setMustStartWithAlpha(false);
- baseDefaultPasswordRuleConf.setMustStartWithDigit(false);
- baseDefaultPasswordRuleConf.setMustStartWithNonAlpha(false);
- baseDefaultPasswordRuleConf.setMustntEndWithAlpha(false);
- baseDefaultPasswordRuleConf.setMustntEndWithDigit(false);
- baseDefaultPasswordRuleConf.setMustntEndWithNonAlpha(false);
- baseDefaultPasswordRuleConf.setMustntStartWithAlpha(false);
- baseDefaultPasswordRuleConf.setMustntStartWithDigit(false);
- baseDefaultPasswordRuleConf.setMustntStartWithNonAlpha(false);
- baseDefaultPasswordRuleConf.setNonAlphanumericRequired(false);
- baseDefaultPasswordRuleConf.setUppercaseRequired(false);
- return baseDefaultPasswordRuleConf;
+ @Test
+ public void digit() {
+ DefaultPasswordRuleConf pwdRuleConf = ImplementationManagerTest.createBaseDefaultPasswordRuleConf();
+ pwdRuleConf.setDigit(1);
+ TestImplementation passwordRule = new TestImplementation();
+ passwordRule.setBody(POJOHelper.serialize(pwdRuleConf));
+
+ String generatedPassword = passwordGenerator.generate(List.of(new TestPasswordPolicy(passwordRule)));
+
+ assertTrue(generatedPassword.chars().anyMatch(Character::isDigit));
}
@Test
- public void startEndWithDigit() throws InvalidPasswordRuleConf {
- DefaultPasswordRuleConf pwdRuleConf1 = createBaseDefaultPasswordRuleConf();
- pwdRuleConf1.setMustStartWithDigit(true);
- TestImplementation passwordRule1 = new TestImplementation();
- passwordRule1.setBody(POJOHelper.serialize(pwdRuleConf1));
- TestPasswordPolicy policy1 = new TestPasswordPolicy();
- policy1.add(passwordRule1);
-
- DefaultPasswordRuleConf pwdRuleConf2 = createBaseDefaultPasswordRuleConf();
- pwdRuleConf2.setMustEndWithDigit(true);
- TestImplementation passwordRule2 = new TestImplementation();
- passwordRule2.setBody(POJOHelper.serialize(pwdRuleConf2));
- TestPasswordPolicy policy2 = new TestPasswordPolicy();
- policy2.add(passwordRule2);
-
- List<PasswordPolicy> policies = new ArrayList<>();
- policies.add(policy1);
- policies.add(policy2);
- String generatedPassword = passwordGenerator.generate(policies);
- assertTrue(Character.isDigit(generatedPassword.charAt(0)));
- assertTrue(Character.isDigit(generatedPassword.charAt(generatedPassword.length() - 1)));
+ public void alphabetical() {
+ DefaultPasswordRuleConf pwdRuleConf = ImplementationManagerTest.createBaseDefaultPasswordRuleConf();
+ pwdRuleConf.setAlphabetical(1);
+ TestImplementation passwordRule = new TestImplementation();
+ passwordRule.setBody(POJOHelper.serialize(pwdRuleConf));
+
+ String generatedPassword = passwordGenerator.generate(List.of(new TestPasswordPolicy(passwordRule)));
+
+ assertTrue(generatedPassword.chars().anyMatch(Character::isAlphabetic));
}
@Test
- public void startWithDigitAndWithAlpha() throws InvalidPasswordRuleConf {
- DefaultPasswordRuleConf pwdRuleConf1 = createBaseDefaultPasswordRuleConf();
- pwdRuleConf1.setMustStartWithDigit(true);
- TestImplementation passwordRule1 = new TestImplementation();
- passwordRule1.setBody(POJOHelper.serialize(pwdRuleConf1));
- TestPasswordPolicy policy1 = new TestPasswordPolicy();
- policy1.add(passwordRule1);
-
- DefaultPasswordRuleConf pwdRuleConf2 = createBaseDefaultPasswordRuleConf();
- pwdRuleConf2.setMustEndWithAlpha(true);
- TestImplementation passwordRule2 = new TestImplementation();
- passwordRule2.setBody(POJOHelper.serialize(pwdRuleConf2));
- TestPasswordPolicy policy2 = new TestPasswordPolicy();
- policy2.add(passwordRule2);
-
- List<PasswordPolicy> policies = new ArrayList<>();
- policies.add(policy1);
- policies.add(policy2);
- String generatedPassword = passwordGenerator.generate(policies);
- assertTrue(Character.isDigit(generatedPassword.charAt(0)));
- assertTrue(Character.isLetter(generatedPassword.charAt(generatedPassword.length() - 1)));
+ public void lowercase() {
+ DefaultPasswordRuleConf pwdRuleConf = ImplementationManagerTest.createBaseDefaultPasswordRuleConf();
+ pwdRuleConf.setLowercase(1);
+ TestImplementation passwordRule = new TestImplementation();
+ passwordRule.setBody(POJOHelper.serialize(pwdRuleConf));
+
+ String generatedPassword = passwordGenerator.generate(List.of(new TestPasswordPolicy(passwordRule)));
+
+ assertTrue(generatedPassword.chars().anyMatch(Character::isLowerCase));
}
@Test
- public void passwordWithNonAlpha() throws InvalidPasswordRuleConf {
- DefaultPasswordRuleConf pwdRuleConf1 = createBaseDefaultPasswordRuleConf();
- pwdRuleConf1.setNonAlphanumericRequired(true);
- TestImplementation passwordRule1 = new TestImplementation();
- passwordRule1.setBody(POJOHelper.serialize(pwdRuleConf1));
- TestPasswordPolicy policy1 = new TestPasswordPolicy();
- policy1.add(passwordRule1);
-
- DefaultPasswordRuleConf pwdRuleConf2 = createBaseDefaultPasswordRuleConf();
- pwdRuleConf2.setMustEndWithAlpha(true);
- TestImplementation passwordRule2 = new TestImplementation();
- passwordRule2.setBody(POJOHelper.serialize(pwdRuleConf2));
- TestPasswordPolicy policy2 = new TestPasswordPolicy();
- policy2.add(passwordRule2);
-
- List<PasswordPolicy> policies = new ArrayList<>();
- policies.add(policy1);
- policies.add(policy2);
- String generatedPassword = passwordGenerator.generate(policies);
- assertTrue(PolicyPattern.NON_ALPHANUMERIC.matcher(generatedPassword).matches());
- assertTrue(Character.isLetter(generatedPassword.charAt(generatedPassword.length() - 1)));
+ public void uppercase() {
+ DefaultPasswordRuleConf pwdRuleConf = ImplementationManagerTest.createBaseDefaultPasswordRuleConf();
+ pwdRuleConf.setUppercase(1);
+ TestImplementation passwordRule = new TestImplementation();
+ passwordRule.setBody(POJOHelper.serialize(pwdRuleConf));
+
+ String generatedPassword = passwordGenerator.generate(List.of(new TestPasswordPolicy(passwordRule)));
+
+ assertTrue(generatedPassword.chars().anyMatch(Character::isUpperCase));
}
@Test
- public void incopatiblePolicies() {
- assertThrows(InvalidPasswordRuleConf.class, () -> {
- DefaultPasswordRuleConf pwdRuleConf1 = createBaseDefaultPasswordRuleConf();
- pwdRuleConf1.setMinLength(12);
- TestImplementation passwordRule1 = new TestImplementation();
- passwordRule1.setBody(POJOHelper.serialize(pwdRuleConf1));
- TestPasswordPolicy policy1 = new TestPasswordPolicy();
- policy1.add(passwordRule1);
-
- DefaultPasswordRuleConf pwdRuleConf2 = createBaseDefaultPasswordRuleConf();
- pwdRuleConf2.setMaxLength(10);
- TestImplementation passwordRule2 = new TestImplementation();
- passwordRule2.setBody(POJOHelper.serialize(pwdRuleConf2));
- TestPasswordPolicy policy2 = new TestPasswordPolicy();
- policy2.add(passwordRule2);
-
- List<PasswordPolicy> policies = new ArrayList<>();
- policies.add(policy1);
- policies.add(policy2);
- passwordGenerator.generate(policies);
- });
+ public void special() {
+ DefaultPasswordRuleConf pwdRuleConf = ImplementationManagerTest.createBaseDefaultPasswordRuleConf();
+ pwdRuleConf.setSpecial(1);
+ pwdRuleConf.getSpecialChars().add('@');
+ pwdRuleConf.getSpecialChars().add('!');
+ pwdRuleConf.getSpecialChars().add('%');
+ TestImplementation passwordRule = new TestImplementation();
+ passwordRule.setBody(POJOHelper.serialize(pwdRuleConf));
+
+ String generatedPassword = passwordGenerator.generate(List.of(new TestPasswordPolicy(passwordRule)));
+
+ assertTrue(generatedPassword.chars().anyMatch(c -> '@' == c || '!' == c || '%' == c));
}
@Test
public void issueSYNCOPE678() {
- String password = null;
- try {
- password = passwordGenerator.generate(Collections.<PasswordPolicy>emptyList());
- } catch (InvalidPasswordRuleConf e) {
- fail(e::getMessage);
- }
+ String password = passwordGenerator.generate(List.of());
assertNotNull(password);
- DefaultPasswordRuleConf pwdRuleConf1 = createBaseDefaultPasswordRuleConf();
+ DefaultPasswordRuleConf pwdRuleConf1 = ImplementationManagerTest.createBaseDefaultPasswordRuleConf();
pwdRuleConf1.setMinLength(0);
- TestImplementation passwordRule1 = new TestImplementation();
- passwordRule1.setBody(POJOHelper.serialize(pwdRuleConf1));
- TestPasswordPolicy policy1 = new TestPasswordPolicy();
-
- password = null;
- try {
- password = passwordGenerator.generate(Collections.<PasswordPolicy>singletonList(policy1));
- } catch (InvalidPasswordRuleConf e) {
- fail(e::getMessage);
- }
+ TestImplementation passwordRule = new TestImplementation();
+ passwordRule.setBody(POJOHelper.serialize(pwdRuleConf1));
+
+ password = passwordGenerator.generate(List.of(new TestPasswordPolicy(passwordRule)));
assertNotNull(password);
}
}
diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/TestPasswordPolicy.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/TestPasswordPolicy.java
index 33ce37a451..521b98dbff 100644
--- a/core/spring/src/test/java/org/apache/syncope/core/spring/security/TestPasswordPolicy.java
+++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/TestPasswordPolicy.java
@@ -29,6 +29,10 @@ public class TestPasswordPolicy implements PasswordPolicy {
private final List<Implementation> rules = new ArrayList<>();
+ public TestPasswordPolicy(final Implementation rule) {
+ rules.add(rule);
+ }
+
@Override
public String getKey() {
return "";
@@ -72,6 +76,5 @@ public class TestPasswordPolicy implements PasswordPolicy {
@Override
public List<? extends Implementation> getRules() {
return this.rules;
-
}
}
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
index 25a5186748..bb99f27d83 100644
--- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
@@ -20,7 +20,6 @@ package org.apache.syncope.fit.core.reference;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@@ -86,76 +85,46 @@ public class ITImplementationLookup implements ImplementationLookup {
private static final Logger LOG = LoggerFactory.getLogger(ITImplementationLookup.class);
- private static final Set<Class<?>> JWTSSOPROVIDER_CLASSES = new HashSet<>(
- List.of(SyncopeJWTSSOProvider.class, CustomJWTSSOProvider.class));
+ private static final Set<Class<?>> JWTSSOPROVIDER_CLASSES =
+ Set.of(SyncopeJWTSSOProvider.class, CustomJWTSSOProvider.class);
private static final Map<Class<? extends ReportletConf>, Class<? extends Reportlet>> REPORTLET_CLASSES =
- new HashMap<>() {
-
- private static final long serialVersionUID = 3109256773218160485L;
-
- {
- put(AuditReportletConf.class, AuditReportlet.class);
- put(ReconciliationReportletConf.class, ReconciliationReportlet.class);
- put(GroupReportletConf.class, GroupReportlet.class);
- put(UserReportletConf.class, UserReportlet.class);
- put(StaticReportletConf.class, StaticReportlet.class);
- }
- };
+ Map.of(
+ AuditReportletConf.class, AuditReportlet.class,
+ ReconciliationReportletConf.class, ReconciliationReportlet.class,
+ GroupReportletConf.class, GroupReportlet.class,
+ UserReportletConf.class, UserReportlet.class,
+ StaticReportletConf.class, StaticReportlet.class);
private static final Map<Class<? extends AccountRuleConf>, Class<? extends AccountRule>> ACCOUNT_RULE_CLASSES =
- new HashMap<>() {
-
- private static final long serialVersionUID = 3109256773218160485L;
-
- {
- put(TestAccountRuleConf.class, TestAccountRule.class);
- put(DefaultAccountRuleConf.class, DefaultAccountRule.class);
- }
- };
+ Map.of(
+ TestAccountRuleConf.class, TestAccountRule.class,
+ DefaultAccountRuleConf.class, DefaultAccountRule.class);
private static final Map<Class<? extends PasswordRuleConf>, Class<? extends PasswordRule>> PASSWORD_RULE_CLASSES =
- new HashMap<>() {
-
- private static final long serialVersionUID = -6624291041977583649L;
-
- {
- put(TestPasswordRuleConf.class, TestPasswordRule.class);
- put(DefaultPasswordRuleConf.class, DefaultPasswordRule.class);
- put(HaveIBeenPwnedPasswordRuleConf.class, HaveIBeenPwnedPasswordRule.class);
- }
- };
+ Map.of(
+ TestPasswordRuleConf.class, TestPasswordRule.class,
+ DefaultPasswordRuleConf.class, DefaultPasswordRule.class,
+ HaveIBeenPwnedPasswordRuleConf.class, HaveIBeenPwnedPasswordRule.class);
private static final Map<
Class<? extends PullCorrelationRuleConf>, Class<? extends PullCorrelationRule>> PULL_CR_CLASSES =
- new HashMap<>() {
-
- private static final long serialVersionUID = 3109256773218160485L;
-
- {
- put(DummyPullCorrelationRuleConf.class, DummyPullCorrelationRule.class);
- put(DefaultPullCorrelationRuleConf.class, DefaultPullCorrelationRule.class);
- put(LinkedAccountSamplePullCorrelationRuleConf.class, LinkedAccountSamplePullCorrelationRule.class);
- }
- };
+ Map.of(
+ DummyPullCorrelationRuleConf.class, DummyPullCorrelationRule.class,
+ DefaultPullCorrelationRuleConf.class, DefaultPullCorrelationRule.class,
+ LinkedAccountSamplePullCorrelationRuleConf.class, LinkedAccountSamplePullCorrelationRule.class);
private static final Map<
Class<? extends PushCorrelationRuleConf>, Class<? extends PushCorrelationRule>> PUSH_CR_CLASSES =
- new HashMap<>() {
-
- private static final long serialVersionUID = 3109256773218160485L;
-
- {
- put(DummyPushCorrelationRuleConf.class, DummyPushCorrelationRule.class);
- put(DefaultPushCorrelationRuleConf.class, DefaultPushCorrelationRule.class);
- }
- };
+ Map.of(
+ DummyPushCorrelationRuleConf.class, DummyPushCorrelationRule.class,
+ DefaultPushCorrelationRuleConf.class, DefaultPushCorrelationRule.class);
- private static final Set<Class<?>> AUDITAPPENDER_CLASSES = new HashSet<>(
- List.of(TestFileAuditAppender.class, TestFileRewriteAuditAppender.class));
+ private static final Set<Class<?>> AUDITAPPENDER_CLASSES =
+ Set.of(TestFileAuditAppender.class, TestFileRewriteAuditAppender.class);
- private static final Set<Class<?>> PROVISION_SORTER_CLASSES = new HashSet<>(
- List.of(DefaultProvisionSorter.class));
+ private static final Set<Class<?>> PROVISION_SORTER_CLASSES =
+ Set.of(DefaultProvisionSorter.class);
private static final Map<String, Set<String>> CLASS_NAMES = new HashMap<>() {
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
index 80aba2b058..661fbe07d0 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
@@ -779,7 +779,7 @@ public abstract class AbstractITCase {
attributes.forEach((key, value) -> items.add(new ModificationItem(DirContext.REPLACE_ATTRIBUTE,
new BasicAttribute(key, value))));
- ctx.modifyAttributes(objectDn, items.toArray(new ModificationItem[] {}));
+ ctx.modifyAttributes(objectDn, items.toArray(ModificationItem[]::new));
} catch (Exception e) {
LOG.error("While updating {} with {}", objectDn, attributes, e);
} finally {
diff --git a/pom.xml b/pom.xml
index 6e547d801d..f8e0fec48e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1864,6 +1864,12 @@ under the License.
<version>${nimbus-jose-jwt.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.passay</groupId>
+ <artifactId>passay</artifactId>
+ <version>1.6.1</version>
+ </dependency>
+
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
diff --git a/sra/src/main/java/org/apache/syncope/sra/SecurityConfig.java b/sra/src/main/java/org/apache/syncope/sra/SecurityConfig.java
index 98f2949d1b..6712428411 100644
--- a/sra/src/main/java/org/apache/syncope/sra/SecurityConfig.java
+++ b/sra/src/main/java/org/apache/syncope/sra/SecurityConfig.java
@@ -122,7 +122,7 @@ public class SecurityConfig {
registrationId(SRAProperties.AMType.OIDC.name()).
clientId(props.getOidc().getClientId()).
clientSecret(props.getOidc().getClientSecret()).
- scope(props.getOidc().getScopes().toArray(new String[0])).
+ scope(props.getOidc().getScopes().toArray(String[]::new)).
build();
}
@@ -177,7 +177,7 @@ public class SecurityConfig {
userNameAttributeName(props.getOauth2().getUserNameAttributeName()).
clientId(props.getOauth2().getClientId()).
clientSecret(props.getOauth2().getClientSecret()).
- scope(props.getOauth2().getScopes().toArray(new String[0])).
+ scope(props.getOauth2().getScopes().toArray(String[]::new)).
authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE).
jwkSetUri(props.getOauth2().getJwkSetUri()).
build();
diff --git a/sra/src/main/java/org/apache/syncope/sra/security/pac4j/ServerWebExchangeContext.java b/sra/src/main/java/org/apache/syncope/sra/security/pac4j/ServerWebExchangeContext.java
index 7cce9f7f59..81df1a1a57 100644
--- a/sra/src/main/java/org/apache/syncope/sra/security/pac4j/ServerWebExchangeContext.java
+++ b/sra/src/main/java/org/apache/syncope/sra/security/pac4j/ServerWebExchangeContext.java
@@ -87,7 +87,7 @@ public class ServerWebExchangeContext implements WebContext {
forEach((key, value) -> params.put(key, new String[] { value.toString() }));
if (this.form != null) {
- form.forEach((key, values) -> params.put(key, values.toArray(new String[0])));
+ form.forEach((key, values) -> params.put(key, values.toArray(String[]::new)));
}
return params;
diff --git a/src/main/asciidoc/reference-guide/concepts/policies.adoc b/src/main/asciidoc/reference-guide/concepts/policies.adoc
index 576d8385c9..51746fdce8 100644
--- a/src/main/asciidoc/reference-guide/concepts/policies.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/policies.adoc
@@ -216,32 +216,25 @@ endif::[]
ifeval::["{snapshotOrRelease}" == "snapshot"]
https://github.com/apache/syncope/blob/master/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultPasswordRuleConf.java[DefaultPasswordRuleConf^]
endif::[]
-) contains the following controls:
-
-* maximum length - the maximum length to allow; `0` means no limit set;
-* minimum length - the minimum length to allow; `0` means no limit set;
-* non-alphanumeric required
-* alphanumeric required
-* digit required
-* lowercase required
-* uppercase required
-* must start with digit
-* must not start with digit
-* must end with digit
-* must not end with digit
-* must start with alphanumeric
-* must start with non-alphanumeric
-* must not start with alphanumeric
-* must not start with non-alphanumeric
-* must end with alphanumeric
-* must end with non-alphanumeric
-* must not end with alphanumeric
-* must not end with non-alphanumeric
-* username allowed - whether a username value can be used
+) is based on https://www.passay.org/[Passay^] and contains the following controls:
+
+* maximum length - the maximum length to allow (`0` means no limit set);
+* minimum length - the minimum length to allow (`0` means no limit set);
+* alphabetical - the number of alphabetical characters required;
+* uppercase - the number of uppercase characters required;
+* lowercase - the number of lowercase characters required;
+* digit - the number of digits required;
+* special - the number of special characters required;
+* special chars - the set of special characters allowed;
+* illegal chars - the set of characters not allowed;
+* repeat same - the size of the longest sequence of repeating characters allowed;
+* username allowed - whether a username value can be used;
* words not permitted - list of words that cannot be present, even as a substring;
* schemas not permitted - list of <<schema,schemas>> whose values cannot be present, even as a substring;
-* prefixes not permitted - list of strings that cannot be present as a prefix;
-* suffixes not permitted - list of strings that cannot be present as a suffix.
+
+[TIP]
+The default password rule can be extended to cover specific needs, relying on the
+https://www.passay.org/reference/[whole set of features^] provided by Passay.
[NOTE]
Before being able to configure the default password rule as mentioned above, you will need to first create a `JAVA`