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 2026/04/02 14:16:15 UTC

(syncope) branch 4_1_X updated (e64b65b3c7 -> fb6ac3487c)

This is an automated email from the ASF dual-hosted git repository.

ilgrosso pushed a change to branch 4_1_X
in repository https://gitbox.apache.org/repos/asf/syncope.git


    from e64b65b3c7 Upgrading Swagger JAX-RS
     new a3ddb0f714 Bump org.mariadb.jdbc:mariadb-java-client from 3.5.7 to 3.5.8
     new fb6ac3487c [SYNCOPE-1958] Check account and passord policy compliance before save

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 ...stClient.java => UserComplianceRestClient.java} |  6 +-
 .../client/ui/commons/wizards/AjaxWizard.java      |  6 +-
 .../ui/commons/wizards/any/PasswordPanel.java      | 65 ++++++++++++++--------
 .../client/console/rest/UserRestClient.java        | 11 +++-
 .../client/console/status/ChangePasswordModal.java |  2 +-
 .../client/console/wizards/any/UserDetails.java    | 43 ++++++++++++--
 .../client/console/wizards/any/UserDetails.html    |  8 ++-
 .../enduser/pages/SelfConfirmPasswordReset.java    |  9 ++-
 .../client/enduser/panels/any/UserDetails.java     | 55 ++++++++++++++----
 .../client/enduser/rest/UserSelfRestClient.java    | 10 +++-
 .../client/enduser/panels/any/UserDetails.html     |  8 ++-
 .../apache/syncope/fit/console/PoliciesITCase.java |  2 +-
 .../apache/syncope/fit/console/RealmsITCase.java   |  6 +-
 .../apache/syncope/fit/console/UsersITCase.java    |  8 +--
 .../syncope/fit/enduser/AnonymousITCase.java       |  2 +-
 pom.xml                                            |  2 +-
 16 files changed, 178 insertions(+), 65 deletions(-)
 copy client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/rest/{RestClient.java => UserComplianceRestClient.java} (83%)


(syncope) 01/02: Bump org.mariadb.jdbc:mariadb-java-client from 3.5.7 to 3.5.8

Posted by il...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ilgrosso pushed a commit to branch 4_1_X
in repository https://gitbox.apache.org/repos/asf/syncope.git

