How to initialize several UserProviders in Keycloak - single-sign-on

I'm having big difficulties when trying to make Keycloak to use default JPA provider that is implemented in Keycloak (package: org.keycloak.models.jpa.JpaUserProviderFactory)
alongside with my custom UserProvider.
My user provider is implemented like this:
public class MyCustomUserProviderFactory extends JpaUserProviderFactory {
#Override
public String getId() {
return "demo";
}
#Override
public UserProvider create(KeycloakSession session) {
EntityManager em = session.getProvider(JpaConnectionProvider.class).getEntityManager();
return new MyCustomUserProvider(session, em);
}
}
and MyCustomUserProvider
public class MyCustomUserProvider extends JpaUserProvider {
private final KeycloakSession session;
public MyCustomUserProvider(KeycloakSession session, EntityManager em) {
super(session, em);
this.session = session;
}
}
startup.cli
/subsystem=keycloak-server/spi=user:add(default-provider=demo)
/subsystem=keycloak-server/spi=user/provider=jpa:add(enabled=true)
/subsystem=keycloak-server/spi=user/provider=demo:add(enabled=true)
also in META-INF/service i have
org.keycloak.models.UserProviderFactory
with com.demo.keycloak.providers.user.MyCustomUserProviderFactory
So with this implementation and settings, keycloak is started successfully but when I need to access both demo and jpa UserProvider I only get default provider that is in this case demo.
I access UserProvider with:
UserProvider demoProvider = session.getProvider(UserProvider.class, "demo");
but if I execute
UserProvider jpaProvider = session.getProvider(UserProvider.class, "jpa");
I only get null.
If I change the default provider in the startup.cli (default-provider=demo) I would then get the jpa but not the demo provider.
Can somebody explan to me how could I have several UserProvider implementation and how can I access to both of them?

Sorry for not answering on my question after quite a long time.
Unfortunately there is no way of having multiple JPAUserProvider at the same time.
The only way to add custom implementation in JpaUserProvider methods is to override them add the additional call and in the configuration itself add the custom version as a default one
/subsystem=keycloak-server/spi=user:add(default-provider=demo).

Related

Keycloak display custom creation page for custom IDP

I try to create a custom IDP for keycloak.
So for test purposes I've written a Provider and a ProviderFactory class both inheriting from their respective SAML counterpart (really only changing the display name and provider ID so far).
public class MySAMLIdentityProviderFactory extends SAMLIdentityProviderFactory {
public static final String PROVIDER_ID = "my-saml";
private DestinationValidator destinationValidator;
#Override
public String getName() {
return "my SAML";
}
#Override
public MySAMLIdentityProvider create(KeycloakSession session, IdentityProviderModel model) {
return new MySAMLIdentityProvider(session, new SAMLIdentityProviderConfig(model), destinationValidator);
}
#Override
public String getId() {
return PROVIDER_ID;
}
#Override
public void init(Scope config) {
super.init(config);
this.destinationValidator = DestinationValidator.forProtocolMap(config.getArray("knownProtocols"));
}
}
public class MySAMLIdentityProvider extends SAMLIdentityProvider {
public MySAMLIdentityProvider(KeycloakSession session, SAMLIdentityProviderConfig config, DestinationValidator destinationValidator) {
super(session, config, destinationValidator);
}
}
Following this advice I also added a realm-identity-provider-my-saml.html and a realm-identity-provider-my-saml-ext.html under ./themes/base/admin/resources/partials.
The -ext being empty and the other html looking like this:
<div data-ng-include data-src="resourceUrl + '/partials/realm-identity-provider-saml.html'"></div>
I can now go to the admin browser console and add this new IDP to my realm.
Uppon creation however, I am not asked for SAML attributes like "Use entity descriptor" or "Display name".
Instead I get the creation screen for social providers asking for "Client-ID" and "Client-Secret".
Only after initial creation, the common SAML attributes appear and can be configured.
How can I get Keycloak to display the propper SAML IDP creation screen?
Edit:
I use Keycloak version 19.0.1.
The issue seems to be connected to the new Keycloak.v2 theme which is weird since as far as I can see the v2 theme does not overwrite the admin resources only the account resources.
So, keycloak has an open issue for this.......

Run method after deploying and save the result with to database

