users@javaee-security-spec.java.net

[javaee-security-spec users] [jsr375-experts] Re: Working example app demonstrating identity store usage

From: Alex Kosowski <alex.kosowski_at_oracle.com>
Date: Sat, 24 Oct 2015 17:50:50 -0700

> The Identity Store implementation is vendor specific. It's not in the
> javax.security API package. This way vendors can optimise the
> implementation and/or map it to their existing artefacts.

This is a very interesting point. Perhaps the standard Identity Store
"implementations" should really be defined just as interfaces and not as
classes. We could then declare configuration of the standard
implementations via annotation (e.g., DatabaseIdentityStoreDefinition,
LdapIdentityStoreDefinition,
JaasIdentityStoreDefinition,JsonFileIdentityStoreDefinition) or via
deployment descriptor, following your example with
EmbeddedIdentityStoreDefinition.


With regards,
Alex


On 10/19/15 10:27 AM, arjan tijms wrote:
> Hi,
>
> I've created a working zero config demo application that shows how an
> authentication mechanism can use an identity store.
>
> It's located here: https://github.com/arjantijms/mechanism-to-store
>
> It uses a selection of the types Alex proposed, with the adjustments
> as discussed. This demo app uses one of the two ways to set an
> Identity Store; "declaratively for a standard provided store" as
> proposed by Reza in issue
> https://java.net/jira/browse/JAVAEE_SECURITY_SPEC-9 (the other option
> which is not demoed here is a store fully defined by the application).
>
> The store is declared using an annotation corresponding to option 3 in
> the list presented earlier. I put this annotation on a test Servlet
> (but it can be put anywhere):
>
> @EmbeddedIdentityStoreDefinition({
> @Credentials(callerName = "reza", password = "secret1", groups = {
> "foo", "bar" }),
> @Credentials(callerName = "alex", password = "secret2", groups = {
> "foo", "kaz" }),
> @Credentials(callerName = "arjan", password = "secret3", groups =
> { "foo" }) })
> @DeclareRoles({ "foo", "bar", "kaz" })
> @WebServlet("/servlet")
> public class Servlet extends HttpServlet
>
> See:
> https://github.com/arjantijms/mechanism-to-store/blob/master/app/src/main/java/test/Servlet.java
>
>
> This annotation is picked up by a CDI extension and a Bean<T> is
> created for it:
>
> public <T> void processBean(@Observes ProcessBean<T> eventIn,
> BeanManager beanManager) {
>
> ProcessBean<T> event = eventIn; // JDK8 u60 workaround
>
> Optional<EmbeddedIdentityStoreDefinition> result =
> getAnnotation(beanManager, event.getAnnotated(),
> EmbeddedIdentityStoreDefinition.class);
> if (result.isPresent()) {
> identityStoreBean = new CdiProducer<IdentityStore>()
> .scope(ApplicationScoped.class)
> .types(IdentityStore.class)
> .create(e -> new
> EmbeddedIdentityStore(result.get().value()));
>
> }
> }
>
> This Bean<T> is subsequently registered with the container:
>
> public void afterBean(final @Observes AfterBeanDiscovery
> afterBeanDiscovery) {
> if (identityStoreBean != null) {
> afterBeanDiscovery.addBean(identityStoreBean);
> }
> }
>
> See:
> https://github.com/arjantijms/mechanism-to-store/blob/master/jsr375/src/main/java/org/glassfish/jsr375/cdi/CdiExtension.java
>
>
> The Identity Store implementation is vendor specific. It's not in the
> javax.security API package. This way vendors can optimise the
> implementation and/or map it to their existing artefacts.
>
> The sample implementation maps the data in the annotation to a Map:
>
> private Map<String, Credentials> callerToCredentials;
>
> public EmbeddedIdentityStore(Credentials[] credentials) {
> callerToCredentials = stream(credentials).collect(toMap(
> e -> e.callerName(),
> e -> e)
> );
> }
>
> And in the validate() method it simply checks if the credentials for
> the requested caller name are present:
>
> public CredentialValidationResult
> validate(UsernamePasswordCredential usernamePasswordCredential) {
> Credentials credentials =
> callerToCredentials.get(usernamePasswordCredential.getCaller());
>
> if (credentials != null &&
> usernamePasswordCredential.getPassword().compareTo(credentials.password()))
> {
> return new CredentialValidationResult(
> VALID,
> credentials.callerName(),
> asList(credentials.groups())
> );
> }
>
> return INVALID_RESULT;
> }
>
> See:
> https://github.com/arjantijms/mechanism-to-store/blob/master/jsr375/src/main/java/org/glassfish/jsr375/identitystores/EmbeddedIdentityStore.java
>
>
> The application uses a very basic SAM. This one uses the plain
> non-simplified JASPIC API. The SAM obtains the identity store via CDI
> and then utilises it to perform the "credential to identity data"
> function:
>
> String name = request.getParameter("name");
> Password password = new Password(request.getParameter("password"));
>
> // Obtain a reference to the Identity Store
> IdentityStore identityStore =
> CDI.current().select(IdentityStore.class).get();
>
> // Delegate the {credentials in -> identity data out} function to
> // the Identity Store
> CredentialValidationResult result = identityStore.validate(new
> UsernamePasswordCredential(name, password));
>
> if (result.getStatus() == VALID) {
> callbacks = new Callback[] {
> // The name of the authenticated caller
> new CallerPrincipalCallback(clientSubject,
> result.getCallerName()),
> // the groups of the authenticated caller (for test
> // assume non-null, non-empty)
> new GroupPrincipalCallback(clientSubject,
> result.getCallerGroups().toArray(new String[0])) };
> } else {
> throw new AuthException("Login failed");
> }
>
> See:
> https://github.com/arjantijms/mechanism-to-store/blob/master/app/src/main/java/test/TestServerAuthModule.java
>
>
> Finally the demo app uses a simple (non-protected) Servlet (the one
> shown above) that prints out the details of the authenticated user. If
> the application is deployed to a stock GlassFish without any
> configuration whatsoever being done it can be requested via:
>
> http://localhost:8080/mechanism-to-store-app/servlet?name=reza&password=secret1
> <http://localhost:8080/mechanism-to-store-app/servlet?name=reza&password=secret1>
>
> If all went well this prints out the following:
>
> This is a servlet
> web username: reza
> web user has role "foo": true
> web user has role "bar": true
> web user has role "kaz": false
>
> Needless to say here that this is just for demo'ing one of the
> smallest possible SAMs that interact with the caller. Putting the
> password in the URL is of course not suited for any real live usage.
>
> Note that this particular demo only demonstrates a few of the
> discussed options. I also made a few practical choices here and there
> to be able to implement the application which can of course be
> discussed further.
>
> Thoughts?
>
> Kind regards,
> Arjan Tijms
>
>
>
>
>
>
>
>