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 7bb3e38f11 [SYNCOPE-1740] Introducing UsernameAttributeProviderConf 
(#426)
7bb3e38f11 is described below

commit 7bb3e38f11a22d09e0e3d3f30939068ddbefef06
Author: Francesco Chicchiriccò <ilgro...@users.noreply.github.com>
AuthorDate: Wed Mar 15 17:37:39 2023 +0100

    [SYNCOPE-1740] Introducing UsernameAttributeProviderConf (#426)
---
 .../clientapps/ClientAppDirectoryPanel.java        |  15 +++
 .../clientapps/ClientAppModalPanelBuilder.java     |  18 ++-
 ...UsernameAttributeProviderModalPanelBuilder.java | 150 +++++++++++++++++++++
 .../AMClassPathScanImplementationContributor.java  |   5 +
 .../console/wizards/AuthModuleWizardBuilder.java   |  25 ++--
 .../clientapps/ClientAppDirectoryPanel.properties  |   5 +
 .../ClientAppDirectoryPanel_fr_CA.properties       |   5 +
 .../ClientAppDirectoryPanel_it.properties          |   5 +
 .../ClientAppDirectoryPanel_ja.properties          |   5 +
 .../ClientAppDirectoryPanel_pt_BR.properties       |   5 +
 .../ClientAppDirectoryPanel_ru.properties          |   5 +
 ...AttributeProviderModalPanelBuilder$Profile.html |  24 ++++
 .../syncope/client/console/panels/BeanPanel.java   |  62 +--------
 .../client/console/wizards/AttrWizardBuilder.java  |   2 +-
 .../common/lib/auth/OAuth20AuthModuleConf.java     |  11 --
 .../AnonymousUsernameAttributeProviderConf.java    |  65 +++++++++
 .../DefaultUsernameAttributeProviderConf.java}     |  16 +--
 .../GroovyUsernameAttributeProviderConf.java       |  64 +++++++++
 .../PairwiseOidcUsernameAttributeProviderConf.java |  65 +++++++++
 ...ipalAttributeUsernameAttributeProviderConf.java |  64 +++++++++
 .../clientapps/UsernameAttributeProviderConf.java} |  28 ++--
 .../apache/syncope/common/lib/to/ClientAppTO.java  |  61 +++++++--
 .../common/lib/types/PersistentIdGenerator.java}   |  14 +-
 .../core/persistence/api/entity/am/ClientApp.java  |  21 ++-
 .../src/test/resources/domains/MasterContent.xml   |   3 +-
 .../jpa/entity/am/AbstractClientApp.java           |  63 +++++++--
 .../src/test/resources/domains/MasterContent.xml   |   2 +-
 .../java/data/ClientAppDataBinderImpl.java         |   8 +-
 .../apache/syncope/fit/core/AuthModuleITCase.java  |   1 -
 .../concepts/clientapplications.adoc               |   3 +
 .../bootstrap/AuthModulePropertySourceMapper.java  |   1 -
 .../AuthModulePropertySourceMapperTest.java        |   1 -
 .../starter/mapping/AbstractClientAppMapper.java   |   6 +
 ...DefaultUsernameAttributeProviderConfMapper.java | 101 ++++++++++++++
 34 files changed, 778 insertions(+), 151 deletions(-)

diff --git 
a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.java
 
b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.java
index 68ea786219..b6c1f4492e 100644
--- 
a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.java
+++ 
b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.java
@@ -128,6 +128,21 @@ public abstract class ClientAppDirectoryPanel<T extends 
ClientAppTO>
             }
         }, ActionLink.ActionType.EDIT, AMEntitlement.CLIENTAPP_UPDATE);
 
+        panel.add(new ActionLink<>() {
+
+            private static final long serialVersionUID = -3722207913631435501L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final 
ClientAppTO ignore) {
+                model.setObject(ClientAppRestClient.read(type, 
model.getObject().getKey()));
+                modal.setContent(new 
UsernameAttributeProviderModalPanelBuilder<>(
+                        type, model.getObject(), modal, 
pageRef).build(actualId, 1, AjaxWizard.Mode.EDIT));
+                modal.header(new 
Model<>(getString("usernameAttributeProviderConf.title", model)));
+                modal.show(true);
+                target.add(modal);
+            }
+        }, ActionLink.ActionType.COMPOSE, AMEntitlement.CLIENTAPP_UPDATE);
+
         panel.add(new ActionLink<>() {
 
             private static final long serialVersionUID = -3722207913631435501L;
diff --git 
a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java
 
b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java
index 89c7303a4a..62a31da3a5 100644
--- 
a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java
+++ 
b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java
@@ -186,6 +186,22 @@ public class ClientAppModalPanelBuilder<T extends 
ClientAppTO> extends AbstractM
                     "field", "logo",
                     new PropertyModel<>(clientAppTO, "logo"), false));
 
+            fields.add(new AjaxTextFieldPanel(
+                    "field", "theme",
+                    new PropertyModel<>(clientAppTO, "theme"), false));
+
+            AjaxTextFieldPanel informationUrl = new AjaxTextFieldPanel(
+                    "field", "informationUrl",
+                    new PropertyModel<>(clientAppTO, "informationUrl"), false);
+            informationUrl.addValidator(new UrlValidator());
+            fields.add(informationUrl);
+
+            AjaxTextFieldPanel privacyUrl = new AjaxTextFieldPanel(
+                    "field", "privacyUrl",
+                    new PropertyModel<>(clientAppTO, "privacyUrl"), false);
+            privacyUrl.addValidator(new UrlValidator());
+            fields.add(privacyUrl);
+
             AjaxDropDownChoicePanel<String> accessPolicy = new 
AjaxDropDownChoicePanel<>(
                     "field", "accessPolicy", new PropertyModel<>(clientAppTO, 
"accessPolicy"), false);
             accessPolicy.setChoiceRenderer(new 
PolicyRenderer(accessPolicies.getObject()));
@@ -208,8 +224,6 @@ public class ClientAppModalPanelBuilder<T extends 
ClientAppTO> extends AbstractM
             ((AbstractSingleSelectChoice<?>) 
authPolicy.getField()).setNullValid(true);
             fields.add(authPolicy);
 
