users@javaee-security-spec.java.net

[javaee-security-spec users] [jsr375-experts] Re: Identity Store Proposal 2.0

From: Pedro Igor Silva <psilva_at_redhat.com>
Date: Mon, 22 Jun 2015 08:50:27 -0400 (EDT)

Hi Arjan,

----- Original Message -----
> From: "arjan tijms" <arjan.tijms_at_gmail.com>
> To: jsr375-experts_at_javaee-security-spec.java.net
> Cc: "Alex Kosowski" <alex.kosowski_at_oracle.com>
> Sent: Saturday, June 20, 2015 6:41:31 AM
> Subject: [jsr375-experts] Re: Identity Store Proposal 2.0
>
> Hi,
>
> I took a quick look at Pedro's code, but I'm afraid that this is perhaps
> not entirely the right direction (unless it's just a quick mock, of course)
>
> What the code is doing now is essentially how a third party framework would
> implement security, largely (or totally) by-passing the existing security
> mechanisms of Java EE.

Yeah, I did that during a quick coding session. So I just wanted to show some ideas regarding the Identity Store API and also the Security Context.

Right now the code is not considering existing security mechanisms of JavaEE and is not tied with the container. However, the implementations (eg.: DefaultSecurityContext, DefaultIdentityStore, etc) are located in a *org.eecontainer*
package, so the idea is that all that pieces of functionality would be integrated and provided by the underlying container.

I didn't want to provide more details on how that integration should work, but just about the parts related with the Identity Store and Security Context.

>
> See this code:
>
> @SessionScoped
> public class DefaultSecurityContext implements SecurityContext {
>
> private static final long serialVersionUID = -5054373464615019115L;
>
> @Inject
> private IdentityStore identityStore;
> private Caller caller;
>
> @Override
> public Caller getCaller() {
> return this.caller;
> }
>
> @Override
> public boolean isCallerInRole(String roleName) {
> return this.identityStore.hasRole(roleName, getCaller());
> }
>
> @Override
> public List<Role> getRoles() {
> if (!isAuthenticated()) {
> return Collections.emptyList();
> }
>
> return this.identityStore.getRoles(this.caller);
> }
>
> @Override
> public boolean isAuthenticated() {
> return this.caller != null;
> }
>
> @Override
> public void login(Credential credential) {
> Support support =
> this.identityStore.getCredentialSupport(credential.getClass());
>
> if (Support.SUPPORTED.equals(support)) {
> Credential validatedCredential =
> this.identityStore.verifyCredential(credential);
>
> if
> (Credential.Status.VALID.equals(validatedCredential.getStatus())) {
> this.caller =
> this.identityStore.getCaller(credential.getCaller().getName());
> }
> }
> }
>
> @Override
> public void logout() {
> this.caller = null;
> }
>
> @Override
> public void runAs(String role, Function<?, ?> function) {
> }
> }
>
> Here the identityStore is injected in the SecurityContext which is a
> session scoped bean, and directly uses it. So when login() is called, the
> result is only known in application space. In particular this means that
> security constraints in web.xml, @RolesAllowed in EJB and JACC providers
> among others all don't see this.

Yeah, you are right. Like I said, the impls are located in a specific package from a fictitious ee container. Need to think how all that could work together.

Like I said, I'm just reusing some code from my previous work around the SecurityContext. I think that would be a different discussion/thread :)

>
> Also the identity store in the code example is a pretty big thing that does
> its own role mapping and own credential selection. I think this is not
> really how all the existing identity stores work and that it can be done
> with much smaller and orthogonal pieces.
>
> The earlier example that we did in Adam's secspike project had just this as
> the identity store, based on interfaces:
>
> public interface IdentityStore extends Serializable {
>
> String getUserName();
> List<String> getApplicationRoles();
> }
>
> And a credential specific interface this:
>
> public interface UsernamePasswordIdentityStore extends IdentityStore {
>
> boolean authenticate(String username, String password);
> }
>
> A concrete implementation is then:
>
> @RequestScoped
> public class TestUsernamePasswordIdentityStore implements
> UsernamePasswordIdentityStore {
>
> private static final long serialVersionUID = 1L;
>
> @Override
> public boolean authenticate(String username, String password) {
> return true;
> }
> @Override
> public String getUserName() {
> return "testuser";
> }
> @Override
> public List<String> getApplicationRoles() {
> return asList("USER");
> }
> }
>
> See
> https://github.com/AdamBien/secspike/blob/master/todo/src/main/java/org/secspike/todo/auth/TestUsernamePasswordIdentityStore.java
>
> This is pretty close to what was requested by Reza in
> https://java.net/jira/browse/JAVAEE_SECURITY_SPEC-18
>
> Of course the exact details can be tuned. Instead of interfaces, we can
> have qualifiers (annotations) and instead of a method per credential type
> we can have the single Credential method. E.g.
>
>
> @RequestScoped
> @UsernamePasswordCredential
> public class TestUsernamePasswordIdentityStore implements IdentityStore {
>
> private static final long serialVersionUID = 1L;
>
> @Override
> public boolean authenticate(Credential credential) {
> return true;
> }
> @Override
> public String getUserName() {
> return "testuser";
> }
> @Override
> public List<String> getApplicationRoles() {
> return asList("USER");
> }
> }
>
> The main difference here is thus that the identity store as shown above is
> just a small and highly orthogonal piece of the security architecture that
> leverages existing APIs as much as possible, while in the proposal it's a
> much bigger thing that embodies a larger part of the security architecture
> internally and does more with its own APIs.

Let's take the SecurityContext discussion aside. From a pure Identity Store API perspective, how my proposal is different than yours ? It is basically encapsulating a repository such as LDAP, database or whatever ...

Another important point here is that I'm not considering credential mapping. The way I did, credential mapping is performed by the IdentityStore itself. However, just like Alex proposal, we probably need some Credential API in order to provide hooks for different credential types and make the IdentityStore even more simple and small.

Also, I don't think it is a good thing to tie a credential type with an IdentityStore. In theory, identity stores should be able to handle multiple credential types (accordingly with its capabilities). Again, here is where the Credential API kicks in.

>
> Kind regards,
> Arjan Tijms
>
>
>
>
>
>
>
>
>
>
>
>
>
> On Fri, Jun 19, 2015 at 5:30 PM, Pedro Igor Silva <psilva_at_redhat.com> wrote:
>
> > Hi,
> >
> > I've updated the project[1] I was using for JAVAEE_SECURITY_SPEC-12
> > with my view of the Identity Store API.
> >
> > I think it is pretty much aligned with what Alex did but with some
> > minor changes. This project also provides two test cases to demonstrate how
> > to use both `IdentityStore` and `SecurityContext` together to authenticate
> > users when using CDI or just the Servlet API.
> >
> > @Arjan, I think with this approach is pretty easy to provide an
> > "Identity Store Selector" based on a specific credential type given that
> > the `IdentityStore` also provides a `getCredentialSupport` method that you
> > can use to check if it supports or not a credential type.
> >
> > I've also considered a `getPermissions` method on the `IdentityStore`
> > interface. That would be very useful for Permission Mapping, providing a
> > very easy and clean way to obtain permissions (and even check them) from an
> > Identity Store.
> >
> > @Alex, as I told you, I think PL IDM API was very good for an initial
> > version. However, I think there is room for simplifications and that is
> > what I tried to achieve in this project.
> >
> > Any feedback is welcome.
> >
> > [1]
> > https://github.com/pedroigor/javaee-security-proposals/tree/identity-store-example/security-context-authentication
> >
> > Thanks.
> > Pedro Igor
> >
> > [1]
> > https://github.com/pedroigor/javaee-security-proposals/tree/identity-store-example/security-context-authentication
> >
> > ----- Original Message -----
> > From: "arjan tijms" <arjan.tijms_at_gmail.com>
> > To: "Alex Kosowski" <alex.kosowski_at_oracle.com>
> > Cc: jsr375-experts_at_javaee-security-spec.java.net
> > Sent: Friday, June 19, 2015 11:04:59 AM
> > Subject: [jsr375-experts] Re: Identity Store Proposal 2.0
> >
> > Hi,
> >
> > On Fri, Jun 19, 2015 at 2:42 PM, Alex Kosowski <alex.kosowski_at_oracle.com>
> > wrote:
> >
> > > Hi Arjan,
> > >
> > > Thank you for being so active with this JSR!
> > >
> >
> > No problem ;)
> >
> >
> >
> > > Is a credential handler really needed if CDI can already do the
> > selection?
> > >
> > > Here a generic IdententityStore is injected, which in CDI terms
> > > may look a bit unusual. Using a CDI qualifier you could inject the
> > correct
> > > type right away.
> > >
> > > Eg
> > >
> > > @Inject @UsernamePassword
> > > IdentityStore identityStore
> > >
> > > Or if it's needed to choose at runtime:
> > >
> > > CDI.current().select(IdentityStore.class, UsernamePassword);
> > >
> > > [Alex] What if we did not know the type of credential ahead of time?
> > >
> >
> > That's where the second form of CDI comes in, the CDI.current().select(...)
> > (or alternatively, the bean manager based API).
> >
> > As soon as you can do:
> >
> > Credentials creds = new UsernamePasswordCredentials("john", new
> > Password("welcome1"));
> >
> > you have the credentials type, namely "UsernamePasswordCredentials". You
> > can then ask CDI at runtime (at that moment): "Give me an IdentityStore
> > that can handle UsernamePasswordCredentials". JSF now leans very heavily at
> > this as mentioned before instead of using it's own handlers and
> > registration factories.
> >
> >
> >
> > > The registry approach enables multiple CredentialHandlers to be
> > > preconfigured to accept varied Credential types. The Credential type
> > itself
> > > would be used to select the CredentialHandler.
> > >
> >
> > That strongly sounds what CDI already provides. For this EG it would be
> > much simpler to go with that (if indeed possible, which it sounds like it
> > is), instead of having to design and implement our own APIs.
> >
> >
> >
> > > [Alex] How would we use CDI to configure different CredentialHandlers or
> > > Backend Stores for different deployments?
> > >
> >
> >
> > That's a core value of CDI actually. In fact, much of the reason to use CDI
> > in the first place is to have the ability to change dependencies based on
> > deployment type. The classical example is for unit testing, but it holds
> > for deployment stages as well. The key technology here is the @Alternative
> > qualifier and stereotypes. Via beans.xml different alternatives can be
> > activated for different deployments. There are quite a number of articles
> > about this, but here's one from Antonio:
> >
> > http://antoniogoncalves.org/2014/05/25/switch-datasource-with-cdi-alternatives-and-stereotypes
> >
> > In addition to swapping out implementations for different deployments, the
> > same mechanism can also be used to let the user provide an alternative
> > implementation to a default one that's provided by the container or a
> > library. I've written a small article that explains that usage here:
> > http://jdevelopment.nl/providing-alternatives-jsf-23s-injected-artifacts
> >
> >
> >
> > > [Alex] I know, I just picked a term. I will change the proposal based on
> > > the outcome of issue 2
> > >
> > >
> > Okay, great ;) Hopefully it will remain caller, but let's see how the EG
> > votes.
> >
> >
> >
> > > [Alex] That is what I was thinking. Would we need to request support from
> > > JASPIC for a RoleCallbackHandler for containers which distinguish groups
> > > and roles?
> > >
> > >
> > I think probably not, at least not if we're standardizing how groups and
> > roles currently work. The group to role mapper should be enough here. The
> > identity store returns a collection of "names". Those names could be seen
> > as either groups or roles. If they are logically seen as roles, then the
> > application can configure a 1:1 group to role mapping. E.g. "admin" will be
> > mapped to "admin", which "effectively" makes the GroupPrincipalCallback a
> > RolePrincipalCallback.
> >
> > If however we want to introduce new APIs so we can say "isCallerInGroup"
> > and "isCallerInRole" down the line, then such new RolePrincipalCallback
> > *may* be needed (this will require more thinking though, especially to see
> > how this will interact with group to role mapping).
> >
> > Kind regards,
> > Arjan
> >
>