0 Replies Latest reply on Apr 26, 2004 1:42 PM by Ken Hohl

    how to implement custom security manager

    Ken Hohl Newbie

      I recently implemented a custom security manager and had to figure out a lot of things on my own to do so due to insufficient/incorrect information in CHAPTER 8 "Security on JBoss - J2EE Security Configuration and Architecture" of "JBoss Administration and Development Third Edition (3.2.x Series)" and these forums. Now that it's functional, I wanted to post my findings here for a couple of reasons. Firstly, to validate them, but also to make this information available to others. Hopefully, this information will make it into a future revision of the documentation.

      Some background - a custom security manager is needed in cases where unconventional EJB and servlet access restrictions are required. In my case, we need to authorize access using a legacy system. Also, the set of authorized users for given EJB is very dynamic. Only users who have first called an unrestricted EJB, which logs them into the legacy system, are allowed to call other EJBs.

      The task requires implementing the necessary security interfaces (in the correct way), and several other undocumented methods and then configuring JBoss to use it.

      First off, the custom manager must implement org.jboss.security.SubjectSecurityManager, org.jboss.security.RealmMapping, and Serializable. It's not sufficient to implement org.jboss.security.AuthenticationManager instead of SubjectSecurityManager because the additional interfaces in SubjectSecurityManager will be called.

      The custom manager must have a constructor with the following signature, which will be called -
      public CustomSecurityManger(String name, javax.security.auth.callback.CallbackHandler handler);
      If your application doesn't use JAAS, it's ok to ignore the 'handler' parameter.

      It must also have a method with the following signature, which will be called -
      public void setCachePolicy(CachePolicy domainCache);

      Since it's likely the custom manager will be dealing with Principals and Groups, it will be necessary to also implement java.security.Principal and java.security.acl.Group. It's likely appropriate for the implementation of Group to extend the implementation of Principal. Don't be tempted to implement equals() and hashCode() in the Group implementation or calls to equals() that should succeed may fail. These methods only need be implemented in the implementation of Principal.

      Now, since it's unlikely the custom manager will take over full responsibility for security of everything running in the server, it will have to delegate those duties to the standard org.jboss.security.plugins.JaasSecurityManager. To delegate to JaasSecurityManager, instantiate an instance from the custom manager constructor and call methods on it from anyplace in the custom manager where the limits of the custom manager's knowledge has been reached. For example, my custom manager doesn't deal with CachePolicies so it just calls JaasSecurityManager.setCachePolicy, forwarding on all incoming parameters, whenever that method is called.

      Regarding the constructor - JBoss will call it several times during startup. If the semantics of the custom manager is to be a singleton, take the usual steps to make it so (i.e. declare all data members as static and only initialize them once). However, even if the custom manager is a singleton, still instantiate a new instance of JaasSecurityManager from the constructor each time it's called for delegation use. Also, the reason Serializable is listed as an implemented interface is because JBoss will lookup instances of the custom manager in JNDI, so bind it to a JNDI name in the constructor.

      Tips on implementing the key methods -

      public Principal getPrincipal(Principal p) // defined by RealmMapping

      It may be sufficient (it was in my case) to just return p.

      public boolean isValid(Principal principal, Object credential) // defined by AuthenticationManager

      This will be called with either or both parameters null, or the name inside Principal null. Delegate such calls if the custom realm doesn't allow that.
      The actual type of the credential parameter is char[].
      Whenever returning true, update a 1:1 map of thread names to current Subject objects which is used by getActiveSubject().

      public Subject getActiveSubject() // defined by SubjectSecurityManager

      Return found entries from map modified by isValid() and return results of delegated call when no entry for current thread name exists.

      public boolean isValid(Principal principal, Object credential, Subject activeSubject) // defined by SubjectSecurityManager

      It may be sufficient (it was in my case) to just delegate this call.

      public void setCachePolicy(CachePolicy domainCache)

      It may be sufficient (it was in my case) to just delegate this call.

      public boolean doesUserHaveRole(Principal p, Set roles) // defined by RealmMapping

      The roles parameter may contain an instance of org.jboss.security.AnybodyPrincipal in which case true should be returned.

      If the application associated with the custom manager uses JMS, Subjects must contain in their set of Principals a Group named "Roles" containing a Group member named "subscriber".

      To configure the custom manager, change value of "SecurityManagerClassname" attribute of "JaasSecurityManagerService" mbean in jboss-service.xml similar to this -