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

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

From: arjan tijms <arjan.tijms_at_gmail.com>
Date: Wed, 23 Dec 2015 15:16:04 +0100

Hi,

Added another update. I added a build-in authentication mechanism (I used a
BASIC implementation) and an associated annotation analogous to the ones
for build-in identity stores: @BasicAuthenticationMechanismDefinition

As mentioned before, it's open question on how to align this with the
Servlet EG, but at least there's now an example.

With that in place, I created the smallest example application until now.
It's basically a single class:

@BasicAuthenticationMechanismDefinition(
    realmName="test realm"
)

@EmbeddedIdentityStoreDefinition({
    @Credentials(callerName = "reza", password = "secret1", groups = {
"foo", "bar" }),
    @Credentials(callerName = "alex", password = "secret2", groups = {
"foo", "kaz" }),
    @Credentials(callerName = "arjan", password = "secret3", groups = {
"foo" }) }
)

@WebServlet("/servlet")
@DeclareRoles({ "foo", "bar", "kaz" })
@ServletSecurity(@HttpConstraint(rolesAllowed = "foo"))
public class Servlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {

        // ...
    }

}

As can be seen this chooses and configures both the authentication
mechanism as well as the identity store. Both adhere to the highlander rule
(there can be only one).

The authentication mechanism is implemented as follows:

public class BasicAuthenticationMechanism implements
HttpAuthenticationMechanism {
    private final String basicHeaderValue;

    public BasicAuthenticationMechanism(String realmName) {
        this.basicHeaderValue = format("Basic realm=\"%s\"", realmName);
    }

@Override
public AuthStatus validateRequest(HttpServletRequest request,
HttpServletResponse response, HttpMsgContext httpMsgContext) throws
AuthException {
String[] credentials = getCredentials(request);
if (!isEmpty(credentials)) {

   IdentityStore identityStore =
CDI.current().select(IdentityStore.class).get();

   CredentialValidationResult result = identityStore.validate(
                new UsernamePasswordCredential(credentials[0], new
Password(credentials[1])));

            if (result.getStatus() == VALID) {
                return httpMsgContext.notifyContainerAboutLogin(
                    result.getCallerName(), result.getCallerGroups());
}
}
if (httpMsgContext.isProtected()) {
response.setHeader("WWW-Authenticate", basicHeaderValue);
return httpMsgContext.responseUnAuthorized();
}
return httpMsgContext.doNothing();
}
private String[] getCredentials(HttpServletRequest request) {
String authorizationHeader = request.getHeader("Authorization");
if (!isEmpty(authorizationHeader) && authorizationHeader.startsWith("Basic
") ) {
return new
String(parseBase64Binary(authorizationHeader.substring(6))).split(":");
}
return null;
}
}

Btw, both very good and some less good news concerning JBoss (WildFly).
Since WildFly 10rc5 (just released today) ***it's no longer necessary to
modify standalone.xml*** :) All JASPIC based examples just run out of the
box now. It's not 100% perfect yet, since the archive still needs to
contain jboss-web.xml, but that's a minor concern for now.

Unfortunately, the basic authentication mechanism doesn't work on WildFly
for now, because of this bug: https://issues.jboss.org/browse/UNDERTOW-577

The other ones do run though.

Kind regards,
Arjan Tijms




















On Thu, Dec 17, 2015 at 9:25 PM, arjan tijms <arjan.tijms_at_gmail.com> wrote:

