users@javaee-security-spec.java.net

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

From: Werner Keil <werner.keil_at_gmail.com>
Date: Mon, 7 Dec 2015 17:22:50 +0100

To further complicate things, the top level
javax.security.auth already in Java SE
https://docs.oracle.com/javase/7/docs/api/javax/security/auth/package-summary.html
says
"This package provides a framework for authentication and authorization."

Guess it will be a good question whether to put anything there or maybe try
use 2 or more new distinct packages like "authentication", etc.?
"identitystore" looks a bit more solid. As we cannot predict what if
anything happens with JSR 351 (javax.security.identity) using a different
sub-package under javax.security sounds decent for that case. It may
interact with JSR 351 if both produce a final release, but that way there's
no interference or conflict (otherwise the second best thing could be
"javax.security.identity.store" but that might seem a bit deep;-)

Werner

On Mon, Dec 7, 2015 at 5:16 PM, Werner Keil <werner.keil_at_gmail.com> wrote:

> Yep, and at the moment most "authorization" elements are under the
> somewhat cryptical
> javax.security.jacc
>
> "Java Authorization Contract for Containers API"
>
> Werner
>
> On Mon, Dec 7, 2015 at 5:03 PM, arjan tijms <arjan.tijms_at_gmail.com> wrote:
>
>> Hi,
>>
>> On Mon, Dec 7, 2015 at 4:42 PM, Werner Keil <werner.keil_at_gmail.com>
>> wrote:
>>
>>> P.s.: I'm not sure, if the API would stick to a rather lengthy package
>>> name like: javax.security.authenticationmechanism ?;-)
>>>
>>> Werner
>>>
>>
>> I hear you, this package name and specifically the classes inside it,
>> have not been given much thought yet, but since they were needed to get the
>> sample to work I had to put them somewhere.
>>
>> In general when we are going to discuss this, there are some issues.
>>
>> There is already an existing package javax.security.auth.message, which
>> is owned by JASPIC, so we won't touch it. But I was wondering about
>> just javax.security.auth, which is the prefix package. I guess we could
>> touch that (create new sub-packages of that), but this is a bit of an issue
>> too since we more or less agreed here that just "auth" is not desirable. It
>> can mean either "authentication" or "authorization".
>>
>> But if we create a package javax.security.authentication next to the
>> existing javax.security.auth, where both mean "authentication", it kinda
>> looks messy as well.
>>
>> Nevertheless, with a javax.security.authentication, we could put the
>> mechanism classes in javax.security.authentication.mechanism (but in total
>> this is actually 1 char longer). The alternative could be
>> javax.security.auth.mechanism, but yeah, troublesome...
>>
>> Kind regards,
>> Arjan Tijms
>>
>>
>>
>>
>>
>>
>>
>>>
>>> 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
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>
>