Hi guys (sorry for the long post),

The security framework that I added is waiting for a couple of small
meta-data fixes, but otherwise is ready for comment.  I wanted to
make clear that this is still a �proposal.�  In other words, I wanted
to give everyone something concrete to try and discuss, and I�m
anxious to hear ideas about how it can be done better. (Of course,
we�re also looking for volunteers to actually do it, but let's wait a
week or so to see what people think. Then we'll open up a specific
list with projects and volunteers in Bugzilla.)  This is a long-ish e-
mail, so I�ll mention the topics I discuss:

1. Associating principal and credential with method call
2. Authentication
3. Access control
4. Some miscellaneous stuff
5. Trying it out

********************************************************************
*** Associating principal and credential with method call ***

The client provides a principal and credential by setting static
variables in org.jboss.system.SecurityAssociation. This is a
flexible approach that allows us to provide a container that uses
JAAS to obtain the principal/credential and set it behind the
scenes, or allows the user to write his or her own interface.  Here
is the simplest example of usage:

import org.jboss.system.SecurityAssociation;
import org.jboss.system.SimplePrincipal;

public class HelloClient
{
  public static void main(String[] args)
  {
    // logon
    SecurityAssociation.setPrincipal( new SimplePrincipal(
"TheRole" ) );
    SecurityAssociation.setCredential( "TheRole" );

TODO: this class also needs to have a �per thread� model like that
implemented in org.jboss.tm.TxManager, for propagation of the
principal and credential within a server. Also, it needs adapters that
will set the thread local variables for other containers�such as web
containers, especially Tomcat--based on their security. Also it
needs to have adapters that will extract security information from
secure communication layers (e.g. SSL).

TOFIX: where in the classpath hierarchy should the principal and
credential go? (Right now I cheated and put SimplePrincipal on the
system classpath for everyone with which to experiment.)

TOCONSIDER: Java 1.4 RMI-based approach.

********************************************************************
*** Authentication ***

The authentication module can be any class that implements the
following interface (org.jboss.system.EJBSecurityManager):

public interface EJBSecurityManager
{
        public boolean isValid( Principal principal, Object credential );
}

The implementation of this interface should be provided by an
MLET and registered at an arbitrary location in the JNDI
namespace.  The idea is that you can configure which security
manager to use on a per-container basis by specifying the JNDI
name of the security manager for each bean (although I haven�t
added this yet).  Right now the hard-coded security manager is just
a demo; it checks to see if the password is the same as the user
name.  Note that without a secure communication layer, you must
re-authenticate on every method call�AND right now I am sending
the credential in-the-clear:

public class EJBSecurityManagerDefaultImpl implements
EJBSecurityManager
{
        public boolean isValid( Principal principal, Object credential )
        {
                return principal.getName().equals( credential.toString() );
        }
}

(Along with org.jboss.security.EJBSecurityManagerDefaultImpl, the
following two classes are necessary for this default
implementation: org.jboss.security.EJBSecurityManagerService
and org.jboss.security. EJBSecurityManagerServiceMBean. These
three are intended as a model for more realistic implementations.)

TODO: Integrate secure communication into jBoss (e.g. SSL).
Add the ability to choose a security manager per-container.  Add a
security manager that will call other security managers, so that
more than one may be consulted.  Add various security managers
for different authentication systems, ESPECIALLY one that will call
JAAS to perform authentication.  Also, provide a jBoss �home-
grown� security manager that will store its information in a
database, and allow users and passwords to be added dynamically
(via JMX?).  Add an authentication interface to the jBoss
application server for this �home-grown� security manager, which
should include a JAAS module.  Add support for trust relationships,
so that authentication need not be performed on every method call.

********************************************************************
*** Access control ***

Once a principal has been authenticated, the user�s right to access
functionality must next be considered. Principals are mapped to
specific roles (from the ejb-jar.xml file) in an EJB container-specific
manner.  The mapping might be stored in a text file, in a database,
in an LDAP directory, etc.  The container might use different
strategies to cache the mapping once accessed from the
underlying store.  These details are hidden from the EJB container
via the following interface (org.jboss.system.RealmMapping):

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

RealmMapping mirrors EJBSecurityManager in how it is used:
implementation as an MLET; instance stored in the JNDI
namespace; configured on a per-container basis.  The default
implementation removes the indirection between the role name and
the principal name, in keeping with our philosophy that we�ll do this
for you in the default case where possible (e.g. with bean names
being used as JNDI names, or a JDBC connection being mapped
to a resource reference if it�s the only one set up.)  Here is the
default implementation:

public class SimpleRealmMapping implements RealmMapping
{

        public boolean doesUserHaveRole( Principal principal, Set
roleNames )
        {
    Iterator iter = roleNames.iterator();
    while (iter.hasNext())
    {
      String roleName = (String) iter.next();
      if (principal.getName().equals( roleName ))
        return true;
    }
    return false;
        }

}

TODO: Add the ability to choose a realm mapping per-container.
Add a realm mapping that will call other realm mappings, so that
more than one may be consulted.  Add various realm mapping
implementations, such as file-based and database-based, with the
ability to allow users to be associated with EJB roles dynamically
(via JMX?). Deal intelligently with a null principal by allowing the
user to specify methods for which all users are allowed (e.g. by
allowing the definition of a special role  for a container, which maps
to the null principal).

********************************************************************
*** Miscellaneous  ***

TODOs

Add logging on authentication or authorization violations.  Add
isCallerInRole and getCallerPrincipal support.  (Note that there is a
level of indirection between the role used in isCallerInRole and the
role declared in the deployment descriptor.)  Add support for
mapping principals (in EJB 2.0 this is the runAs-specified-identity,
soon to become run-as-specified-identity).

FIXMEs

Should I change the RemoteExceptions I�m throwing to
java.security.Exception?  I have to check�

ISSUES

JAAS uses Subject as the basic unit of identification, which has a
one-to-many relationship with Principals and credentials.  How to
resolve when more than one subject or principal is available?

How should we integrate with CORBA/IIOP?


********************************************************************
*** Trying it out ***

If you set a principal in org.jboss.system.SecurityAssociation, the
security will be turned on (although it won�t be propagated right now
in bean-to-bean calls.)  For the principal, use the type
org.jboss.system.SimplePrincipal until the classpath issues are
worked out.  The roles defined in your ejb-jar.xml deployment
descriptor will be mapped directly onto principals of the same
name, and the �credential� you need to set is just a java.lang.String
with the same content as your SimplePrincipal�s name.  Here
again is how the client might look (everything else is the same; just
get a reference to the bean and call your methods):

package jtest.helloworld;

import javax.ejb.*;
import javax.naming.*;
import java.rmi.RemoteException;
import javax.rmi.PortableRemoteObject;
import java.util.Properties;

import org.jboss.system.SecurityAssociation;
import org.jboss.system.SimplePrincipal;

public class HelloClient
{
  public static void main(String[] args)
  {
    // logon
    SecurityAssociation.setPrincipal( new SimplePrincipal(
"TheRole" ) );
    SecurityAssociation.setCredential( "TheRole" );

Here is a sample deployment descriptor assembly element with
some roles and method permissions.  Note that these roles will
correspond one-to-one with Principal names (in this case,
�TheRole� and �TheOtherRole�), which removes one layer of
indirection normally present in EJB:

<assembly-descriptor>

    <container-transaction>
      <method>
         <ejb-name>HelloBean</ejb-name>
         <method-name>*</method-name>
      </method>
      <trans-attribute>Required</trans-attribute>
    </container-transaction>

        <security-role>
                <role-name>TheRole</role-name>
        </security-role>
        <security-role>
                <role-name>TheOtherRole</role-name>
        </security-role>

        <method-permission>
                <role-name>TheRole</role-name>
                <role-name>TheOtherRole</role-name>
                <method>
                        <ejb-name>HelloBean</ejb-name>
                        <method-intf>Home</method-intf>
                        <method-name>*</method-name>
                </method>
        </method-permission>

        <method-permission>
                <role-name>TheRole</role-name>
                <method>
                        <ejb-name>HelloBean</ejb-name>
                        <method-name>getHelloString1</method-name>
                </method>
                <method>
                        <ejb-name>HelloBean</ejb-name>
                        <method-name>getHelloString2</method-name>
                        <method-params>
                                <method-param>java.lang.String</method-param>
                        </method-params>
                </method>
        </method-permission>

        <method-permission>
                <role-name>TheOtherRole</role-name>
                <method>
                        <ejb-name>HelloBean</ejb-name>
                        <method-name>getHelloString2</method-name>
                </method>
        </method-permission>

  </assembly-descriptor>

Please note, finally, that until a few details are worked out with
meta-data, method permissions below the level of an interface (i.e.
method-name more specific than *) only work on my machine. :-)

-Dan



Reply via email to