[keycloak-user] UserStorageProvider for an external database

Bart Lievens bart.lievens at unifiedpost.com
Sun Dec 9 07:26:16 EST 2018


Hello, 

I solved the problem not by adding a datasource to WildFly but by adding the configuration parameters to the UserStorageProviderFactory 
and creating the EntityManager inside the UserStorageProviderFactory and then passing it on when creating a UserStorageProvider.

My UserStorageProviderFactory looks something like (with 4.6.0.Final & 4.7.0.Final) :
public class ExternalUserStorageProviderFactory implements UserStorageProviderFactory<ExternalUserStorageProvider> {
    private static final transient Logger logger = LoggerFactory.getLogger(ExternalUserStorageProviderFactory.class);
    private static final String CONF_NAME_JDBC_URL = "jdbcUrl";
    private static final String CONF_NAME_JDBC_USER = "user";
    private static final String CONF_NAME_JDBC_PASSWORD = "password";

    protected static final List<ProviderConfigProperty> configMetadata;
    private Map<String, EntityManager> entityManagers = new HashMap<>();

    static {
        ProviderConfigurationBuilder builder = ProviderConfigurationBuilder.create();
        builder.property().name(CONF_NAME_JDBC_URL).type(ProviderConfigProperty.STRING_TYPE).label("Jdbc Url")
               .defaultValue("jdbc:postgresql://host:port/database")
               .helpText("Postgres JDBC Connection URL to external user db")
               .add();
        builder.property().name(CONF_NAME_JDBC_USER).type(ProviderConfigProperty.STRING_TYPE).label("Jdbc User")
               .helpText("JDBC Connection User")
               .add();
        builder.property().name(CONF_NAME_JDBC_PASSWORD).type(ProviderConfigProperty.PASSWORD).label("Jdbc Password")
               .helpText("JDBC Connection Password")
               .add();
        configMetadata = builder.build();
    }

    @Override
    public List<ProviderConfigProperty> getConfigProperties() {
        return configMetadata;
    }

    @Override
    public void validateConfiguration(KeycloakSession session, RealmModel realm, ComponentModel componentModel) throws ComponentValidationException {
        if (componentModel.getConfig().getFirst(CONF_NAME_JDBC_URL) == null
            || componentModel.getConfig().getFirst(CONF_NAME_JDBC_USER) == null
            || componentModel.getConfig().getFirst(CONF_NAME_JDBC_PASSWORD) == null) {
            throw new ComponentValidationException("The jdbc Url, User and Password are requirec");
        }
        try {
            createEntityManager(componentModel);
        } catch (Exception e) {
            logger.warn("Invalid configuration {}", e.getCause() == null ? e.getMessage() : e.getCause().getMessage());
            throw new ComponentValidationException("Could not setup jdbc connection : " + (e.getCause() == null ? e.getMessage() : e.getCause().getMessage()));
        }
    }

    @Override
    public ExternalUserStorageProvider create(KeycloakSession session, ComponentModel model) {
        try {
            if (entityManagers.get(model.getId()) == null) {
                createEntityManager(model);
            }
            return new ExternalUserStorageProvider(entityManagers.get(model.getId()), model, session);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public String getId() {
        return "external-user-db";
    }

    @Override
    public String getHelpText() {
        return "External User Database Storage Provider";
    }

    private void createEntityManager(ComponentModel model) {
        logger.info("creating entityManager for {}", model.getName());
        Properties properties = getProperties(model);
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("external-user-storage", properties);
        entityManagers.put(model.getId(), entityManagerFactory.createEntityManager());
    }

    private Properties getProperties(ComponentModel model) {
        Properties properties = new Properties();
        // Add class loader needed to find persistence.xml
        properties.put(AvailableSettings.CLASSLOADERS, Arrays.asList(this.getClass().getClassLoader()));
        // Set JPA properties
        properties.put(AvailableSettings.JPA_PERSISTENCE_PROVIDER, HibernatePersistenceProvider.class.getName());
        properties.put(AvailableSettings.JPA_TRANSACTION_TYPE, PersistenceUnitTransactionType.JTA.name());
        // postgresql jdbc connection config 
        properties.put(AvailableSettings.JPA_JDBC_DRIVER, "org.postgresql.Driver");
        properties.put(AvailableSettings.JPA_JDBC_URL, EnvUtil.replace(model.getConfig().getFirst(CONF_NAME_JDBC_URL)));
        properties.put(AvailableSettings.JPA_JDBC_USER, EnvUtil.replace(model.getConfig().getFirst(CONF_NAME_JDBC_USER)));
        properties.put(AvailableSettings.JPA_JDBC_PASSWORD, EnvUtil.replace(model.getConfig().getFirst(CONF_NAME_JDBC_PASSWORD)));
        // hibernate
        properties.put(AvailableSettings.DIALECT, org.hibernate.dialect.PostgreSQL95Dialect.class.getName());
        properties.put(AvailableSettings.SHOW_SQL, Boolean.FALSE);
        // set JTA properties
        properties.put(AvailableSettings.JTA_PLATFORM, JBossAppServerJtaPlatform.class.getName());
        return properties;
    }
}



More information about the keycloak-user mailing list