Hi,
On Sun, Sep 18, 2016 at 4:19 PM, arjan tijms <arjan.tijms_at_gmail.com> wrote:
> As for the algorithm, what about something along the lines of:
>
> 1. Loop over all stores in order of priority that are configured to
> authenticate using the exact same initial credential until one succeeds. If
> one succeeds, break the loop.
>
> 3. If we have a valid outcome, seed the group collection with the groups
> from the store that succeeded and take the caller principal from that
> store. If we don't have a valid outcome, return immediately from the
> handler.
>
> 4. Loop over all stores that are set to authorize using the caller
> principal from the previous step. From each such store, add the groups it
> returns to the group collection.
>
> 5. As the final result, return the caller principal and the combined
> groups.
>
I didn't test the code yet, but I did a quick sketch up in code to
illustrate the above:
public class DefaultIdentityStoreHandler implements IdentityStoreHandler {
private List<IdentityStore> authenticationIdentityStores;
private List<IdentityStore> authorizationIdentityStores;
public void init() {
List<IdentityStore> identityStores =
getBeanReferencesByType(IdentityStore.class, false);
authenticationIdentityStores = identityStores.stream()
.filter(i -> isOneOf(i.validationType(), BOTH, AUTHENTICATION))
.sorted(comparing(IdentityStore::priority))
.collect(toList());
authorizationIdentityStores = identityStores.stream()
.filter(i -> i.validationType() == AUTHORIZATION)
.sorted(comparing(IdentityStore::priority))
.collect(toList());
}
@Override
public CredentialValidationResult validate(Credential credential) {
CredentialValidationResult validationResult = null;
// Check stores to authenticate until one succeeds.
for (IdentityStore authenticationIdentityStore :
authenticationIdentityStores) {
validationResult =
authenticationIdentityStore.validate(credential);
if (validationResult.getStatus() == VALID) {
break;
}
}
if (validationResult.getStatus() != VALID) {
// No store authenticated, no need to continue
return validationResult;
}
CallerOnlyCredential callerOnlyCredential = new
CallerOnlyCredential(validationResult.getCallerPrincipal());
List<String> groups = new
ArrayList<>(validationResult.getCallerGroups());
// Ask all stores that were configured for authorization to get the
groups for the
// authenticated caller
for (IdentityStore authorizationIdentityStore :
authorizationIdentityStores) {
groups.addAll(authorizationIdentityStore.validate(callerOnlyCredential).getCallerGroups());
}
return new CredentialValidationResult(VALID,
validationResult.getCallerPrincipal(), groups);
}
That should be able to gather for the most common use cases, in a very
straightforward way. Users with less common use cases can still replace or
decorate the handler. We can take a separate look at your "restriction" use
case. It can already be done here by a custom decorator for the Handler,
but we could look at something more dedicated as well.
Kind regards,
Arjan Tijms