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

[jsr375-experts] Re: Updated Multi IdentityStore Proposal

From: Rudy De Busscher <rdebusscher_at_gmail.com>
Date: Fri, 9 Sep 2016 09:10:04 +0200

Hi Arjan,

Thanks for the clarifications. It is clear for me now.

Regards
Rudy


On 8 September 2016 at 23:29, arjan tijms <arjan.tijms_at_gmail.com> wrote:

> Hi,
>
> On Thu, Sep 8, 2016 at 8:00 PM, Rudy De Busscher <rdebusscher_at_gmail.com>
> wrote:
>
>> Will this method be called (when for example no suitable overloaded
>> method is found).
>>
>
> The default method (the validate(Credential credential) one) is always
> called first, because the polymorphic binding happens compile time here,
> plus the CDI proxy would not have the overloaded methods on it (since the
> IdentityStore interface declared for the injection point doesn't have the
> overloads).
>
> This default method then uses the MethodHandle to look for the correct
> overloaded method that best matches the actual runtime type of the
> Credential sub-type being passed in, and if it finds that invokes it. If it
> doesn't find one it should return NOT_VALIDATED. If I remember correctly
> it was Alex who came up with that return value initially (although then
> after the manual instanceof checks).
>
> Arguably a better name for NOT_VALIDATED might be
> DOES_NOT_SUPPORT_CREDENTIAL.
>
>
>
>> What should we advise the implementers to put in there? Always a return
>> NOT_VALIDATED status?
>>
>
> You mean other implementors of the JSR 375 spec, or implementors of an
> IdentityStore?
>
> Spec implementors should do as described above. Implementors of an
> IdentityStore would normally not have to touch the default
> validate(Credential credential) method.
>
> E.g. for them it would be:
>
> public class MyStore implements IdentityStore {
>
> public CredentialValidationResult validate(UsernamePasswordCredential
> credential) {
> ...
> }
>
> public CredentialValidationResult validate(TokenCredential credential) {
> ...
> }
>
> }
>
>
> In the current version of Soteria, IdentityStore implementations have to
> do the instaneof checks and casts in the validate(Credential credential)
> method, as the example applications in Soteria are doing. But as you
> correctly pointed out, these checks are indeed not so nice, so that's why I
> started looking how to remove the need for that. So thanks for pointing
> that out ;)
>
> Kind regards,
> Arjan Tijms
>
>
>
>
>
>
>
>
>
>
>
>
>
>>
>> I'm not familiar with the technique presented to select a certain
>> overloaded method.
>>
>> best regards
>> Rudy
>>
>>
>> On 8 September 2016 at 09:29, arjan tijms <arjan.tijms_at_gmail.com> wrote:
>>
>>> Hi,
>>>
>>> The interface has the general "default CredentialValidationResult
>>> validate(Credential credential) { ... }" method.
>>>
>>> The store has to implement an overload for each credential type that it
>>> supports. Stepping away from multiple stores for a second, this means that
>>> the store will receive a different credential depending on which
>>> authentication mechanism it's used with. E.g. UsernamePasswordCredential
>>> for FORM, TokenCredential for StatelessJAXRS, X509Credential for DIGEST,
>>> etc.
>>>
>>> The implementors initially don't know about any of those parameters, but
>>> have to look up which actual credential types there are for any of those
>>> situations.
>>>
>>> Kind regards,
>>> Arjan Tijms
>>>
>>>
>>>
>>>
>>>
>>> On Thu, Sep 8, 2016 at 7:37 AM, Rudy De Busscher <rdebusscher_at_gmail.com>
>>> wrote:
>>>
>>>> Hi Arjan,
>>>>
>>>> It doesn't change the fact that an IdentityStore can receive a
>>>> UserNamePasswordCredential or AuthorizeOnlyCredential depending it is
>>>> used alone or in combination with another IdentityStore.
>>>>
>>>> And what validate() method will be defined in the interface (if any)?
>>>> Will it be clear for the implementers which parameter they should use (if
>>>> they don't read the spec)?
>>>>
>>>> Best regards
>>>> Rudy
>>>>
>>>>
>>>> On 7 September 2016 at 17:52, arjan tijms <arjan.tijms_at_gmail.com>
>>>> wrote:
>>>>
>>>>> Hi everyone,
>>>>>
>>>>> I took another look at a way to remove the series of instance of
>>>>> casts, e.g.
>>>>>
>>>>> @Override
>>>>> public CredentialValidationResult validate(Credential credential) {
>>>>> if (credential instanceof UsernamePasswordCredential) {
>>>>> return validate((UsernamePasswordCredential) credential);
>>>>> }
>>>>>
>>>>> if (credential instanceof TokenCredential) {
>>>>> return validate((TokenCredential) credential);
>>>>> }
>>>>>
>>>>> if (credential instanceof CallerOnlyCredential) {
>>>>> return validate((CallerOnlyCredential) credential);
>>>>> }
>>>>>
>>>>> // etc for JWTCredential, StrongCryptCredential, ...
>>>>>
>>>>> return NOT_VALIDATED_RESULT;
>>>>> }
>>>>>
>>>>>
>>>>> With the help of my friend Jan Beernink we came up with the following
>>>>> initial solution for a default method in the IdentityStore interface:
>>>>>
>>>>>
>>>>> default CredentialValidationResult validate(Credential credential)
>>>>> {
>>>>> try {
>>>>> return CredentialValidationResult.class.cast(
>>>>> MethodHandles.lookup()
>>>>> .bind(this, "validate",
>>>>> methodType(CredentialValidationResult.class, credential.getClass()))
>>>>> .invoke(credential));
>>>>> } catch (Throwable e) {
>>>>> e.printStackTrace(); // tmp
>>>>> }
>>>>>
>>>>> return NOT_VALIDATED_RESULT;
>>>>> }
>>>>>
>>>>>
>>>>> This will find the right overloaded method automatically, so the
>>>>> checks and casts can be omitted and the store only has to implement the
>>>>> overloads. Some work needs to go into it still to prevent a recursive loop
>>>>> if no methods at all are defined in the class that implements
>>>>> Identitystore, but I think this is a good start.
>>>>>
>>>>> Rudy, does this take away some of your concerns?
>>>>>
>>>>> Kind regards,
>>>>> Arjan Tijms
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> On Tue, Sep 6, 2016 at 10:49 PM, arjan tijms <arjan.tijms_at_gmail.com>
>>>>> wrote:
>>>>>
>>>>>> Hi,
>>>>>>
>>>>>> It's indeed my suggestion to create another type of Credential.
>>>>>>
>>>>>> The reasoning is that authorize only is really a special case of any
>>>>>> other authenticate + authorize, where the credential secret to check is
>>>>>> trivial (namely, the empty check).
>>>>>>
>>>>>>
>>>>>> >Because you can end up that a certain IdentityStore receives
>>>>>> another type of Credentials in the case it is used alone or in combination
>>>>>> with other stores.
>>>>>>
>>>>>> I know, and I can see how this may introduce some confusion. In fact,
>>>>>> we started the core IdentityStore discussion with exactly this point. This
>>>>>> is why the current IdentityStore has the super type Credential as input.
>>>>>> It's already required to handle multiple concrete Credential types. Back
>>>>>> then no consensus was reached on how to simplify this aspect.
>>>>>>
>>>>>> If the special system Credential I proposed was the first additional
>>>>>> credential a store had to handle, then indeed, it's an extra complexity
>>>>>> that wasn't there before. But in this case, that complexity, for better or
>>>>>> worse, is already there.
>>>>>>
>>>>>> E.g. suppose IdentityStoreX is able to handle
>>>>>> UsernamePasswordCredential and TokenCredential today. In the current
>>>>>> situation, what happens is:
>>>>>>
>>>>>> 1. UsernamePasswordCredential is passed in. Store validates password
>>>>>> matches username and/or uses username and (hashed) password to lookup and
>>>>>> return principal + groups
>>>>>> 2. TokenCredential is passed in. Store validates token and uses it to
>>>>>> lookup and return principal + groups
>>>>>>
>>>>>> Now add the CallerPrincipalCredential:
>>>>>>
>>>>>> 1. UsernamePasswordCredential is passed in. Store validates password
>>>>>> matches username and uses username or both to lookup and return principal +
>>>>>> groups
>>>>>> 2. TokenCredential is passed in. Store validates token and uses it to
>>>>>> lookup and return principal + groups
>>>>>> 3. CallerPrincipalCredential is passed in. Store validates principal
>>>>>> and uses it to lookup and return principal + groups
>>>>>>
>>>>>> So there's no difference in what the store already does.
>>>>>>
>>>>>> It doesn't really matter so much if the store is used alone or in
>>>>>> combination, but it matters that the store is capable of handling a
>>>>>> credential of type "CallerPrincipal". That credential could be used in case
>>>>>> of multiple stores for authorize only, but it could also be used for a
>>>>>> RunAs feature, it could be used to automatically log-in a user after
>>>>>> registration and to programmatically log-out/log-in the user (e.g. to
>>>>>> refresh the current name/groups).
>>>>>>
>>>>>> So if anything this should only increase reusability, as the
>>>>>> capability to do authorize only, can be re-used for the cases mentioned
>>>>>> above.
>>>>>>
>>>>>> What I could see down the line (and was a previous proposal) is that
>>>>>> we add several Interfaces that a store could optionally implement.
>>>>>> Something like
>>>>>>
>>>>>> public interface GroupStore {
>>>>>> List<String> getGroupsByCallerPrincipal(CallerPrincipal
>>>>>> callerPrincipal);
>>>>>> }
>>>>>>
>>>>>> Or using the not yet introduced type Caller (which holds the
>>>>>> principal + groups):
>>>>>>
>>>>>> public class Caller {
>>>>>> CallerPrincipal callerPrincipal;
>>>>>> List<String> groups;
>>>>>> }
>>>>>>
>>>>>> public interface GroupStore {
>>>>>> Caller getCallerByCallerPrincipal(CallerPrincipal
>>>>>> callerPrincipal);
>>>>>> }
>>>>>>
>>>>>> >Maybe it is better that you create the code and that I verify how I
>>>>>> can use them in those situations.
>>>>>>
>>>>>> Okay sure, let's do that then. Perhaps easiest is if I first accept
>>>>>> the PR, then make some small changes, declare it a version m2, and then for
>>>>>> m3 see how we go from there. Would that be okay?
>>>>>>
>>>>>> Kind regards,
>>>>>> Arjan Tijms
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>> On Tue, Sep 6, 2016 at 9:50 PM, Rudy De Busscher <
>>>>>> rdebusscher_at_gmail.com> wrote:
>>>>>>
>>>>>>> Arjan,
>>>>>>>
>>>>>>> We are again at the point where you suggest that the system should
>>>>>>> create another Credential and pass this to the IdentityStore. I don't like
>>>>>>> that and it is also confusing.
>>>>>>> Because you can end up that a certain IdentityStore receives another
>>>>>>> type of Credentials in the case it is used alone or in combination with
>>>>>>> other stores. Or depending the developer defines it as ValidationType.BOTH
>>>>>>> or ValidationType.AUTHORIZATION.
>>>>>>>
>>>>>>> This limits the reusability of them enormously.
>>>>>>>
>>>>>>> I modeled the system according to the needs I had the last five
>>>>>>> years or so. Maybe it is better that you create the code and that I verify
>>>>>>> how I can use them in those situations.
>>>>>>>
>>>>>>> Best Regards
>>>>>>> Rudy
>>>>>>>
>>>>>>>
>>>>>>> On 6 September 2016 at 17:18, arjan tijms <arjan.tijms_at_gmail.com>
>>>>>>> wrote:
>>>>>>>
>>>>>>>> Hi,
>>>>>>>>
>>>>>>>> On Tue, Sep 6, 2016 at 4:31 PM, Rudy De Busscher <
>>>>>>>> rdebusscher_at_gmail.com> wrote:
>>>>>>>>
>>>>>>>>> Hi Arjan,
>>>>>>>>>
>>>>>>>>> Thanks for your feedback.
>>>>>>>>>
>>>>>>>>
>>>>>>>> Thank you too ;)
>>>>>>>>
>>>>>>>>
>>>>>>>>>
>>>>>>>>> A quick reply to your comments.
>>>>>>>>>
>>>>>>>>> I wonder, would it be possible to omit the CallerPrincipal
>>>>>>>>>> argument here? There's already a caller in the Credential, and I'm afraid
>>>>>>>>>> this would confuse people who have to implement the interface.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> I don't like the caller in the Credential interface
>>>>>>>>> - Credentials are user supplied and should be separated from the
>>>>>>>>> "System determined values". The caller(Principal) is something which is
>>>>>>>>> determined based on the credentials and thus I should not mix these 2
>>>>>>>>> sources.
>>>>>>>>>
>>>>>>>>
>>>>>>>> Perhaps, but if the store is asked for authorization only no actual
>>>>>>>> user supplied credential is needed, just the caller name to get the groups
>>>>>>>> by, which can be put in a dedicated system credential.
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>> - The current implementation of UsernamePasswordCredential uses
>>>>>>>>> the user-supplied username as value.
>>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> If the UsernamePasswordCredential is used for authentication, then
>>>>>>>> this can be passed in as-is. If the caller is authenticated via an earlier
>>>>>>>> call to an earlier identity store, then the handler knows this. If a
>>>>>>>> followup store is configured for authorization only, the handler knows
>>>>>>>> this, and can pass in the right system credential. This system credential
>>>>>>>> can only contain the caller principal.
>>>>>>>>
>>>>>>>> So in these two situations only ever 1 caller name/principal is
>>>>>>>> needed. By passing them in both at the same time, depending on the
>>>>>>>> situation one or the other is used.
>>>>>>>>
>>>>>>>> The problem now is that a store that doesn't want to have anything
>>>>>>>> to do with multi stores, sees two incoming principals (names). The one in
>>>>>>>> the credential, and the one passed in as a separate argument. We have to
>>>>>>>> tell users that unless they want to support authorise only and be multi
>>>>>>>> store capable, they have to ignore the second parameter. IMHO this goes
>>>>>>>> against the simplest possible design.
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>> So there is no way to determine if the 'Caller' is already
>>>>>>>>> identified or not. And this is a key principal when the Authorization and
>>>>>>>>> Authentication is separated in different identityStores.
>>>>>>>>>
>>>>>>>>
>>>>>>>> I think determining if the Caller is identified is a responsibility
>>>>>>>> that should lie solely with the handler. The store just does what it's
>>>>>>>> asked to do based on the incoming data and whether it's configured to do
>>>>>>>> authentication, authorization or both. The handler on its turn knows
>>>>>>>> everything, via the returned status in the CredentialValidationResult and
>>>>>>>> the returned Principal of each store.
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>> - The unique caller identification doesn't need to be the same as
>>>>>>>>> user-supplied username (you can decide to use the internal id/sequence,
>>>>>>>>> email address, LDAP node cn value, ...)
>>>>>>>>>
>>>>>>>>
>>>>>>>> Indeed, and that's a key design element in the current store. The
>>>>>>>> credential may contain a caller name (like e.g. in
>>>>>>>> UsernamePasswordCredential), but the CredentialValidationResult contains
>>>>>>>> both CallerPrincipal and CallerGroups return values. That CallerPrincipal
>>>>>>>> is then taken as the one belonging to the authenticated identity.
>>>>>>>>
>>>>>>>> As an example: suppose we have the LDAPIdentityStore set to
>>>>>>>> authenticate against and the DatabaseIdentityStore set to get the groups
>>>>>>>> from. Now suppose we couple that to a mechanism that uses the
>>>>>>>> UsernamePasswordCredential.
>>>>>>>>
>>>>>>>> Now let the caller authenticate with caller name "pete" and
>>>>>>>> password "secret", then:
>>>>>>>>
>>>>>>>> 1. The handler constructs a UsernamePasswordCredential and passes
>>>>>>>> that into the LDAPIdentityStore.
>>>>>>>>
>>>>>>>> 2. The LDAPIdentityStore validates that and returns a VALID result
>>>>>>>> with CallerPrincipal(name="cn=pete, dn=peter parker") and no groups.
>>>>>>>>
>>>>>>>> 3. The handler then takes CallerPrincipal(name="cn=pete, dn=peter
>>>>>>>> parker"), puts that into a suitable system Credential and passes that into
>>>>>>>> the DatabaseIdentityStore.
>>>>>>>>
>>>>>>>> 4. The DatabaseIdentityStore then returns the groups associated
>>>>>>>> with CallerPrincipal(name="cn=pete, dn=peter parker") say {"foo", "bar",
>>>>>>>> "kaz"}
>>>>>>>>
>>>>>>>> 5. The handler now combines CallerPrincipal(name="cn=pete, dn=peter
>>>>>>>> parker") and {"foo", "bar", "kaz"} puts that into the final
>>>>>>>> CredentialValidationResult and returns it.
>>>>>>>>
>>>>>>>> Hope the above demonstrates that it should not be needed to have 2
>>>>>>>> caller principals passed in, and that the stores can do their work without
>>>>>>>> having knowledge of what happened before.
>>>>>>>>
>>>>>>>>
>>>>>>>>>
>>>>>>>>> callerPrincipal.getName() would be null here
>>>>>>>>>
>>>>>>>>> This should never return null. callerPrincipal being not null
>>>>>>>>> means a successful Authorization is carried out and thus the user should be
>>>>>>>>> identifiable (and null is thus not an option)
>>>>>>>>>
>>>>>>>>
>>>>>>>> Indeed, that's what I thought, so therefor if it can never be null
>>>>>>>> perhaps the store implementations don't have to check against null?
>>>>>>>>
>>>>>>>> Kind regards,
>>>>>>>> Arjan Tijms
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>>
>>>>>>>>> maybe it's better to have ValidationType directly on the
>>>>>>>>>> EmbeddedIdentityStoreDefinition (and other definitions)?
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> That is indeed an option.
>>>>>>>>>
>>>>>>>>> Best regards
>>>>>>>>> Rudy
>>>>>>>>>
>>>>>>>>> On 4 September 2016 at 21:28, arjan tijms <arjan.tijms_at_gmail.com>
>>>>>>>>> wrote:
>>>>>>>>>
>>>>>>>>>> Hi Rudy,
>>>>>>>>>>
>>>>>>>>>> Looks much improved, thanks!
>>>>>>>>>>
>>>>>>>>>> I have a few comments, but some are just cosmetic and could be
>>>>>>>>>> looked at later.
>>>>>>>>>>
>>>>>>>>>> The most important one concerns the IdentityStore interface, it
>>>>>>>>>> now has this method:
>>>>>>>>>>
>>>>>>>>>> public CredentialValidationResult validate(Credential credential,
>>>>>>>>>> CallerPrincipal callerPrincipal) {
>>>>>>>>>>
>>>>>>>>>> I wonder, would it be possible to omit the CallerPrincipal
>>>>>>>>>> argument here? There's already a caller in the Credential, and I'm afraid
>>>>>>>>>> this would confuse people who have to implement the interface. I may also
>>>>>>>>>> give the impression to implementors that they have to take the history of
>>>>>>>>>> what happened before with other stores somehow into account. I think it
>>>>>>>>>> would be best if the store itself is totally unaware of the concept of
>>>>>>>>>> multiple stores, since that's what the handler should do. Since the handler
>>>>>>>>>> is replaceable, a store implementation can't assume anything about it.
>>>>>>>>>>
>>>>>>>>>> In the implementations of the provided stores (e.g. the
>>>>>>>>>> EmbeddedIdentityStore), there's this comment with some code below it.
>>>>>>>>>>
>>>>>>>>>> // We check also if caller != null to be sure the Authentication
>>>>>>>>>> by another IdentityStore succeeded.
>>>>>>>>>>
>>>>>>>>>> If you look at the code flow that applies to it's:
>>>>>>>>>>
>>>>>>>>>> String caller = null;
>>>>>>>>>> ...
>>>>>>>>>> // We are Authorize Only mode, so get the caller
>>>>>>>>>> determined previously.
>>>>>>>>>> if (callerPrincipal != null) {
>>>>>>>>>> caller = callerPrincipal.getName();
>>>>>>>>>> }
>>>>>>>>>>
>>>>>>>>>> if (authenticated && caller != null) {
>>>>>>>>>> ...
>>>>>>>>>> }
>>>>>>>>>>
>>>>>>>>>> return INVALID_RESULT;
>>>>>>>>>>
>>>>>>>>>> So it callerPrincipal or callerPrincipal.getName() would be null
>>>>>>>>>> here, the store would return INVALID_RESULT. But in that case, wouldn't it
>>>>>>>>>> be better if the handler did not call the store at all? That would safe the
>>>>>>>>>> store from having to check this case or even having to think of what other
>>>>>>>>>> stores before it did. In other words, define in the contract and spec that
>>>>>>>>>> if the store is used for authorization, the passed in caller is not null.
>>>>>>>>>> Would that make sense?
>>>>>>>>>>
>>>>>>>>>> The idea here would be that a store is kept as simple as it can
>>>>>>>>>> possibly be, as an often heard complaint is that all proprietary stores are
>>>>>>>>>> quite complex. The multi-store algorithm should then 100% be with the
>>>>>>>>>> handler.
>>>>>>>>>>
>>>>>>>>>> A somewhat cosmetic and implementation thing, but maybe still
>>>>>>>>>> worth it to look at is this pattern in the stores:
>>>>>>>>>>
>>>>>>>>>> private EmbeddedIdentityStoreDefinitio
>>>>>>>>>> n embeddedIdentityStoreDefinition;
>>>>>>>>>>
>>>>>>>>>> private ValidationType validationType;
>>>>>>>>>>
>>>>>>>>>> private void determineValidationType() {
>>>>>>>>>> validationType = ValidationType.BOTH;
>>>>>>>>>> if (embeddedIdentityStoreDefinition.authenticateOnly()) {
>>>>>>>>>> validationType = ValidationType.AUTHENTICATION;
>>>>>>>>>> } else {
>>>>>>>>>> if (embeddedIdentityStoreDefinition.authorizeOnly())
>>>>>>>>>> {
>>>>>>>>>> validationType = ValidationType.AUTHORIZATION;
>>>>>>>>>> }
>>>>>>>>>> }
>>>>>>>>>> }
>>>>>>>>>>
>>>>>>>>>> public ValidationType validationType() {
>>>>>>>>>> return validationType;
>>>>>>>>>> }
>>>>>>>>>>
>>>>>>>>>> Seeing this code repeating, maybe it's better to have
>>>>>>>>>> ValidationType directly on the EmbeddedIdentityStoreDefinition
>>>>>>>>>> (and other definitions)?
>>>>>>>>>>
>>>>>>>>>> Then it could just be:
>>>>>>>>>>
>>>>>>>>>> public ValidationType validationType() {
>>>>>>>>>> return embeddedIdentityStoreDefinition.validationType();
>>>>>>>>>> }
>>>>>>>>>>
>>>>>>>>>> Another small thing, also cosmetic mostly, but there's now this
>>>>>>>>>> kind of check often:
>>>>>>>>>>
>>>>>>>>>> if (validationType == ValidationType.AUTHENTICATION ||
>>>>>>>>>> validationType == ValidationType.BOTH) {
>>>>>>>>>> ...
>>>>>>>>>> }
>>>>>>>>>>
>>>>>>>>>> What about making two helper methods on the ValidationType enum
>>>>>>>>>> like e.g.
>>>>>>>>>>
>>>>>>>>>> public boolean isAuthentication() {
>>>>>>>>>> return isOneOf(this, AUTHENTICATION, BOTH);
>>>>>>>>>> }
>>>>>>>>>>
>>>>>>>>>> then the code using it could just do:
>>>>>>>>>>
>>>>>>>>>> if (validationType.isAuthentication()) {
>>>>>>>>>> ...
>>>>>>>>>> }
>>>>>>>>>>
>>>>>>>>>> Hope this feedback is useful ;)
>>>>>>>>>>
>>>>>>>>>> Kind regards,
>>>>>>>>>> Arjan Tijms
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> On Sun, Sep 4, 2016 at 8:07 PM, Rudy De Busscher <
>>>>>>>>>> rdebusscher_at_gmail.com> wrote:
>>>>>>>>>>
>>>>>>>>>>> Hi all,
>>>>>>>>>>>
>>>>>>>>>>> This is the updated multi IdentityStore based on earlier
>>>>>>>>>>> feedback on this mailing list.
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> *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.
>>>>>>>>>>>
>>>>>>>>>>> *IdentityStore interface*
>>>>>>>>>>>
>>>>>>>>>>> * CredentialValidationResult validate(Credential credential,
>>>>>>>>>>> CallerPrincipal callerPrincipal);*
>>>>>>>>>>>
>>>>>>>>>>> * default int priority() {*
>>>>>>>>>>> * return 100;*
>>>>>>>>>>> * }*
>>>>>>>>>>>
>>>>>>>>>>> * default ValidationType validationType() {*
>>>>>>>>>>> * return ValidationType.BOTH;*
>>>>>>>>>>> * }*
>>>>>>>>>>>
>>>>>>>>>>> *validate method*
>>>>>>>>>>>
>>>>>>>>>>> the CallerPrincipal parameter contains previous validated Caller
>>>>>>>>>>> information (authentication) if any. Value can be null.
>>>>>>>>>>>
>>>>>>>>>>> *ValidationType method*
>>>>>>>>>>>
>>>>>>>>>>> ValidationType contains meta information for the IdentityStore
>>>>>>>>>>> handler. It defines if the store does authentication only
>>>>>>>>>>> (ValidationType.AUTHENTICATION), authorization only
>>>>>>>>>>> (ValidationType.AUTHORIZATION) or both; the default.
>>>>>>>>>>> This information is used by the handler to define if the store
>>>>>>>>>>> should be called. For example, the default IdentityStore handler does not
>>>>>>>>>>> call a store who does Authorization only when no successful authentication
>>>>>>>>>>> took place.
>>>>>>>>>>>
>>>>>>>>>>> *priority method *
>>>>>>>>>>>
>>>>>>>>>>> determines the order in the case when there are multiple ones.
>>>>>>>>>>> They are ordered by ascending values, meaning that the IdentityStore with
>>>>>>>>>>> the lowest value is processed first.
>>>>>>>>>>>
>>>>>>>>>>> *Status rules*
>>>>>>>>>>>
>>>>>>>>>>> Stores should return the following Status (contained in the
>>>>>>>>>>> CredentialValidationResult result parameter)
>>>>>>>>>>> *NOT_VALIDATED*: Type of Credential isn't compatible with the
>>>>>>>>>>> ones expected by the store.
>>>>>>>>>>> *INVALID* : In case the store does Authentication, the
>>>>>>>>>>> validation of the credential failed.
>>>>>>>>>>> *AUTHENTICATED* : In case the store does Authentication only
>>>>>>>>>>> and the validation succeeded.
>>>>>>>>>>> *VALID* : In case the store does Authorization and groups are
>>>>>>>>>>> supplied for the Caller (can be an empty list indicating no groups assigned
>>>>>>>>>>> to caller)
>>>>>>>>>>>
>>>>>>>>>>> So When Store does handle both (Authentication and
>>>>>>>>>>> Authorization) it should return NOT_VALIDATED, INVALID or VALID
>>>>>>>>>>> In case store does Authentication only; it is NOT_VALIDATED,
>>>>>>>>>>> INVALID or AUTHENTICATED
>>>>>>>>>>> In case store does Authorization only; it is NOT_VALIDATED or
>>>>>>>>>>> VALID (the assumption is that when store can handle the type of
>>>>>>>>>>> Credentials, the
>>>>>>>>>>>
>>>>>>>>>>> *IdentityStore Handler *
>>>>>>>>>>>
>>>>>>>>>>> Default IdentityStore Handler uses the following algorithm
>>>>>>>>>>> 1) Order all identityStores based on the priority value
>>>>>>>>>>> 2) Loop over all IdentityStores unless one of the stores return
>>>>>>>>>>> the status INVALID
>>>>>>>>>>> 3) Call a certain IdentityStore when
>>>>>>>>>>> * The Store does Authentication, regardless of the result of
>>>>>>>>>>> the calls to previous IdentityStores.
>>>>>>>>>>> * The Store does Authorization only and another Store already
>>>>>>>>>>> successful validated the Credentials (Result is AUTHENTICATED or VALID)
>>>>>>>>>>> 4) The result of the call to the IdentityStore is combined with
>>>>>>>>>>> 'previous' result of the there calls to the IdentityStores.
>>>>>>>>>>> * Groups are added to the already defined groups of the
>>>>>>>>>>> caller.
>>>>>>>>>>>
>>>>>>>>>>> *Issues*
>>>>>>>>>>>
>>>>>>>>>>> The current default IdentityStore Handler handler can't handle
>>>>>>>>>>> this use case
>>>>>>>>>>> - Caller can be authenticated by one IdentityStore (for example
>>>>>>>>>>> LDAP or Database)
>>>>>>>>>>>
>>>>>>>>>>> This is because the loop stops when an INVALID result is
>>>>>>>>>>> returned by a IdentityStore. This is required to veto another successful
>>>>>>>>>>> authentication like in this use case
>>>>>>>>>>> - The first IdentityStore based on OAuth2 allows any Google
>>>>>>>>>>> account, but the second store limits the access to a certain google domain.
>>>>>>>>>>>
>>>>>>>>>>> So probably we need some configuration to be able to combine
>>>>>>>>>>> both use cases in one algorithm.
>>>>>>>>>>>
>>>>>>>>>>> *Code status*
>>>>>>>>>>>
>>>>>>>>>>> The current code needs probably some more tests and
>>>>>>>>>>> verification. I try to write them in the following weeks.
>>>>>>>>>>>
>>>>>>>>>>> Changes for this updated proposal can be viewed in this commit
>>>>>>>>>>> https://github.com/rdebusscher/soteria/commit/afa4cecf626266
>>>>>>>>>>> d0ae400f806b0358ed7b07bc61
>>>>>>>>>>> And are added to the first pull request to the Soteria
>>>>>>>>>>> repository.
>>>>>>>>>>>
>>>>>>>>>>> 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/CAL%2Bwt-5Q
>>>>>>>>>>> mUqZiZ9V2uTYXAE-A%2B%2BFrzNwYKkYxmZBxaHyYLq15g%40mail.gmail.com
>>>>>>>>>>> <https://groups.google.com/d/msgid/jsr375-experts/CAL%2Bwt-5QmUqZiZ9V2uTYXAE-A%2B%2BFrzNwYKkYxmZBxaHyYLq15g%40mail.gmail.com?utm_medium=email&utm_source=footer>
>>>>>>>>>>> .
>>>>>>>>>>> For more options, visit https://groups.google.com/d/optout.
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>
>