-            fields.add(new AjaxTextFieldPanel("field", "theme", new 
PropertyModel<>(clientAppTO, "theme"), false));
-
             switch (type) {
                 case CASSP:
                     fields.add(new AjaxTextFieldPanel(
diff --git 
a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/UsernameAttributeProviderModalPanelBuilder.java
 
b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/UsernameAttributeProviderModalPanelBuilder.java
new file mode 100644
index 0000000000..c70d211529
--- /dev/null
+++ 
b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/UsernameAttributeProviderModalPanelBuilder.java
@@ -0,0 +1,150 @@
+/*
+ * 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.console.clientapps;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.SyncopeWebApplication;
+import org.apache.syncope.client.console.panels.AbstractModalPanel;
+import org.apache.syncope.client.console.panels.BeanPanel;
+import org.apache.syncope.client.console.rest.ClientAppRestClient;
+import 
org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.client.ui.commons.Constants;
+import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel;
+import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
+import org.apache.syncope.client.ui.commons.panels.WizardModalPanel;
+import org.apache.syncope.client.ui.commons.wizards.AbstractModalPanelBuilder;
+import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
+import org.apache.syncope.common.lib.clientapps.UsernameAttributeProviderConf;
+import org.apache.syncope.common.lib.to.ClientAppTO;
+import org.apache.syncope.common.lib.types.ClientAppType;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxEventBehavior;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.model.LoadableDetachableModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.PropertyModel;
+import org.springframework.util.ClassUtils;
+
+public class UsernameAttributeProviderModalPanelBuilder<T extends ClientAppTO> 
extends AbstractModalPanelBuilder<T> {
+
+    private static final long serialVersionUID = -4106998301911573852L;
+
+    private final LoadableDetachableModel<List<String>> 
usernameAttributeProviderConfs =
+            new LoadableDetachableModel<>() {
+
+        private static final long serialVersionUID = 5275935387613157437L;
+
+        @Override
+        protected List<String> load() {
+            return 
SyncopeWebApplication.get().getLookup().getClasses(UsernameAttributeProviderConf.class).stream().
+                    map(Class::getName).sorted().collect(Collectors.toList());
+        }
+    };
+
+    private final BaseModal<T> modal;
+
+    private final ClientAppType type;
+
+    public UsernameAttributeProviderModalPanelBuilder(
+            final ClientAppType type, final T defaultItem, final BaseModal<T> 
modal, final PageReference pageRef) {
+
+        super(defaultItem, pageRef);
+        this.type = type;
+        this.modal = modal;
+    }
+
+    @Override
+    public WizardModalPanel<T> build(final String id, final int index, final 
AjaxWizard.Mode mode) {
+        return new Profile(newModelObject(), modal, pageRef);
+    }
+
+    private class Profile extends AbstractModalPanel<T> {
+
+        private static final long serialVersionUID = 7647959917047450318L;
+
+        private final T clientAppTO;
+
+        Profile(final T clientAppTO, final BaseModal<T> modal, final 
PageReference pageRef) {
+            super(modal, pageRef);
+            modal.setFormModel(clientAppTO);
+
+            this.clientAppTO = clientAppTO;
+
+            AjaxDropDownChoicePanel<String> conf = new 
AjaxDropDownChoicePanel<>(
+                    "conf", "conf", new Model<>());
+            
Optional.ofNullable(clientAppTO.getUsernameAttributeProviderConf()).
+                    ifPresent(uapc -> 
conf.setModelObject(uapc.getClass().getName()));
+            conf.setChoices(usernameAttributeProviderConfs.getObject());
+            conf.setNullValid(true);
+            add(conf);
+
+            PropertyModel<UsernameAttributeProviderConf> beanPanelModel =
+                    new PropertyModel<>(clientAppTO, 
"usernameAttributeProviderConf");
+            BeanPanel<UsernameAttributeProviderConf> bean = new BeanPanel<>(
+                    "bean",
+                    beanPanelModel,
+                    pageRef,
+                    Constants.NAME_FIELD_NAME,
+                    "reportlet");
+            add(bean.setRenderBodyOnly(false));
+
+            conf.add(new AjaxEventBehavior(Constants.ON_CHANGE) {
+
+                private static final long serialVersionUID = 
-7133385027739964990L;
+
+                @SuppressWarnings("unchecked")
+                @Override
+                protected void onEvent(final AjaxRequestTarget target) {
+                    if (conf.getModelObject() == null) {
+                        beanPanelModel.setObject(null);
+                    } else {
+                        try {
+                            Class<? extends UsernameAttributeProviderConf> 
clazz =
+                                    (Class<? extends 
UsernameAttributeProviderConf>) ClassUtils.resolveClassName(
+                                            conf.getModelObject(), 
ClassUtils.getDefaultClassLoader());
+
+                            
beanPanelModel.setObject(clazz.getConstructor().newInstance());
+                        } catch (Exception e) {
+                            LOG.error("Cannot instantiate {}", 
conf.getModelObject(), e);
+                        }
+                    }
+
+                    target.add(bean);
+                }
+            });
+        }
+
+        @Override
+        public void onSubmit(final AjaxRequestTarget target) {
+            try {
+                ClientAppRestClient.update(type, clientAppTO);
+
+                
SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
+                
UsernameAttributeProviderModalPanelBuilder.Profile.this.modal.close(target);
+            } catch (Exception e) {
+                LOG.error("While creating/updating clientApp", e);
+                SyncopeConsoleSession.get().onException(e);
+            }
+            ((BaseWebPage) 
pageRef.getPage()).getNotificationPanel().refresh(target);
+        }
+    }
+}
diff --git 
a/client/am/console/src/main/java/org/apache/syncope/client/console/init/AMClassPathScanImplementationContributor.java
 
b/client/am/console/src/main/java/org/apache/syncope/client/console/init/AMClassPathScanImplementationContributor.java
index 9a67d508cf..f995e5de2a 100644
--- 
a/client/am/console/src/main/java/org/apache/syncope/client/console/init/AMClassPathScanImplementationContributor.java
+++ 
b/client/am/console/src/main/java/org/apache/syncope/client/console/init/AMClassPathScanImplementationContributor.java
@@ -21,6 +21,7 @@ package org.apache.syncope.client.console.init;
 import java.util.Optional;
 import org.apache.syncope.common.lib.attr.AttrRepoConf;
 import org.apache.syncope.common.lib.auth.AuthModuleConf;
+import org.apache.syncope.common.lib.clientapps.UsernameAttributeProviderConf;
 import org.apache.syncope.common.lib.policy.AccessPolicyConf;
 import org.apache.syncope.common.lib.policy.AttrReleasePolicyConf;
 import org.apache.syncope.common.lib.policy.AuthPolicyConf;
@@ -38,6 +39,7 @@ public class AMClassPathScanImplementationContributor 
implements ClassPathScanIm
         scanner.addIncludeFilter(new 
AssignableTypeFilter(AccessPolicyConf.class));
         scanner.addIncludeFilter(new 
AssignableTypeFilter(AttrReleasePolicyConf.class));
         scanner.addIncludeFilter(new 
AssignableTypeFilter(AuthPolicyConf.class));
+        scanner.addIncludeFilter(new 
AssignableTypeFilter(UsernameAttributeProviderConf.class));
     }
 
     @Override
@@ -57,6 +59,9 @@ public class AMClassPathScanImplementationContributor 
implements ClassPathScanIm
         if (AuthPolicyConf.class.isAssignableFrom(clazz)) {
             return Optional.of(AuthPolicyConf.class.getName());
         }
+        if (UsernameAttributeProviderConf.class.isAssignableFrom(clazz)) {
+            return Optional.of(UsernameAttributeProviderConf.class.getName());
+        }
         return Optional.empty();
     }
 }
diff --git 
a/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder.java
 
b/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder.java
index d976f5ab8a..b07475b0a4 100644
--- 
a/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder.java
+++ 
b/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder.java
@@ -52,24 +52,21 @@ public class AuthModuleWizardBuilder extends 
BaseAjaxWizardBuilder<AuthModuleTO>
 
     private static final long serialVersionUID = -6163230263062920394L;
 
-    protected final LoadableDetachableModel<List<String>> authModuleConfs;
+    protected final LoadableDetachableModel<List<String>> authModuleConfs = 
new LoadableDetachableModel<>() {
+
+        private static final long serialVersionUID = 5275935387613157437L;
+
+        @Override
+        protected List<String> load() {
+            return 
SyncopeWebApplication.get().getLookup().getClasses(AuthModuleConf.class).stream().
+                    map(Class::getName).sorted().collect(Collectors.toList());
+        }
+    };
 
     protected Model<Class<? extends AuthModuleConf>> authModuleConfClass = 
Model.of();
 
     public AuthModuleWizardBuilder(final AuthModuleTO defaultItem, final 
PageReference pageRef) {
-
         super(defaultItem, pageRef);
-
-        authModuleConfs = new LoadableDetachableModel<>() {
-
-            private static final long serialVersionUID = 5275935387613157437L;
-
-            @Override
-            protected List<String> load() {
-                return 
SyncopeWebApplication.get().getLookup().getClasses(AuthModuleConf.class).stream().
-                        
map(Class::getName).sorted().collect(Collectors.toList());
-            }
-        };
     }
 
     @Override
@@ -152,7 +149,7 @@ public class AuthModuleWizardBuilder extends 
BaseAjaxWizardBuilder<AuthModuleTO>
                         
authModule.setConf(clazz.getConstructor().newInstance());
                         authModuleConfClass.setObject(clazz);
                     } catch (Exception e) {
-                        LOG.error("During deserialization", e);
+                        LOG.error("Cannot instantiate {}", 
conf.getModelObject(), e);
                     }
                 }
             });
diff --git 
a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.properties
 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.properties
index a51a303773..c238d9877b 100644
--- 
a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.properties
+++ 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.properties
@@ -59,3 +59,8 @@ properties.title=Properties for ${name}
 type_extensions.title=properties
 bypassApprovalPrompt=Bypass Approval Prompt
 scopes=Scopes
+logo=Logo
+informationUrl=Information URL
+privacyUrl=Privacy URL
+compose.title=username attribute provider
+usernameAttributeProviderConf.title=Username Attribute Provider for ${name}
diff --git 
a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_fr_CA.properties
 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_fr_CA.properties
index 6ab6528c25..a32e6c0626 100644
--- 
a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_fr_CA.properties
+++ 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_fr_CA.properties
@@ -59,3 +59,8 @@ properties.title=Properties for ${name}
 type_extensions.title=properties
 bypassApprovalPrompt=Bypass Approval Prompt
 scopes=Scopes
+logo=Logo
+informationUrl=Information URL
+privacyUrl=Privacy URL
+compose.title=username attribute provider
+usernameAttributeProviderConf.title=Username Attribute Provider for ${name}
diff --git 
a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_it.properties
 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_it.properties
index 1e4aa4611b..61a171ccef 100644
--- 
a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_it.properties
+++ 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_it.properties
@@ -59,3 +59,8 @@ properties.title=Propriet\u00e0 di ${name}
 type_extensions.title=propriet\u00e0
 bypassApprovalPrompt=Salta richiesta approvazione
 scopes=Scope
+logo=Logo
+informationUrl=URL informativa
+privacyUrl=URL privacy
+compose.title=username attribute provider
+usernameAttributeProviderConf.title=Username Attribute Provider per ${name}
diff --git 
a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_ja.properties
 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_ja.properties
index ce61864b46..670175063f 100644
--- 
a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_ja.properties
+++ 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_ja.properties
@@ -59,3 +59,8 @@ properties.title=Properties for ${name}
 type_extensions.title=properties
 bypassApprovalPrompt=Bypass Approval Prompt
 scopes=Scopes
+logo=Logo
+informationUrl=Information URL
+privacyUrl=Privacy URL
+compose.title=username attribute provider
+usernameAttributeProviderConf.title=Username Attribute Provider for ${name}
diff --git 
a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_pt_BR.properties
 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_pt_BR.properties
index a51a303773..c238d9877b 100644
--- 
a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_pt_BR.properties
+++ 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_pt_BR.properties
@@ -59,3 +59,8 @@ properties.title=Properties for ${name}
 type_extensions.title=properties
 bypassApprovalPrompt=Bypass Approval Prompt
 scopes=Scopes
+logo=Logo
+informationUrl=Information URL
+privacyUrl=Privacy URL
+compose.title=username attribute provider
+usernameAttributeProviderConf.title=Username Attribute Provider for ${name}
diff --git 
a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_ru.properties
 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_ru.properties
index b9af8ebe0f..a88ccce746 100644
--- 
a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_ru.properties
+++ 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_ru.properties
@@ -60,3 +60,8 @@ properties.title=Properties for ${name}
 type_extensions.title=properties
 bypassApprovalPrompt=Bypass Approval Prompt
 scopes=Scopes
+logo=Logo
+informationUrl=Information URL
+privacyUrl=Privacy URL
+compose.title=username attribute provider
+usernameAttributeProviderConf.title=Username Attribute Provider for ${name}
diff --git 
a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/UsernameAttributeProviderModalPanelBuilder$Profile.html
 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/UsernameAttributeProviderModalPanelBuilder$Profile.html
new file mode 100644
index 0000000000..d86e001927
--- /dev/null
+++ 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/UsernameAttributeProviderModalPanelBuilder$Profile.html
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml"; 
xmlns:wicket="http://wicket.apache.org";>
+  <wicket:panel>
+    <div class="form-group"><span wicket:id="conf">[conf]</span></div>
+    <span wicket:id="bean"/>
+  </wicket:panel>
+</html>
diff --git 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/BeanPanel.java
 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/BeanPanel.java
index 40eee868fd..bdbb57d963 100644
--- 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/BeanPanel.java
+++ 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/BeanPanel.java
@@ -34,18 +34,11 @@ import java.util.Map;
 import java.util.Optional;
 import java.util.stream.Collectors;
 import org.apache.commons.lang3.time.DateFormatUtils;
-import org.apache.commons.lang3.tuple.Pair;
 import org.apache.commons.lang3.tuple.Triple;
 import org.apache.syncope.client.console.SyncopeConsoleSession;
 import org.apache.syncope.client.console.SyncopeWebApplication;
-import org.apache.syncope.client.console.panels.search.AnyObjectSearchPanel;
-import org.apache.syncope.client.console.panels.search.GroupSearchPanel;
-import org.apache.syncope.client.console.panels.search.SearchClause;
-import org.apache.syncope.client.console.panels.search.SearchUtils;
-import org.apache.syncope.client.console.panels.search.UserSearchPanel;
 import org.apache.syncope.client.console.rest.SchemaRestClient;
 import 
org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
-import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.client.ui.commons.DateOps;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxDateTimeFieldPanel;
@@ -55,8 +48,6 @@ import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxSpinnerFieldPanel;
 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.common.lib.report.SearchCondition;
-import org.apache.syncope.common.lib.search.AbstractFiqlSearchConditionBuilder;
 import org.apache.syncope.common.lib.to.SchemaTO;
 import org.apache.syncope.common.lib.types.SchemaType;
 import org.apache.wicket.PageReference;
@@ -75,8 +66,6 @@ import org.apache.wicket.model.ResourceModel;
 import org.apache.wicket.model.util.ListModel;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.BeanWrapper;
-import org.springframework.beans.PropertyAccessorFactory;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.ReflectionUtils;
 
@@ -88,24 +77,10 @@ public class BeanPanel<T extends Serializable> extends 
Panel {
 
     private final List<String> excluded;
 
-    private final Map<String, Pair<AbstractFiqlSearchConditionBuilder<?, ?, 
?>, List<SearchClause>>> sCondWrapper;
-
     public BeanPanel(final String id, final IModel<T> bean, final 
PageReference pageRef, final String... excluded) {
-        this(id, bean, null, pageRef, excluded);
-    }
-
-    public BeanPanel(
-            final String id,
-            final IModel<T> bean,
-            final Map<String, Pair<AbstractFiqlSearchConditionBuilder<?, ?, 
?>, List<SearchClause>>> sCondWrapper,
-            final PageReference pageRef,
-            final String... excluded) {
-
         super(id, bean);
         setOutputMarkupId(true);
 
-        this.sCondWrapper = sCondWrapper;
-
         this.excluded = new ArrayList<>(List.of(excluded));
         this.excluded.add("serialVersionUID");
         this.excluded.add("class");
@@ -157,7 +132,7 @@ public class BeanPanel<T extends Serializable> extends 
Panel {
                 item.replace(fragment);
             }
 
-            @SuppressWarnings({"unchecked", "rawtypes"})
+            @SuppressWarnings({ "unchecked", "rawtypes" })
             @Override
             protected void populateItem(final ListItem<String> item) {
                 item.add(new Fragment("required", "emptyFragment", this));
@@ -172,40 +147,9 @@ public class BeanPanel<T extends Serializable> extends 
Panel {
                     return;
                 }
 
-                BeanWrapper wrapper = 
PropertyAccessorFactory.forBeanPropertyAccess(bean.getObject());
-
                 Panel panel;
 
-                SearchCondition scondAnnot = 
field.getAnnotation(SearchCondition.class);
-                if (scondAnnot != null) {
-                    String fiql = (String) wrapper.getPropertyValue(fieldName);
-
-                    List<SearchClause> clauses = 
SearchUtils.getSearchClauses(fiql);
-
-                    AbstractFiqlSearchConditionBuilder<?, ?, ?> builder;
-                    switch (scondAnnot.type()) {
-                        case "USER":
-                            panel = new UserSearchPanel.Builder(
-                                    new ListModel<>(clauses), 
pageRef).required(false).build("value");
-                            builder = 
SyncopeClient.getUserSearchConditionBuilder();
-                            break;
-
-                        case "GROUP":
-                            panel = new GroupSearchPanel.Builder(
-                                    new ListModel<>(clauses), 
pageRef).required(false).build("value");
-                            builder = 
SyncopeClient.getGroupSearchConditionBuilder();
-                            break;
-
-                        default:
-                            panel = new AnyObjectSearchPanel.Builder(
-                                    scondAnnot.type(),
-                                    new ListModel<>(clauses), 
pageRef).required(false).build("value");
-                            builder = 
SyncopeClient.getAnyObjectSearchConditionBuilder(scondAnnot.type());
-                    }
-
-                    Optional.ofNullable(BeanPanel.this.sCondWrapper).
-                            ifPresent(scw -> scw.put(fieldName, 
Pair.of(builder, clauses)));
-                } else if (List.class.equals(field.getType())) {
+                if (List.class.equals(field.getType())) {
                     Class<?> listItemType = field.getGenericType() instanceof 
ParameterizedType
                             ? (Class<?>) ((ParameterizedType) 
field.getGenericType()).getActualTypeArguments()[0]
                             : String.class;
@@ -278,7 +222,7 @@ public class BeanPanel<T extends Serializable> extends 
Panel {
 
                 item.add(panel.setRenderBodyOnly(true));
             }
-        }.setReuseItems(true).setOutputMarkupId(true));
+        }.setReuseItems(false));
     }
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
diff --git 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/AttrWizardBuilder.java
 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/AttrWizardBuilder.java
index 03e0221e03..8eb63d4ef7 100644
--- 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/AttrWizardBuilder.java
+++ 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/AttrWizardBuilder.java
@@ -30,7 +30,7 @@ import org.apache.wicket.model.PropertyModel;
 
 public abstract class AttrWizardBuilder extends BaseAjaxWizardBuilder<Attr> {
 
-    private static final long serialVersionUID = 1L;
+    private static final long serialVersionUID = 7618745848468848923L;
 
     public AttrWizardBuilder(final Attr defaultItem, final PageReference 
pageRef) {
         super(defaultItem, pageRef);
diff --git 
a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/OAuth20AuthModuleConf.java
 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/OAuth20AuthModuleConf.java
index 34d445743f..86fbde2282 100644
--- 
a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/OAuth20AuthModuleConf.java
+++ 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/OAuth20AuthModuleConf.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.common.lib.auth;
 
-import java.util.LinkedHashMap;
 import java.util.Map;
 import org.apache.syncope.common.lib.to.AuthModuleTO;
 
@@ -30,8 +29,6 @@ public class OAuth20AuthModuleConf extends 
AbstractOIDCAuthModuleConf implements
 
     protected String profileUrl;
 
-    protected Map<String, String> profileAttrs = new LinkedHashMap<>();
-
     protected boolean withState;
 
     protected String profileVerb = "POST";
@@ -44,14 +41,6 @@ public class OAuth20AuthModuleConf extends 
AbstractOIDCAuthModuleConf implements
         this.authUrl = authUrl;
     }
 
-    public Map<String, String> getProfileAttrs() {
-        return profileAttrs;
-    }
-
-    public void setProfileAttrs(final Map<String, String> profileAttrs) {
-        this.profileAttrs = profileAttrs;
-    }
-
     public boolean isWithState() {
         return withState;
     }
diff --git 
a/common/am/lib/src/main/java/org/apache/syncope/common/lib/clientapps/AnonymousUsernameAttributeProviderConf.java
 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/clientapps/AnonymousUsernameAttributeProviderConf.java
new file mode 100644
index 0000000000..101f145a89
--- /dev/null
+++ 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/clientapps/AnonymousUsernameAttributeProviderConf.java
@@ -0,0 +1,65 @@
+/*
+ * 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.common.lib.clientapps;
+
+import java.util.Objects;
+import org.apache.syncope.common.lib.types.PersistentIdGenerator;
+
+public class AnonymousUsernameAttributeProviderConf implements 
UsernameAttributeProviderConf {
+
+    private static final long serialVersionUID = -4762223354637243358L;
+
+    private PersistentIdGenerator persistentIdGenerator = 
PersistentIdGenerator.SHIBBOLETH;
+
+    public PersistentIdGenerator getPersistentIdGenerator() {
+        return persistentIdGenerator;
+    }
+
+    public void setPersistentIdGenerator(final PersistentIdGenerator 
persistentIdGenerator) {
+        this.persistentIdGenerator = persistentIdGenerator;
+    }
+
+    @Override
+    public void map(final Mapper mapper) {
+        mapper.map(this);
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 97 * hash + Objects.hashCode(this.persistentIdGenerator);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final AnonymousUsernameAttributeProviderConf other =
+                (AnonymousUsernameAttributeProviderConf) obj;
+        return Objects.equals(this.persistentIdGenerator, 
other.persistentIdGenerator);
+    }
+}
diff --git 
a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/report/SearchCondition.java
 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/clientapps/DefaultUsernameAttributeProviderConf.java
similarity index 70%
copy from 
common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/report/SearchCondition.java
copy to 
common/am/lib/src/main/java/org/apache/syncope/common/lib/clientapps/DefaultUsernameAttributeProviderConf.java
index 0570dabdfe..b72113a9cf 100644
--- 
a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/report/SearchCondition.java
+++ 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/clientapps/DefaultUsernameAttributeProviderConf.java
@@ -16,16 +16,14 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.common.lib.report;
+package org.apache.syncope.common.lib.clientapps;
 
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
+public class DefaultUsernameAttributeProviderConf implements 
UsernameAttributeProviderConf {
 
-@Target({ ElementType.FIELD })
-@Retention(RetentionPolicy.RUNTIME)
-public @interface SearchCondition {
+    private static final long serialVersionUID = 4315599812817709524L;
 
-    String type() default "USER";
+    @Override
+    public void map(final Mapper mapper) {
+        mapper.map(this);
+    }
 }
diff --git 
a/common/am/lib/src/main/java/org/apache/syncope/common/lib/clientapps/GroovyUsernameAttributeProviderConf.java
 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/clientapps/GroovyUsernameAttributeProviderConf.java
new file mode 100644
index 0000000000..1c00786bd7
--- /dev/null
+++ 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/clientapps/GroovyUsernameAttributeProviderConf.java
@@ -0,0 +1,64 @@
+/*
+ * 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.common.lib.clientapps;
+
+import java.util.Objects;
+
+public class GroovyUsernameAttributeProviderConf implements 
UsernameAttributeProviderConf {
+
+    private static final long serialVersionUID = -4762223354637243358L;
+
+    private String groovyScript;
+
+    public String getGroovyScript() {
+        return groovyScript;
+    }
+
+    public void setGroovyScript(final String groovyScript) {
+        this.groovyScript = groovyScript;
+    }
+
+    @Override
+    public void map(final Mapper mapper) {
+        mapper.map(this);
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 97 * hash + Objects.hashCode(this.groovyScript);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final GroovyUsernameAttributeProviderConf other =
+                (GroovyUsernameAttributeProviderConf) obj;
+        return Objects.equals(this.groovyScript, other.groovyScript);
+    }
+}
diff --git 
a/common/am/lib/src/main/java/org/apache/syncope/common/lib/clientapps/PairwiseOidcUsernameAttributeProviderConf.java
 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/clientapps/PairwiseOidcUsernameAttributeProviderConf.java
new file mode 100644
index 0000000000..9cf00f514a
--- /dev/null
+++ 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/clientapps/PairwiseOidcUsernameAttributeProviderConf.java
@@ -0,0 +1,65 @@
+/*
+ * 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.common.lib.clientapps;
+
+import java.util.Objects;
+import org.apache.syncope.common.lib.types.PersistentIdGenerator;
+
+public class PairwiseOidcUsernameAttributeProviderConf implements 
UsernameAttributeProviderConf {
+
+    private static final long serialVersionUID = -4762223354637243358L;
+
+    private PersistentIdGenerator persistentIdGenerator = 
PersistentIdGenerator.OIDC;
+
+    public PersistentIdGenerator getPersistentIdGenerator() {
+        return persistentIdGenerator;
+    }
+
+    public void setPersistentIdGenerator(final PersistentIdGenerator 
persistentIdGenerator) {
+        this.persistentIdGenerator = persistentIdGenerator;
+    }
+
+    @Override
+    public void map(final Mapper mapper) {
+        mapper.map(this);
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 97 * hash + Objects.hashCode(this.persistentIdGenerator);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final PairwiseOidcUsernameAttributeProviderConf other =
+                (PairwiseOidcUsernameAttributeProviderConf) obj;
+        return Objects.equals(this.persistentIdGenerator, 
other.persistentIdGenerator);
+    }
+}
diff --git 
a/common/am/lib/src/main/java/org/apache/syncope/common/lib/clientapps/PrincipalAttributeUsernameAttributeProviderConf.java
 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/clientapps/PrincipalAttributeUsernameAttributeProviderConf.java
new file mode 100644
index 0000000000..4f9c4121bd
--- /dev/null
+++ 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/clientapps/PrincipalAttributeUsernameAttributeProviderConf.java
@@ -0,0 +1,64 @@
+/*
+ * 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.common.lib.clientapps;
+
+import java.util.Objects;
+
+public class PrincipalAttributeUsernameAttributeProviderConf implements 
UsernameAttributeProviderConf {
+
+    private static final long serialVersionUID = -4762223354637243358L;
+
+    private String usernameAttribute;
+
+    public String getUsernameAttribute() {
+        return usernameAttribute;
+    }
+
+    public void setUsernameAttribute(final String usernameAttribute) {
+        this.usernameAttribute = usernameAttribute;
+    }
+
+    @Override
+    public void map(final Mapper mapper) {
+        mapper.map(this);
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 97 * hash + Objects.hashCode(this.usernameAttribute);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final PrincipalAttributeUsernameAttributeProviderConf other =
+                (PrincipalAttributeUsernameAttributeProviderConf) obj;
+        return Objects.equals(this.usernameAttribute, other.usernameAttribute);
+    }
+}
diff --git 
a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/report/SearchCondition.java
 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/clientapps/UsernameAttributeProviderConf.java
similarity index 53%
copy from 
common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/report/SearchCondition.java
copy to 
common/am/lib/src/main/java/org/apache/syncope/common/lib/clientapps/UsernameAttributeProviderConf.java
index 0570dabdfe..d349670d5a 100644
--- 
a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/report/SearchCondition.java
+++ 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/clientapps/UsernameAttributeProviderConf.java
@@ -16,16 +16,26 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.common.lib.report;
+package org.apache.syncope.common.lib.clientapps;
 
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import org.apache.syncope.common.lib.BaseBean;
 
-@Target({ ElementType.FIELD })
-@Retention(RetentionPolicy.RUNTIME)
-public @interface SearchCondition {
+@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, 
property = "_class")
+public interface UsernameAttributeProviderConf extends BaseBean {
 
-    String type() default "USER";
+    interface Mapper {
+
+        void map(AnonymousUsernameAttributeProviderConf conf);
+
+        void map(DefaultUsernameAttributeProviderConf conf);
+
+        void map(GroovyUsernameAttributeProviderConf conf);
+
+        void map(PairwiseOidcUsernameAttributeProviderConf conf);
+
+        void map(PrincipalAttributeUsernameAttributeProviderConf conf);
+    }
+
+    void map(Mapper mapper);
 }
diff --git 
a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/ClientAppTO.java 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/ClientAppTO.java
index 4fcee875bf..b25a8b9dfc 100644
--- 
a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/ClientAppTO.java
+++ 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/ClientAppTO.java
@@ -30,6 +30,7 @@ import java.util.List;
 import org.apache.commons.lang3.builder.EqualsBuilder;
 import org.apache.commons.lang3.builder.HashCodeBuilder;
 import org.apache.syncope.common.lib.Attr;
+import org.apache.syncope.common.lib.clientapps.UsernameAttributeProviderConf;
 
 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = 
JsonTypeInfo.As.EXISTING_PROPERTY, property = "_class")
 @JsonPropertyOrder(value = { "_class", "key", "description" })
@@ -55,14 +56,20 @@ public abstract class ClientAppTO implements NamedEntityTO {
 
     private String logo;
 
+    private String theme;
+
+    private String informationUrl;
+
+    private String privacyUrl;
+
+    private UsernameAttributeProviderConf usernameAttributeProviderConf;
+
     private String authPolicy;
 
     private String accessPolicy;
 
     private String attrReleasePolicy;
 
-    private String theme;
-
     private final List<Attr> properties = new ArrayList<>();
 
     @Schema(name = "_class", requiredMode = Schema.RequiredMode.REQUIRED)
@@ -96,14 +103,6 @@ public abstract class ClientAppTO implements NamedEntityTO {
         this.authPolicy = authPolicy;
     }
 
-    public String getTheme() {
-        return theme;
-    }
-
-    public void setTheme(final String theme) {
-        this.theme = theme;
-    }
-
     @Override
     public String getKey() {
         return key;
@@ -157,6 +156,38 @@ public abstract class ClientAppTO implements NamedEntityTO 
{
         this.logo = logo;
     }
 
+    public String getTheme() {
+        return theme;
+    }
+
+    public void setTheme(final String theme) {
+        this.theme = theme;
+    }
+
+    public String getInformationUrl() {
+        return informationUrl;
+    }
+
+    public void setInformationUrl(final String informationUrl) {
+        this.informationUrl = informationUrl;
+    }
+
+    public String getPrivacyUrl() {
+        return privacyUrl;
+    }
+
+    public void setPrivacyUrl(final String privacyUrl) {
+        this.privacyUrl = privacyUrl;
+    }
+
+    public UsernameAttributeProviderConf getUsernameAttributeProviderConf() {
+        return usernameAttributeProviderConf;
+    }
+
+    public void setUsernameAttributeProviderConf(final 
UsernameAttributeProviderConf usernameAttributeProviderConf) {
+        this.usernameAttributeProviderConf = usernameAttributeProviderConf;
+    }
+
     @JacksonXmlElementWrapper(localName = "properties")
     @JacksonXmlProperty(localName = "property")
     public List<Attr> getProperties() {
@@ -173,10 +204,13 @@ public abstract class ClientAppTO implements 
NamedEntityTO {
                 .append(name)
                 .append(description)
                 .append(logo)
+                .append(theme)
+                .append(informationUrl)
+                .append(privacyUrl)
+                .append(usernameAttributeProviderConf)
                 .append(authPolicy)
                 .append(accessPolicy)
                 .append(attrReleasePolicy)
-                .append(theme)
                 .append(properties)
                 .toHashCode();
     }
@@ -201,10 +235,13 @@ public abstract class ClientAppTO implements 
NamedEntityTO {
                 .append(this.name, rhs.name)
                 .append(this.description, rhs.description)
                 .append(this.logo, rhs.logo)
+                .append(this.theme, rhs.theme)
+                .append(this.informationUrl, rhs.informationUrl)
+                .append(this.privacyUrl, rhs.privacyUrl)
+                .append(this.usernameAttributeProviderConf, 
rhs.usernameAttributeProviderConf)
                 .append(this.authPolicy, rhs.authPolicy)
                 .append(this.accessPolicy, rhs.accessPolicy)
                 .append(this.attrReleasePolicy, rhs.attrReleasePolicy)
-                .append(this.theme, rhs.theme)
                 .append(this.properties, rhs.properties)
                 .isEquals();
     }
diff --git 
a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/report/SearchCondition.java
 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/PersistentIdGenerator.java
similarity index 70%
rename from 
common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/report/SearchCondition.java
rename to 
common/am/lib/src/main/java/org/apache/syncope/common/lib/types/PersistentIdGenerator.java
index 0570dabdfe..f30c0818e0 100644
--- 
a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/report/SearchCondition.java
+++ 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/PersistentIdGenerator.java
@@ -16,16 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.common.lib.report;
+package org.apache.syncope.common.lib.types;
 
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
+public enum PersistentIdGenerator {
+    SHIBBOLETH,
+    OIDC;
 
-@Target({ ElementType.FIELD })
-@Retention(RetentionPolicy.RUNTIME)
-public @interface SearchCondition {
-
-    String type() default "USER";
 }
diff --git 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/ClientApp.java
 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/ClientApp.java
index 800f0c9923..e82890ee0d 100644
--- 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/ClientApp.java
+++ 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/ClientApp.java
@@ -20,6 +20,7 @@ package org.apache.syncope.core.persistence.api.entity.am;
 
 import java.util.List;
 import org.apache.syncope.common.lib.Attr;
+import org.apache.syncope.common.lib.clientapps.UsernameAttributeProviderConf;
 import org.apache.syncope.core.persistence.api.entity.Entity;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
@@ -44,6 +45,22 @@ public interface ClientApp extends Entity {
 
     void setLogo(String logo);
 
+    void setTheme(String name);
+
+    String getTheme();
+
+    String getInformationUrl();
+
+    void setInformationUrl(String informationUrl);
+
+    String getPrivacyUrl();
+
+    void setPrivacyUrl(String privacyUrl);
+
+    UsernameAttributeProviderConf getUsernameAttributeProviderConf();
+
+    void setUsernameAttributeProviderConf(UsernameAttributeProviderConf conf);
+
     AuthPolicy getAuthPolicy();
 
     void setAuthPolicy(AuthPolicy policy);
@@ -60,10 +77,6 @@ public interface ClientApp extends Entity {
 
     void setRealm(Realm realm);
 
-    void setTheme(String name);
-
-    String getTheme();
-
     List<Attr> getProperties();
 
     void setProperties(List<Attr> properties);
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 cbfd264869..223c6b0719 100644
--- a/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
@@ -88,8 +88,7 @@ under the License.
   <AuthModule id="DefaultU2FAuthModule" authModuleState="ACTIVE"
               description="U2F auth module" 
jsonConf='{"_class":"org.apache.syncope.common.lib.auth.U2FAuthModuleConf","expireDevices":40}'/>
   <AuthModule id="DefaultOAuth20AuthModule" description="OAuth20 auth module" 
authModuleOrder="0" 
-              
jsonConf='{"_class":"org.apache.syncope.common.lib.auth.OAuth20AuthModuleConf","clientName":"oauth20","clientId":"OAUTH20","clientSecret":"secret","enabled":true,"customParams":{},"tokenUrl":"https://localhost/oauth2/token","responseType":"code","scope":"oauth
 
test","userIdAttribute":"username","authUrl":"https://localhost/oauth2/auth","profileUrl":"https://localhost/oauth2/profile","profileAttrs":{},"withState":false,"profileVerb":"POST"}'
 authModuleState="ACTIVE"/>
-
+              
jsonConf='{"_class":"org.apache.syncope.common.lib.auth.OAuth20AuthModuleConf","clientName":"oauth20","clientId":"OAUTH20","clientSecret":"secret","enabled":true,"customParams":{},"tokenUrl":"https://localhost/oauth2/token","responseType":"code","scope":"oauth
 
test","userIdAttribute":"username","authUrl":"https://localhost/oauth2/auth","profileUrl":"https://localhost/oauth2/profile","withState":false,"profileVerb":"POST"}'
 authModuleState="ACTIVE"/>
 
   <!-- Attribute repositories -->
   <AttrRepo id="DefaultLDAPAttrRepo" attrRepoState="ACTIVE"
diff --git 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/AbstractClientApp.java
 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/AbstractClientApp.java
index aa6acd3311..8a76bb9ed9 100644
--- 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/AbstractClientApp.java
+++ 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/AbstractClientApp.java
@@ -26,7 +26,9 @@ import jakarta.persistence.ManyToOne;
 import jakarta.persistence.MappedSuperclass;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 import org.apache.syncope.common.lib.Attr;
+import org.apache.syncope.common.lib.clientapps.UsernameAttributeProviderConf;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.api.entity.am.ClientApp;
 import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
@@ -53,15 +55,19 @@ public class AbstractClientApp extends 
AbstractGeneratedKeyEntity implements Cli
     @Column(unique = true, nullable = false)
     private Long clientAppId;
 
-    @Column
     private String description;
 
-    @Column
     private String logo;
 
-    @Column
+    @Lob
+    private String usernameAttributeProviderConf;
+
     private String theme;
 
+    private String informationUrl;
+
+    private String privacyUrl;
+
     @ManyToOne(fetch = FetchType.EAGER)
     private JPARealm realm;
 
@@ -117,6 +123,47 @@ public class AbstractClientApp extends 
AbstractGeneratedKeyEntity implements Cli
         this.logo = logo;
     }
 
+    @Override
+    public String getTheme() {
+        return theme;
+    }
+
+    @Override
+    public void setTheme(final String theme) {
+        this.theme = theme;
+    }
+
+    @Override
+    public String getInformationUrl() {
+        return informationUrl;
+    }
+
+    @Override
+    public void setInformationUrl(final String informationUrl) {
+        this.informationUrl = informationUrl;
+    }
+
+    @Override
+    public String getPrivacyUrl() {
+        return privacyUrl;
+    }
+
+    @Override
+    public void setPrivacyUrl(final String privacyUrl) {
+        this.privacyUrl = privacyUrl;
+    }
+
+    @Override
+    public UsernameAttributeProviderConf getUsernameAttributeProviderConf() {
+        return Optional.ofNullable(usernameAttributeProviderConf).
+                map(conf -> POJOHelper.deserialize(conf, 
UsernameAttributeProviderConf.class)).orElse(null);
+    }
+
+    @Override
+    public void setUsernameAttributeProviderConf(final 
UsernameAttributeProviderConf conf) {
+        this.usernameAttributeProviderConf = conf == null ? null : 
POJOHelper.serialize(conf);
+    }
+
     @Override
     public JPAAuthPolicy getAuthPolicy() {
         return authPolicy;
@@ -161,16 +208,6 @@ public class AbstractClientApp extends 
AbstractGeneratedKeyEntity implements Cli
         this.realm = (JPARealm) realm;
     }
 
-    @Override
-    public String getTheme() {
-        return theme;
-    }
-
-    @Override
-    public void setTheme(final String theme) {
-        this.theme = theme;
-    }
-
     @Override
     public List<Attr> getProperties() {
         return properties == null
diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml 
b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
index a2231a184c..a2f86a8566 100644
--- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
@@ -88,7 +88,7 @@ under the License.
   <AuthModule id="DefaultU2FAuthModule" authModuleState="ACTIVE"
               description="U2F auth module" 
jsonConf='{"_class":"org.apache.syncope.common.lib.auth.U2FAuthModuleConf","expireDevices":40}'/>
   <AuthModule id="DefaultOAuth20AuthModule" description="OAuth20 auth module" 
authModuleOrder="0"
-              
jsonConf='{"_class":"org.apache.syncope.common.lib.auth.OAuth20AuthModuleConf","clientName":"oauth20","clientId":"OAUTH20","clientSecret":"secret","enabled":true,"customParams":{},"tokenUrl":"https://localhost/oauth2/token","responseType":"code","scope":"oauth
 
test","userIdAttribute":"username","authUrl":"https://localhost/oauth2/auth","profileUrl":"https://localhost/oauth2/profile","profileAttrs":{},"withState":false,"profileVerb":"POST"}'
 authModuleState="ACTIVE"/>
+              
jsonConf='{"_class":"org.apache.syncope.common.lib.auth.OAuth20AuthModuleConf","clientName":"oauth20","clientId":"OAUTH20","clientSecret":"secret","enabled":true,"customParams":{},"tokenUrl":"https://localhost/oauth2/token","responseType":"code","scope":"oauth
 
test","userIdAttribute":"username","authUrl":"https://localhost/oauth2/auth","profileUrl":"https://localhost/oauth2/profile","withState":false,"profileVerb":"POST"}'
 authModuleState="ACTIVE"/>
 
   <!-- Attribute repositories -->
   <AttrRepo id="DefaultLDAPAttrRepo" attrRepoState="ACTIVE"
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ClientAppDataBinderImpl.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ClientAppDataBinderImpl.java
index 13b858d728..d5c441c7c5 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ClientAppDataBinderImpl.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ClientAppDataBinderImpl.java
@@ -154,10 +154,13 @@ public class ClientAppDataBinderImpl implements 
ClientAppDataBinder {
         clientAppTO.setKey(clientApp.getKey());
         
clientAppTO.setRealm(Optional.ofNullable(clientApp.getRealm()).map(Realm::getFullPath).orElse(null));
         clientAppTO.setName(clientApp.getName());
+        clientAppTO.setClientAppId(clientApp.getClientAppId());
         clientAppTO.setDescription(clientApp.getDescription());
         clientAppTO.setLogo(clientApp.getLogo());
-        clientAppTO.setClientAppId(clientApp.getClientAppId());
         clientAppTO.setTheme(clientApp.getTheme());
+        clientAppTO.setInformationUrl(clientApp.getInformationUrl());
+        clientAppTO.setPrivacyUrl(clientApp.getPrivacyUrl());
+        
clientAppTO.setUsernameAttributeProviderConf(clientApp.getUsernameAttributeProviderConf());
 
         clientAppTO.setAuthPolicy(
                 
Optional.ofNullable(clientApp.getAuthPolicy()).map(AuthPolicy::getKey).orElse(null));
@@ -274,6 +277,9 @@ public class ClientAppDataBinderImpl implements 
ClientAppDataBinder {
         clientApp.setDescription(clientAppTO.getDescription());
         clientApp.setLogo(clientAppTO.getLogo());
         clientApp.setTheme(clientAppTO.getTheme());
+        clientApp.setInformationUrl(clientAppTO.getInformationUrl());
+        clientApp.setPrivacyUrl(clientAppTO.getPrivacyUrl());
+        
clientApp.setUsernameAttributeProviderConf(clientAppTO.getUsernameAttributeProviderConf());
 
         if (clientAppTO.getAuthPolicy() == null) {
             clientApp.setAuthPolicy(null);
diff --git 
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthModuleITCase.java
 
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthModuleITCase.java
index 9b46d13a75..99bd35631b 100644
--- 
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthModuleITCase.java
+++ 
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthModuleITCase.java
@@ -147,7 +147,6 @@ public class AuthModuleITCase extends AbstractITCase {
                 OAuth20AuthModuleConf.class.cast(conf).setEnabled(true);
                 
OAuth20AuthModuleConf.class.cast(conf).setCustomParams(Map.of("param1", 
"param1"));
                 
OAuth20AuthModuleConf.class.cast(conf).setAuthUrl("https://localhost/oauth2/auth";);
-                
OAuth20AuthModuleConf.class.cast(conf).setProfileAttrs(Map.of("uid", "id"));
                 
OAuth20AuthModuleConf.class.cast(conf).setProfileUrl("https://localhost/oauth2/profile";);
                 
OAuth20AuthModuleConf.class.cast(conf).setTokenUrl("https://localhost/oauth2/token";);
                 OAuth20AuthModuleConf.class.cast(conf).setResponseType("code");
diff --git a/src/main/asciidoc/reference-guide/concepts/clientapplications.adoc 
b/src/main/asciidoc/reference-guide/concepts/clientapplications.adoc
index 46f741723f..dd7b1a9494 100644
--- a/src/main/asciidoc/reference-guide/concepts/clientapplications.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/clientapplications.adoc
@@ -30,8 +30,11 @@ Depending on the communication protocol, the following 
client applications are s
 When defining a client application, the following parameters shall be 
specified:
 
 . id - unique number identifier of the current client application
+. <<realms,realm>> - used to inherit policies
 . name - regular expression to match requests
 . description - optional textual description
+. username attribute provider, mapping to
+https://apereo.github.io/cas/6.6.x/integration/Attribute-Release-PrincipalId-Attribute.html[CAS
 Attribute-based Principal Id^]
 . <<policies-authentication,authentication policy>>
 . <<policies-access,access policy>>
 . <<policies-attribute-release,attribute release policy>>
diff --git 
a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/AuthModulePropertySourceMapper.java
 
b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/AuthModulePropertySourceMapper.java
index 7044758142..04386f5606 100644
--- 
a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/AuthModulePropertySourceMapper.java
+++ 
b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/AuthModulePropertySourceMapper.java
@@ -176,7 +176,6 @@ public class AuthModulePropertySourceMapper extends 
PropertySourceMapper impleme
         props.setEnabled(authModuleTO.getState() == AuthModuleState.ACTIVE);
         props.setCustomParams(conf.getCustomParams());
         props.setAuthUrl(conf.getAuthUrl());
-        props.setProfileAttrs(conf.getProfileAttrs());
         props.setProfileVerb(conf.getProfileVerb());
         props.setProfileUrl(conf.getProfileUrl());
         props.setTokenUrl(conf.getTokenUrl());
diff --git 
a/wa/bootstrap/src/test/java/org/apache/syncope/wa/bootstrap/AuthModulePropertySourceMapperTest.java
 
b/wa/bootstrap/src/test/java/org/apache/syncope/wa/bootstrap/AuthModulePropertySourceMapperTest.java
index 88d0452182..a6934e0c6a 100644
--- 
a/wa/bootstrap/src/test/java/org/apache/syncope/wa/bootstrap/AuthModulePropertySourceMapperTest.java
+++ 
b/wa/bootstrap/src/test/java/org/apache/syncope/wa/bootstrap/AuthModulePropertySourceMapperTest.java
@@ -62,7 +62,6 @@ public class AuthModulePropertySourceMapperTest {
         conf.setEnabled(true);
         conf.setCustomParams(Map.of("param1", "param1"));
         conf.setAuthUrl("https://localhost/oauth2/auth";);
-        conf.setProfileAttrs(Map.of("uid", "id"));
         conf.setProfileUrl("https://localhost/oauth2/profile";);
         conf.setTokenUrl("https://localhost/oauth2/token";);
         conf.setResponseType("code");
diff --git 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AbstractClientAppMapper.java
 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AbstractClientAppMapper.java
index 37e3f1b25e..75aebb1030 100644
--- 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AbstractClientAppMapper.java
+++ 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AbstractClientAppMapper.java
@@ -19,6 +19,7 @@
 package org.apache.syncope.wa.starter.mapping;
 
 import java.util.Map;
+import java.util.Optional;
 import java.util.stream.Collectors;
 import org.apache.syncope.common.lib.Attr;
 import org.apache.syncope.common.lib.to.ClientAppTO;
@@ -37,6 +38,11 @@ abstract class AbstractClientAppMapper implements 
ClientAppMapper {
         service.setName(clientApp.getName());
         service.setDescription(clientApp.getDescription());
         service.setLogo(clientApp.getLogo());
+        service.setTheme(clientApp.getTheme());
+        service.setInformationUrl(clientApp.getInformationUrl());
+        service.setPrivacyUrl(clientApp.getPrivacyUrl());
+        Optional.ofNullable(clientApp.getUsernameAttributeProviderConf()).
+                ifPresent(conf -> conf.map(new 
DefaultUsernameAttributeProviderConfMapper(service)));
 
         if (!clientApp.getProperties().isEmpty()) {
             Map<String, RegisteredServiceProperty> properties = 
clientApp.getProperties().stream().
diff --git 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/DefaultUsernameAttributeProviderConfMapper.java
 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/DefaultUsernameAttributeProviderConfMapper.java
new file mode 100644
index 0000000000..10746e8b79
--- /dev/null
+++ 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/DefaultUsernameAttributeProviderConfMapper.java
@@ -0,0 +1,101 @@
+/*
+ * 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.wa.starter.mapping;
+
+import java.util.Optional;
+import 
org.apache.syncope.common.lib.clientapps.AnonymousUsernameAttributeProviderConf;
+import 
org.apache.syncope.common.lib.clientapps.DefaultUsernameAttributeProviderConf;
+import 
org.apache.syncope.common.lib.clientapps.GroovyUsernameAttributeProviderConf;
+import 
org.apache.syncope.common.lib.clientapps.PairwiseOidcUsernameAttributeProviderConf;
+import 
org.apache.syncope.common.lib.clientapps.PrincipalAttributeUsernameAttributeProviderConf;
+import org.apache.syncope.common.lib.clientapps.UsernameAttributeProviderConf;
+import org.apache.syncope.common.lib.types.PersistentIdGenerator;
+import 
org.apereo.cas.authentication.principal.OidcPairwisePersistentIdGenerator;
+import 
org.apereo.cas.authentication.principal.ShibbolethCompatiblePersistentIdGenerator;
+import 
org.apereo.cas.services.AnonymousRegisteredServiceUsernameAttributeProvider;
+import org.apereo.cas.services.BaseWebBasedRegisteredService;
+import org.apereo.cas.services.DefaultRegisteredServiceUsernameProvider;
+import org.apereo.cas.services.GroovyRegisteredServiceUsernameProvider;
+import 
org.apereo.cas.services.PairwiseOidcRegisteredServiceUsernameAttributeProvider;
+import 
org.apereo.cas.services.PrincipalAttributeRegisteredServiceUsernameProvider;
+import org.apereo.cas.util.RandomUtils;
+
+public class DefaultUsernameAttributeProviderConfMapper implements 
UsernameAttributeProviderConf.Mapper {
+
+    protected static 
Optional<org.apereo.cas.authentication.principal.PersistentIdGenerator> 
toPersistentIdGenerator(
+            final PersistentIdGenerator persistentIdGenerator) {
+
+        if (persistentIdGenerator == null) {
+            return Optional.empty();
+        }
+
+        org.apereo.cas.authentication.principal.PersistentIdGenerator result = 
null;
+        switch (persistentIdGenerator) {
+            case SHIBBOLETH:
+                result = new 
ShibbolethCompatiblePersistentIdGenerator(RandomUtils.randomAlphanumeric(16));
+                break;
+
+            case OIDC:
+                result = new OidcPairwisePersistentIdGenerator();
+                break;
+
+            default:
+        }
+
+        return Optional.ofNullable(result);
+    }
+
+    protected final BaseWebBasedRegisteredService service;
+
+    public DefaultUsernameAttributeProviderConfMapper(final 
BaseWebBasedRegisteredService service) {
+        this.service = service;
+    }
+
+    @Override
+    public void map(final AnonymousUsernameAttributeProviderConf conf) {
+        AnonymousRegisteredServiceUsernameAttributeProvider arsuap =
+                new AnonymousRegisteredServiceUsernameAttributeProvider();
+        
toPersistentIdGenerator(conf.getPersistentIdGenerator()).ifPresent(arsuap::setPersistentIdGenerator);
+        service.setUsernameAttributeProvider(arsuap);
+    }
+
+    @Override
+    public void map(final DefaultUsernameAttributeProviderConf conf) {
+        service.setUsernameAttributeProvider(new 
DefaultRegisteredServiceUsernameProvider());
+    }
+
+    @Override
+    public void map(final GroovyUsernameAttributeProviderConf conf) {
+        service.setUsernameAttributeProvider(new 
GroovyRegisteredServiceUsernameProvider(conf.getGroovyScript()));
+    }
+
+    @Override
+    public void map(final PairwiseOidcUsernameAttributeProviderConf conf) {
+        PairwiseOidcRegisteredServiceUsernameAttributeProvider porsuap =
+                new PairwiseOidcRegisteredServiceUsernameAttributeProvider();
+        
toPersistentIdGenerator(conf.getPersistentIdGenerator()).ifPresent(porsuap::setPersistentIdGenerator);
+        service.setUsernameAttributeProvider(porsuap);
+    }
+
+    @Override
+    public void map(final PrincipalAttributeUsernameAttributeProviderConf 
conf) {
+        service.setUsernameAttributeProvider(
+                new 
PrincipalAttributeRegisteredServiceUsernameProvider(conf.getUsernameAttribute()));
+    }
+}

Reply via email to