I have started a Quarkus project and i have there 2 tables, i am trying to run a Method immediately after deploying, In the method i use Entitymanger to save some results in the database.
In pure Jakarta EE, you could an EJB and annotate it with #Startup. But Since quarkus uses CDI.
#ApplicationScoped
public class StartApp {
private static final String PERSISTENCE_UNIT_NAME = "Employee";
public void init(#Observes #Initialized(ApplicationScoped.class) Object init) {
EntityManagerFactory factory =
Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
EntityManager em = factory.createEntityManager();
Directory directory = new Directory("/some/info", true, false, ".xml");
em.persist(directory);
em.close();
}
}
How can i do that!? some guess !
I think what you need is this:
#ApplicationScoped
class StartApp {
void startup(#Observes StartupEvent event) {
// Do whatever needs to be done.
}
}
More information and options can be found on the very well documented quarkus pages. https://quarkus.io/guides/cdi-reference#startup-event
Ps. don't forget about your transactions and maybe take a look at Hibernate ORM.

Security Claims in DBCommandInterceptor

i am currently using EF and .NET Core 3 through Radzen to build an application. This is working fine, but I want to add additional logging to the database. In order to do so, I would like to make use of the DBCommandInterceptor as shown here to do some post query commands.
Is it possible to get the Claims of the Microsoft Authorization in this Interceptor class? In my normal controller class, I can simply call
var userId = User.FindFirst(ClaimTypes.Name).Value;
This doesn't work in the Interceptor and to be honest, my knowledge about that framework is very poor. I cannot even tell you why I can access the User reference in my ObjectController against the DBCommandInterceptor
If you add the Interceptor in DbContext.OnConfiguring, you can pass any state to it you want.
So require your DbContext to accept an Identity, or with a service dependency it can use to access the user. something like:
public class Db : DbContext
{
ClaimsIdentity user;
public Db(ClaimsIdentity user)
{
this.user = user;
}
Then configure the Interceptor to accept the User, or the DbContext instance. eg
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=localhost;Database=EFCore3Test;Integrated Security = true", a => a.UseRelationalNulls(true))
.ConfigureWarnings(c => c.Log((RelationalEventId.CommandExecuting, LogLevel.Information)))
.UseLoggerFactory(MyLoggerFactory)
.AddInterceptors(new MyCommandInterceptor(this));
base.OnConfiguring(optionsBuilder);
}
And have the interceptor use the constructor argument:
public class MyCommandInterceptor : DbCommandInterceptor
{
private Db db;
public MyCommandInterceptor(Db db)
{
this.db = db;
}
public override InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)
{
var userId = db.user.FindFirst(ClaimTypes.Name).Value;
//. . .
return base.ReaderExecuting(command, eventData, result);
}
}

microprofile-config custom ConfigSource using JPA

I am currently trying to setup a custom ConfigSource reading config values from our DB2. As the ConfigSources are loaded via ServiceLoader it looks like there is no way to access the database via JPA as the ServiceLoader is scanning for custom ConfigSources very early.
Any ideas?
You can anotate your ConfigSource as a singleton session bean and mark it for eager initialization during the application startup sequence.
Also you need to define a static member variable holding your config values.
With this setup you can lazy load your properties values from an injected JPA source or also from any other CDI or EJB.
See the following example Code
#Startup
#Singleton
public class MyConfigSource implements ConfigSource {
public static final String NAME = "MyConfigSource";
public static Map<String, String> properties = null; // note to use static here!
#PersistenceContext(unitName = ".....")
private EntityManager manager;
#PostConstruct
void init() {
// load your data from teh JPA source or EJB
....
}
#Override
public int getOrdinal() {
return 890;
}
#Override
public String getValue(String key) {
if (properties != null) {
return properties.get(key);
} else {
return null;
}
}
#Override
public String getName() {
return NAME;
}
#Override
public Map<String, String> getProperties() {
return properties;
}
}
ConfigSources are POJO’s because if a CDI bean expected config to be injected into it at startup based on a ConfigSource that had dependencies on CDI’s you could get into startup looping issues.
For this reason the example CongigSoruce is constructed twice - once at the beginning from the Config-API and later from the CDI implemenation on #PostConstruct. With the static variable 'properties' we overload the values from the already constructed ConfigSource. Of course you can also separate the code in two classes if you like.