commit a3ddb0f7144048a73b98eab5e1f73899cda08b5b
Author: dependabot[bot] <49...@users.noreply.github.com>
AuthorDate: Thu Apr 2 03:17:32 2026 +0000

    Bump org.mariadb.jdbc:mariadb-java-client from 3.5.7 to 3.5.8
    
    Bumps [org.mariadb.jdbc:mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 3.5.7 to 3.5.8.
    - [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases)
    - [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/main/CHANGELOG.md)
    - [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/3.5.7...3.5.8)
    
    ---
    updated-dependencies:
    - dependency-name: org.mariadb.jdbc:mariadb-java-client
      dependency-version: 3.5.8
      dependency-type: direct:production
      update-type: version-update:semver-patch
    ...
    
    Signed-off-by: dependabot[bot] <su...@github.com>
---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 7de3f4d73b..60d023d43e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -536,7 +536,7 @@ under the License.
 
     <jdbc.postgresql.version>42.7.10</jdbc.postgresql.version>
     <jdbc.mysql.version>9.6.0</jdbc.mysql.version>
-    <jdbc.mariadb.version>3.5.7</jdbc.mariadb.version>
+    <jdbc.mariadb.version>3.5.8</jdbc.mariadb.version>
     <jdbc.oracle.version>23.26.1.0.0</jdbc.oracle.version>
 
     <bundles.directory>${project.build.directory}/bundles</bundles.directory>


(syncope) 02/02: [SYNCOPE-1958] Check account and passord policy compliance before save

Posted by il...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ilgrosso pushed a commit to branch 4_1_X
in repository https://gitbox.apache.org/repos/asf/syncope.git

commit fb6ac3487ca1a9c8f4daba894b0142fdc19b6b89
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Thu Apr 2 15:31:13 2026 +0200

    [SYNCOPE-1958] Check account and passord policy compliance before save
---
 .../ui/commons/rest/UserComplianceRestClient.java  | 26 +++++++++
 .../client/ui/commons/wizards/AjaxWizard.java      |  6 +-
 .../ui/commons/wizards/any/PasswordPanel.java      | 65 ++++++++++++++--------
 .../client/console/rest/UserRestClient.java        | 11 +++-
 .../client/console/status/ChangePasswordModal.java |  2 +-
 .../client/console/wizards/any/UserDetails.java    | 43 ++++++++++++--
 .../client/console/wizards/any/UserDetails.html    |  8 ++-
 .../enduser/pages/SelfConfirmPasswordReset.java    |  9 ++-
 .../client/enduser/panels/any/UserDetails.java     | 55 ++++++++++++++----
 .../client/enduser/rest/UserSelfRestClient.java    | 10 +++-
 .../client/enduser/panels/any/UserDetails.html     |  8 ++-
 .../apache/syncope/fit/console/PoliciesITCase.java |  2 +-
 .../apache/syncope/fit/console/RealmsITCase.java   |  6 +-
 .../apache/syncope/fit/console/UsersITCase.java    |  8 +--
 .../syncope/fit/enduser/AnonymousITCase.java       |  2 +-
 15 files changed, 199 insertions(+), 62 deletions(-)

diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/rest/UserComplianceRestClient.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/rest/UserComplianceRestClient.java
new file mode 100644
index 0000000000..e541717cb1
--- /dev/null
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/rest/UserComplianceRestClient.java
@@ -0,0 +1,26 @@
+/*
+ * 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.client.ui.commons.rest;
+
+import org.apache.syncope.common.rest.api.beans.ComplianceQuery;
+
+public interface UserComplianceRestClient extends RestClient {
+
+    void compliance(ComplianceQuery query);
+}
diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/wizards/AjaxWizard.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/wizards/AjaxWizard.java
index d47a412536..e79e5d435c 100644
--- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/wizards/AjaxWizard.java
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/wizards/AjaxWizard.java
@@ -199,11 +199,11 @@ public abstract class AjaxWizard<T extends Serializable> extends Wizard
     public final void onFinish() {
         AjaxRequestTarget target = RequestCycle.get().find(AjaxRequestTarget.class).orElse(null);
         try {
-            final Serializable res = onApply(target);
+            Serializable applied = onApply(target);
             if (eventSink == null) {
-                send(this, Broadcast.BUBBLE, new NewItemFinishEvent<>(item, target).setResult(res));
+                send(this, Broadcast.BUBBLE, new NewItemFinishEvent<>(item, target).setResult(applied));
             } else {
-                send(eventSink, Broadcast.EXACT, new NewItemFinishEvent<>(item, target).setResult(res));
+                send(eventSink, Broadcast.EXACT, new NewItemFinishEvent<>(item, target).setResult(applied));
             }
         } catch (TimeoutException te) {
             LOG.warn("Operation took too long", te);
diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel.java
index 214c64ff92..8c99e37a9a 100644
--- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel.java
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel.java
@@ -22,9 +22,14 @@ import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.password.stre
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPasswordFieldPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.SyncopePasswordStrengthConfig;
+import org.apache.syncope.client.ui.commons.rest.UserComplianceRestClient;
+import org.apache.syncope.common.rest.api.beans.ComplianceQuery;
 import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.FormComponent;
 import org.apache.wicket.markup.html.form.PasswordTextField;
 import org.apache.wicket.markup.html.form.validation.EqualPasswordInputValidator;
+import org.apache.wicket.markup.html.form.validation.IFormValidator;
 import org.apache.wicket.markup.html.panel.Panel;
 import org.apache.wicket.model.Model;
 import org.apache.wicket.model.PropertyModel;
@@ -38,17 +43,8 @@ public class PasswordPanel extends Panel {
             final String id,
             final UserWrapper wrapper,
             final Boolean storePasswordInSyncope,
-            final boolean templateMode) {
-
-        this(id, wrapper, templateMode, storePasswordInSyncope, null);
-    }
-
-    public PasswordPanel(
-            final String id,
-            final UserWrapper wrapper,
             final boolean templateMode,
-            final Boolean storePasswordInSyncope,
-            final PasswordStrengthBehavior passwordStrengthBehavior) {
+            final UserComplianceRestClient restClient) {
 
         super(id);
         setOutputMarkupId(true);
@@ -56,14 +52,14 @@ public class PasswordPanel extends Panel {
         Form<?> form = new Form<>("passwordInnerForm");
         add(form);
 
-        AjaxPasswordFieldPanel confirmPasswordField = new AjaxPasswordFieldPanel(
+        AjaxPasswordFieldPanel confirmPassword = new AjaxPasswordFieldPanel(
                 "confirmPassword", "confirmPassword", Model.of(), false);
-        ((PasswordTextField) confirmPasswordField.getField()).setResetPassword(false);
-        form.add(confirmPasswordField.setPlaceholder("confirmPassword").setMarkupId("confirmPassword"));
+        ((PasswordTextField) confirmPassword.getField()).setResetPassword(false);
+        form.add(confirmPassword.setPlaceholder("confirmPassword").setMarkupId("confirmPassword"));
 
         if (templateMode) {
-            confirmPasswordField.setEnabled(false);
-            confirmPasswordField.setVisible(false);
+            confirmPassword.setEnabled(false);
+            confirmPassword.setVisible(false);
 
             AjaxTextFieldPanel passwordField = new AjaxTextFieldPanel(
                     "password", "password", new PropertyModel<>(wrapper.getInnerObject(), "password"), false);
@@ -73,18 +69,41 @@ public class PasswordPanel extends Panel {
             form.add(passwordField);
             passwordField.enableJexlHelp();
         } else {
-            AjaxPasswordFieldPanel passwordField = new AjaxPasswordFieldPanel(
+            AjaxPasswordFieldPanel password = new AjaxPasswordFieldPanel(
                     "password",
                     "password",
                     new PropertyModel<>(wrapper.getInnerObject(), "password"),
                     false,
-                    passwordStrengthBehavior);
-            passwordField.setRequired(true);
-            passwordField.setMarkupId("password");
-            passwordField.setPlaceholder("password");
-            ((PasswordTextField) passwordField.getField()).setResetPassword(false);
-            form.add(passwordField);
-            form.add(new EqualPasswordInputValidator(passwordField.getField(), confirmPasswordField.getField()));
+                    new PasswordStrengthBehavior(new SyncopePasswordStrengthConfig()));
+            password.setRequired(true);
+            password.setMarkupId("password");
+            password.setPlaceholder("password");
+            ((PasswordTextField) password.getField()).setResetPassword(false);
+            form.add(password);
+            form.add(new EqualPasswordInputValidator(password.getField(), confirmPassword.getField()));
+            form.add(new IFormValidator() {
+
+                private static final long serialVersionUID = -73522462874258637L;
+
+                @Override
+                public FormComponent<?>[] getDependentFormComponents() {
+                    return null;
+                }
+
+                @Override
+                public void validate(final Form<?> form) {
+                    ComplianceQuery quey = new ComplianceQuery.Builder().
+                            realm(wrapper.getInnerObject().getRealm()).
+                            password(password.getField().getInput()).
+                            resources(wrapper.getInnerObject().getResources()).
+                            build();
+                    try {
+                        restClient.compliance(quey);
+                    } catch (Exception e) {
+                        password.getField().error(e.getMessage());
+                    }
+                }
+            });
         }
 
         AjaxCheckBoxPanel storePasswordInSyncopePanel = new AjaxCheckBoxPanel("storePasswordInSyncope",
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/UserRestClient.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/UserRestClient.java
index 224bbb8065..cc3cae9679 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/UserRestClient.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/UserRestClient.java
@@ -22,7 +22,9 @@ import jakarta.ws.rs.core.GenericType;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
 import org.apache.syncope.client.ui.commons.Constants;
+import org.apache.syncope.client.ui.commons.rest.UserComplianceRestClient;
 import org.apache.syncope.client.ui.commons.status.Status;
 import org.apache.syncope.client.ui.commons.status.StatusBean;
 import org.apache.syncope.client.ui.commons.status.StatusUtils;
@@ -35,14 +37,16 @@ import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.ExecStatus;
 import org.apache.syncope.common.lib.types.StatusRType;
 import org.apache.syncope.common.rest.api.beans.AnyQuery;
+import org.apache.syncope.common.rest.api.beans.ComplianceQuery;
 import org.apache.syncope.common.rest.api.service.AnyService;
+import org.apache.syncope.common.rest.api.service.UserSelfService;
 import org.apache.syncope.common.rest.api.service.UserService;
 import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 
 /**
  * Console client for invoking rest users services.
  */
-public class UserRestClient extends AbstractAnyRestClient<UserTO> {
+public class UserRestClient extends AbstractAnyRestClient<UserTO> implements UserComplianceRestClient {
 
     private static final long serialVersionUID = -1575748964398293968L;
 
@@ -51,6 +55,11 @@ public class UserRestClient extends AbstractAnyRestClient<UserTO> {
         return UserService.class;
     }
 
+    @Override
+    public void compliance(final ComplianceQuery query) {
+        SyncopeConsoleSession.get().getAnonymousService(UserSelfService.class).compliance(query);
+    }
+
     public ProvisioningResult<UserTO> create(final UserCR createReq) {
         return getService(UserService.class).create(createReq).readEntity(new GenericType<>() {
         });
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/status/ChangePasswordModal.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/status/ChangePasswordModal.java
index bc77114bb6..02696dce1a 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/status/ChangePasswordModal.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/status/ChangePasswordModal.java
@@ -60,7 +60,7 @@ public class ChangePasswordModal extends AbstractModalPanel<AnyWrapper<UserTO>>
         super(baseModal, pageRefer);
         this.wrapper = wrapper;
 
-        PasswordPanel passwordPanel = new PasswordPanel("passwordPanel", wrapper, false, false);
+        PasswordPanel passwordPanel = new PasswordPanel("passwordPanel", wrapper, false, false, userRestClient);
         passwordPanel.setOutputMarkupId(true);
         add(passwordPanel);
 
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserDetails.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserDetails.java
index ccb2003c32..a82f23c814 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserDetails.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserDetails.java
@@ -20,6 +20,7 @@ package org.apache.syncope.client.console.wizards.any;
 
 import java.util.List;
 import org.apache.commons.lang3.Strings;
+import org.apache.syncope.client.console.rest.UserRestClient;
 import org.apache.syncope.client.ui.commons.Constants;
 import org.apache.syncope.client.ui.commons.ajax.markup.html.LabelInfo;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel;
@@ -28,6 +29,7 @@ import org.apache.syncope.client.ui.commons.wicket.markup.html.bootstrap.tabs.Ac
 import org.apache.syncope.client.ui.commons.wizards.any.PasswordPanel;
 import org.apache.syncope.client.ui.commons.wizards.any.UserWrapper;
 import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.rest.api.beans.ComplianceQuery;
 import org.apache.wicket.Component;
 import org.apache.wicket.PageReference;
 import org.apache.wicket.ajax.AjaxRequestTarget;
@@ -35,15 +37,22 @@ import org.apache.wicket.ajax.markup.html.AjaxLink;
 import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
 import org.apache.wicket.extensions.markup.html.tabs.ITab;
 import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.FormComponent;
+import org.apache.wicket.markup.html.form.validation.IFormValidator;
 import org.apache.wicket.markup.html.panel.Panel;
 import org.apache.wicket.model.Model;
 import org.apache.wicket.model.PropertyModel;
 import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.spring.injection.annot.SpringBean;
 
 public class UserDetails extends Details<UserTO> {
 
     private static final long serialVersionUID = 6592027822510220463L;
 
+    @SpringBean
+    protected UserRestClient restClient;
+
     public UserDetails(
             final UserWrapper wrapper,
             final boolean templateMode,
@@ -58,9 +67,13 @@ public class UserDetails extends Details<UserTO> {
         // ------------------------
         // Username
         // ------------------------
+        Form<?> form = new Form<>("usernameInnerForm");
+        add(form);
+
         AjaxTextFieldPanel username = new AjaxTextFieldPanel(
                 Constants.USERNAME_FIELD_NAME, Constants.USERNAME_FIELD_NAME,
                 new PropertyModel<>(userTO, Constants.USERNAME_FIELD_NAME), false);
+        form.add(username);
 
         if (wrapper.getPreviousUserTO() != null && Strings.CS.compare(
                 wrapper.getPreviousUserTO().getUsername(), wrapper.getInnerObject().getUsername()) != 0) {
@@ -72,8 +85,31 @@ public class UserDetails extends Details<UserTO> {
             username.enableJexlHelp();
         } else {
             username.addRequiredLabel();
+
+            form.add(new IFormValidator() {
+
+                private static final long serialVersionUID = -73522462874258637L;
+
+                @Override
+                public FormComponent<?>[] getDependentFormComponents() {
+                    return null;
+                }
+
+                @Override
+                public void validate(final Form<?> form) {
+                    ComplianceQuery quey = new ComplianceQuery.Builder().
+                            realm(wrapper.getInnerObject().getRealm()).
+                            username(username.getField().getInput()).
+                            resources(wrapper.getInnerObject().getResources()).
+                            build();
+                    try {
+                        restClient.compliance(quey);
+                    } catch (Exception e) {
+                        username.getField().error(e.getMessage());
+                    }
+                }
+            });
         }
-        add(username);
         // ------------------------
 
         // ------------------------
@@ -121,7 +157,6 @@ public class UserDetails extends Details<UserTO> {
                 }.setBody(new ResourceModel("password.change", "Change password ..."));
             }
         };
-
         accordion.setOutputMarkupId(true);
         accordion.setVisible(showPasswordManagement);
         add(accordion);
@@ -133,7 +168,7 @@ public class UserDetails extends Details<UserTO> {
         return new UserInformationPanel(id, anyTO);
     }
 
-    public static class EditUserPasswordPanel extends Panel {
+    protected class EditUserPasswordPanel extends Panel {
 
         private static final long serialVersionUID = -8198836979773590078L;
 
@@ -141,7 +176,7 @@ public class UserDetails extends Details<UserTO> {
             super(id);
             setOutputMarkupId(true);
             add(new Label("warning", new ResourceModel("password.change.warning")));
-            add(new PasswordPanel("passwordPanel", wrapper, false, templateMode));
+            add(new PasswordPanel("passwordPanel", wrapper, false, templateMode, restClient));
         }
     }
 }
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wizards/any/UserDetails.html b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wizards/any/UserDetails.html
index e7fa169f79..58b2e693fb 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wizards/any/UserDetails.html
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wizards/any/UserDetails.html
@@ -18,9 +18,11 @@ under the License.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
   <wicket:extend>
-    <div class="form-group mb-3">
-      <span wicket:id="username"/>
-    </div>
+    <form wicket:id="usernameInnerForm">    
+      <div class="form-group mb-3">
+        <span wicket:id="username"/>
+      </div>
+    </form>
 
     <div class="form-group mb-3">
       <span wicket:id="mustChangePassword"/>
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/SelfConfirmPasswordReset.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/SelfConfirmPasswordReset.java
index e3663ee547..3f30f4ae5a 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/SelfConfirmPasswordReset.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/SelfConfirmPasswordReset.java
@@ -18,11 +18,10 @@
  */
 package org.apache.syncope.client.enduser.pages;
 
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.password.strength.PasswordStrengthBehavior;
 import org.apache.syncope.client.enduser.SyncopeEnduserSession;
 import org.apache.syncope.client.enduser.commons.EnduserConstants;
+import org.apache.syncope.client.enduser.rest.UserSelfRestClient;
 import org.apache.syncope.client.ui.commons.Constants;
-import org.apache.syncope.client.ui.commons.markup.html.form.SyncopePasswordStrengthConfig;
 import org.apache.syncope.client.ui.commons.panels.CardPanel;
 import org.apache.syncope.client.ui.commons.wizards.any.PasswordPanel;
 import org.apache.syncope.client.ui.commons.wizards.any.UserWrapper;
@@ -37,6 +36,7 @@ import org.apache.wicket.markup.html.form.Form;
 import org.apache.wicket.markup.html.form.StatelessForm;
 import org.apache.wicket.model.Model;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.spring.injection.annot.SpringBean;
 import org.wicketstuff.kendo.ui.widget.notification.Notification;
 
 public class SelfConfirmPasswordReset extends BasePage {
@@ -45,6 +45,9 @@ public class SelfConfirmPasswordReset extends BasePage {
 
     private static final String CONFIRM_PASSWORD_RESET = "confirmPasswordReset";
 
+    @SpringBean
+    protected UserSelfRestClient restClient;
+
     public SelfConfirmPasswordReset(final PageParameters parameters) {
         super(parameters, CONFIRM_PASSWORD_RESET);
 
@@ -74,7 +77,7 @@ public class SelfConfirmPasswordReset extends BasePage {
                 new UserWrapper(fakeUserTO),
                 false,
                 false,
-                new PasswordStrengthBehavior(new SyncopePasswordStrengthConfig()));
+                restClient);
         passwordPanel.setOutputMarkupId(true);
 
         form.add(new CardPanel.Builder<PasswordPanel>()
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/UserDetails.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/UserDetails.java
index aa0bbabafc..730d3ea728 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/UserDetails.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/UserDetails.java
@@ -18,30 +18,35 @@
  */
 package org.apache.syncope.client.enduser.panels.any;
 
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.password.strength.PasswordStrengthBehavior;
 import java.util.List;
 import java.util.Optional;
 import org.apache.commons.lang3.Strings;
+import org.apache.syncope.client.enduser.rest.UserSelfRestClient;
 import org.apache.syncope.client.ui.commons.ajax.markup.html.LabelInfo;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel;
-import org.apache.syncope.client.ui.commons.markup.html.form.SyncopePasswordStrengthConfig;
 import org.apache.syncope.client.ui.commons.wizards.any.PasswordPanel;
 import org.apache.syncope.client.ui.commons.wizards.any.UserWrapper;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.rest.api.beans.ComplianceQuery;
 import org.apache.wicket.PageReference;
 import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.FormComponent;
+import org.apache.wicket.markup.html.form.validation.IFormValidator;
 import org.apache.wicket.markup.html.panel.Panel;
 import org.apache.wicket.model.PropertyModel;
 import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.spring.injection.annot.SpringBean;
 
 public class UserDetails extends Details<UserTO> {
 
     private static final long serialVersionUID = 6592027822510220463L;
 
-    protected final AjaxTextFieldPanel username;
+    @SpringBean
+    protected UserSelfRestClient restClient;
 
     protected final UserTO userTO;
 
@@ -49,18 +54,46 @@ public class UserDetails extends Details<UserTO> {
         super(id, pageRef);
 
         userTO = wrapper.getInnerObject();
+
         // ------------------------
         // Username
         // ------------------------
-        username = new AjaxTextFieldPanel("username", "username", new PropertyModel<>(userTO, "username"), false);
+        Form<?> form = new Form<>("usernameInnerForm");
+        add(form);
+
+        AjaxTextFieldPanel username = new AjaxTextFieldPanel(
+                "username", "username", new PropertyModel<>(userTO, "username"), false);
+
+        if (wrapper.getPreviousUserTO() != null && Strings.CS.compare(
+                wrapper.getPreviousUserTO().getUsername(), wrapper.getInnerObject().getUsername()) != 0) {
 
-        if (wrapper.getPreviousUserTO() != null && Strings.CS.
-                compare(wrapper.getPreviousUserTO().getUsername(), wrapper.getInnerObject().getUsername()) != 0) {
             username.showExternAction(new LabelInfo("externalAction", wrapper.getPreviousUserTO().getUsername()));
         }
 
-        username.addRequiredLabel();
-        add(username);
+        form.add(username.addRequiredLabel());
+        form.add(new IFormValidator() {
+
+            private static final long serialVersionUID = -73522462874258637L;
+
+            @Override
+            public FormComponent<?>[] getDependentFormComponents() {
+                return null;
+            }
+
+            @Override
+            public void validate(final Form<?> form) {
+                ComplianceQuery quey = new ComplianceQuery.Builder().
+                        realm(wrapper.getInnerObject().getRealm()).
+                        username(username.getField().getInput()).
+                        resources(wrapper.getInnerObject().getResources()).
+                        build();
+                try {
+                    restClient.compliance(quey);
+                } catch (Exception e) {
+                    username.getField().error(e.getMessage());
+                }
+            }
+        });
         // ------------------------
 
         // ------------------------
@@ -78,7 +111,7 @@ public class UserDetails extends Details<UserTO> {
         return destinationRealm;
     }
 
-    protected static class EditUserPasswordPanel extends Panel {
+    protected class EditUserPasswordPanel extends Panel {
 
         private static final long serialVersionUID = -8198836979773590078L;
 
@@ -89,9 +122,9 @@ public class UserDetails extends Details<UserTO> {
             add(new PasswordPanel(
                     "passwordPanel",
                     wrapper,
-                    false,
                     wrapper.getInnerObject().getKey() == null,
-                    new PasswordStrengthBehavior(new SyncopePasswordStrengthConfig())));
+                    false,
+                    restClient));
         }
     }
 }
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/UserSelfRestClient.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/UserSelfRestClient.java
index 93e114b10e..8c90bffde3 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/UserSelfRestClient.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/UserSelfRestClient.java
@@ -19,14 +19,17 @@
 package org.apache.syncope.client.enduser.rest;
 
 import jakarta.ws.rs.core.GenericType;
+import org.apache.syncope.client.enduser.SyncopeEnduserSession;
+import org.apache.syncope.client.ui.commons.rest.UserComplianceRestClient;
 import org.apache.syncope.common.lib.request.PasswordPatch;
 import org.apache.syncope.common.lib.request.UserCR;
 import org.apache.syncope.common.lib.request.UserUR;
 import org.apache.syncope.common.lib.to.ProvisioningResult;
 import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.rest.api.beans.ComplianceQuery;
 import org.apache.syncope.common.rest.api.service.UserSelfService;
 
-public class UserSelfRestClient extends BaseRestClient {
+public class UserSelfRestClient extends BaseRestClient implements UserComplianceRestClient {
 
     private static final long serialVersionUID = -1575748964398293968L;
 
@@ -38,6 +41,11 @@ public class UserSelfRestClient extends BaseRestClient {
         getService(UserSelfService.class).requestPasswordReset(username, securityAnswer);
     }
 
+    @Override
+    public void compliance(final ComplianceQuery query) {
+        SyncopeEnduserSession.get().getAnonymousService(UserSelfService.class).compliance(query);
+    }
+
     public ProvisioningResult<UserTO> create(final UserCR createReq) {
         return getService(UserSelfService.class).create(createReq).readEntity(new GenericType<>() {
         });
diff --git a/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/panels/any/UserDetails.html b/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/panels/any/UserDetails.html
index 85d266ef57..18587c0c2a 100644
--- a/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/panels/any/UserDetails.html
+++ b/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/panels/any/UserDetails.html
@@ -23,9 +23,11 @@ under the License.
       <div class="form-group mb-3">
         <span wicket:id="destinationRealm">[DESTINATION REALM]</span>
       </div>
-      <div class="form-group mb-3">
-        <span wicket:id="username"/>
-      </div>
+      <form wicket:id="usernameInnerForm">
+        <div class="form-group mb-3">
+          <span wicket:id="username"/>
+        </div>
+      </form>
       <wicket:child/>
     </div>
   </wicket:panel>
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/console/PoliciesITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/console/PoliciesITCase.java
index ac0c5ad673..337423b3e1 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/console/PoliciesITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/console/PoliciesITCase.java
@@ -785,7 +785,7 @@ public class PoliciesITCase extends AbstractConsoleITCase {
 
         TESTER.assertComponent(
                 "body:content:body:container:content:tabbedPanel:panel:searchResult:"
-                + "outerObjectsRepeater:0:outer:form:content:form:view:username:textField",
+                + "outerObjectsRepeater:0:outer:form:content:form:view:usernameInnerForm:username:textField",
                 TextField.class);
 
         for (int i = 0; i < 4; i++) {
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/console/RealmsITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/console/RealmsITCase.java
index 478923c43a..8193c34b9d 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/console/RealmsITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/console/RealmsITCase.java
@@ -129,7 +129,7 @@ public class RealmsITCase extends AbstractConsoleITCase {
         TESTER.assertComponent("body:templateModal", Modal.class);
 
         formTester = TESTER.newFormTester("body:templateModal:form:content:form");
-        formTester.setValue("view:username:textField", "'k' + firstname");
+        formTester.setValue("view:usernameInnerForm:username:textField", "'k' + firstname");
         formTester.submit("buttons:finish");
 
         assertSuccessMessage();
@@ -147,11 +147,11 @@ public class RealmsITCase extends AbstractConsoleITCase {
 
         TESTER.assertComponent("body:templateModal", Modal.class);
 
-        TESTER.assertModelValue("body:templateModal:form:content:form:view:username:textField",
+        TESTER.assertModelValue("body:templateModal:form:content:form:view:usernameInnerForm:username:textField",
                 "'k' + firstname");
 
         formTester = TESTER.newFormTester("body:templateModal:form:content:form");
-        formTester.setValue("view:username:textField", "");
+        formTester.setValue("view:usernameInnerForm:username:textField", "");
         formTester.submit("buttons:finish");
 
         assertSuccessMessage();
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/console/UsersITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/console/UsersITCase.java
index 8223b93901..e57a3b5edf 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/console/UsersITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/console/UsersITCase.java
@@ -100,7 +100,7 @@ public class UsersITCase extends AbstractConsoleITCase {
                 + "actions:actions:actionRepeater:10:action:action");
 
         TESTER.assertComponent(TAB_PANEL
-                + "outerObjectsRepeater:0:outer:form:content:form:view:username:textField",
+                + "outerObjectsRepeater:0:outer:form:content:form:view:usernameInnerForm:username:textField",
                 TextField.class);
 
         FormTester formTester = TESTER.newFormTester(TAB_PANEL
@@ -125,7 +125,7 @@ public class UsersITCase extends AbstractConsoleITCase {
                 + "actions:actions:actionRepeater:0:action:action");
 
         TESTER.assertComponent(TAB_PANEL
-                + "outerObjectsRepeater:0:outer:form:content:form:view:username:textField",
+                + "outerObjectsRepeater:0:outer:form:content:form:view:usernameInnerForm:username:textField",
                 TextField.class);
 
         FormTester formTester = TESTER.newFormTester(TAB_PANEL
@@ -541,7 +541,7 @@ public class UsersITCase extends AbstractConsoleITCase {
                 + "actions:actions:actionRepeater:0:action:action");
 
         TESTER.assertComponent(TAB_PANEL
-                + "outerObjectsRepeater:0:outer:form:content:form:view:username:textField",
+                + "outerObjectsRepeater:0:outer:form:content:form:view:usernameInnerForm:username:textField",
                 TextField.class);
 
         FormTester formTester = TESTER.newFormTester(TAB_PANEL
@@ -587,7 +587,7 @@ public class UsersITCase extends AbstractConsoleITCase {
                 + "actions:actions:actionRepeater:0:action:action");
 
         TESTER.assertComponent(TAB_PANEL
-                + "outerObjectsRepeater:0:outer:form:content:form:view:username:textField",
+                + "outerObjectsRepeater:0:outer:form:content:form:view:usernameInnerForm:username:textField",
                 TextField.class);
 
         formTester = TESTER.newFormTester(TAB_PANEL + "outerObjectsRepeater:0:outer:form:content:form");
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/enduser/AnonymousITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/enduser/AnonymousITCase.java
index ba881a1469..b190552831 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/enduser/AnonymousITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/enduser/AnonymousITCase.java
@@ -51,7 +51,7 @@ public class AnonymousITCase extends AbstractEnduserITCase {
         String form = "body:contentWrapper:content:selfRegistrationPanel:form";
         FormTester formTester = TESTER.newFormTester(form);
 
-        formTester.setValue("userDetailsPanelCard:contentPanel:username:textField", username);
+        formTester.setValue("userDetailsPanelCard:contentPanel:usernameInnerForm:username:textField", username);
 
         formTester.setValue(
                 "userDetailsPanelCard:contentPanel:password:passwordPanel:passwordInnerForm:password:passwordField",