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

[jsr375-experts] Re: Identity Store Proposal

From: arjan tijms <arjan.tijms_at_gmail.com>
Date: Sat, 13 Jun 2015 17:29:27 +0200

Hi,

On Sat, Jun 13, 2015 at 3:10 AM, Alex Kosowski <alex.kosowski_at_oracle.com>
wrote:

> Hi Arjan,
>
> Thanks for your comments! I just wanted to mention a subtlety. The
> Identity Store proposal contains an SPI called an IdentityStore interface,
> which is responsible for interacting with the persistence mechanism. I
> think IdM or Identity Management system would really be a better name for
> the proposal.
>

No issues with that term itself ;)

But it looks to be a broader concept than just "identity store", which if
I'm not mistaken is still pretty much what was discussed here, right?



[Alex] After credential validation, the Credentials type returns both the
> status of credential validation and the associated Account. The status
> serves as a convenient result code to inform the caller why validation
> failed. If the status is VALID, the Account is available. I suppose the
> concern about mutability is that the caller and the credential handling may
> be on different threads. Both Account and Status could be assigned
> internally using volatile variables, so no locking would be needed although
> there is a slight performance hit as shared memory is resolved. So I do not
> see a big problem. Do you have a specific use case?
>

It's more a concern of API design and usability. In the last few years Java
seems to be moving to a somewhat more functional style and putting less
emphasis on mutable types. The old Data/Calendar versus the JSR 310 types
is maybe a good example. Although this was a concern of Jan, I share it,
and think it's just cleaner and easier to understand to have a Credentials
which only functions as an input type.

The Account and Status could also be returned as the return type of the
validateCredentials method, couldn't they? E.g.

    AuthenticationResult result =
identityStore.validateCredentials(credentials);

To me this just looks more natural then:

    identityStore.validateCredentials(credentials);

And then discovering that my input type has been updated with a result.




