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 2019/04/01 15:22:59 UTC

[syncope] branch master updated: [SYNCOPE-1410] Allowing Groovy implementations for OIDCClient ext

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 113fb0f  [SYNCOPE-1410] Allowing Groovy implementations for OIDCClient ext
113fb0f is described below

commit 113fb0f1eb42aa2e1e0f3ea56fa666bb2d1956e5
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Mon Apr 1 17:22:46 2019 +0200

    [SYNCOPE-1410] Allowing Groovy implementations for OIDCClient ext
---
 .../client/console/wizards/WizardMgtPanel.java     |  2 -
 .../common/lib/types/IdMImplementationType.java    | 28 ++++----
 .../common/lib/types/EntitlementsHolder.java       | 14 +++-
 .../common/lib/types/IdRepoImplementationType.java | 32 ++++-----
 .../lib/types/ImplementationTypesHolder.java       | 20 +++++-
 .../logic/init/IdMImplementationTypeLoader.java    |  2 +-
 .../syncope/core/logic/ImplementationLogic.java    |  2 +-
 .../apache/syncope/core/logic/SyncopeLogic.java    | 16 +++--
 .../init/ClassPathScanImplementationLookup.java    | 30 +++------
 .../logic/init/IdRepoImplementationTypeLoader.java |  2 +-
 .../persistence/jpa/outer/PlainSchemaTest.java     | 54 ++++------------
 .../console/rest/OIDCProviderRestClient.java       |  6 --
 .../console/wizards/OIDCProviderWizardBuilder.java | 73 +++++++++++----------
 .../panels/OIDCProvidersDirectoryPanel.properties  |  2 +-
 .../OIDCProvidersDirectoryPanel_it.properties      |  2 +-
 .../OIDCProvidersDirectoryPanel_pt_BR.properties   |  2 +-
 .../OIDCProvidersDirectoryPanel_ru.properties      |  2 +-
 .../wizards/OIDCProviderWizardBuilder$OP.html      |  4 +-
 .../OIDCProviderWizardBuilder$OP.properties        |  2 +-
 .../OIDCProviderWizardBuilder$OP_it.properties     |  2 +-
 .../OIDCProviderWizardBuilder$OP_pt_BR.properties  |  2 +-
 .../OIDCProviderWizardBuilder$OP_ru.properties     |  2 +-
 .../syncope/common/lib/to/OIDCProviderTO.java      | 15 ++---
 .../lib/types/OIDCClientImplementationType.java    | 21 +++---
 .../syncope/core/logic/OIDCProviderLogic.java      | 10 ---
 ...IDCClientClassPathScanImplementationLookup.java | 75 ----------------------
 .../syncope/core/logic/init/OIDCClientLoader.java  |  3 +
 .../syncope/core/logic/oidc/OIDCUserManager.java   | 14 ++--
 .../core/persistence/api/entity/OIDCProvider.java  |  4 +-
 .../persistence/jpa/entity/JPAOIDCProvider.java    | 29 +++++----
 .../java/data/OIDCProviderDataBinderImpl.java      | 22 +++++--
 .../rest/api/service/OIDCProviderService.java      | 11 ----
 .../rest/cxf/service/OIDCProviderServiceImpl.java  |  6 --
 33 files changed, 203 insertions(+), 308 deletions(-)

diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java
index 722d007..9aba441 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java
@@ -56,8 +56,6 @@ public abstract class WizardMgtPanel<T extends Serializable> extends AbstractWiz
 
     private static final long serialVersionUID = -4152438633429194882L;
 
-    protected static final String WIZARD_ID = "wizard";
-
     private boolean readOnly = false;
 
     private final String actualId;
