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

[jsr375-experts] Re: Comments on Current Spec Content (take 3)

From: arjan tijms <arjan.tijms_at_gmail.com>
Date: Wed, 1 Feb 2017 22:07:42 +0100

Hi,

On Wed, Feb 1, 2017 at 8:05 PM, Will Hopkins <will.hopkins_at_oracle.com>
wrote:

> Third try's a charm?
>

Seems to look allright now ;)


>
> Experts:
>
> First of all, many thanks to Arjan for putting together the first draft of
> our spec -- it's an excellent start.
>


Thanks!


>
> I've had a look at it, and have some comments/questions for discussion.
>

These are really excellent comments and questions, thanks!




> I think the biggest issues to resolve are probably:
>
> - The interaction (or lack thereof) between
> HttpAuthenticationMechanism and JASPIC.
>
>
Here the JSR 375 authentication mechanism is an authentication mechanism
such as intended by 13.6.5 of the Servlet spec. All Servlet containers have
to provide public interfaces to plug-in additional authentication
mechanisms, but these can be proprietary. JASPIC specifies a standardised
version of those interfaces, which the Servlet spec already mentions. A
full Java EE product must provide support for those interfaces. In
practice, all current (Java EE 7) web profile and standalone Servlet 3.1
containers (Tomcat and Jetty) support JASPIC as well.



>
>
>
> - On a similar note, what is the behavior if
> HttpAuthenticationMechanism is called both by the container and by
> application code.
>
>
An HttpAuthenticationMechanism is not intended to be called by application
code, just as e.g. a Servlet or a Filter is not called directly by
application code. You may for instance be able to construct
HttpServletRequest and HttpServletResponse objects, and instantiate a
Servlet class, but that would (of course) not automatically connects these
application instantiated objects to the actual request and response.

So if application code would call an HttpAuthenticationMechanism directly,
the behaviour would simply be nothing (no-op). An
HttpAuthenticationMechanism only does what it does when called by the
container, and when the container uses the data it sets on the
HttpMessageContext instance that is passed-in to its methods.



>
>
> - How is SecurityContext integrated with containers such that
> authenticate() can know what authentication mechanisms are configured and
> getXYZ() can report the correct information?
>
>
SecurityContext#authenticate() simply piggy-backs onto the existing
HttpServletRequest#authenticate() method. The implementation of that knows
about the authentication mechanisms that are configured via either
proprietary means, or via the Java EE standard AuthConfigFactory.



>
>
>
> - Is CallerPrincipal a sufficient credential for IdentityStore.getGroupsByCallerPrincipal()?
> Can it be safely used when there are multiple ID stores configured (since
> the wrong groups could potentially be returned)?
>
>
This is a good question that we should explore further I think. One the one
hand, some proprietary solutions have complex principals that contain much
information to disambiguate callers. This however does hurt the simple case
when scaling down. In JSR 375 one of the key drivers is that security
should be simpler to implement and comprehend by application developers.

In especially smaller system, "Peter.Johnson_at_somedomain.com" is enough to
full distinguish the caller.

Do we want to introduce extra attributes to allow for simple named,
distinguished names, etc? If they are optional, maybe. I don't know yet.



>
>
>
> - Related: should we define CallerAttributes in addition to
> CallerGroups? Any and all comments welcome.
>
>
This is what JSR 351 was specifying. We had a discussion about that in this
EG some while ago, and it was more or less decided to no overlap with the
work there. In the meantime however JSR 351 has been withdrawn, so maybe we
should re-open that discussion.



> 0.0 General
>
> - Use of CDI vs. other mechanisms for accessing the interfaces -- I
> think it's reasonable to say that containers must use/support CDI in the
> ways described by the spec. (Am I correct that all EE Profiles that include
> servlet would also include CDI?) Is it worthwhile specifying that EE
> implementations can optionally choose to make the interfaces available in
> some other way (factory method, etc.)? Should we specify such a mechanism?
>
> Indeed, all EE profiles include CDI. CDI is relatively easy to add to
standalone Servlet containers like Tomcat. Likely with CDI 2.0 and its SE
support this may even be easier/more standardised (but I'm not 100% sure
about that).

