-
1. Re: JPA + LDAP auth
pcraveiro Apr 28, 2014 4:25 PM (in response to sslamm)1 of 1 people found this helpfulHey Valentin,
I would suggest you to take a quick look at this documentation:
Basically, what you need is provide the configuration for both stores, defining which types must be supported by each one:
IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder(); builder .named("multiple.store.config") .stores() .jpa() .supportGlobalRelationship(Relationship.class) // tells that all Relationship types should be managed by the JPA store .supportAttributes(true) // tells that ad-hoc attributes should be supported by the JPA store .ldap() .mapping(User.class) .mapping(Group.class) .mapping(Role.class) .supportCredentials(true) // only the LDAP store should be used to authenticate users
If you want some code, please take a look at the following test case:
The configuration above is using JPA for relationships and attributes only. And LDAP to store users, groups and roles.
Regards.
Pedro Igor
-
2. Re: JPA + LDAP auth
sslamm Apr 29, 2014 2:43 AM (in response to pcraveiro)Thanks Pedro.
But this is not exactly what I am looking for. Since I need to store users in both JPA and LDAP, I suppose I need to set supportCredentials to true for both stores, but it is not possible: IdentityConfiguration validation fails in this case. Also it is not possible to map the same entity, User for example, to both stores. Do you know how this issues can be solved?
-
3. Re: Re: JPA + LDAP auth
pcraveiro Apr 29, 2014 9:52 AM (in response to sslamm)Hi Valentin,
Yes, this is also possible. To understand how, let me give you a brief overview about the PicketLink IDM Configuration.
You can build a PartitionManager using a single or multiple IdentityConfiguration instances. Those instances are usually built using the Configuration API, so if you want to provide a single configuration you just need to:
IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder(); builder .named("my.single.config") .stores() .jpa() // jpa configuration .ldap()// ldap configuration DefaultPartitionManager partitionManager = new DefaultPartitionManager(builder.buildAll());
That was exactly what I told you before. When using a single configuration, a identity type can only be supported by a single identity store. That is why you're getting that error. The same thing with credential or even attribute support, only one store can provide those features.
However, with multiple configurations the story is a bit different and it provides you great flexibility. To provide multiple configurations you just need to:
IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder(); builder .named("my.jpa.config") .stores() .jpa() .supportAllFeatures() .named("my.ldap.config") .stores() .ldap() .supportType(User.class, Agent.class) .supportCredentials(true)
As you can see, we're providing two "named" configurations. One with the JPA and another for the LDAP store. Please note that the configuration above is supporting all features (including all types) in JPA. But restricting which features/types are supported by LDAP. You can take a look at this test case for a working example configuration:
If you're using CDI, you can provide multiple configurations as demonstrated by the test case below. Basically, you just need a producer method that returns a IdentityConfiguration instance. You can provide multiple producer methods.
Another important aspect when using multiple configurations is that you need to properly manage partitions, so you can define whether your application should use the JPA or LDAP config to manage your users, etc.
Fell free to join us on #picketlink/freenode, if you need more guidance.
Regards.
-
4. Re: Re: JPA + LDAP auth
shane.bryzak Apr 29, 2014 6:58 PM (in response to sslamm)Just to add to this advice given by Pedro, another possibility is that you subclass your user class and store one type in LDAP, one in the database. So for example say your User class looks like this:
public class User implements Account { // properties, getters and setters }
Then extend this to create another user class:
public class LDAPUser extends User { }
Once you've done that, configure PicketLink to store User identities in the database, and LDAPUser identities in the LDAP directory. The only additional step should be adjusting your authentication logic so that it will attempt authentication using both identity types (i.e. try the first one, if it fails then try the second).
-
5. Re: JPA + LDAP auth
sslamm Apr 30, 2014 9:26 AM (in response to pcraveiro)Thanks, Pedro. If I configure jpa and ldap for separate partitions, will they be able to use the same groups and roles that are store in jpa?
Thanks, Shane. Is it possible to inject\acquire StoreSelector instance in the custom CredentialHandler? Seems that I need to switch between JPAIdentityStore and LDAPIdentityStore in my CredentialHandler realization and as far as I can see, they can be acquired via StoreSelector.getStoreForIdentityOperation(...)
-
6. Re: JPA + LDAP auth
shane.bryzak Apr 30, 2014 7:05 PM (in response to sslamm)1 of 1 people found this helpfulI think that you would need to implement the switching logic at a higher level than the CredentialHandler. Perhaps it might be necessary to also have two Credentials implementations to support both types of authentication.
-
7. Re: JPA + LDAP auth
pcraveiro Apr 30, 2014 7:09 PM (in response to sslamm)Yes, you are still able to use the same groups and roles for users stored in ldap and jpa. The test case below is demonstrating how you can grant a role for different users stored in different partitions/stores.
Regards.
-
8. Re: JPA + LDAP auth
sslamm May 5, 2014 4:32 AM (in response to pcraveiro)When I use separate partitions I noticed one strange thing:
* when acquiring JPA user via BasicModel.getUser(), users partition property (AbstractIdentityType.partiton) is set to 'default'
* when acquiring LDAP user via BasicModel.getUser(), users partition property is also 'default'
I have found that is property is always set to 'default' in the DefaultIdentityQuery.getResultList() method.
I suppose that LDAP and JPA users should have different 'partition' property...
Could you possibly clarify, it is normal or I have some configuration issues?
-
9. Re: JPA + LDAP auth
alex1985 Sep 1, 2014 3:31 AM (in response to sslamm)Hi,
anything new here? I have the same problem. I have an ldap-directory which conatains user, These users should get an role within my application. The roles and the releationships should be stored in an file base store. Therefore i have two configs (see below). ldap should only be in read-mode and be used to authenticate users which works fine for me.
The problem is that i want to add all users of ldap-store the role for e.g. 'member'. if i call BasicModel.grantRole() and after that BasicModel.hasRole() i got an error at DefaultRelationshipQuery.resolveIdentityTypes(). -->
- org.picketlink.idm.IdentityManagementException: PLIDM000500: Could not query Relationship using query [org.picketlink.idm.query.internal.DefaultRelationshipQuery@5189112b].
- at org.picketlink.idm.query.internal.DefaultRelationshipQuery.getResultList(DefaultRelationshipQuery.java:142)
- at org.picketlink.idm.model.basic.BasicModel.hasRole(BasicModel.java:447)
- at de.hr.filesearch.controller.security.LDAPIdentityManagmentConfigurationTest.testRoleManagement(LDAPIdentityManagmentConfigurationTest.java:150)
- at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
- at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
- at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
- at java.lang.reflect.Method.invoke(Method.java:606)
- at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
- at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
- at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
- at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
- at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
- at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
- at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
- at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
- at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
- at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
- at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
- at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
- at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
- at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
- at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
- at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
- at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
- at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
- at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
- at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
- at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
- Caused by: org.picketlink.idm.IdentityManagementException: Referenced IdentityType [4f1e99a2-6ef9-4d90-bd0c-d3e5505f8726] from relationship [class org.picketlink.idm.model.basic.Grant] does not exists in any store.
- at org.picketlink.idm.query.internal.DefaultRelationshipQuery.resolveIdentityTypes(DefaultRelationshipQuery.java:176)
- at org.picketlink.idm.query.internal.DefaultRelationshipQuery.getResultList(DefaultRelationshipQuery.java:130)
- ... 27 more
Thanks for help
- @PicketLink
- @Produces
- public PartitionManager producePartitionManager()
- {
- if ( this.partitionManager == null )
- {
- IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();
- builder
- .named( FILE_CONFIGURATION_NAME )
- .stores()
- .file()
- .supportAllFeatures()
- .named( LDAP_CONFIGURATION_NAME )
- .stores()
- .ldap()
- .baseDN( BASE_DN )
- .bindDN( bindDn )
- .bindCredential( bindCredential )
- .url( LDAP_URL )
- .supportType( Agent.class, User.class )
- .supportCredentials( true )
- .mapping( Agent.class )
- .baseDN( AGENT_DN_SUFFIX )
- .objectClasses( "inetOrgPerson", "organizationalPerson" )
- .attribute( "loginName", UID, true )
- .readOnlyAttribute( "createdDate", CREATE_TIMESTAMP )
- .mapping( User.class )
- .baseDN( USER_DN_SUFFIX )
- .objectClasses( "inetOrgPerson", "organizationalPerson" )
- .attribute( "loginName", UID, true )
- .attribute( "firstName", CN ).attribute( "lastName", SN )
- .attribute( "email", EMAIL )
- .readOnlyAttribute( "createdDate", CREATE_TIMESTAMP );
- DefaultPartitionManager defaultPartitionManager = new DefaultPartitionManager( builder.buildAll() );
- if ( defaultPartitionManager.getPartition( Realm.class, Realm.DEFAULT_REALM ) == null )
- {
- defaultPartitionManager.add( new Realm( Realm.DEFAULT_REALM ), FILE_CONFIGURATION_NAME );
- }
- if ( defaultPartitionManager.getPartition( Realm.class, LDAP_PARTITION ) == null )
- {
- defaultPartitionManager.add( new Realm( LDAP_PARTITION ), LDAP_CONFIGURATION_NAME );
- }
- this.partitionManager = defaultPartitionManager;
- }
- return this.partitionManager;
- }
-
10. Re: JPA + LDAP auth
pcraveiro Sep 1, 2014 10:09 AM (in response to alex1985)Hey Alexander,
Can you try using the JPA store instead of the File store ? I'm not 100% sure if the file store is able to resolve types stored in a different identity store.
-
11. Re: JPA + LDAP auth
alex1985 Sep 2, 2014 3:57 AM (in response to pcraveiro)Hi,
i spent some time in debugging and it seems that the problem for the users which already exists in ldap-store is inside org.picketlink.idm.query.internal.DefaultIdentityQuery.getResultList(). The partition of the user which was found inside this methode is null and that is why org.picketlink.idm.util.IDMUtil.configureDefaultPartition(IdentityType, IdentityStore, PartitionManager) will set it to the default partition.
Is there anything which i can change? It was found via a query of an identityManager which uses my ldap realm/partition so why this partition is not set as partition of the user which was found inside org.picketlink.idm.query.internal.DefaultIdentityQuery.getResultList()?
A workaournd for me is now to manualy set the partion to the ldap-partition if i found the user, but i don't think that corresponds to the expected behaviour.
-
12. Re: JPA + LDAP auth
pcraveiro Sep 2, 2014 10:05 AM (in response to alex1985)Hi,
The LDAP identity store does not support partition management. That is why it is always set to the Realm.DEFAULT_REALM.
I was reviewing your configuration and I think you should specify which types are supported from your file/jpa config instead of using supportAllFeatures(). This can cause some confusion to PicketLink, because you are telling that both file/jpa and ldap config are supporting User, what I think is not your objective.
I would define the config like this:
IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder(); builder .named("default.config") .stores() .file() .supportType(Role.class, Group.class, Relationship.class) // we explicity define the types here .supportAttributes(true) .supportCredentials(false) // credentials are only support by the ldap store config .ldap() .baseDN(BASE_DN) .bindDN(bindDn) .bindCredential(bindCredential) .url(LDAP_URL) .supportCredentials(true) .mapping(Agent.class) .baseDN(AGENT_DN_SUFFIX) .objectClasses("inetOrgPerson", "organizationalPerson") .attribute("loginName", UID, true) .readOnlyAttribute("createdDate", LDAPConstants.CREATE_TIMESTAMP) .mapping(User.class) .baseDN(USER_DN_SUFFIX) .objectClasses("inetOrgPerson", "organizationalPerson") .attribute("loginName", UID, true) .attribute("firstName", LDAPConstants.CN).attribute( "lastName", SN ) .attribute("email", LDAPConstants.EMAIL) .readOnlyAttribute("createdDate", LDAPConstants.CREATE_TIMESTAMP);
I think you can also use a single config with both stores in it.
Also, please notice that you don't need to call supportType in LDAP config. You just need to use mapping.