Hi guys,

I had a little time to think more about a security implementation, 
and I wanted to run some thoughts by you all for comments.  My 
goal is to get a simple but extensible implementation up and 
running.

First, I no longer think that Subject should be the class we 
propogate, for several reasons: 1. it doesn't serialize its 
credentials; 2. it would be complicated to handle multiple 
principals, e.g. in getCallerPrincipal; and 3.  it seems redundant; 
the administrator can map that principal to additional principals if 
needed (e.g. for authentication to connected resource factories).  I 
would be interested in hearing thoughts about this.  For now, I 
would add a credential member variable (of type Object) to 
MethodInvocation, JRMPContainerInvoker, etc.  This credential 
might be a password, a digital signature, or whatever.  It might also 
be a wrapper of that credential to indicate that the jboss server 
trusts the invoker about the principal and no authentication is 
necessary (e.g. for in-VM invocations, or invocations from a web 
container).

There would be two interfaces that the application server used 
during an invocation--one to check a credential (authentication) and 
one to protect a resource (authorization).

The authentication interface would be like this:

public interface org.jboss.system.SecurityManager
{
        public boolean isValid( Principal principal, Object credential );
}

This interface would be on the system classpath (like 
javax.transaction.TransactionManager for transactions).  [I propose 
to add an org.jboss.system package for interfaces that should be 
on the system classpath.  Right now I'm just dumping this package 
into run.jar.  Is there some other class loader that I should be 
using, that is a parent to all the class loaders that might need to 
use these interfaces?]  Implementations would be provided as 
MBeans, and could make judgements about validity based on the 
Principal class type, Principal name, and credential. Typically, one 
implementation would exist per security realm.

The implementations would register with a system-level 
implementation of the SecurityManager registered in the JNDI 
namespace as "SecurityManager."  This system level 
implementation would just delegate to the realm-level 
implementations to see if any would allow that the 
Principal/credential pair were valid.

The security manager would be set in the container and available to 
the security plugin as follows (in the container factory):

// Set security manager
container.setSecurityManager((SecurityManager)new 
InitialContext().lookup("SecurityManager"));

The SecurityInterceptor's first task would be to use the 
SecurityManager to authenticate the Principal, based on the 
credential available in MethodInvocation.

Next, the interface for authorization would be as follows:

public interface RealmMapping
{
        public boolean doesUserHaveRole( Principal principal, Set 
roleNames );
}

A RealmMapping maintains a relation between a list of principals, 
and a set of roles assigned to each principal.  Unlike 
SecurityManagers, RealmMappings are specific to a particular 
J2EE application.  So the relationship is J2EE app has many 
realms, a realm has many principals, a principal has many roles.  

The RealmMapping interface would be used in conjunction with the 
authorization information in the EJB 1.1 or 2.0 deployment 
descriptor.  It would also be used for the implementation of 
"isCallerInRole"; the Set of roleNames would have only one role in 
that case.

A CacheRealmMapping is a "meta-level" implementation of 
RealmMapping, that handles lists of realms for a particular J2EE 
application.  I named it "CacheRealmMapping" because eventually 
I would like to cache information about a particular principal if 
access to the persistent mapping is expensive.  Right now its 
implementation would be pretty simple:

public class CacheRealmMapping implements RealmMapping
{
        private LinkedList realms = new LinkedList();

        public void addRealmMapping( RealmMapping realmMapping )
        {
                realms.add( realmMapping );
        }

        public boolean doesUserHaveRole( Principal principal, Set 
roleNames )
        {
                Iterator iter=realms.iterator();
                while( iter.hasNext() )
                {
                        RealmMapping realmMapping = (RealmMapping) 
iter.next();
                        if (realmMapping.doesUserHaveRole( principal, 
roleNames ))
                                return true;
                }
                return false;
        }
        
}

The actual implementations of mappings would be MBeans whose 
names would be specified in the jboss container configuration.  
This would allow mappings to use various persistence 
mechanisms: an XML file saved in the EJB jar (like JAWS O/R 
mappings), or a database, and would allow the mappings to be 
edited at runtime based on any appropriate mechanism--a third 
party application, JMX, etc.  It would also allow mappings to be 
reused when appropriate, simply by specifying the same MBean in 
the container configuration.  This will help the potentially common 
case where EJBs roles and permissions are configured according 
to consistent company-wide standards.  It's possible that only a 
single mapping MBean instance would be necessary for many 
uses.  (An ASP would obviously need to register many mapping 
MBeans.  These would probably point to various database tables.)

Notice that I didn't mention JAAS in this.  It would be used for 
authentication in realm-level MBean implementations of the 
interface org.jboss.system.SecurityManager.  A single MBean 
might provide for authentication for multiple realms, with little 
programming, simply by using JAAS.  (JAAS could also be used 
on the client side.)

I'd appreciate comments that anyone might have.

-Dan





Reply via email to