I personally think we should not try to abstract over CDI, so that we for
instance should not specify an alternative factory (in CDI, @Inject, the
BeanManager and CDI.current() are basically those factories). @Inject is
already a general annotation that can be implemented by other frameworks.



>
>
> 2.4 Installation
>
> - Container should be able to override ID store contained within app.
>
> Indeed, this is so an application can ship with a default identity store,
but an ops team can override that with an environment specific one. This is
basically how "classic" security works in Java EE and it's IMHO an
important feature that should be preserved.



>
>
> 2.6 Validation and obtaining caller data
>
> - Should the API support other types of user attributes beyond
> groups? A generic attribute type?
>
>
> - What if the information in a caller principal is insufficient to
> distinguish the caller's identity (if, for example, the credential provided
> when authenticating included a full DN, or a duplicate username was
> disambiguated by virtue of the fact that the password matched)? In that
> case, the correct groups could not be determined. Should we limit the use
> of this to validate, and require the caller to make use of the returned
> groups when creating a subject, or retain the CredentialValidationResult?
>
>
> - Would overloading actually work in this case, or would the runtime
> always call validate(Credential) even if a Credential sub-class type is
> passed? Might need generics for this.
>
>
Normal overloading does not work indeed, since the correct method is
determined at compile time, not run time.

The trick is that a default method is used that makes use of MethodHandles
to find the *exact* overloaded method. It does not find the *most specific*
overloaded method such as specified by JLS 15.2. The RI implements that
default method as follows:

 default CredentialValidationResult validate(Credential credential) {
        try {
        return CredentialValidationResult.class.cast(
                    MethodHandles.lookup()
                                 .bind(this, "validate",
methodType(CredentialValidationResult.class, credential.getClass()))
                                 .invoke(credential));
        } catch (NoSuchMethodException e) {
        return NOT_VALIDATED_RESULT;
} catch (Throwable e) {
throw new IllegalStateException(e);
}
    }

See
https://github.com/javaee-security-spec/soteria/blob/master/api/src/main/java/javax/security/identitystore/IdentityStore.java#L69



>
>
>
> - Last paragraph -- what if it can't, because it can't distinguish
> between two foos, or because name is insufficient to identify the user. Can
> (or should) an ID store support getGroupsByCallerPrincipal() for a
> CallerPrincipal not originally validated by that ID store?
>
>
I'd say for now, and with some reservation, that in the simple case an
identity store should contain only callers/users that can be uniquely
defined using the information contained within the CallerPrincipal. In
other words, "foo_at_bar.com" *must* be unique.

If this is insufficient for a particular application, it may be a matter of
adding url like parameters to the name string, e.g. "
foo_at_bar.com?department=xyz&branch=abc". Interpretation of such name is up
to the identity store implementation and the spec would not have to know
about it.



