Hi Arjan,
Responses inline.
Thanks,
Will
On 02/01/2017 04:07 PM, arjan tijms wrote:
> Hi,
>
> On Wed, Feb 1, 2017 at 8:05 PM, Will Hopkins <will.hopkins_at_oracle.com
> <mailto: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.
The wording in section 13.6.5 of Servlet 3.1 says "should" and
"recommend", not "must", WRT other authentication mechanisms and JASPIC.
Leaving that aside, though, it doesn't specify the integration; i.e.,
there is no defined relationship between JASPIC and the authentication
mechanisms defined by Servlet 3.1 (BASIC, FORM, etc.). Integration with
the container is exclusively defined by the Servlet Container Profile in
JASPIC, which makes no reference to the standard authentication
mechanisms defined by Servlet 3.1, or to the web.xml declarations that
trigger them. Instead, JASPIC defines its own programmatic configuration
mechanism, distinct from, and orthogonal to, any Servlet-based
authentication configuration. In my mind, the Servlet-defined mechanisms
and the JASPIC mechanism exist separately and side-by-side, and there
are some gray areas as to which mechanism would take precedence if both
were configured.
That's not really the concern I had about HttpAuthenticationMechanism,
though. Section 3.5 of the Security Spec draft says this:
"All of these three methods must be called right after a Servlet
container calls the similarly named methods on a ServerAuthModule
following the Servlet Container Profile of the JASPIC spec."
I read that as implying that HttpAuthenticationMechanism and JASPIC
exist side-by-side; i.e., that, if both were configured, the servlet
container would have to invoke both mechanisms, one after the other.
That doesn't seem like a viable approach, since they would almost
certainly interfere with each other; my suggestion would have been to
specify that one or the other, but not both, could be active in any
given context, and define how the servlet container should determine
which to use.
Having read your subsequent comments, I now believe that you intend
HttpAuthenticationMechanism to be invoked as part of JASPIC processing
(because it's wrapped in a ServerAuthModule), and not in addition to
JASPIC processing. That makes a lot more sense.
Given that, I wonder if we should define what containers must do to
enable that -- i.e., provide a default ServerAuthModule (and associated
AuthConfig objects, etc.), configure the SAM with the configured
HttpAuthenticationMechanism, and register the whole thing with the
AuthConfigFactory so that it is invoked by the container at runtime?
Something like JASPIC's LoginModule Bridge Profile, but for
HttpAuthenticationMechanisms.
> * 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.
Yes, but application code can call SecurityContext.authenticate(), which
section 4.4 says, "... responds as-if the caller had attempted to access
a constrained resource and responds with invoking a configured
authentication mechanism (such as the HttpAuthenticationMechanism)."
On closer reading, though, the key phrase is, "as if the caller had
attempted to access a constrained resource", which is consistent with
Servlet 3.1, section 13.3, "The authenticate methods allow an
application to instigate authentication of the request caller by the
container from within an unconstrained request context."
The important part there, which I think we should clarify in the
Security Spec, is that authenticate() is only defined if the user is not
already authenticated; i.e., in the context of an unconstrained request
context. It should do nothing, or return an error, if called when the
user is already authenticated.
> * 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.
OK. In the case of the getCallerPrincipal() and isCallerInRole()
methods, though, that doesn't work as well, because we might be in
servlet context, or EJB context, or some other context. Also, the
implication in the spec is that we're superceding/standardizing the
corresponding calls in other containers, which would imply that the
legacy container calls should piggy-back on the new mechanism.
There are ways to solve this problem -- a context stack similar to the
Subject stack, a properly-scoped CDI context, etc. -- we just need to
pick one.
> * 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
> <mailto: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.
I'm not sure we can always expect names that are as unique as email
addresses -- often, they are just usernames, like "joe" or "whopkins".
In addition to the ambiguity problem -- which I think is significant --
I think there's also a problem in that the API would enable unprivileged
code to query group memberships for arbitrary users -- all I need to do
is construct a principal with the name I want to query, then invoke the
interface. Is there anything that would prevent an application from
doing that?
> * 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.
I had in mind something considerably simpler than what JSR 351 was
considering -- a new AttributePrincipal type, or perhaps a Credential
rather than a Principal. The trouble with using Principal is that, for
attributes, we need both name and value, but Principal only defines
Name. We'd need to extend the Principal interface for this use case --
or define a single, formatted String with an "=" in the middle, which
the user would have to parse after calling getName(). The problem with
using a non-Principal type and keeping it in one of the Credential sets
is that credentials aren't serialized when a Subject is serialized.
This is something we could consider, that might make IdentityStore more
useful, but I don't have strong feelings about it, and we're probably
better off keeping things simple.
> 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.
I'm OK relying only on CDI.
> 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.
Agree; JASPIC took another approach -- an application that registers an
AuthConfig gets what it wants; the container can't legally override that.
> 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 <mailto: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
> <http://foo@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.
But if the spec didn't know, that implies non-public contracts between
IdentityStores and HttpAuthenticationMechanisms -- a nightmare for
interoperability.
Could we say that we only support configuration of a single
IdentityStore, and if there are multiple real-life stores that must be
aggregated, the aggregation is done within the configured IdentityStore
implementation? That ensures that the complexity is managed by the
entity that understands it.
I still have strong reservations above a method that returns groups
based on an input Principal. I think it's much safer to only return
groups as part of an authentication operation, because then: 1) you can
be sure the returned groups are associated with the authenticated
identity; and, 2) you only return groups to a caller who has presented
proof of the right to access them (i.e., the login credential).
> 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.
Agreed.
> * 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.
Yes. We could also look to provide a generic way of specifying
parameters that we can't anticipate.
> 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.
OK, makes sense. I wonder if it would be better to say that aggregation
must be provided by an IdentityStore implementation, rather than the
framework. We could probably define some additional flags similar to
JAAS that would provide flexibility in how providers were aggregated.
But that doesn't address the security issue of returning groups to a
caller that presents only a Principal as a credential.
> 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.
Ah, OK. I misread that.
> 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.
FORM itself would normally be supplied by the container, and most other
schemes would presumably -- like JASPIC modules -- interact only with
system APIs/SPIs. But I agree, there could be some applications with an
integrated HAM that would not work with a different HAM.
We could warn people about portability/interoperability issues with
writing HAMs like that.
> * 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
As noted above, I misunderstood the intended integration, and thought
HAM and JASPIC might both be configured, and invoked serially.
> * 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.
So we'll need to define these as part of the spec ...
>
>
> 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.
Right. It would either need to maintain it's own context -- which the
legacy calls delegated to -- or figure out what container it was in and
delegate to that.
> 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
Could we not leave the exact mechanism up to the containers, as is
currently the case for, e.g., HttpServletRequest.isUserInRole()? I'm
reluctant to rely on JACC because, as you point out, it's not always
present/enabled. It seems easier to just make the language more general
-- i.e., remove the reference to groups, as in the HttpServletRequest
javadoc -- and let the container figure it out, since all containers
must already be able to do so.
> 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).
JACC seems sub-optimal; we could declare that SecurityContext is the new
source of truth, and containers must ensure it's kept current. That
implies lots of touch-points for container integration, though.
>
> 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.
While I think JASPIC is a natural implementation for those mechanisms, I
don't think mandating an implementation makes sense -- presumably, the
externally-visible behavior would not change, so containers should be
free to do what they like. I was just making sure I understood the
implications of the statement.
Was there a reason for specifying the ability to downcast? For container
implementations I'm familiar with, that's automatically true, because
the container simply returns the same principal that was created by the
ATN mechanism. Have you had cases where doing this was necessary, but
not possible?
>
> * 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).
Even with that, I believe the language is too restrictive. AFAIK, there
is no requirement for roles to be represented by groups, even after
mapping. In the case of WebLogic (and assuming JACC is not in use), role
mapping is done dynamically at runtime, and roles are distinct from
groups. Roles are mapped, using a proprietary descriptor, to either user
or group principals, and, at runtime, the existence of a mapped
principal implies the role -- but the role is not, itself, a group, and
is never represented in the Subject by a principal of any time.
>
>
> Kind regards,
> Arjan Tijms
>
>
> *
>
>
>
>
> --
> Will Hopkins | Platform Security Architect |+1.781.442.0310 <tel:%28781%29%20442-0310>
> Oracle Cloud Application Foundation
> 35 Network Drive, Burlington, MA 01803
>
>
--
Will Hopkins | WebLogic Security Architect | +1.781.442.0310
Oracle Application Development
35 Network Drive, Burlington, MA 01803