> Hi,
>
> On Thu, Dec 17, 2015 at 7:01 PM, Werner Keil <werner.keil_at_gmail.com>
> wrote:
>
>> Dear All,
>>
>> A little follow-up. I'll also plan to upload the slides either today or
>> over the weekend.
>> The app-db example feels like something gets stuck, either the data
>> source is no longer accessible or similar. Even adding and removing the app
>> to GlassFish 4 did not help..
>>
>
> Unfortunately data sources seem to be a little buggy on GlassFish, at
> least embedded ones. java:app doesn't work at all, I encountered some
> issues with XA connection pooling, and indeed, data sources stick around
> and you need to restart GlassFish :(
>
> Payara recently fixed this last bug though, see
> https://github.com/payara/Payara/pull/565
>
> A couple of other data source problems are in the associated issue:
> https://github.com/payara/Payara/issues/510
>
> In general, bugs in various servers is part of what causes some seemingly
> simple example applications to take up quite a lot of time.
>
>
>
>
>> One question I'm not entirely sure, if it's applicable was, if vendors of
>> JCE implementations were to adopt JSR 375. Maybe it's simply the
>> "java.security" package name that created such impression. The only area
>> which I brushed answering someone else's question was, that at least in
>> production where passwords aren't masked using aliasing the actual password
>> store should always be encrypted, too.
>>
>
> Indeed, so password aliasing is as you know on the schedule of this JSR
> and I assume this may use JCE, but this is a topic Alex likely knows more
> about.
>
> Btw, the provided examples are all aimed at demonstrating the API
> interactions, and the current implementations are as simple as can be. The
> data in the various stores is now all unencrypted. Naturally this should
> never be done in production.
>
>
>
>> Not from Tel Aviv, but the host of Hackergarten Zürich, Oliver Nautsch
>> asked based on actual needs we help the current client meet on Java EE 6
>> with proprietary extensions to CDI and other frameworks built in-house.
>> Taking the "Movie" DB test case
>> https://github.com/javaee-security-spec/javaee-security-examples/tree/master/roles-allowed-and-runas
>> he hinted, whether it was possible to add a "content aspect" to the
>> SecurityContext / RunAs. Say "Manager1" could only manipulate or see movies
>> by "Quentin Tarantino" while "Manager2" could only do so for "Joel Coen".
>>
>
> It sounds interesting and a bit like the entity equivalent of row level
> security in a SQL DB. I have to leave it to Alex to answer anything
> definite here about this being in scope of this JSR.
>
> As mentioned by Alex, an important goal of the JSR is to grab the really
> low hanging fruit; the obvious things that for some reason or the other
> never have been standardised before. It's an open question if we'll be able
> to get to the more advanced things.
>
>
>>
>> Actually looking at the large PDF Alex presented at least in Paris (and
>> probably also London or JavaOne?) this sounds like involving "Dynamic Role
>> Mapping" with roles like "EDIT_ACCOUNTS", "CLOSE_ACCOUNT", etc. If this was
>> done on a fine grained level like "EDIT_TARANTINO_FILMS" vs.
>> "EDIT_COEN_FILMS" just to give an example (or say "VIEW_ADULT_FILMS" based
>> on a person's age;-) it might go in the direction of what the client uses
>> here and Oliver suggested.
>>
>
> In case of the row level security equivalent, my guess would be that (JPA)
> entities have to support some notion of security constraints; a kind of
> cross between bean validation and @RolesAllowed. I.e. a roles allowed
> placed on a "value", and the entity manager checking this for various
> operations.
>
> Fine grained roles could be done by simply giving user "Manager1" the role
> "EDIT_TARANTINO_FILMS", etc, right?
>
> Or is "Manager1" a role name here, and do you mean that someone with role
> "Manager1" should also get role "EDIT_TARANTINO_FILMS"? If that's the case
> it would more be a matter of (standardised) group to role mapping perhaps.
>
> Dynamic role mapping, where for every authorization decision roles are
> dynamically evaluated is in fact today possible via JACC. A (custom) JACC
> provider is called every time @RolesAllowed and access to a URL is
> evaluated and can do things like taking time into account.
>
> JEUS has a native authorization system that is very JACC like. They have
> some interested added concepts, like that in a role mapping you have the
> tuple {role name, action, class name}.
>
> See
> https://translate.google.com/translate?&u=http%3A%2F%2Ftechnet.tmaxsoft.com%2Fupload%2Fdownload%2Fonline%2Fjeus%2Fpver-20150722-000001%2Fsecurity%2Fchapter_security_system_API_programming.html
>
> Taking age into account is more difficult, since the authorisation system
> has to know where it can find the age of the caller. For the proposed new
> annotation that takes an EL expression this should be quite doable (you
> pass the age in), but for web.xml security constraints this is more
> difficult.
>
> But these are all good points for the authorization epic of this JSR ;)
>
>
>
>
>
>>
>> Kind Regards,
>> Werner
>>
>> On Thu, Dec 17, 2015 at 5:24 AM, Werner Keil <werner.keil_at_gmail.com>
>> wrote:
>>
>>> All,
>>>
>>> Had a great talk yesterday. At least 50-70 people (guess DevoXX Be
>>> probably had more, not sure about FR or Uk?) in a cinema very similar to
>>> Antwerp ;-)
>>>
>>> I invited those interested to join the mailing list or Github. Right
>>> before my Session was one on the value of Open Source which was a good
>>> introduction. Will Post slides on Slideshare. Thanks everyone especially
>>> Arjan.
>>>
>>> Regards,
>>> Werner
>>> Am 15.12.2015 18:48 schrieb "arjan tijms" <arjan.tijms_at_gmail.com>:
>>>
>>>> Hi,
>>>>
>>>> Another update: I finally came around to implementing an experimental
>>>> version of the simplified SAM.
>>>>
>>>> This version is based on the HttpServerAuthModule that was used in
>>>> several examples before, and which in turn was based on the one from
>>>> OmniSecurity.
>>>>
>>>> The difference is that instead of a base class, it's now an interface
>>>> (as previously discussed). It closely follows the methods of a SAM, but the
>>>> type implementing it is no longer a SAM itself.
>>>>
>>>> For now I've defined the interface as follows:
>>>>
>>>> public interface HttpAuthenticationMechanism {
>>>>
>>>> AuthStatus validateRequest(HttpServletRequest request,
>>>> HttpServletResponse response, HttpMsgContext httpMessageContext) throws
>>>> AuthException;
>>>>
>>>> default AuthStatus secureResponse(HttpServletRequest request,
>>>> HttpServletResponse response, HttpMsgContext httpMessageContext) throws
>>>> AuthException {
>>>> return SEND_SUCCESS;
>>>> }
>>>>
>>>> default void cleanSubject(HttpServletRequest request,
>>>> HttpServletResponse response, HttpMsgContext httpMessageContext) {
>>>> httpMessageContext.cleanClientSubject();
>>>> }
>>>>
>>>> }
>>>>
>>>> See
>>>> https://github.com/arjantijms/mechanism-to-store-x/blob/master/jsr375/src/main/java/javax/security/authenticationmechanism/http/HttpAuthenticationMechanism.java
>>>>
>>>>
>>>> The JSR 375 CDI extension scans for the one and only implementation of
>>>> HttpAuthenticationMechanism, and if found installs a bridge SAM for it that
>>>> calls this implementation for each method: E.g. for validateRequest it
>>>> looks like this:
>>>>
>>>> public AuthStatus validateRequest(MessageInfo messageInfo, Subject
>>>> clientSubject, Subject serviceSubject) throws AuthException {
>>>> HttpMsgContext msgContext = new HttpMsgContext(handler, options,
>>>> messageInfo, clientSubject);
>>>> return CDI.current()
>>>> .select(HttpAuthenticationMechanism.class).get()
>>>> .validateRequest(msgContext.getRequest(),
>>>> msgContext.getResponse(), msgContext);
>>>> }
>>>>
>>>> Just like the identity store, applications can now use a "sam" by only
>>>> having an implementation of HttpAuthenticationMechanism on their classpath.
>>>> The code now looks much simpler too. E.g. the example SAM used before is
>>>> simplified into this:
>>>>
>>>> @RequestScoped
>>>> public class TestAuthenticationMechanism implements
>>>> HttpAuthenticationMechanism {
>>>>
>>>> @Inject
>>>> private IdentityStore identityStore;
>>>>
>>>> @Override
>>>> public AuthStatus validateRequest(HttpServletRequest request,
>>>> HttpServletResponse response, HttpMsgContext httpMessageContext) throws
>>>> AuthException {
>>>>
>>>> if (request.getParameter("name") != null &&
>>>> request.getParameter("password") != null) {
>>>>
>>>> // Get the (caller) name and password from the request
>>>> // NOTE: This is for the smallest possible example only. In
>>>> practice
>>>> // putting the password in a request query parameter is
>>>> highly
>>>> // insecure
>>>> String name = request.getParameter("name");
>>>> Password password = new
>>>> Password(request.getParameter("password"));
>>>>
>>>> // Delegate the {credentials in -> identity data out}
>>>> function to
>>>> // the Identity Store
>>>> CredentialValidationResult result = identityStore.validate(
>>>> new UsernamePasswordCredential(name, password));
>>>>
>>>> if (result.getStatus() == VALID) {
>>>> // Communicate the details of the authenticated user to
>>>> the
>>>> // container. In many cases the underlying handler will
>>>> just store the details
>>>> // and the container will actually handle the login
>>>> after we return from
>>>> // this method.
>>>> return httpMessageContext.notifyContainerAboutLogin(
>>>> result.getCallerName(), result.getCallerGroups());
>>>> } else {
>>>> throw new AuthException("Login failed");
>>>> }
>>>> }
>>>>
>>>> return httpMessageContext.doNothing();
>>>> }
>>>>
>>>> }
>>>>
>>>> See:
>>>> https://github.com/arjantijms/mechanism-to-store-x/blob/master/app-db/src/main/java/test/TestAuthenticationMechanism.java
>>>>
>>>> One partially unsolved problem, which I happen to have discussed with
>>>> Ron Monzillo some time ago, is how module options should neatly arrive in
>>>> this CDI enabled SAM. Currently they can be obtained from the
>>>> HttpMsgContext. Ideally though, you may want to inject them and do
>>>> something in the @PostConstruct method.
>>>>
>>>> But as those module options are per SAM, and there can be multiple
>>>> SAMs, even of the same class type, I was struggling a little on how to
>>>> support that.
>>>>
>>>> CDI itself has something like this with their Bean<T>, and they use a
>>>> unique ID for that that is typically set to just the classname, but can be
>>>> more eloborate if needed. Typically the values of constructor parameters
>>>> are included if the Bean<T> has one. See e.g.
>>>> https://github.com/javaserverfaces/mojarra/blob/master/jsf-ri/src/main/java/com/sun/faces/cdi/CdiProducer.java#L97
>>>>
>>>> Kind regards,
>>>> Arjan Tijms
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>> On Mon, Dec 14, 2015 at 11:43 AM, Werner Keil <werner.keil_at_gmail.com>
>>>> wrote:
>>>>
>>>>> Hi,
>>>>>
>>>>> Thanks for the update. I will try it in WildFly at some point, but
>>>>> especially the current client and environment (where especially some ideas
>>>>> in the JSR like "roles allowed" found here
>>>>> https://github.com/javaee-security-spec/javaee-security-examples
>>>>> would be of interest in theory) are not even able to use WildFly or Java EE
>>>>> 7 and probably won't be for another decade, so it is not so urgent before
>>>>> codemotion.
>>>>>
>>>>> Kind Regards,
>>>>>
>>>>> Werner Keil
>>>>>
>>>>> On Mon, Dec 14, 2015 at 11:21 AM, arjan tijms <arjan.tijms_at_gmail.com>
>>>>> wrote:
>>>>>
>>>>>> Hi,
>>>>>>
>>>>>> On Mon, Dec 14, 2015 at 12:25 AM, Werner Keil <werner.keil_at_gmail.com>
>>>>>> wrote:
>>>>>>
>>>>>>> It works well in Glassfish 4, Wildfly 10 did not,
>>>>>>
>>>>>> [...]
>>>>>>
>>>>>> Yes, I did not modify it
>>>>>>>
>>>>>>
>>>>>> Again a life "proof" of how important it is that WildFly does not
>>>>>> require the modification ;)
>>>>>>
>>>>>> You're not the first one to fall into this. Quite a few people think
>>>>>> it doesn't work on JBoss, since this modification is so non-obvious.
>>>>>>
>>>>>> Or the modification is done once, and then later a new install of
>>>>>> JBoss (WildFly) is done and the modification is forgotten again (this
>>>>>> happens all the time over at the Java EE 7 samples project for the CI).
>>>>>>
>>>>>> Kind regards,
>>>>>> Arjan Tijms
>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>
>>
>