>
>
> 2.7 Build-in Identity Store Beans
>
> - Embedded -- annotation only? No support for deploying a file or
> other mechanism? Safety of embedding passwords in file or in code
> (annotation)?
>
>
Good points! For the moment it's annotation only, but files are indeed very
common (I put some examples of those here:
https://dzone.com/refcardz/getting-started-java-ee).

It should indeed be made abundantly clear that the annotation is intended
for testing / demo purposes, and not normally suited for production usage.



>
>
>
> - LDAP -- is annotation rich enough to support real-life deployment
> requirements?
>
>
I asked the exact same question in the EG a while back. I personally did
the initial specification and implementation, but I mentioned that I'm not
an LDAP expert and based the attributes on the way I happened to setup an
LDAP server once. Rudy later added enriched the annotation.

We should definitely verify that the annotation is now rich enough, or
whether it need more. I was hoping some of the EG members who implemented
the proprietary LDAP stores for their server product could weigh in here.




>
>
> 2.8. Handling multiple identity stores
>
> - When validation fails, would it not be better to remember if any of
> the identity stores accepted the credential, and return INVALID instead of
> NOT_VALIDATED if any of them did?
>
>
> - If we have a VALID result, won't we already have the groupsu
> associated with the validate() that succeeded? Do we want to aggregate
> groups from all stores, that return them, or return only groups from the
> store that validated the user? I would argue for the latter.
>
> The idea is now indeed to aggregate groups, although the stores can
indicate they only do authentication or only provide groups (or do both).

The use case for aggregation was brought forward by among others Rudy, who
pointed out that it's common for 2 stores to be combined; when store that
only validates the caller (e.g. an LDAP store), and then another store that
provided the groups (e.g. a JDBC store). Certificate based stores quite
often have to be combined with a separate store that provided the groups.


This issue also complicates the provision of a separate group lookup method
> when more than one id store is present -- what if store A has a user foo,
> but validation fails. Store B also has a user foo, which is validated with
> the supplied credential. A subsequent call to get groups would succeed for
> store A, but return groups for the wrong user (i.e, the "foo" whose
> credential was not validated).



I agree, but that's not how the current algorithm works ;)

In the first iteration, all stores that either report to do
"AUTHENTICATION" or "BOTH" are consulted. If store A and B are in this
group, and A fails and B succeeds, then in the subsequent iteration only
stores that report to only do "AUTHORIZATION" are consulted. This would be
store C and D for instance, and would never included A or B.



>
>
> 3.4 Installation
>
> - It must be possible to package an HttpAuthenticationMechanism in an
> app, but can it be possible for server to override app-provided mechanism?
>
> This should be possible, with the restriction that we have to realise that
some mechanisms have some points where they interact with the application,
such as FORM does.


>
>
>
> - Should we define a web.xml token for indicating that an
> HttpAuthenticationMechanism is in use, similar to BASIC, FORM, CERT? (since
> the analogy is presented in section 3.3).
>
> I think we absolutely should. There's already a JIRA issue that asks for
this: https://java.net/jira/browse/JAVAEE_SECURITY_SPEC-19 (although the
JIRA issue was written a long time ago and may have to be rewritten to
better reflect what we have done here).