> This is essentially what issue 18 asks (
>> https://java.net/jira/browse/JAVAEE_SECURITY_SPEC-18). Existing examples
>> are the simple identity stores that are provided by e.g. Resin, Tomcat,
>> JBoss, etc:
>>
>> * http://caucho.com/resin-4.0/admin/security-overview.xtp#Authenticators
>> *
>> https://tomcat.apache.org/tomcat-8.0-doc/realm-howto.html#Standard_Realm_Implementations
>> *
>> http://docs.jboss.org/jbosssecurity/docs/6.0/security_guide/html/Login_Modules.html#sect-UsersRolesLoginModule
>>
>> I was planning on doing a comparison article about which identity
>> stores each server provides now, how their features compare, and what their
>> interfaces in code look like. Unfortunately I haven't found the time to
>> write this yet, but I'll look at the soon. Might be handy here ;)
>>
>> As for the other CRUD elements, these could be moved into a secondary
>> case. This is in recognition that some applications may wish to fully store
>> their users into the JSR 375 provided identity store, but other
>> applications really only want to be able to pass the username and roles to
>> the container from an authentication module and manage users via their own
>> services.
>>
> [Alex] Regarding: "but other applications really only want to be able
> to pass the username and roles to the container from an authentication
> module and manage users via their own services. "
> Would you please explain this use case? I guess the proposed Identity
> Store would not be used in this case?
>

Well, the idea here is that a lot of applications are already using things
like Resin's Authenticators, Tomcat's Realms, JBoss' Login modules, etc.

My proposal was just to standardize these first. They are all highly
similar and a standardized interface for those would be trivial to
implement. Applications would then have a standard way to authenticate
using a users.xml file (for testing, obviously), LDAP, JDBCDataBase, etc,
and since they are standardized Java EE could find user provided
implementations as well (e.g. following a call to HttpServletrequest#login,
which is currently unspecified).

For this use case, it's not necessary that the other CRUD operations also
go via the identity store interface, as their main purpose is that the Java
EE container and/or the authentication mechanism (e.g. the standard FORM),
can put the credentials into those and get the username/roles back.

For instance, if I take Reza's example from
https://java.net/jira/browse/JAVAEE_SECURITY_SPEC-18:

@IdentityStore
public class MyIdentityStore {

  @Inject UserService userService;

  @OnAuthentication
  // The parameters could suit the credentials mechanism being used.
  public Principal getPrincipal(String username, String password) {
    // Construct the principal using the user service.
  }

  @OnAuthorization
  public String[] getRoles (Principal principal) {
    // Construct an array of roles using the principal and user service.
  }
}

(As Reza commented, the exact semantics are not important, but just the
general concept.)

Here we see credentials (username/password in this case) are put it, and a
principal/roles come out.

For this application the underlying store is "UserService", but it's not
necessary for the other CRUD operations to be available via the
IdentityStore, for the simple reason that the application already has
access to the UserService and presumably can create/update/delete users via
that.

A similar thing holds for some of the proposed standard identity stores (as
proposed by Reza in https://java.net/jira/browse/JAVAEE_SECURITY_SPEC-9).
Take e.g.

@DatabaseIdentityStore(
    lookup="somedatabase",
    userQuery="SELECT password FROM principals WHERE username=?",
    rolesQuery="SELECT role FROM roles where username=?", ...)

In this case the provided identity store would not necessarily have to have
the other CRUD operations either, since the underlying store is the
database here, which has already been provided by the application, so
presumably the application will have no problems accessing this databasse
(since a database identity store is a very well known and frequently used
kind of identity store I know from practice that normally this is indeed no
problem).

So long story short, there's definitely value in providing the full set of
CRUD operations for identity stores, but just standardizing all the
existing Authenticators/Realms/LoginModules with a {credentials in,
username/roles out} interface is very low hanging fruit and already
provides tremendous value.




> It may additionally be that existing security datastores only allow a READ
>> of type getUsernameAndGroupsByCredential.
>>
>> [Alex] We could accommodate read only data stores in an
> implementation of an IdentityStore interface
>

Yes, that would basically be what I (and Reza) proposed above, right? If it
would be interfaces that are used, maybe an IdentityStore interface for
just the read operation, and e.g. a FullCrudIdentityStore (exact name could
be improved) interface for the store with the additional C, U and D
operations?





> [Alex] The Grant class (i.e., role mapping, could be renamed) provides the
> ability to programmatically assign and persist roles mapped to Callers and
> Groups. If an application were to create/manage users stored in the
> proposed Identity Store, that application would want to assign roles to
> those users. That would be the purpose of the Grant.
>
> How would the container learn about the Identity Store role mappings to
> apply them to @ RolesAllowed, etc. ? I think we would need to define that
> callback mechanism, unless we can think of a way to leverage existing EE
> frameworks. I think the JASPIC GroupPrincipalCallback is for groups not
> roles.
>

That one is for groups indeed. The Java EE container can then at a later
time map these to roles (which can be a 1:1 mapping). I wrote an article
about the process involved here a while back, see:

http://arjan-tijms.omnifaces.org/2014/12/java-ee-authorization-jacc-revisited.html

And in this article I discuss wrapping the native role mappers of 3 well
known servers (GlassFish, WebLogic and Geronimo):

http://arjan-tijms.omnifaces.org/2015/01/java-ee-authorization-jacc-revisited.html

I know it's a bit of a long read altogether, but the following then brings
it all together:

http://arjan-tijms.omnifaces.org/2015/01/java-ee-authorization-jacc-revisited.html

The most important part for this discussion is in the following code, which
is what a container will approximately execute to determine the outcome of
@RolesAllowed("somerole") or when accessing a protected URL:

        TestPolicyConfiguration policyConfiguration =
getCurrentPolicyConfiguration();
        TestRoleMapper roleMapper = policyConfiguration.getRoleMapper();

        if (isExcluded(policyConfiguration.getExcludedPermissions(),
permission)) {
            // Excluded permissions cannot be accessed by anyone
            return false;
        }

        if (isUnchecked(policyConfiguration.getUncheckedPermissions(),
permission)) {
            // Unchecked permissions are free to be accessed by everyone
            return true;
        }

        List<Principal> currentUserPrincipals =
asList(domain.getPrincipals());

        if (!roleMapper.isAnyAuthenticatedUserRoleMapped() &&
!currentUserPrincipals.isEmpty()) {
            // The "any authenticated user" role is not mapped, so
available to anyone and the current
            // user is assumed to be authenticated (we assume that an
unauthenticated user doesn't have any principals
            // whatever they are)
            if
(hasAccessViaRole(policyConfiguration.getPerRolePermissions(), "**",
permission)) {
                // Access is granted purely based on the user being
authenticated (the actual roles, if any, the user has it not important)
                return true;
            }
        }

        if (hasAccessViaRoles(policyConfiguration.getPerRolePermissions(),
roleMapper.getMappedRolesFromPrincipals(currentUserPrincipals),
permission)) {
            // Access is granted via role. Note that if this returns false
it doesn't mean the permission is not
            // granted. A role can only grant, not take away permissions.
            return true;
        }

Here the important thing is that the JACC provider can get access to the
role mapper. It does not have to know anything about the identity store.

It has to know a few things:

1. Is the ** role mapped?
2. Which of the unknown principal types (e.g.
weblogic.security.principal.WLSGroupImpl) in the current user principal
list are roles?
3. The fact that e.g. "manager" is mapped to "admin"

The default JACC providers (or native authorization code) of containers
currently use their own proprietary role mapper. So the idea was to
standardize just the role mapper itself as a separate entity.

I think the connection with the identity store should be loose.

Often the role mapping will be expressed via configuration that's fully
external to an identity store. This is currently done via proprietary
configuration (e.g. glassfish-web.xml), but there's a proposal to
standardize this configuration. See
https://java.net/jira/browse/JAVAEE_SECURITY_SPEC-8 and
https://java.net/jira/browse/JAVAEE_SECURITY_SPEC-27

A use case where the mapping is separate from the store is an application
configuring an LDAP identity store that's shared in a organization. In that
case the users are not under control of the application (it should therefor
be a read only store), but the application does want to configure it in a
standard way and does want to do role mapping in a standard way (currently
both tasks are server specific).

A use case where the mapping would reside within the store is an
application fully managing its own users, and where group to role mapping
is not used to map one name to the other, but to have a 2 level hierarchy
(e.g. "manager" maps to "update_interest", "close_account", ...).

But for the second use case, having the identity store return the
collection of {"update_interest", "close_account", ...} and use them
directly with the GroupPrincipalCallback in combination with 1:1 group to
role mapping would yield the exact same result.

Since the mail is perhaps getting a bit long, I'll reply on the other
points in a next mail.

Thanks for your patience reading this all ;)

Kind regards,
Arjan Tijms