Hello Bart,
Regarding your initial question on JNDI datasource vs. manual configuration - from the
architect's standpoint, each of the approaches has its pros and cons (as always).
Your approach has an obvious benefit of automatic config propagation to the nodes, thanks
to Keycloak components model backed by Infinispan and shared database. But if (or when) it
comes to advanced configuration options like SSL, connection pooling, tracing etc.,
you'll end up reimplementing more and more datasource handling logic that has already
been implemented in the corresponding Wildfly subsystem.
Having datasources configured in Wildfly (and accessed via JNDI) will get you rid of
reinventing the wheel. However, in this case you need to take care of the proper config
propagation to the cluster members. This is done automatically in the Keycloak/Wildfly
domain mode, but for standalone and its variants you will have to use jboss-cli (maybe in
combination with configuration management tools like Ansible etc.)
Many software products allow for both variants, either setting up connection properties
manually or using JNDI datasource configured at the application server level. I suggest
that if you strive for a production-ready solution, you should consider implementing JNDI
too.
Cheers,
Dmitry Telegin
CTO, Acutus s.r.o.
Keycloak Consulting and Training
Pod lipami street 339/52, 130 00 Prague 3, Czech Republic
+42 (022) 888-30-71
E-mail: info(a)acutus.pro
On Sun, 2018-12-09 at 13:26 +0100, Bart Lievens wrote:
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;
}
}
_______________________________________________
keycloak-user mailing list
keycloak-user(a)lists.jboss.org
https://lists.jboss.org/mailman/listinfo/keycloak-user