jsr375-experts@javaee-security-spec.java.net

[jsr375-experts] Re: Multi IdentityStore proposal

From: arjan tijms <arjan.tijms_at_gmail.com>
Date: Sat, 27 Aug 2016 21:09:15 +0200

Hi,

Thanks for the reply. Hope we can have the proposal ready before JavaOn
(but if not would't be the end of the world of course)

On Sat, Aug 27, 2016 at 8:45 PM, Rudy De Busscher <rdebusscher_at_gmail.com>
wrote:

> The annotation will have the members like *authenticateOnly="true"* but
> the interface will have a new method which returns an enum
> ValidationType.AUTHENTICATION, ValidationType.*AUTHORIZATION *and
> ValidationType.*BOTH*.
>

Ok, sounds good, and then a default implementation that returns
ValidationType.*BOTH? *The ValidationType method indicates what the store
is capable of, right?


> Due to the nice weather here, the code is not ready yet :)
>

As for the weather, same here (Amsterdam) ;)

Kind regards,
Arjan Tijms




>
>
> regards
> Rudy
>
>
> On 22 August 2016 at 21:07, arjan tijms <arjan.tijms_at_gmail.com> wrote:
>
>> Hi,
>>
>> Does someone else from the EG or user list have any input on this?
>>
>> Rudy, correct me if I'm wrong, but it think it boils down to
>>
>> 1. More explicit (but optional) configuration of which store does
>> authentication (validate credentials) and which store does authorization
>> (provide groups), but with identity stores that remain as simple as can be
>> and the multi-algorithm fully pluggable.
>>
>> vs
>>
>> 2. No configuration other than ordering, with the implicit semantics that
>> the first stores in the order authenticate (validate credentials), and once
>> authentication has happened all stores coming after that authorize (provide
>> groups), but with identity stores being required to check a passed-in
>> status and act accordingly and the multi-algorithm somewhat hardcoded in
>> the stores.
>>
>>
>> For 2)
>>
>> The checks a store has to do are:
>>
>> if (partialValidationResult.getStatus() != CredentialValidationResult.Status.AUTHENTICATED
>> &&
>> partialValidationResult.getStatus() !=
>> CredentialValidationResult.Status.VALID) {
>>
>> // validate credentials (only?)
>>
>> and
>>
>> if (partialValidationResult.getStatus() == CredentialValidationResult.Sta
>> tus.VALID
>> || partialValidationResult.getStatus() ==
>> CredentialValidationResult.Status.AUTHENTICATED) {
>>
>> // provide groups only
>>
>>
>> Ad 2)
>>
>> One approach to relief implementations of the identity store interface
>> somewhat may be to provide default methods in the IdentityStore interface:
>>
>> public interface IdentityStore {
>>
>> default CredentialValidationResult validate(CredentialValidationResult
>> partialValidationResult, Credential credential) {
>> if (partialValidationResult.getStatus() !=
>> CredentialValidationResult.Status.AUTHENTICATED &&
>> partialValidationResult.getStatus() !=
>> CredentialValidationResult.Status.VALID) {
>> return validate(Credential credential);
>> }
>>
>> if (partialValidationResult.getStatus() ==
>> CredentialValidationResult.Status.VALID
>> || partialValidationResult.getStatus() ==
>> CredentialValidationResult.Status.AUTHENTICATED) {
>> List<String> groups = getGroupsByPrincipal(partialVa
>> lidationResult.getCallerPrincipal().getName());
>> if (groups != null) {
>>
>> return new CredentialValidationResult(partialValidationResult,
>> groups);
>> }
>> }
>>
>> return partialValidationResult;
>>
>> }
>>
>> default int priority() {
>> return 0;
>> }
>>
>> default List<String> getGroupsByPrincipal(Principal principal) {
>> return null; // or Optional, or return validate(new
>> CallerOnlyPrincipal(principal)), etc...
>> }
>>
>> CredentialValidationResult validate(Credential credential);
>>
>> }
>>
>> Thoughts?
>>
>> Kind regards,
>> Arjan Tijms
>>
>>
>>
>>
>>
>>
>>
>>
>>
>>
>>
>>
>>
>>
>>
>>
>>
>>
>> On Thu, Aug 18, 2016 at 11:08 PM, arjan tijms <arjan.tijms_at_gmail.com>
>> wrote:
>>
>>> Hi,
>>>
>>> On Thu, Aug 18, 2016 at 9:42 PM, Rudy De Busscher <rdebusscher_at_gmail.com
>>> > wrote:
>>>>
>>>> The app-multiple-store in the pull request is already an example.
>>>>
>>>
>>> Ah, sorry I somehow missed that. Thanks
>>>
>>>
>>>>
>>>> don't see how the handler knows what the IdentityStore does (unless we
>>>> expose that information)
>>>> The Handler can just call the individual IdentityStores (and can abort
>>>> the consulting of the different stores based on some condition) but has no
>>>> idea
>>>> - if it does Authentication, Authorization or both
>>>>
>>>
>>> I think I see the issue here, particularly based on the
>>> AuthorizationIdentityStore and the AuthenticationIdentityStore in the test.
>>>
>>> There the identity stores themselves do either authentication,
>>> authorization purely based on what happened in the past (which is steered
>>> by their priority).
>>>
>>> What I was referring to was normal identity stores that can always be
>>> asked or used to do authentication, authorization or both; they themselves
>>> don't have to be restricted internally to be doing either of those based on
>>> the order in which they are called.
>>>
>>> For example, let's say we an LDAPIdentityStore and a DataBaseIdentity
>>> store. They are both "normal" stores. Now consider the following use cases:
>>>
>>> * User 1 wants to configure the application such that authentication
>>> happens against the LDAPIdentityStore, and the groups are retrieved from
>>> the DataBaseIdentityStore.
>>>
>>> * User 2 wants to configure the application such that authentication
>>> happens against the DataBaseIdentityStore and the groups are retrieved
>>> from the LDAPIdentityStore.
>>>
>>> * User 3 wants to configure the application such that authentication
>>> happens against the DataBaseIdentityStore and the groups are retrieved
>>> from both the LDAPIdentityStore and DataBaseIdentityStore.
>>>
>>> Initially the handler wouldn't necessarily know if the configured
>>> identity store is capable of any of those things, but that's not really
>>> different from the current situation where the authentication mechanism
>>> doesn't know if the available store is capable of handling say the
>>> UsernamePasswordCredential.
>>>
>>> If the store isn't capable, it handles this currently by returning
>>> NOT_VALIDATED_RESULT.
>>>
>>> So for user 1, the handler would invoke the LDAPIdentityStore with the
>>> normal credential from the authentication mechanism, then it would invoke
>>> the DataBaseIdentityStore with the CallerOnlyCredential. If the
>>> DataBaseIdentityStore doesn't support the CallerOnlyCredential the handler
>>> would learn about this via its NOT_VALIDATED_RESULT return value. If it
>>> does, its groups are taken, and the return from the handler is the
>>> CallerPrincipal from the LDAPIdentityStore with the groups from the
>>> DataBaseIdentityStore.
>>>
>>> Note that in this case the priority is not used, which I think is the
>>> main difference with your proposal. I forgot to mention this earlier, sorry
>>> for that.
>>>
>>> The priority would only be used for the default algorithm when multiple
>>> stores happen to be present without any further configuration: loop over
>>> them in the right order and first to authenticate wins.
>>>
>>> That does beg the question, how does the user configure any of the above
>>> use cases for user 1, 2 and 3?
>>>
>>> Currently servers mostly use some .xml file for this to do this in a
>>> proprietary way, which may be a good idea to study. For JSR 375 we could do
>>> perhaps something like the following for user 1:
>>>
>>> @DataBaseIdentityStoreDefinition(
>>> ...
>>> authenticateOnly="true"
>>> )
>>> @LdapIdentityStoreDefinition(
>>> ...
>>> authorizeOnly="true"
>>> )
>>>
>>> For user 2:
>>>
>>> @ LdapIdentityStoreDefinition(
>>> ...
>>> authenticateOnly="true"
>>> )
>>> @ DataBaseIdentityStoreDefinition(
>>> ...
>>> authorizeOnly="true"
>>> )
>>>
>>> For user 3:
>>>
>>> @ LdapIdentityStoreDefinition(
>>> ...
>>> ) // default is authenticate + authorize
>>> @ DataBaseIdentityStoreDefinition(
>>> ...
>>> authorizeOnly="true"
>>> )
>>>
>>>
>>>
>>>> - That it can handle an already authenticated user (for the algorithm
>>>> that mandates all stores to succeed)
>>>>
>>>
>>> I don't think the handler has to know this. It would just keep feeding
>>> the credentials into each store (order is not important here). In theory,
>>> this could even be done in parallel then. Each store either authenticates
>>> or it doesn't.
>>>
>>>
>>>
>>>> - That it should veto another store (like One store handles the OAuth2
>>>> authentication, but another allows only authenticated users fro a certain
>>>> domain as we have an ap now which uses Google OAuth2 but only from certain
>>>> google work domains)
>>>>
>>>
>>> This would need some further explanation, but I'd say if the caller
>>> principal name is say john_at_example.com then the handler can just put
>>> that caller principal into this other store. If it excepts it fine,
>>> otherwise it rejects it and the handler then learns about this,
>>>
>>>
>>>
>>>> There are also issues with this solution.
>>>> For example an LDAP based store used for Authorization purposes. But
>>>> that LDAP stores should not care where the authentication took place, by
>>>> username - password or by JWT for example. But these Authentication
>>>> IdentityStores will use different types of Credentials. The LDAP identity
>>>> store however, needs only to concentrate on the Status and the
>>>> CallerPrincipal.
>>>>
>>>
>>> Indeed, but that doesn't seem to clash or contradict anything I think.
>>> In the above example for user 1, the handler would call the
>>> DataBaseIdentityStore with the UsernamePasswordCredential, and then it
>>> would call the LDAPIdentityStore with the CallerOnlyCredential, if and only
>>> if the DataBaseIdentityStore authenticated the user.
>>>
>>> The LDAPIdentityStore would simply fetch the groups belonging to the
>>> caller principal in that credential. The previous status would not matter
>>> to it, as the handler already made sure to only call it when necessary.
>>>
>>> Hope the above makes it a bit more clear and hope it's no too length ;)
>>>
>>>
>>>
>>>> I'll try to produce some more code during my holiday break ;)
>>>>
>>>
>>> Okay, thanks ;)
>>>
>>> Kind regards,
>>> Arjan Tijms
>>>
>>>
>>>
>>>
>>>
>>>>
>>>> Best regards
>>>> Rudy
>>>>
>>>>
>>>>> Kind regards,
>>>>> Arjan Tijms
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>> - When the store has another order in the chain, it can receive
>>>>>> another type for the Credential parameter (AuthorizeOnlyCredential instead
>>>>>> of UsernamePasswordCredential)
>>>>>>
>>>>>> But I agree, the partialValidationresult is not the best solution.
>>>>>> But didn't found anything better (yet) which fulfills all the requirements
>>>>>> (of passing Status and CallerCredentials) to the IdentityStore (except
>>>>>> adding those 2 parameters).
>>>>>>
>>>>>>
>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>> *Regarding Annotated / All discovery*
>>>>>>>>
>>>>>>>> When in annotated mode, only beans with a scope are considered. So
>>>>>>>> when we introduce @IdentityStore, it needs to be a scope or it needs to be
>>>>>>>> recognized by the CDI extension and registered 'manually'
>>>>>>>> And we need to veto the normal procedure because otherwise the bean
>>>>>>>> gets defined as CDI (because fulfills all requirements) and will be added
>>>>>>>> by CDI extension.
>>>>>>>>
>>>>>>>
>>>>>>> This is already the case now. The CDI extension doesn't so much add
>>>>>>> user provided beans itself, but just checks if there's a normally activated
>>>>>>> bean available that implements the IdentityStore interface.
>>>>>>>
>>>>>>> The confusion may be with the build-in beans, which are only
>>>>>>> activated using the *IdentityStoreDefinition annotations. These beans
>>>>>>> reside within the Soteria jar and are already excluded from being scanned.
>>>>>>>
>>>>>>> With or without our own annotation, user provided IdentityStore
>>>>>>> implementations should always normally be activated by CDI.
>>>>>>>
>>>>>>>
>>>>>>>> When we don't define @IdentityStore, the developer can always
>>>>>>>> annotate his own implementations of the IdentityStore with @RequestScoped
>>>>>>>> or @ApplicationScoped when he is using the annotated mode.
>>>>>>>>
>>>>>>>
>>>>>>> That's true, so just asking the developer to put an existing scope
>>>>>>> on the IdentityStore would solve the issue as well ;)
>>>>>>>
>>>>>>> In fact, at the moment such scope is actually required as I recently
>>>>>>> found out. Soteria doesn't yet check if the identity store it obtains is
>>>>>>> (implicitly) @Dependent. If it is, it has to release the reference,
>>>>>>> otherwise proxy/reference leaks will occur :O
>>>>>>>
>>>>>>> Kind regards,
>>>>>>> Arjan Tijms
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> Any other thoughts about this?
>>>>>>>>
>>>>>>>> Best regards
>>>>>>>> Rudy
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> On 17 August 2016 at 17:07, Werner Keil <werner.keil_at_gmail.com>
>>>>>>>> wrote:
>>>>>>>>
>>>>>>>>> Arjan/Rudy,
>>>>>>>>>
>>>>>>>>> Thanks a lot for the suggestion. Arjan's reply for some reason was
>>>>>>>>> considered "suspicious" and possible fishing or malware by Gmail. Not sure,
>>>>>>>>> why?
>>>>>>>>>
>>>>>>>>> I CC my reply to the new Google Group, because whatever Oracle may
>>>>>>>>> do about JSR 375 after JavaOne, java.net is sure to go away and
>>>>>>>>> as of now there's no sign JSRs, Glassfish or other projects (except OpenJDK
>>>>>>>>> which only uses the mere URL but otherwise was said to have its own cluster
>>>>>>>>> for a long time now) get seamless migration support of e.g. this mailing
>>>>>>>>> list or JIRA.
>>>>>>>>>
>>>>>>>>> There is no harm sending future discussions to both lists. In the
>>>>>>>>> unlikely event there's going to be some "java.oracle.com" or so
>>>>>>>>> hosting a new mailing list and importing everything, we have a backup-copy.
>>>>>>>>> Otherwise all that's left of the java.net list will be a huge
>>>>>>>>> tarball (if Alex has the time to request it soon enouth, AFAIK only Spec
>>>>>>>>> Leads and project owners on java.net are eligable to request that)
>>>>>>>>>
>>>>>>>>> Kind Regards,
>>>>>>>>>
>>>>>>>>> Werner Keil | JCP Executive Committee Member, JSR 363 Co Spec
>>>>>>>>> Lead | Eclipse UOMo Lead, Babel Language Champion | Apache
>>>>>>>>> Committer
>>>>>>>>>
>>>>>>>>> Twitter @wernerkeil | @UnitAPI | @JSR354 | @AgoravaProj | @DeviceMap
>>>>>>>>> | #DevOps | #EclipseUOMo
>>>>>>>>> Skype werner.keil | Google+ gplus.to/wernerkeil
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On Wed, Aug 17, 2016 at 4:52 PM, arjan tijms <
>>>>>>>>> arjan.tijms_at_gmail.com> wrote:
>>>>>>>>>
>>>>>>>>>> Hi Rudy,
>>>>>>>>>>
>>>>>>>>>> Looks really cool and thanks for putting the effort in!
>>>>>>>>>>
>>>>>>>>>> Big +1 from me on the very idea of having multiple stores. This
>>>>>>>>>> is essential for a large array of use cases, specifically the one you
>>>>>>>>>> mention in your example.
>>>>>>>>>>
>>>>>>>>>> >Proposal : *IdentityStoreHandler* which has the same signature
>>>>>>>>>> as the IdentityStore.
>>>>>>>>>>
>>>>>>>>>> +1
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> > IdentityStore interface receives a new method which determines
>>>>>>>>>> the order of the IdentityStores when there are multiple ones.
>>>>>>>>>> *int priority();*
>>>>>>>>>>
>>>>>>>>>> I've been thinking about this for some time, contemplating
>>>>>>>>>> whether @Priority would be an easier option, but Guillermo
>>>>>>>>>> mentioned correctly that this would clash with @Alternative on an identity
>>>>>>>>>> store. So with int priority() being available as a default method in the
>>>>>>>>>> interface, users who don't care about priority don't really have to see it
>>>>>>>>>> in their store implementations (which is always a big benefit of
>>>>>>>>>> annotations, that you can use them optionally).
>>>>>>>>>>
>>>>>>>>>> One alternative, again hinted at by Guillermo, is to have an
>>>>>>>>>> annotation for the identity store. We now kinda assume CDI scanning mode
>>>>>>>>>> *all*, but users may prefer scanning mode *annotated*. If we were to
>>>>>>>>>> introduce an annotation for the IdentityStore, we could also put the
>>>>>>>>>> priority there. Then either the handler could read it from the annotation,
>>>>>>>>>> or the default method could. The annotation would only be a convenience for
>>>>>>>>>> custom stores not for the build-in ones.
>>>>>>>>>>
>>>>>>>>>> E.g.
>>>>>>>>>>
>>>>>>>>>> @CustomIdentityStore(priority=100)
>>>>>>>>>> public class MyStore implement IdentityStore {
>>>>>>>>>> ...
>>>>>>>>>> }
>>>>>>>>>>
>>>>>>>>>> So +1 for me on the int priority() method, but worthwhile to
>>>>>>>>>> discuss if "some annotation" that we may need anyway could also be used.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> >* IdentityStores are *ordered by ascending values of the
>>>>>>>>>> priority value*. This means that the IdentityStore with the
>>>>>>>>>> lowest value is processed first. see next bullet.
>>>>>>>>>>
>>>>>>>>>> +1
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> >* The default IdentityStoreHandler follows this algorithm when
>>>>>>>>>> the validate() method is called:
>>>>>>>>>> - *Loop over all stores*, in order of the priority value,
>>>>>>>>>> until ValidationResult.Status is INVALID or loop is terminated.
>>>>>>>>>> - At the end of the loop, change the status in following
>>>>>>>>>> cases
>>>>>>>>>> * NONE -> INVALID
>>>>>>>>>> * AUTHENTICATED -> VALID
>>>>>>>>>>
>>>>>>>>>> +1 for that loop.
>>>>>>>>>>
>>>>>>>>>> I do have a concern about the implementation as done by the
>>>>>>>>>> current PR.
>>>>>>>>>>
>>>>>>>>>> The IdentityStore interface has been modified with a
>>>>>>>>>> CredentialValidationResult as input (partialValidationResult).
>>>>>>>>>>
>>>>>>>>>> See https://github.com/rdebusscher/soteria/blob/f93aece6a04e
>>>>>>>>>> 57f073157b30ec5d50248bd50837/api/src/main/java/javax/securit
>>>>>>>>>> y/identitystore/IdentityStore.java
>>>>>>>>>>
>>>>>>>>>> As far as I can see, this is for the implementations (like
>>>>>>>>>> DataBaseIdentityStore) just an opaque object that they need to pass around
>>>>>>>>>> and eventually put into the CredentialValidationResult that they return.
>>>>>>>>>> E,g,
>>>>>>>>>>
>>>>>>>>>> See https://github.com/rdebusscher/soteria/blob/f93aece6a04e
>>>>>>>>>> 57f073157b30ec5d50248bd50837/impl/src/main/java/org/glassfis
>>>>>>>>>> h/soteria/identitystores/DataBaseIdentityStore.java#L81
>>>>>>>>>>
>>>>>>>>>> The CredentialValidationResult constructor then performs some
>>>>>>>>>> logic on the passed in partial result:
>>>>>>>>>>
>>>>>>>>>> See https://github.com/rdebusscher/soteria/blob/f93aece6a04e57f0
>>>>>>>>>> 73157b30ec5d50248bd50837/api/src/main/java/javax/security/id
>>>>>>>>>> entitystore/CredentialValidationResult.java#L166
>>>>>>>>>>
>>>>>>>>>> I wonder if this logic could not be moved to the
>>>>>>>>>> IdentityStoreHandler. This one currently has a loop where it feeds the
>>>>>>>>>> result continuously into the available stores:
>>>>>>>>>>
>>>>>>>>>> CredentialValidationResult result =
>>>>>>>>>> CredentialValidationResult.NONE_RESULT;
>>>>>>>>>> Iterator<IdentityStore> storeIterator =
>>>>>>>>>> identityStores.iterator();
>>>>>>>>>> while (storeIterator.hasNext() && result.getStatus() !=
>>>>>>>>>> CredentialValidationResult.Status.INVALID) {
>>>>>>>>>> result = storeIterator.next().validate(result, credential);
>>>>>>>>>> }
>>>>>>>>>>
>>>>>>>>>> See https://github.com/rdebusscher/soteria/blob/f93aece6a04e
>>>>>>>>>> 57f073157b30ec5d50248bd50837/impl/src/main/java/org/glassfis
>>>>>>>>>> h/soteria/cdi/DefaultIdentityStoreHandler.java#L67
>>>>>>>>>>
>>>>>>>>>> This could become something like:
>>>>>>>>>>
>>>>>>>>>> CredentialValidationResult globalResult = NONE_RESULT;
>>>>>>>>>> Iterator<IdentityStore> storeIterator =
>>>>>>>>>> identityStores.iterator();
>>>>>>>>>>
>>>>>>>>>> while (storeIterator.hasNext() && globalResult.getStatus() !=
>>>>>>>>>> INVALID) {
>>>>>>>>>> CredentialValidationResult latestResult =
>>>>>>>>>> storeIterator.next().validate(credential);
>>>>>>>>>>
>>>>>>>>>> if (!isOneOf(latestResult.getStatus(), AUTHENTICATED, VALID)
>>>>>>>>>> {
>>>>>>>>>> if (result != null) {
>>>>>>>>>> latestResult.setGroups(globalResult.getGroups());
>>>>>>>>>> latestResult.setCallerPrincipa
>>>>>>>>>> l(globalResult.getCallerPrincipal());
>>>>>>>>>> } else {
>>>>>>>>>> if (latestResult.getStatus() != NONE) {
>>>>>>>>>> throw new IllegalArgumentException("non null
>>>>>>>>>> global result required when Status is not NONE");
>>>>>>>>>> } else {
>>>>>>>>>> latestResult.setGroups(null);
>>>>>>>>>> latestResult.setCallerPrincipal(null);
>>>>>>>>>> }
>>>>>>>>>> }
>>>>>>>>>>
>>>>>>>>>> if (globalResult != null && globalResult.getStatus() ==
>>>>>>>>>> VALID && latestResult.getStatus() == AUTHENTICATED) {
>>>>>>>>>> latestResult.setStatus(VALID);
>>>>>>>>>> }
>>>>>>>>>>
>>>>>>>>>> globalResult = latestResult;
>>>>>>>>>> }
>>>>>>>>>>
>>>>>>>>>> We could additionally split out the merging algorithm to a
>>>>>>>>>> reusable method (and this method could be in CredentialValidationResult,
>>>>>>>>>> going full circle ;)), so custom handlers can re-use it if they want. By
>>>>>>>>>> externalizing this logic we not only keep the IdentityStore interface
>>>>>>>>>> simpler and unburden the developer from passing around a partial result
>>>>>>>>>> (which they may not care for), but it also allows custom handlers to
>>>>>>>>>> customize the merge algorithm. You'd then get e.g.
>>>>>>>>>>
>>>>>>>>>> CredentialValidationResult globalResult = NONE_RESULT;
>>>>>>>>>> Iterator<IdentityStore> storeIterator =
>>>>>>>>>> identityStores.iterator();
>>>>>>>>>>
>>>>>>>>>> while (storeIterator.hasNext() && globalResult.getStatus() !=
>>>>>>>>>> INVALID) {
>>>>>>>>>> globalResult = CredentialValidationResult.merge(globalResult,
>>>>>>>>>> storeIterator.next().validate(credential));
>>>>>>>>>> }
>>>>>>>>>>
>>>>>>>>>> Kind regards,
>>>>>>>>>> Arjan Tijms
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> On Sat, Aug 13, 2016 at 2:24 PM, Rudy De Busscher <
>>>>>>>>>> rdebusscher_at_gmail.com> wrote:
>>>>>>>>>>
>>>>>>>>>>> Hi all,
>>>>>>>>>>>
>>>>>>>>>>> I have put together the text around my idea of the multi
>>>>>>>>>>> IdentityStore.
>>>>>>>>>>>
>>>>>>>>>>> The code is not yet completely ready and is missing an example
>>>>>>>>>>> and test. I'll try to create that in the next few days and make a pull
>>>>>>>>>>> request at that time.
>>>>>>>>>>>
>>>>>>>>>>> *Multiple IdentityStore proposal*
>>>>>>>>>>>
>>>>>>>>>>> *Why multiple stores?*
>>>>>>>>>>> There are various use cases where multiple identity stores can
>>>>>>>>>>> be useful.
>>>>>>>>>>> And it allows us to decouple the authentication and
>>>>>>>>>>> authorization parts of the current mechanism. So that groups can be
>>>>>>>>>>> retrieved by another store then the credential checking.
>>>>>>>>>>>
>>>>>>>>>>> *Examples*
>>>>>>>>>>> * When your application should allow users which are defined in
>>>>>>>>>>> multiple stores, like an LDAP for your internal users and a Database store
>>>>>>>>>>> for the external ones.
>>>>>>>>>>> * Users are authenticated by an OAuth2 provider and the groups
>>>>>>>>>>> are retrieved from a database table.
>>>>>>>>>>> * The first IdentityStore based on OAuth2 allows any Google
>>>>>>>>>>> account, but the second store limits the access to a certain google domain.
>>>>>>>>>>>
>>>>>>>>>>> *Technical changes*
>>>>>>>>>>> * When multiple stores are allowed, we need another interface
>>>>>>>>>>> which can be injected into the AuthenticationMechanism.
>>>>>>>>>>> Proposal : *IdentityStoreHandler* which has the same signature
>>>>>>>>>>> as the IdentityStore.
>>>>>>>>>>>
>>>>>>>>>>> * IdentityStore interface receives a new method which determines
>>>>>>>>>>> the order of the IdentityStores when there are multiple ones.
>>>>>>>>>>> *int priority();*
>>>>>>>>>>>
>>>>>>>>>>> * The current **Definition annotations are extended by a member
>>>>>>>>>>> which is transferred to the priority value of the IdentityStore.
>>>>>>>>>>>
>>>>>>>>>>> * The *ValidationResult.Status enum* is extended with some
>>>>>>>>>>> constants. Values and meanings are
>>>>>>>>>>>
>>>>>>>>>>> *NONE* : The default status, indicating that no
>>>>>>>>>>> IdentityStore has been consulted yet.
>>>>>>>>>>>
>>>>>>>>>>> *AUTHENTICATED* : Indicates that the credential is
>>>>>>>>>>> validated but that no Groups are assigned yet.
>>>>>>>>>>>
>>>>>>>>>>> *NOT_VALIDATED* : Indicates that the credential could
>>>>>>>>>>> not be validated
>>>>>>>>>>>
>>>>>>>>>>> *INVALID* : Indicates that the credential is not valid
>>>>>>>>>>> after a validation attempt.
>>>>>>>>>>>
>>>>>>>>>>> *VALID* : Indicates that the credential is valid after
>>>>>>>>>>> a validation attempt and Groups are assigned.
>>>>>>>>>>>
>>>>>>>>>>> * IdentityStores are *ordered by ascending values of the
>>>>>>>>>>> priority value*. This means that the IdentityStore with the
>>>>>>>>>>> lowest value is processed first. see next bullet.
>>>>>>>>>>>
>>>>>>>>>>> * The default IdentityStoreHandler follows this algorithm when
>>>>>>>>>>> the validate() method is called:
>>>>>>>>>>> - *Loop over all stores*, in order of the priority value,
>>>>>>>>>>> until ValidationResult.Status is INVALID or loop is terminated.
>>>>>>>>>>> - At the end of the loop, change the status in following
>>>>>>>>>>> cases
>>>>>>>>>>> * NONE -> INVALID
>>>>>>>>>>> * AUTHENTICATED -> VALID
>>>>>>>>>>>
>>>>>>>>>>> * The IdentityStores logic is
>>>>>>>>>>> - If the store decides to do *authentication logic* (a
>>>>>>>>>>> store can also decide to perform only the logic in the case the Status is
>>>>>>>>>>> NONE or NOT_VALIDATED, meaning the user is not authenticated yet) it should
>>>>>>>>>>> set the status to
>>>>>>>>>>> + *NOT_VALIDATED* : When credentials could not be
>>>>>>>>>>> validated (should not set this Status when the Status was already
>>>>>>>>>>> AUTHENTICATED or VALID)
>>>>>>>>>>> + *AUTHENTICATED* : When credentials are valid but
>>>>>>>>>>> the store didn't assign any groups (Should not set this status when the
>>>>>>>>>>> original Status was VALID)
>>>>>>>>>>> + *INVALID* : Credentials are not valid and the store
>>>>>>>>>>> is allowed to veto another authentication by another IdentityStore.
>>>>>>>>>>> + *VALID* : When credentials are valid and the store
>>>>>>>>>>> did assign groups (which can be the empty set)
>>>>>>>>>>>
>>>>>>>>>>> - The IdentityStore can decide that only *authorization*
>>>>>>>>>>> is performed, and should only do this in the case where Status has the
>>>>>>>>>>> value AUTHENTICATED or VALID.
>>>>>>>>>>> When the store adds Groups, it should change the Status to
>>>>>>>>>>> VALID.
>>>>>>>>>>>
>>>>>>>>>>> Best regards
>>>>>>>>>>> Rudy
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>> --
>>>>>>>>> You received this message because you are subscribed to the Google
>>>>>>>>> Groups "Java EE Security API - JSR 375 - Experts" group.
>>>>>>>>> To unsubscribe from this group and stop receiving emails from it,
>>>>>>>>> send an email to jsr375-experts+unsubscribe_at_googlegroups.com.
>>>>>>>>> To post to this group, send email to
>>>>>>>>> jsr375-experts_at_googlegroups.com.
>>>>>>>>> To view this discussion on the web visit
>>>>>>>>> https://groups.google.com/d/msgid/jsr375-experts/CAAGawe0esY
>>>>>>>>> x-FJiGWCDnyHLYUMi6Aw0AjMJ%3DSyAzN5E17B%2BP9Q%40mail.gmail.com
>>>>>>>>> <https://groups.google.com/d/msgid/jsr375-experts/CAAGawe0esYx-FJiGWCDnyHLYUMi6Aw0AjMJ%3DSyAzN5E17B%2BP9Q%40mail.gmail.com?utm_medium=email&utm_source=footer>
>>>>>>>>> .
>>>>>>>>> For more options, visit https://groups.google.com/d/optout.
>>>>>>>>>
>>>>>>>>
>>>>>>>> --
>>>>>>>> You received this message because you are subscribed to the Google
>>>>>>>> Groups "Java EE Security API - JSR 375 - Experts" group.
>>>>>>>> To unsubscribe from this group and stop receiving emails from it,
>>>>>>>> send an email to jsr375-experts+unsubscribe_at_googlegroups.com.
>>>>>>>> To post to this group, send email to jsr375-experts_at_googlegroups.co
>>>>>>>> m.
>>>>>>>> To view this discussion on the web visit
>>>>>>>> https://groups.google.com/d/msgid/jsr375-experts/CAL%2Bwt-6j
>>>>>>>> iXHCKm1RnUzqB3VNgAQoh5X6XR04oXV2V4SuU9t0Lw%40mail.gmail.com
>>>>>>>> <https://groups.google.com/d/msgid/jsr375-experts/CAL%2Bwt-6jiXHCKm1RnUzqB3VNgAQoh5X6XR04oXV2V4SuU9t0Lw%40mail.gmail.com?utm_medium=email&utm_source=footer>
>>>>>>>> .
>>>>>>>>
>>>>>>>> For more options, visit https://groups.google.com/d/optout.
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>
>