>
>
> 3.5 Orchestrating the authentication dialog
>
> - Does validateRequest() get called twice, then, if called by the
> container and again when the application calls authenticate()? Must it
> therefore be idempotent?
>
> For this we can piggy-back on the JASPIC spec, which extensively and in
great detail specifies the behaviour here. In short, it indeeds gets called
twice. The initial call (initiated by the container) can choose not to
authenticate. In the second call, when the application calls
authenticate(), it *must* authenticate (or, if it can't, fail).



>
>
>
> - Should the ServerAuthModule's secureResponse() method be called if
> the HttpAuthenticationMechanism's is?
>
> An HttpAuthenticationMechanism effectively is an ServerAuthModule,
although one that doesn't implement that interface directly but is called
via a bridge ServerAuthModule as shown here:

https://github.com/javaee-security-spec/soteria/blob/master/impl/src/main/java/org/glassfish/soteria/mechanisms/jaspic/HttpBridgeServerAuthModule.java#L117

This is a bit similar to how a JAX-RS resource is normally called in
response to a request to a Servlet.


>
>
>
> - What happens if there is also a ServerAuthModule
> installed/configured for the app? Are both mechanisms invoked (it seems
> likely they'd interfere with each other)? How does, e.g., an error returned
> from a ServerAuthModule affect the subsequent invokation of the
> HttpAuthenticationMechanism?
>
> The bridge ServerAuthModule mentioned above simply overrides the existing
ServerAuthModule (technically it overrides the entire wrapper chain, namely
the ServerAuthContext, the ServerAuthConfig and the AuthConfigProvider).

I provided some more information about that chain here:
http://arjan-tijms.omnifaces.org/2012/11/implementing-container-authentication.html



>
>
> - The JASPIC spec goes into some detail about exit codes from
> validateRequest() and the subsequent behavior of the container in terms of
> returning errors vs. proceeding to invoke the servlet, codes that indicate
> success but a requirement to continue dialog with the client before
> invoking the servlet, etc. What should HAMs do? Should we specify behavior
> that matches SAMs? Different behavior?
>
> It's the same behaviour now, although there was a discussion not the use
the exact exit codes and constants for them, but use more specific enums
instead. These enums would then be mapped to the existing JASPIC return
codes, so they would still imply the same behaviour.



>
>
> 4.0 General
>
> - How does SecurityContext determine the caller principal and query
> the roles? Is it the responsibility of containers to ensure the
> SecurityContext is populated with the correct information? Does it get the
> current Subject of the stack and use that to get the CallerPrincipal and
> test roles? Etc.
>
> This is an open question. Currently it piggy-backs again on
HttpServletRequest (calling through to getUserPrincipal and isUserInRole).
But the HttpServletRequest is obviously not available in every context.

Now Java EE actually does have an API available to consistently query the
roles, and that's via JACC. See Authorization queries -> Get all users
roles here:
http://arjan-tijms.omnifaces.org/2014/03/implementing-container-authorization-in.html

JACC should be available by default on every full Java EE server (the spec
demands it), but in practice it's not. I think this is because of a
somewhat lacking TCK.

With a portable role mapper and a portable caller principal it's also
possible to determine the caller principal. The simplified authorization
rules that I presented to the EG a while back and prototyped also depends
on this, but it's not merged into the RI yet. For that see:
http://arjan-tijms.omnifaces.org/2016/07/simplified-custom-authorization-rules.html



>
>
> 4.2 Relationship to other specs
>
> - This spec declares that the new security context supercedes all
> these other mechanisms. Should we say something about what the legacy
> behavior is, for older apps, and the extent to which the old and new
> mechanisms must return the same values? I.e., we could declare that the
> older mechanisms remain, for legacy support, but must be implemented by
> delegating to the new SecurityContext, or, at minimum, behave at all times
> as if they delegated to SecurityContext. (This also implies that the new
> SecurityContext must return values consistent with the older APIs.)
>
> Something to think about indeed. If the SecurityContext is implemented by
piggy-backing on HttpServletRequest this would of course not be possible,
but otherwise it would. It depends on how we exactly want to go forward
with the intended implementation - base it on JACC or require a proprietary
implementation).



>
>
> 4.3 Testing for Caller Data
>
> - Downcasting -- to be clear, this section does not assume that HAM
> and SAM are the only possible sources of Caller Principals, correct? I.e.,
> a container might choose to implement BASIC, FORM, and CERT using
> proprietary interfaces. Or are we suggesting that all containers should
> provide BASIC/FORM/CERT via HAM or SAM?
>
> Indeed, the HAM and SAM are not the only sources, just as the Identity
Store is not the only entity to validate them. The reality is that there
are proprietary implementations of those in every existing container that
we cannot easily ignore.

I had a discussion in the Servlet EG where I proposed to have the Servlet
Container Profile a mandated part of the Servlet spec (it's already
recommended by that spec), and there was initially some level of enthusiasm
to have those standard Servlet authentication mechanisms implemented using
the JASPIC interfaces (thus making them SAMs). Unfortunately that
enthusiasm seemed to have dried up a little when I asked about it again a
while later, but it may be an option to re-open that discussion.

In the ideal case BASIC, FORM, etc are implemented using SAMs I think.
That's a lower level interface more suited to plain Servlet container than
the HAM is, which is as mentioned basically a higher level SAM.


>
>
>
> - isCallerInRole() -- this should not be defined to map explicitly to
> a group principal in the subject; rather, it should map to a role in a
> security constraint, which may be mapped arbitrarily by the container. See,
> e.g., the javadoc for isCallerInRole/isUserInRole for
> EJBContext/HttpServletRequest.
>
>
Agreed. The draft spec text says this though:

"The isCallerInRole method takes a string argument that represents the
application role that is to be tested for. The application role is defined
as the group name as set by the HttpAuthenticationMechanism (possibly via
an IdentityStore) or a JASPIC ServerAuthModule after group to role mapping
has taken place."

The important bit here is "after group to role mapping has taken place".

(there's btw an open issue to provide a standard interface and way to get
hold of the container's group to role mapper).


Kind regards,
Arjan Tijms




>
> -
>
>
> --
> Will Hopkins | Platform Security Architect | +1.781.442.0310 <(781)%20442-0310>
> Oracle Cloud Application Foundation
> 35 Network Drive, Burlington, MA 01803
>
>