OSGI - two objects of a bundle service

I have a bundle that provides a service.
My bundle implementation looks like this:
class ServiceImpl implements Service
{
Object value;
#Override
public void setValue(Object value)
{
this.value = value;
}
#Override
public Object getValue()
{
return value;
}
}
In my java application, I load this bundle to OSGI framework, and create TWO references to the service, in an attempt to have two objects with different values for "value".
Unfortunately, this does not seem to work. The service always returns the last value set by either objects. How can I overcome this issue?
Here's an example for the problem:
Service object1 = context.getService(reference1);
Service object2 = context.getService(reference2);
Integer one= 1;
Integer two =2;
object1.setValue(1);
object2.setValue(2);
System.out.println(object1.getValue() ); //returns 2 !!!!!!!!!!!!!!!!!!
System.out.println(object2.getValue() ); //returns 2
I used ServiceFactory but it seems not useful for my case. What should I do? Thanks.
Both BJ and Balazs offer valuable information, but no solution that works with current versions of the OSGi specification.
What you can do is register your service with a second "Factory" interface. This factory then allows you to create instances of the service. Because you probably don't want to do that manually, you can hide this logic in a ServiceTracker.
There are a few "downsides" to this approach. First of all, you need to register the service and have the instance implement both Factory and Service. Secondly, you always have to use this custom ServiceTracker to access it. If you use a dependency manager that allows you to extend its dependencies (such as Apache Felix Dependency Manager) you can easily hide all of this in a custom ServiceDependency.
Anyway, to show you that this actually works, here is a simple example:
public class Activator implements BundleActivator {
#Override
public void start(final BundleContext context) throws Exception {
context.registerService(Service.class.getName(), new FactoryImpl(), null);
ServiceTrackerCustomizer customizer = new ServiceTrackerCustomizer() {
#Override
public Object addingService(ServiceReference reference) {
Object service = context.getService(reference);
if (service instanceof Factory) {
return ((Factory) service).createInstance();
}
return service;
}
#Override
public void modifiedService(ServiceReference reference,
Object service) {
// TODO Auto-generated method stub
}
#Override
public void removedService(ServiceReference reference,
Object service) {
// TODO Auto-generated method stub
}
};
ServiceTracker st1 = new ServiceTracker(context, Service.class.getName(), customizer);
ServiceTracker st2 = new ServiceTracker(context, Service.class.getName(), customizer);
st1.open();
st2.open();
Service s1 = (Service) st1.getService();
Service s2 = (Service) st2.getService();
s1.setValue("test1");
s2.setValue("test2");
System.out.println(s1.getValue());
System.out.println(s2.getValue());
}
#Override
public void stop(BundleContext context) throws Exception {
}
static interface Factory {
public Object createInstance();
}
static class FactoryImpl extends ServiceImpl implements Factory, Service {
#Override
public Object createInstance() {
return new ServiceImpl();
}
}
static interface Service {
public void setValue(Object value);
public Object getValue();
}
static class ServiceImpl implements Service {
private Object m_value;
#Override
public void setValue(Object value) {
m_value = value;
}
#Override
public Object getValue() {
return m_value;
}
}
}
You need to wait for R6. Pre-R6, each bundle can be exposed to at most one instance of a service. Even registering a ServiceFactory will not change that since the framework will cache the service object from the ServiceFactory to return to the bundle on subsequent calls to getService.
In R6, we introduce service scopes which allows a service implementation to return multiple service objects to a bundle. Using this requires both the service provider and the service consumer to use new API added in R6.
You can play with this now as it is implemented in Eclipse Equinox Luna.
Even if you use ServiceFactory, for the same bundle the same service object will be returned.
There might be a PrototypeServiceFactory in the future as there is an RFP about it: https://github.com/osgi/design/tree/master/rfcs/rfc0195
That would fit to your needs.
Although there might be a PrototypeServiceFactory in the future, I think it is better to solve this use-case programmatically by yourself. E.g.:
Instead of creating a mutuable OSGi service (I do not think creating mutuable services is a good idea) create a factory.
On the client side you would use:
BusinessLogicFactory factory = context.getService(reference);
BusinessLogic object1 = factory.createInstance();
BusinessLogic object2 = factory.createInstance();
...