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

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

From: Werner Keil <werner.keil_at_gmail.com>
Date: Mon, 7 Dec 2015 16:42:57 +0100

P.s.: I'm not sure, if the API would stick to a rather lengthy package name
like: javax.security.authenticationmechanism ?;-)

Werner

On Mon, Dec 7, 2015 at 10:45 AM, Werner Keil <werner.keil_at_gmail.com> wrote:

> Thanks, I hope to have a closer look at it during the week or weekend.
> Would be nice to show something like it in Tel Aviv in 10 days ;-)
>
> Kind Regards,
>
> Werner Keil | JCP Executive Committee Member, JSR 363 Co Spec Lead |
> Eclipse UOMo Lead, Babel Language Champion | Apache Committer
>
> Twitter @wernerkeil | @UnitAPI | @JSR354 | @AgoravaProj | @DeviceMap
> | #DevOps | #EclipseUOMo
> Skype werner.keil | Google+ gplus.to/wernerkeil
>
>
>
> Werner Keil
> [image: https://]about.me/wernerkeil
> <https://about.me/wernerkeil?promo=email_sig>
>
>
> On Mon, Dec 7, 2015 at 10:37 AM, arjan tijms <arjan.tijms_at_gmail.com>
> wrote:
>
>> Hi,
>>
>> I have extended the example a little. To keep the original example as-is
>> and as small/simple as possible, I did the extended work in a new repo
>> here: https://github.com/arjantijms/mechanism-to-store-x
>>
>> This adds:
>>
>> * Working implementation and example of @DataBaseIdentityStoreDefinition
>> * Example of custom (app provided) identity store.
>>
>> See:
>>
>> *
>> https://github.com/arjantijms/mechanism-to-store-x/blob/master/app-db/src/main/java/test/Servlet.java
>> *
>> https://github.com/arjantijms/mechanism-to-store-x/blob/master/app-custom/src/main/java/test/TestIdentityStore.java
>>
>> I also fixed the "mechanism-to-store-app-1.0-SNAPSHOT" name. The example
>> now uses simple names for the war and therefor the URL:
>> localhost:8080/app-db, localhost:8080/app-mem, localhost:8080/app-custom.
>>
>> The examples were tested on GlassFish (4.1.1) and JBoss (WildFly 10rc4).
>> Note that JBoss needs the proprietary JASPIC activation before this works
>> (still hoping Darran can do something here to lift this).
>>
>> In order to work around a GlassFish bug (see
>> https://java.net/jira/browse/GLASSFISH-21447) I had to put the data
>> source in java:global. Due to another GlassFish bug where GF doesn't unbind
>> the data source when the application is undeployed you actually have to
>> stop and start GlassFish before the deploying the app again.
>>
>> Kind regards,
>> Arjan Tijms
>>
>>
>>
>>
>> On Sun, Oct 25, 2015 at 5:55 AM, Alex Kosowski <alex.kosowski_at_oracle.com>
>> wrote:
>>
>>> Hi Arjan,
>>>
>>> That example app is terrific! I would like to demonstrate it during the
>>> JavaOne JSR 375 BOF. The app does not look like much, but when you realize
>>> the caller was authenticated using data from an annotation, you realize how
>>> these simple standardizations will make a BIG impact.
>>>
>>> The only issue I ran into when deploying on GlassFish 4.1 was the
>>> default app name was " mechanism-to-store-app-1.0-SNAPSHOT", which made the
>>> context root " /mechanism-to-store-app-1.0-SNAPSHOT". But I changed the
>>> context root to "/mechanism-to-store-app" in the GF admin console and the
>>> example works as you described.
>>>
>>> Thanks again!
>>> Alex
>>>
>>>
>>> On 10/24/15 6:19 PM, Alex Kosowski wrote:
>>>
>>> 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
>>> > 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
>>>>> >
>>>>>
>>>>> 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
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>
>>
>