diff --git a/common/idm/lib/src/main/java/org/apache/syncope/common/lib/types/IdMImplementationType.java b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/types/IdMImplementationType.java
index 7413a16..f8cc797 100644
--- a/common/idm/lib/src/main/java/org/apache/syncope/common/lib/types/IdMImplementationType.java
+++ b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/types/IdMImplementationType.java
@@ -18,11 +18,8 @@
  */
 package org.apache.syncope.common.lib.types;
 
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.Collections;
-import java.util.Set;
-import java.util.TreeSet;
+import java.util.Map;
+import org.apache.commons.lang3.tuple.Pair;
 
 public final class IdMImplementationType {
 
@@ -40,19 +37,16 @@ public final class IdMImplementationType {
 
     public static final String PUSH_CORRELATION_RULE = "PUSH_CORRELATION_RULE";
 
-    private static final Set<String> VALUES;
+    private static final Map<String, String> VALUES = Map.ofEntries(
+            Pair.of(ITEM_TRANSFORMER, "org.apache.syncope.core.spring.security.JWTSSOProvider"),
+            Pair.of(RECON_FILTER_BUILDER, "org.apache.syncope.core.persistence.api.dao.Reportlet"),
+            Pair.of(PROPAGATION_ACTIONS, "org.apache.syncope.core.persistence.api.dao.AccountRule"),
+            Pair.of(PULL_ACTIONS, "org.apache.syncope.core.persistence.api.dao.PasswordRule"),
+            Pair.of(PUSH_ACTIONS, "org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate"),
+            Pair.of(PULL_CORRELATION_RULE, "org.apache.syncope.core.provisioning.api.LogicActions"),
+            Pair.of(PUSH_CORRELATION_RULE, "org.apache.syncope.core.persistence.api.attrvalue.validation.Validator"));
 
-    static {
-        Set<String> values = new TreeSet<>();
-        for (Field field : IdMImplementationType.class.getDeclaredFields()) {
-            if (Modifier.isStatic(field.getModifiers()) && String.class.equals(field.getType())) {
-                values.add(field.getName());
-            }
-        }
-        VALUES = Collections.unmodifiableSet(values);
-    }
-
-    public static Set<String> values() {
+    public static Map<String, String> values() {
         return VALUES;
     }
 
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/EntitlementsHolder.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/EntitlementsHolder.java
index 067629d..d7a34b4 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/EntitlementsHolder.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/EntitlementsHolder.java
@@ -18,10 +18,12 @@
  */
 package org.apache.syncope.common.lib.types;
 
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 
-public final class EntitlementsHolder extends ValueHolder<String> {
+public final class EntitlementsHolder {
 
     private static final Object MONITOR = new Object();
 
@@ -36,10 +38,16 @@ public final class EntitlementsHolder extends ValueHolder<String> {
         return INSTANCE;
     }
 
+    private final Set<String> values = Collections.synchronizedSet(new HashSet<>());
+
     private EntitlementsHolder() {
         // private constructor for singleton
     }
 
+    public void addAll(final Collection<String> values) {
+        this.values.addAll(values);
+    }
+
     public Set<String> addFor(final String anyType) {
         Set<String> added = new HashSet<>();
 
@@ -61,4 +69,8 @@ public final class EntitlementsHolder extends ValueHolder<String> {
 
         return removed;
     }
+
+    public Set<String> getValues() {
+        return Collections.unmodifiableSet(values);
+    }
 }
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/IdRepoImplementationType.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/IdRepoImplementationType.java
index 9145e6d..013cb06 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/IdRepoImplementationType.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/IdRepoImplementationType.java
@@ -18,11 +18,8 @@
  */
 package org.apache.syncope.common.lib.types;
 
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.Collections;
-import java.util.Set;
-import java.util.TreeSet;
+import java.util.Map;
+import org.apache.commons.lang3.tuple.Pair;
 
 public final class IdRepoImplementationType {
 
@@ -44,19 +41,18 @@ public final class IdRepoImplementationType {
 
     public static final String AUDIT_APPENDER = "AUDIT_APPENDER";
 
-    private static final Set<String> VALUES;
-
-    static {
-        Set<String> values = new TreeSet<>();
-        for (Field field : IdRepoImplementationType.class.getDeclaredFields()) {
-            if (Modifier.isStatic(field.getModifiers()) && String.class.equals(field.getType())) {
-                values.add(field.getName());
-            }
-        }
-        VALUES = Collections.unmodifiableSet(values);
-    }
-
-    public static Set<String> values() {
+    private static final Map<String, String> VALUES = Map.ofEntries(
+            Pair.of(JWT_SSO_PROVIDER, "org.apache.syncope.core.spring.security.JWTSSOProvider"),
+            Pair.of(REPORTLET, "org.apache.syncope.core.persistence.api.dao.Reportlet"),
+            Pair.of(ACCOUNT_RULE, "org.apache.syncope.core.persistence.api.dao.AccountRule"),
+            Pair.of(PASSWORD_RULE, "org.apache.syncope.core.persistence.api.dao.PasswordRule"),
+            Pair.of(TASKJOB_DELEGATE, "org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate"),
+            Pair.of(LOGIC_ACTIONS, "org.apache.syncope.core.provisioning.api.LogicActions"),
+            Pair.of(VALIDATOR, "org.apache.syncope.core.persistence.api.attrvalue.validation.Validator"),
+            Pair.of(RECIPIENTS_PROVIDER, "org.apache.syncope.core.provisioning.api.notification.RecipientsProvider"),
+            Pair.of(AUDIT_APPENDER, "org.apache.syncope.core.logic.audit.AuditAppender"));
+
+    public static Map<String, String> values() {
         return VALUES;
     }
 
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/ImplementationTypesHolder.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/ImplementationTypesHolder.java
index 1960352..5ef7d81 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/ImplementationTypesHolder.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/ImplementationTypesHolder.java
@@ -18,7 +18,11 @@
  */
 package org.apache.syncope.common.lib.types;
 
-public final class ImplementationTypesHolder extends ValueHolder<String> {
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public final class ImplementationTypesHolder {
 
     private static final Object MONITOR = new Object();
 
@@ -33,7 +37,21 @@ public final class ImplementationTypesHolder extends ValueHolder<String> {
         return INSTANCE;
     }
 
+    private final Map<String, String> values = Collections.synchronizedMap(new HashMap<>());
+
     private ImplementationTypesHolder() {
         // private constructor for singleton
     }
+
+    public void putAll(final Map<String, String> value2Class) {
+        this.values.putAll(value2Class);
+    }
+
+    public Map<String, String> getValues() {
+        return Collections.unmodifiableMap(values);
+    }
+
+//    public String getClass(final String implementationType) {
+//        return values.get(implementationType);
+//    }
 }
diff --git a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/init/IdMImplementationTypeLoader.java b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/init/IdMImplementationTypeLoader.java
index 34a85ca..b3a039c 100644
--- a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/init/IdMImplementationTypeLoader.java
+++ b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/init/IdMImplementationTypeLoader.java
@@ -33,6 +33,6 @@ public class IdMImplementationTypeLoader implements SyncopeCoreLoader {
 
     @Override
     public void load() {
-        ImplementationTypesHolder.getInstance().addAll(IdMImplementationType.values());
+        ImplementationTypesHolder.getInstance().putAll(IdMImplementationType.values());
     }
 }
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/ImplementationLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/ImplementationLogic.java
index fee7c8d..8b45f86 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/ImplementationLogic.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/ImplementationLogic.java
@@ -78,7 +78,7 @@ public class ImplementationLogic extends AbstractTransactionalLogic<Implementati
     private NotificationDAO notificationDAO;
 
     private void checkType(final String type) {
-        if (!ImplementationTypesHolder.getInstance().getValues().contains(type)) {
+        if (!ImplementationTypesHolder.getInstance().getValues().containsKey(type)) {
             SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidImplementationType);
             sce.getElements().add("Implementation type not found: ");
             throw sce;
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
index 20749b4..5a8f331 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
@@ -30,6 +30,7 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.Set;
 import java.util.stream.Collectors;
 import javax.annotation.Resource;
 import org.apache.commons.lang3.StringUtils;
@@ -282,12 +283,15 @@ public class SyncopeLogic extends AbstractLogic<EntityTO> {
                 PLATFORM_INFO.getPersistenceInfo().
                         setConfDAO(AopUtils.getTargetClass(confDAO).getName());
 
-                ImplementationTypesHolder.getInstance().getValues().forEach(type -> {
-                    JavaImplInfo javaImplInfo = new JavaImplInfo();
-                    javaImplInfo.setType(type);
-                    javaImplInfo.getClasses().addAll(implLookup.getClassNames(type));
+                ImplementationTypesHolder.getInstance().getValues().forEach((typeName, typeInterface) -> {
+                    Set<String> classNames = implLookup.getClassNames(typeName);
+                    if (classNames != null) {
+                        JavaImplInfo javaImplInfo = new JavaImplInfo();
+                        javaImplInfo.setType(typeName);
+                        javaImplInfo.getClasses().addAll(classNames);
 
-                    PLATFORM_INFO.getJavaImplInfos().add(javaImplInfo);
+                        PLATFORM_INFO.getJavaImplInfos().add(javaImplInfo);
+                    }
                 });
             }
 
@@ -299,7 +303,7 @@ public class SyncopeLogic extends AbstractLogic<EntityTO> {
             PLATFORM_INFO.getEntitlements().addAll(EntitlementsHolder.getInstance().getValues());
 
             PLATFORM_INFO.getImplementationTypes().clear();
-            PLATFORM_INFO.getImplementationTypes().addAll(ImplementationTypesHolder.getInstance().getValues());
+            PLATFORM_INFO.getImplementationTypes().addAll(ImplementationTypesHolder.getInstance().getValues().keySet());
 
             AuthContextUtils.execWithAuthContext(AuthContextUtils.getDomain(), () -> {
                 PLATFORM_INFO.getAnyTypes().clear();
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
index db296da..690bdb0 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
@@ -107,8 +107,16 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
     @Override
     public void load() {
         classNames = new HashMap<>();
-        ImplementationTypesHolder.getInstance().getValues().
-                forEach(type -> classNames.put(type, new HashSet<>()));
+        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
+        ImplementationTypesHolder.getInstance().getValues().forEach((typeName, typeInterface) -> {
+            classNames.put(typeName, new HashSet<>());
+            try {
+                scanner.addIncludeFilter(new AssignableTypeFilter(
+                        ClassUtils.resolveClassName(typeInterface, ClassUtils.getDefaultClassLoader())));
+            } catch (IllegalArgumentException e) {
+                LOG.error("Could not find class {}, ignoring...", e);
+            }
+        });
 
         jwtSSOProviderClasses = new HashSet<>();
         reportletClasses = new HashMap<>();
@@ -118,24 +126,6 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
         pushCRClasses = new HashMap<>();
         auditAppenderClasses = new HashSet<>();
 
-        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
-        scanner.addIncludeFilter(new AssignableTypeFilter(JWTSSOProvider.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(Reportlet.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(AccountRule.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(PasswordRule.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(PullCorrelationRule.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(PushCorrelationRule.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(ItemTransformer.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(SchedTaskJobDelegate.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(ReconFilterBuilder.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(LogicActions.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(PropagationActions.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(PullActions.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(PushActions.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(Validator.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(RecipientsProvider.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(AuditAppender.class));
-
         scanner.findCandidateComponents(getBasePackage()).forEach(bd -> {
             try {
                 Class<?> clazz = ClassUtils.resolveClassName(
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/IdRepoImplementationTypeLoader.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/IdRepoImplementationTypeLoader.java
index aef14be..3735d1c 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/IdRepoImplementationTypeLoader.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/IdRepoImplementationTypeLoader.java
@@ -33,6 +33,6 @@ public class IdRepoImplementationTypeLoader implements SyncopeCoreLoader {
 
     @Override
     public void load() {
-        ImplementationTypesHolder.getInstance().addAll(IdRepoImplementationType.values());
+        ImplementationTypesHolder.getInstance().putAll(IdRepoImplementationType.values());
     }
 }
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PlainSchemaTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PlainSchemaTest.java
index 0049d4a..02de590 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PlainSchemaTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PlainSchemaTest.java
@@ -25,12 +25,11 @@ import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
 
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 import java.util.UUID;
 import java.util.stream.Collectors;
 import javax.persistence.EntityExistsException;
+import javax.persistence.TypedQuery;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.types.AttrSchemaType;
 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
@@ -41,11 +40,11 @@ import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.entity.PlainSchema;
 import org.apache.syncope.core.persistence.api.entity.SchemaLabel;
-import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
 import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
 import org.apache.syncope.core.persistence.jpa.entity.JPASchemaLabel;
+import org.apache.syncope.core.persistence.jpa.entity.resource.JPAMappingItem;
 import org.apache.syncope.core.spring.security.SyncopeAuthenticationDetails;
 import org.apache.syncope.core.spring.security.SyncopeGrantedAuthority;
 import org.junit.jupiter.api.AfterAll;
@@ -110,6 +109,14 @@ public class PlainSchemaTest extends AbstractTest {
         }
     }
 
+    private List<MappingItem> getMappingItems(final String intAttrName) {
+        TypedQuery<MappingItem> mapItemsQuery = entityManager().createQuery(
+                "SELECT e FROM " + JPAMappingItem.class.getSimpleName() + " e WHERE e.intAttrName=:intAttrName",
+                MappingItem.class);
+        mapItemsQuery.setParameter("intAttrName", intAttrName);
+        return mapItemsQuery.getResultList();
+    }
+
     @Test
     public void deleteFullname() {
         // fullname is mapped as ConnObjectKey for ws-target-resource-2, need to swap it otherwise validation errors 
@@ -129,18 +136,7 @@ public class PlainSchemaTest extends AbstractTest {
         assertNotNull(schema);
 
         // check for associated mappings
-        Set<MappingItem> mapItems = new HashSet<>();
-        for (ExternalResource resource : resourceDAO.findAll()) {
-            if (resource.getProvision(anyTypeDAO.findUser()).isPresent()
-                    && resource.getProvision(anyTypeDAO.findUser()).get().getMapping() != null) {
-
-                for (MappingItem mapItem : resource.getProvision(anyTypeDAO.findUser()).get().getMapping().getItems()) {
-                    if (schema.getKey().equals(mapItem.getIntAttrName())) {
-                        mapItems.add(mapItem);
-                    }
-                }
-            }
-        }
+        List<MappingItem> mapItems = getMappingItems("fullname");
         assertFalse(mapItems.isEmpty());
 
         // delete user schema fullname
@@ -155,18 +151,7 @@ public class PlainSchemaTest extends AbstractTest {
         plainSchemaDAO.clear();
 
         // check for mappings deletion
-        mapItems = new HashSet<>();
-        for (ExternalResource resource : resourceDAO.findAll()) {
-            if (resource.getProvision(anyTypeDAO.findUser()).isPresent()
-                    && resource.getProvision(anyTypeDAO.findUser()).get().getMapping() != null) {
-
-                for (MappingItem mapItem : resource.getProvision(anyTypeDAO.findUser()).get().getMapping().getItems()) {
-                    if ("fullname".equals(mapItem.getIntAttrName())) {
-                        mapItems.add(mapItem);
-                    }
-                }
-            }
-        }
+        mapItems = getMappingItems("fullname");
         assertTrue(mapItems.isEmpty());
 
         assertNull(findPlainAttr("01f22fbd-b672-40af-b528-686d9b27ebc4", UPlainAttr.class));
@@ -182,19 +167,8 @@ public class PlainSchemaTest extends AbstractTest {
         assertNotNull(schema);
 
         // check for associated mappings
-        Set<MappingItem> mappings = new HashSet<>();
-        for (ExternalResource resource : resourceDAO.findAll()) {
-            if (resource.getProvision(anyTypeDAO.findUser()).isPresent()
-                    && resource.getProvision(anyTypeDAO.findUser()).get().getMapping() != null) {
-
-                for (MappingItem item : resource.getProvision(anyTypeDAO.findUser()).get().getMapping().getItems()) {
-                    if (schema.getKey().equals(item.getIntAttrName())) {
-                        mappings.add(item);
-                    }
-                }
-            }
-        }
-        assertFalse(mappings.isEmpty());
+        List<MappingItem> mapItems = getMappingItems("surname");
+        assertFalse(mapItems.isEmpty());
 
         // check for labels
         List<SchemaLabel> labels = entityManager().createQuery(
diff --git a/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/rest/OIDCProviderRestClient.java b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/rest/OIDCProviderRestClient.java
index a666d74..4477c2d 100644
--- a/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/rest/OIDCProviderRestClient.java
+++ b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/rest/OIDCProviderRestClient.java
@@ -19,7 +19,6 @@
 package org.apache.syncope.client.console.rest;
 
 import java.util.List;
-import java.util.Set;
 import org.apache.syncope.client.console.SyncopeConsoleSession;
 import org.apache.syncope.common.lib.to.OIDCProviderTO;
 import org.apache.syncope.common.rest.api.service.OIDCProviderService;
@@ -32,10 +31,6 @@ public class OIDCProviderRestClient extends BaseRestClient {
         return getService(OIDCProviderService.class).list();
     }
 
-    public Set<String> getActionsClasses() {
-        return getService(OIDCProviderService.class).getActionsClasses();
-    }
-
     public void create(final OIDCProviderTO op) {
         SyncopeConsoleSession.get().getService(OIDCProviderService.class).create(op);
     }
@@ -55,5 +50,4 @@ public class OIDCProviderRestClient extends BaseRestClient {
     public void delete(final String key) {
         getService(OIDCProviderService.class).delete(key);
     }
-
 }
diff --git a/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder.java b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder.java
index a9dc598..73ad117 100644
--- a/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder.java
+++ b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder.java
@@ -18,27 +18,30 @@
  */
 package org.apache.syncope.client.console.wizards;
 
-import org.apache.syncope.client.console.wizards.resources.OIDCProviderMappingPanel;
 import java.io.Serializable;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.concurrent.Future;
+import java.util.stream.Collectors;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.client.console.SyncopeConsoleSession;
 import org.apache.syncope.client.console.SyncopeWebApplication;
 import org.apache.syncope.client.console.commons.Constants;
 import org.apache.syncope.client.console.panels.OIDCProvidersDirectoryPanel;
+import org.apache.syncope.client.console.rest.ImplementationRestClient;
 import org.apache.syncope.client.console.rest.OIDCProviderRestClient;
 import org.apache.syncope.client.console.wizards.resources.ItemTransformersTogglePanel;
 import org.apache.syncope.client.console.wizards.resources.JEXLTransformersTogglePanel;
+import org.apache.syncope.client.console.wizards.resources.OIDCProviderMappingPanel;
 import org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
 import org.apache.syncope.client.ui.commons.wizards.AjaxWizardBuilder;
+import org.apache.syncope.common.lib.to.EntityTO;
 import org.apache.syncope.common.lib.to.OIDCProviderTO;
+import org.apache.syncope.common.lib.types.OIDCClientImplementationType;
 import org.apache.wicket.PageReference;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.extensions.wizard.WizardModel;
@@ -58,15 +61,18 @@ public class OIDCProviderWizardBuilder extends AjaxWizardBuilder<OIDCProviderTO>
 
     private final OIDCProviderRestClient restClient = new OIDCProviderRestClient();
 
+    private final ImplementationRestClient implRestClient = new ImplementationRestClient();
+
     private final OIDCProvidersDirectoryPanel directoryPanel;
 
-    private final IModel<List<String>> actionsClasses = new LoadableDetachableModel<List<String>>() {
+    private final IModel<List<String>> opActions = new LoadableDetachableModel<List<String>>() {
 
         private static final long serialVersionUID = 5275935387613157437L;
 
         @Override
         protected List<String> load() {
-            return new ArrayList<>(restClient.getActionsClasses());
+            return implRestClient.list(OIDCClientImplementationType.OP_ACTION).stream().
+                    map(EntityTO::getKey).sorted().collect(Collectors.toList());
         }
     };
 
@@ -141,45 +147,45 @@ public class OIDCProviderWizardBuilder extends AjaxWizardBuilder<OIDCProviderTO>
 
         public OP(final OIDCProviderTO opTO) {
             AjaxTextFieldPanel name = new AjaxTextFieldPanel(
-                    "name", "name", new PropertyModel<String>(opTO, "name"), false);
+                    "name", "name", new PropertyModel<>(opTO, "name"), false);
             name.addRequiredLabel();
             name.setEnabled(true);
             add(name);
 
             AjaxTextFieldPanel clientID = new AjaxTextFieldPanel(
-                    "clientID", "clientID", new PropertyModel<String>(opTO, "clientID"), false);
+                    "clientID", "clientID", new PropertyModel<>(opTO, "clientID"), false);
             clientID.addRequiredLabel();
             clientID.setEnabled(true);
             add(clientID);
 
             AjaxTextFieldPanel clientSecret = new AjaxTextFieldPanel(
-                    "clientSecret", "clientSecret", new PropertyModel<String>(opTO, "clientSecret"), false);
+                    "clientSecret", "clientSecret", new PropertyModel<>(opTO, "clientSecret"), false);
             clientSecret.addRequiredLabel();
             clientSecret.setEnabled(true);
             add(clientSecret);
 
             AjaxCheckBoxPanel createUnmatching = new AjaxCheckBoxPanel(
-                    "createUnmatching", "createUnmatching", new PropertyModel<Boolean>(opTO, "createUnmatching"),
+                    "createUnmatching", "createUnmatching", new PropertyModel<>(opTO, "createUnmatching"),
                     false);
             add(createUnmatching);
 
             AjaxCheckBoxPanel selfRegUnmatching = new AjaxCheckBoxPanel(
-                    "selfRegUnmatching", "selfRegUnmatching", new PropertyModel<Boolean>(opTO, "selfRegUnmatching"),
+                    "selfRegUnmatching", "selfRegUnmatching", new PropertyModel<>(opTO, "selfRegUnmatching"),
                     false);
             add(selfRegUnmatching);
 
             AjaxCheckBoxPanel updateMatching = new AjaxCheckBoxPanel(
-                    "updateMatching", "updateMatching", new PropertyModel<Boolean>(opTO, "updateMatching"), false);
+                    "updateMatching", "updateMatching", new PropertyModel<>(opTO, "updateMatching"), false);
             add(updateMatching);
 
-            AjaxPalettePanel<String> actionsClassNames = new AjaxPalettePanel.Builder<String>().
+            AjaxPalettePanel<String> actions = new AjaxPalettePanel.Builder<String>().
+                    setName(new StringResourceModel("actions", directoryPanel).getString()).
                     setAllowMoveAll(true).setAllowOrder(true).
-                    setName(new StringResourceModel("actionsClassNames", directoryPanel).getString()).
-                    build("actionsClassNames",
-                            new PropertyModel<List<String>>(opTO, "actionsClassNames"),
-                            new ListModel<>(actionsClasses.getObject()));
-            actionsClassNames.setOutputMarkupId(true);
-            add(actionsClassNames);
+                    build("actions",
+                            new PropertyModel<>(opTO, "actions"),
+                            new ListModel<>(opActions.getObject()));
+            actions.setOutputMarkupId(true);
+            add(actions);
         }
     }
 
@@ -196,40 +202,40 @@ public class OIDCProviderWizardBuilder extends AjaxWizardBuilder<OIDCProviderTO>
 
             UrlValidator urlValidator = new UrlValidator();
             final AjaxTextFieldPanel issuer = new AjaxTextFieldPanel(
-                    "issuer", "issuer", new PropertyModel<String>(opTO, "issuer"));
+                    "issuer", "issuer", new PropertyModel<>(opTO, "issuer"));
             issuer.addValidator(urlValidator);
             issuer.addRequiredLabel();
             content.add(issuer);
 
             final AjaxCheckBoxPanel hasDiscovery = new AjaxCheckBoxPanel(
-                    "hasDiscovery", "hasDiscovery", new PropertyModel<Boolean>(opTO, "hasDiscovery"));
+                    "hasDiscovery", "hasDiscovery", new PropertyModel<>(opTO, "hasDiscovery"));
             content.add(hasDiscovery);
 
             final AjaxTextFieldPanel authorizationEndpoint = new AjaxTextFieldPanel("authorizationEndpoint",
-                    "authorizationEndpoint", new PropertyModel<String>(opTO, "authorizationEndpoint"));
+                    "authorizationEndpoint", new PropertyModel<>(opTO, "authorizationEndpoint"));
             authorizationEndpoint.addRequiredLabel();
             authorizationEndpoint.addValidator(urlValidator);
             content.add(authorizationEndpoint);
 
             final AjaxTextFieldPanel userinfoEndpoint = new AjaxTextFieldPanel("userinfoEndpoint",
-                    "userinfoEndpoint", new PropertyModel<String>(opTO, "userinfoEndpoint"));
+                    "userinfoEndpoint", new PropertyModel<>(opTO, "userinfoEndpoint"));
             userinfoEndpoint.addValidator(urlValidator);
             content.add(userinfoEndpoint);
 
             final AjaxTextFieldPanel tokenEndpoint = new AjaxTextFieldPanel("tokenEndpoint",
-                    "tokenEndpoint", new PropertyModel<String>(opTO, "tokenEndpoint"));
+                    "tokenEndpoint", new PropertyModel<>(opTO, "tokenEndpoint"));
             tokenEndpoint.addRequiredLabel();
             tokenEndpoint.addValidator(urlValidator);
             content.add(tokenEndpoint);
 
             final AjaxTextFieldPanel jwksUri = new AjaxTextFieldPanel("jwksUri",
-                    "jwksUri", new PropertyModel<String>(opTO, "jwksUri"));
+                    "jwksUri", new PropertyModel<>(opTO, "jwksUri"));
             jwksUri.addRequiredLabel();
             jwksUri.addValidator(urlValidator);
             content.add(jwksUri);
 
             final AjaxTextFieldPanel endSessionEndpoint = new AjaxTextFieldPanel("endSessionEndpoint",
-                    "endSessionEndpoint", new PropertyModel<String>(opTO, "endSessionEndpoint"));
+                    "endSessionEndpoint", new PropertyModel<>(opTO, "endSessionEndpoint"));
             endSessionEndpoint.addValidator(urlValidator);
             content.add(endSessionEndpoint);
 
@@ -254,48 +260,46 @@ public class OIDCProviderWizardBuilder extends AjaxWizardBuilder<OIDCProviderTO>
                     target.add(visibleParam);
                 }
             });
-
         }
 
         public OPContinue(final OIDCProviderTO opTO, final boolean readOnly) {
-
-            final WebMarkupContainer content = new WebMarkupContainer("content");
+            WebMarkupContainer content = new WebMarkupContainer("content");
             this.setOutputMarkupId(true);
             content.setOutputMarkupId(true);
             add(content);
 
             final AjaxTextFieldPanel issuer = new AjaxTextFieldPanel(
-                    "issuer", "issuer", new PropertyModel<String>(opTO, "issuer"));
+                    "issuer", "issuer", new PropertyModel<>(opTO, "issuer"));
             issuer.setReadOnly(readOnly);
             content.add(issuer);
 
             final AjaxCheckBoxPanel hasDiscovery = new AjaxCheckBoxPanel(
-                    "hasDiscovery", "hasDiscovery", new PropertyModel<Boolean>(opTO, "hasDiscovery"));
+                    "hasDiscovery", "hasDiscovery", new PropertyModel<>(opTO, "hasDiscovery"));
             hasDiscovery.setReadOnly(readOnly);
             content.add(hasDiscovery);
 
             final AjaxTextFieldPanel authorizationEndpoint = new AjaxTextFieldPanel("authorizationEndpoint",
-                    "authorizationEndpoint", new PropertyModel<String>(opTO, "authorizationEndpoint"));
+                    "authorizationEndpoint", new PropertyModel<>(opTO, "authorizationEndpoint"));
             authorizationEndpoint.setReadOnly(readOnly);
             content.add(authorizationEndpoint);
 
             final AjaxTextFieldPanel userinfoEndpoint = new AjaxTextFieldPanel("userinfoEndpoint",
-                    "userinfoEndpoint", new PropertyModel<String>(opTO, "userinfoEndpoint"));
+                    "userinfoEndpoint", new PropertyModel<>(opTO, "userinfoEndpoint"));
             userinfoEndpoint.setReadOnly(readOnly);
             content.add(userinfoEndpoint);
 
             final AjaxTextFieldPanel tokenEndpoint = new AjaxTextFieldPanel("tokenEndpoint",
-                    "tokenEndpoint", new PropertyModel<String>(opTO, "tokenEndpoint"));
+                    "tokenEndpoint", new PropertyModel<>(opTO, "tokenEndpoint"));
             tokenEndpoint.setReadOnly(readOnly);
             content.add(tokenEndpoint);
 
             final AjaxTextFieldPanel jwksUri = new AjaxTextFieldPanel("jwksUri",
-                    "jwksUri", new PropertyModel<String>(opTO, "jwksUri"));
+                    "jwksUri", new PropertyModel<>(opTO, "jwksUri"));
             jwksUri.setReadOnly(readOnly);
             content.add(jwksUri);
 
             final AjaxTextFieldPanel endSessionEndpoint = new AjaxTextFieldPanel("endSessionEndpoint",
-                    "endSessionEndpoint", new PropertyModel<String>(opTO, "endSessionEndpoint"));
+                    "endSessionEndpoint", new PropertyModel<>(opTO, "endSessionEndpoint"));
             endSessionEndpoint.setReadOnly(readOnly);
             content.add(endSessionEndpoint);
 
@@ -330,5 +334,4 @@ public class OIDCProviderWizardBuilder extends AjaxWizardBuilder<OIDCProviderTO>
             setSummaryModel(Model.of(StringUtils.EMPTY));
         }
     }
-
 }
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.properties
index 569d0dc..0c6e946 100644
--- a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.properties
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.properties
@@ -24,7 +24,7 @@ any.new=New Provider
 template.title=user template
 createUnmatching=Create unmatching users
 updateMatching=Update matching users
-actionsClassNames=Actions
+actions=Actions
 
 
 
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_it.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_it.properties
index 569d0dc..0c6e946 100644
--- a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_it.properties
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_it.properties
@@ -24,7 +24,7 @@ any.new=New Provider
 template.title=user template
 createUnmatching=Create unmatching users
 updateMatching=Update matching users
-actionsClassNames=Actions
+actions=Actions
 
 
 
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_pt_BR.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_pt_BR.properties
index 569d0dc..0c6e946 100644
--- a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_pt_BR.properties
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_pt_BR.properties
@@ -24,7 +24,7 @@ any.new=New Provider
 template.title=user template
 createUnmatching=Create unmatching users
 updateMatching=Update matching users
-actionsClassNames=Actions
+actions=Actions
 
 
 
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_ru.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_ru.properties
index 569d0dc..0c6e946 100644
--- a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_ru.properties
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_ru.properties
@@ -24,7 +24,7 @@ any.new=New Provider
 template.title=user template
 createUnmatching=Create unmatching users
 updateMatching=Update matching users
-actionsClassNames=Actions
+actions=Actions
 
 
 
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP.html b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP.html
index cfe5df7..caea371 100644
--- a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP.html
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP.html
@@ -14,7 +14,7 @@ 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.  actionsClassNames
+under the License.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
   <wicket:panel>
@@ -24,6 +24,6 @@ under the License.  actionsClassNames
     <div class="form-group"><span wicket:id="createUnmatching">[createUnmatching]</span></div>
     <div class="form-group"><span wicket:id="selfRegUnmatching">[selfRegUnmatching]</span></div>
     <div class="form-group"><span wicket:id="updateMatching">[updateMatching]</span></div>
-    <div class="form-group"><span wicket:id="actionsClassNames">[actionsClassNames]</span></div>
+    <div class="form-group"><span wicket:id="actions">[actions]</span></div>
   </wicket:panel>
 </html>
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP.properties
index 123372f..9b3313e 100644
--- a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP.properties
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP.properties
@@ -20,6 +20,6 @@ clientSecret=Client Secret
 createUnmatching=Create unmatching users
 selfRegUnmatching=OIDC-initiated self-registration
 updateMatching=Update matching users
-actionsClassNames=Actions
+actions=Actions
 
 
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_it.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_it.properties
index 123372f..9b3313e 100644
--- a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_it.properties
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_it.properties
@@ -20,6 +20,6 @@ clientSecret=Client Secret
 createUnmatching=Create unmatching users
 selfRegUnmatching=OIDC-initiated self-registration
 updateMatching=Update matching users
-actionsClassNames=Actions
+actions=Actions
 
 
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_pt_BR.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_pt_BR.properties
index 123372f..9b3313e 100644
--- a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_pt_BR.properties
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_pt_BR.properties
@@ -20,6 +20,6 @@ clientSecret=Client Secret
 createUnmatching=Create unmatching users
 selfRegUnmatching=OIDC-initiated self-registration
 updateMatching=Update matching users
-actionsClassNames=Actions
+actions=Actions
 
 
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_ru.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_ru.properties
index 123372f..9b3313e 100644
--- a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_ru.properties
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_ru.properties
@@ -20,6 +20,6 @@ clientSecret=Client Secret
 createUnmatching=Create unmatching users
 selfRegUnmatching=OIDC-initiated self-registration
 updateMatching=Update matching users
-actionsClassNames=Actions
+actions=Actions
 
 
diff --git a/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCProviderTO.java b/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCProviderTO.java
index 60ecf41..135e2b2 100644
--- a/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCProviderTO.java
+++ b/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCProviderTO.java
@@ -20,9 +20,7 @@ package org.apache.syncope.common.lib.to;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 import javax.ws.rs.PathParam;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlElementWrapper;
@@ -67,7 +65,7 @@ public class OIDCProviderTO implements EntityTO, ItemContainerTO {
 
     private final List<ItemTO> items = new ArrayList<>();
 
-    private final Set<String> actionsClassNames = new HashSet<>();
+    private final List<String> actions = new ArrayList<>();
 
     @Override
     public String getKey() {
@@ -228,11 +226,10 @@ public class OIDCProviderTO implements EntityTO, ItemContainerTO {
         return this.items.remove(item);
     }
 
-    @XmlElementWrapper(name = "actionsClassNames")
-    @XmlElement(name = "actionsClassName")
-    @JsonProperty("actionsClassNames")
-    public Set<String> getActionsClassNames() {
-        return actionsClassNames;
+    @XmlElementWrapper(name = "actions")
+    @XmlElement(name = "action")
+    @JsonProperty("actions")
+    public List<String> getActions() {
+        return actions;
     }
-
 }
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/ValueHolder.java b/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/types/OIDCClientImplementationType.java
similarity index 63%
rename from common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/ValueHolder.java
rename to ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/types/OIDCClientImplementationType.java
index 8c514cb..ca94cec 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/ValueHolder.java
+++ b/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/types/OIDCClientImplementationType.java
@@ -18,20 +18,21 @@
  */
 package org.apache.syncope.common.lib.types;
 
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.Map;
+import org.apache.commons.lang3.tuple.Pair;
 
-abstract class ValueHolder<T> {
+public final class OIDCClientImplementationType {
 
-    protected final Set<T> values = Collections.synchronizedSet(new HashSet<>());
+    public static final String OP_ACTION = "OP_ACTION";
 
-    public void addAll(final Collection<T> values) {
-        this.values.addAll(values);
+    private static final Map<String, String> VALUES = Map.ofEntries(
+            Pair.of(OP_ACTION, "org.apache.syncope.core.provisioning.api.OIDCProviderActions"));
+
+    public static Map<String, String> values() {
+        return VALUES;
     }
 
-    public Set<T> getValues() {
-        return Collections.unmodifiableSet(values);
+    private OIDCClientImplementationType() {
+        // private constructor for static utility class
     }
 }
diff --git a/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/OIDCProviderLogic.java b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/OIDCProviderLogic.java
index a73155d..496618e 100644
--- a/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/OIDCProviderLogic.java
+++ b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/OIDCProviderLogic.java
@@ -22,7 +22,6 @@ import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
 import java.lang.reflect.Method;
 import java.util.Arrays;
 import java.util.List;
-import java.util.Set;
 import java.util.stream.Collectors;
 import javax.ws.rs.ClientErrorException;
 import javax.ws.rs.core.MediaType;
@@ -33,7 +32,6 @@ import org.apache.syncope.common.lib.to.ItemTO;
 import org.apache.syncope.common.lib.to.OIDCProviderTO;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
 import org.apache.syncope.common.lib.types.OIDCClientEntitlement;
-import org.apache.syncope.core.logic.init.OIDCClientClassPathScanImplementationLookup;
 import org.apache.syncope.core.logic.model.OIDCProviderDiscoveryDocument;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.dao.OIDCProviderDAO;
@@ -53,14 +51,6 @@ public class OIDCProviderLogic extends AbstractTransactionalLogic<OIDCProviderTO
     @Autowired
     private OIDCProviderDataBinder binder;
 
-    @Autowired
-    private OIDCClientClassPathScanImplementationLookup implLookup;
-
-    @PreAuthorize("isAuthenticated()")
-    public Set<String> getActionsClasses() {
-        return implLookup.getActionsClasses();
-    }
-
     private OIDCProviderDiscoveryDocument getDiscoveryDocument(final String issuer) {
         String discoveryDocumentURL = issuer + "/.well-known/openid-configuration";
         WebClient client = WebClient.create(discoveryDocumentURL, Arrays.asList(new JacksonJsonProvider())).
diff --git a/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/init/OIDCClientClassPathScanImplementationLookup.java b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/init/OIDCClientClassPathScanImplementationLookup.java
deleted file mode 100644
index 169b342..0000000
--- a/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/init/OIDCClientClassPathScanImplementationLookup.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.init;
-
-import java.lang.reflect.Modifier;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-import org.apache.syncope.core.persistence.api.ImplementationLookup;
-import org.apache.syncope.core.persistence.api.SyncopeCoreLoader;
-import org.apache.syncope.core.provisioning.api.OIDCProviderActions;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
-import org.springframework.core.type.filter.AssignableTypeFilter;
-import org.springframework.stereotype.Component;
-import org.springframework.util.ClassUtils;
-
-@Component
-public class OIDCClientClassPathScanImplementationLookup implements SyncopeCoreLoader {
-
-    private static final Logger LOG = LoggerFactory.getLogger(ImplementationLookup.class);
-
-    private static final String DEFAULT_BASE_PACKAGE = "org.apache.syncope.core";
-
-    private Set<String> actionsClasses;
-
-    @Override
-    public int getOrder() {
-        return 999;
-    }
-
-    @Override
-    public void load() {
-        actionsClasses = new HashSet<>();
-
-        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
-        scanner.addIncludeFilter(new AssignableTypeFilter(OIDCProviderActions.class));
-
-        scanner.findCandidateComponents(DEFAULT_BASE_PACKAGE).forEach(bd -> {
-            try {
-                Class<?> clazz = ClassUtils.resolveClassName(bd.getBeanClassName(), ClassUtils.getDefaultClassLoader());
-                boolean isAbstractClazz = Modifier.isAbstract(clazz.getModifiers());
-
-                if (OIDCProviderActions.class.isAssignableFrom(clazz) && !isAbstractClazz) {
-                    actionsClasses.add(clazz.getName());
-                }
-            } catch (Throwable t) {
-                LOG.warn("Could not inspect class {}", bd.getBeanClassName(), t);
-            }
-        });
-
-        actionsClasses = Collections.unmodifiableSet(actionsClasses);
-    }
-
-    public Set<String> getActionsClasses() {
-        return actionsClasses;
-    }
-}
diff --git a/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/init/OIDCClientLoader.java b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/init/OIDCClientLoader.java
index 53a0386..11270a0 100644
--- a/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/init/OIDCClientLoader.java
+++ b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/init/OIDCClientLoader.java
@@ -19,7 +19,9 @@
 package org.apache.syncope.core.logic.init;
 
 import org.apache.syncope.common.lib.types.EntitlementsHolder;
+import org.apache.syncope.common.lib.types.ImplementationTypesHolder;
 import org.apache.syncope.common.lib.types.OIDCClientEntitlement;
+import org.apache.syncope.common.lib.types.OIDCClientImplementationType;
 import org.apache.syncope.core.persistence.api.SyncopeCoreLoader;
 import org.springframework.stereotype.Component;
 
@@ -34,5 +36,6 @@ public class OIDCClientLoader implements SyncopeCoreLoader {
     @Override
     public void load() {
         EntitlementsHolder.getInstance().addAll(OIDCClientEntitlement.values());
+        ImplementationTypesHolder.getInstance().putAll(OIDCClientImplementationType.values());
     }
 }
diff --git a/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCUserManager.java b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCUserManager.java
index 2adf66f..cdde240 100644
--- a/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCUserManager.java
+++ b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCUserManager.java
@@ -55,11 +55,10 @@ import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
 import org.apache.syncope.core.provisioning.java.IntAttrNameParser;
 import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
 import org.apache.syncope.core.provisioning.java.utils.TemplateUtils;
-import org.apache.syncope.core.spring.ApplicationContextProvider;
+import org.apache.syncope.core.spring.ImplementationManager;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.support.AbstractBeanDefinition;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
@@ -164,16 +163,11 @@ public class OIDCUserManager {
 
     private List<OIDCProviderActions> getActions(final OIDCProvider op) {
         List<OIDCProviderActions> actions = new ArrayList<>();
-
-        op.getActionsClassNames().forEach(className -> {
+        op.getActions().forEach(impl -> {
             try {
-                Class<?> actionsClass = Class.forName(className);
-                OIDCProviderActions opActions = (OIDCProviderActions) ApplicationContextProvider.getBeanFactory().
-                        createBean(actionsClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, true);
-
-                actions.add(opActions);
+                actions.add(ImplementationManager.build(impl));
             } catch (Exception e) {
-                LOG.warn("Class '{}' not found", className, e);
+                LOG.warn("While building {}", impl, e);
             }
         });
 
diff --git a/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCProvider.java b/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCProvider.java
index ad96052..b6daf54 100644
--- a/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCProvider.java
+++ b/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCProvider.java
@@ -20,7 +20,6 @@ package org.apache.syncope.core.persistence.api.entity;
 
 import java.util.List;
 import java.util.Optional;
-import java.util.Set;
 
 public interface OIDCProvider extends Entity {
 
@@ -88,6 +87,7 @@ public interface OIDCProvider extends Entity {
 
     boolean add(OIDCProviderItem item);
 
-    Set<String> getActionsClassNames();
+    boolean add(Implementation action);
 
+    List<? extends Implementation> getActions();
 }
diff --git a/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCProvider.java b/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCProvider.java
index ab85cfd..ae748b4 100644
--- a/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCProvider.java
+++ b/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCProvider.java
@@ -19,22 +19,22 @@
 package org.apache.syncope.core.persistence.jpa.entity;
 
 import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Optional;
-import java.util.Set;
 import javax.persistence.Cacheable;
 import javax.persistence.CascadeType;
-import javax.persistence.CollectionTable;
 import javax.persistence.Column;
-import javax.persistence.ElementCollection;
 import javax.persistence.Entity;
 import javax.persistence.FetchType;
 import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
 import javax.persistence.OneToMany;
 import javax.persistence.OneToOne;
 import javax.persistence.Table;
 import javax.validation.constraints.NotNull;
+import org.apache.syncope.common.lib.types.OIDCClientImplementationType;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.OIDCProvider;
 import org.apache.syncope.core.persistence.api.entity.OIDCProviderItem;
 import org.apache.syncope.core.persistence.api.entity.OIDCUserTemplate;
@@ -95,12 +95,13 @@ public class JPAOIDCProvider extends AbstractGeneratedKeyEntity implements OIDCP
     @NotNull
     private Boolean updateMatching = false;
 
-    @ElementCollection(fetch = FetchType.EAGER)
-    @Column(name = "actionClassName")
-    @CollectionTable(name = TABLE + "_actionsClassNames",
+    @ManyToMany(fetch = FetchType.EAGER)
+    @JoinTable(name = "OIDCProviderAction",
             joinColumns =
-            @JoinColumn(name = "oidcOP_id", referencedColumnName = "id"))
-    private Set<String> actionsClassNames = new HashSet<>();
+            @JoinColumn(name = "op_id"),
+            inverseJoinColumns =
+            @JoinColumn(name = "implementation_id"))
+    private List<JPAImplementation> actions = new ArrayList<>();
 
     @Override
     public String getName() {
@@ -266,8 +267,14 @@ public class JPAOIDCProvider extends AbstractGeneratedKeyEntity implements OIDCP
     }
 
     @Override
-    public Set<String> getActionsClassNames() {
-        return actionsClassNames;
+    public boolean add(final Implementation action) {
+        checkType(action, JPAImplementation.class);
+        checkImplementationType(action, OIDCClientImplementationType.OP_ACTION);
+        return actions.contains((JPAImplementation) action) || actions.add((JPAImplementation) action);
     }
 
+    @Override
+    public List<? extends Implementation> getActions() {
+        return actions;
+    }
 }
diff --git a/ext/oidcclient/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCProviderDataBinderImpl.java b/ext/oidcclient/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCProviderDataBinderImpl.java
index 45f19ab..6e59535 100644
--- a/ext/oidcclient/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCProviderDataBinderImpl.java
+++ b/ext/oidcclient/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCProviderDataBinderImpl.java
@@ -31,8 +31,10 @@ import org.apache.syncope.common.lib.types.ClientExceptionType;
 import org.apache.syncope.common.lib.types.MappingPurpose;
 import org.apache.syncope.common.lib.types.SchemaType;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
+import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
 import org.apache.syncope.core.persistence.api.dao.OIDCProviderDAO;
 import org.apache.syncope.core.persistence.api.entity.Entity;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.OIDCEntityFactory;
 import org.apache.syncope.core.persistence.api.entity.OIDCProvider;
 import org.apache.syncope.core.persistence.api.entity.OIDCProviderItem;
@@ -51,8 +53,6 @@ public class OIDCProviderDataBinderImpl implements OIDCProviderDataBinder {
 
     private static final Logger LOG = LoggerFactory.getLogger(OIDCProviderDataBinder.class);
 
-    private static final String[] ITEM_IGNORE_PROPERTIES = { "key", "purpose" };
-
     @Autowired
     private AnyTypeDAO anyTypeDAO;
 
@@ -60,6 +60,9 @@ public class OIDCProviderDataBinderImpl implements OIDCProviderDataBinder {
     private OIDCProviderDAO oidcOPDAO;
 
     @Autowired
+    private ImplementationDAO implementationDAO;
+
+    @Autowired
     private OIDCEntityFactory entityFactory;
 
     @Autowired
@@ -214,8 +217,16 @@ public class OIDCProviderDataBinderImpl implements OIDCProviderDataBinder {
         });
         populateItems(opTO, op, allowedSchemas);
 
-        op.getActionsClassNames().clear();
-        op.getActionsClassNames().addAll(opTO.getActionsClassNames());
+        opTO.getActions().forEach(action -> {
+            Implementation implementation = implementationDAO.find(action);
+            if (implementation == null) {
+                LOG.debug("Invalid " + Implementation.class.getSimpleName() + " {}, ignoring...", action);
+            } else {
+                op.add(implementation);
+            }
+        });
+        // remove all implementations not contained in the TO
+        op.getActions().removeIf(impl -> !opTO.getActions().contains(impl.getKey()));
 
         return oidcOPDAO.save(op);
     }
@@ -266,7 +277,8 @@ public class OIDCProviderDataBinderImpl implements OIDCProviderDataBinder {
 
         populateItems(op, opTO);
 
-        opTO.getActionsClassNames().addAll(op.getActionsClassNames());
+        opTO.getActions().addAll(
+                op.getActions().stream().map(Entity::getKey).collect(Collectors.toList()));
 
         return opTO;
     }
diff --git a/ext/oidcclient/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/OIDCProviderService.java b/ext/oidcclient/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/OIDCProviderService.java
index be7c7be..de816d0 100644
--- a/ext/oidcclient/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/OIDCProviderService.java
+++ b/ext/oidcclient/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/OIDCProviderService.java
@@ -37,7 +37,6 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.apache.syncope.common.lib.to.OIDCProviderTO;
 import java.util.List;
-import java.util.Set;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.PUT;
 import javax.ws.rs.PathParam;
@@ -55,16 +54,6 @@ import org.apache.syncope.common.rest.api.RESTHeaders;
 public interface OIDCProviderService extends JAXRSService {
 
     /**
-     * Returns the list of available OIDCProviderActions implementations.
-     *
-     * @return the list of available OIDCProviderActions implementations
-     */
-    @GET
-    @Path("actionsClasses")
-    @Produces({ MediaType.APPLICATION_JSON })
-    Set<String> getActionsClasses();
-
-    /**
      * Returns a list of all defined OIDC Providers.
      *
      * @return list of all defined OIDC Providers
diff --git a/ext/oidcclient/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/OIDCProviderServiceImpl.java b/ext/oidcclient/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/OIDCProviderServiceImpl.java
index 7af4366..7fc586e 100644
--- a/ext/oidcclient/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/OIDCProviderServiceImpl.java
+++ b/ext/oidcclient/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/OIDCProviderServiceImpl.java
@@ -20,7 +20,6 @@ package org.apache.syncope.core.rest.cxf.service;
 
 import java.net.URI;
 import java.util.List;
-import java.util.Set;
 import javax.ws.rs.core.Response;
 import org.apache.syncope.common.lib.to.OIDCProviderTO;
 import org.apache.syncope.common.rest.api.RESTHeaders;
@@ -36,11 +35,6 @@ public class OIDCProviderServiceImpl extends AbstractServiceImpl implements OIDC
     private OIDCProviderLogic logic;
 
     @Override
-    public Set<String> getActionsClasses() {
-        return logic.getActionsClasses();
-    }
-
-    @Override
     public Response create(final OIDCProviderTO oidcProviderTO) {
         String created = logic.create(oidcProviderTO);