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

[jsr375-experts] Re: Working example app demonstrating identity store usage

From: Alex Kosowski <alex.kosowski_at_oracle.com>
Date: Sat, 24 Oct 2015 18:19:57 -0700

Thanks Arjan,

Perhaps you would provide an example of using the
@CredentialCapable(UsernamePasswordCredential.class) qualifier? Also,
perhaps you would have an example of extending a standard identity store
with a custom defined one to support a custom defined credential?

Just some suggestions.

With regards,
Alex

On 10/19/15 2:53 PM, arjan tijms wrote:
> Hi,
>
> On Mon, Oct 19, 2015 at 7:46 PM, Alex Kosowski
> <alex.kosowski_at_oracle.com <mailto:alex.kosowski_at_oracle.com>> wrote:
>
> Thanks Arjan! I cannot wait to try it!
>
>
> Looking forward to hearing feedback, thanks.
>
> I'll try later this week to create another project that implements
> some more identity stores and also has an example for a user defined one.
>
> Kind regards,
> Arjan Tijms
>
>
>
>
>
>
> On 10/19/15 1:27 PM, 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>
> <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
>
>
>
>
>
>
>
>
>