Save This Page
Home » jboss-5.0.0.CR1-src » org » jboss » security » auth » spi » [javadoc | source]
    1   /*
    2    * JBoss, the OpenSource WebOS
    3    *
    4    * Distributable under LGPL license.
    5    * See terms of license at gnu.org.
    6    */
    7   package org.jboss.security.auth.spi;
    8   
    9   import java.security.acl.Group;
   10   import java.util.HashMap;
   11   import java.util.Map;
   12   import java.sql.Connection;
   13   import java.sql.PreparedStatement;
   14   import java.sql.ResultSet;
   15   import java.sql.SQLException;
   16   import javax.naming.InitialContext;
   17   import javax.naming.NamingException;
   18   import javax.sql.DataSource;
   19   import javax.security.auth.Subject;
   20   import javax.security.auth.callback.CallbackHandler;
   21   import javax.security.auth.login.LoginException;
   22   import javax.security.auth.login.FailedLoginException;
   23   
   24   import org.jboss.security.SimpleGroup;
   25   import org.jboss.security.SimplePrincipal;
   26   import org.jboss.security.auth.spi.UsernamePasswordLoginModule;
   27   
   28   /**
   29    * A JDBC based login module that supports authentication and role mapping.
   30    * It is based on two logical tables:
   31    * <ul>
   32    * <li>Principals(PrincipalID text, Password text)
   33    * <li>Roles(PrincipalID text, Role text, RoleGroup text)
   34    * </ul>
   35    * <p>
   36    * LoginModule options:
   37    * <ul>
   38    * <li><em>dsJndiName</em>: The name of the DataSource of the database containing the Principals, Roles tables
   39    * <li><em>principalsQuery</em>: The prepared statement query, equivalent to:
   40    * <pre>
   41    *    "select Password from Principals where PrincipalID=?"
   42    * </pre>
   43    * <li><em>rolesQuery</em>: The prepared statement query, equivalent to:
   44    * <pre>
   45    *    "select Role, RoleGroup from Roles where PrincipalID=?"
   46    * </pre>
   47    * </ul>
   48    *
   49    * @author <a href="mailto:on@ibis.odessa.ua">Oleg Nitz</a>
   50    * @author Scott.Stark@jboss.org
   51    * @version $Revision: 1.6.4.1 $
   52    */
   53   public class DatabaseServerLoginModule extends UsernamePasswordLoginModule
   54   {
   55      private String dsJndiName;
   56      private String principalsQuery = "select Password from Principals where PrincipalID=?";
   57      private String rolesQuery = "select Role, RoleGroup from Roles where PrincipalID=?";
   58      
   59      /**
   60       * Initialize this LoginModule.
   61       */
   62      public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options)
   63      {
   64         super.initialize(subject, callbackHandler, sharedState, options);
   65         dsJndiName = (String) options.get("dsJndiName");
   66         if( dsJndiName == null )
   67            dsJndiName = "java:/DefaultDS";
   68         Object tmp = options.get("principalsQuery");
   69         if( tmp != null )
   70            principalsQuery = tmp.toString();
   71         tmp = options.get("rolesQuery");
   72         if( tmp != null )
   73            rolesQuery = tmp.toString();
   74         log.trace("DatabaseServerLoginModule, dsJndiName="+dsJndiName);
   75         log.trace("principalsQuery="+principalsQuery);
   76         log.trace("rolesQuery="+rolesQuery);
   77      }
   78   
   79      /** Get the expected password for the current username available via
   80       * the getUsername() method. This is called from within the login()
   81       * method after the CallbackHandler has returned the username and
   82       * candidate password.
   83       * @return the valid password String
   84       */
   85      protected String getUsersPassword() throws LoginException
   86      {
   87         String username = getUsername();
   88         String password = null;
   89         Connection conn = null;
   90         PreparedStatement ps = null;
   91         
   92         try
   93         {
   94            InitialContext ctx = new InitialContext();
   95            DataSource ds = (DataSource) ctx.lookup(dsJndiName);
   96            conn = ds.getConnection();
   97            // Get the password
   98            ps = conn.prepareStatement(principalsQuery);
   99            ps.setString(1, username);
  100            ResultSet rs = ps.executeQuery();
  101            if( rs.next() == false )
  102               throw new FailedLoginException("No matching username found in Principals");
  103            
  104            password = rs.getString(1);
  105            password = convertRawPassword(password);
  106            rs.close();
  107         }
  108         catch(NamingException ex)
  109         {
  110            throw new LoginException(ex.toString(true));
  111         }
  112         catch(SQLException ex)
  113         {
  114            log.error("Query failed", ex);
  115            throw new LoginException(ex.toString());
  116         }
  117         finally
  118         {
  119            if( ps != null )
  120            {
  121               try
  122               {
  123                  ps.close();
  124               }
  125               catch(SQLException e)
  126               {}
  127            }
  128            if( conn != null )
  129            {
  130               try
  131               {
  132                  conn.close();
  133               }
  134               catch (SQLException ex)
  135               {}
  136            }
  137         }
  138         return password;
  139      }
  140   
  141      /** Overriden by subclasses to return the Groups that correspond to the
  142       to the role sets assigned to the user. Subclasses should create at
  143       least a Group named "Roles" that contains the roles assigned to the user.
  144       A second common group is "CallerPrincipal" that provides the application
  145       identity of the user rather than the security domain identity.
  146       @return Group[] containing the sets of roles
  147       */
  148      protected Group[] getRoleSets() throws LoginException
  149      {
  150         String username = getUsername();
  151         Connection conn = null;
  152         HashMap setsMap = new HashMap();
  153         PreparedStatement ps = null;
  154   
  155         try
  156         {
  157            InitialContext ctx = new InitialContext();
  158            DataSource ds = (DataSource) ctx.lookup(dsJndiName);
  159            conn = ds.getConnection();
  160            // Get the user role names
  161            ps = conn.prepareStatement(rolesQuery);
  162            try
  163            {
  164               ps.setString(1, username);
  165            }
  166            catch(ArrayIndexOutOfBoundsException ignore)
  167            {
  168               // The query may not have any parameters so just try it
  169            }
  170            ResultSet rs = ps.executeQuery();
  171            if( rs.next() == false )
  172            {
  173               if( getUnauthenticatedIdentity() == null )
  174                  throw new FailedLoginException("No matching username found in Roles");
  175               /* We are running with an unauthenticatedIdentity so create an
  176                  empty Roles set and return.
  177               */
  178               Group[] roleSets = { new SimpleGroup("Roles") };
  179               return roleSets;
  180            }
  181   
  182            do
  183            {
  184               String name = rs.getString(1);
  185               String groupName = rs.getString(2);
  186               if( groupName == null || groupName.length() == 0 )
  187                  groupName = "Roles";
  188               Group group = (Group) setsMap.get(groupName);
  189               if( group == null )
  190               {
  191                  group = new SimpleGroup(groupName);
  192                  setsMap.put(groupName, group);
  193               }
  194               group.addMember(new SimplePrincipal(name));
  195            } while( rs.next() );
  196            rs.close();
  197         }
  198         catch(NamingException ex)
  199         {
  200            throw new LoginException(ex.toString(true));
  201         }
  202         catch(SQLException ex)
  203         {
  204            super.log.error("SQL failure", ex);
  205            throw new LoginException(ex.toString());
  206         }
  207         finally
  208         {
  209            if( ps != null )
  210            {
  211               try
  212               {
  213                  ps.close();
  214               }
  215               catch(SQLException e)
  216               {}
  217            }
  218            if( conn != null )
  219            {
  220               try
  221               {
  222                  conn.close();
  223               }
  224               catch (Exception ex)
  225               {}
  226            }
  227         }
  228         
  229         Group[] roleSets = new Group[setsMap.size()];
  230         setsMap.values().toArray(roleSets);
  231         return roleSets;
  232      }
  233      
  234      /** A hook to allow subclasses to convert a password from the database
  235       into a plain text string or whatever form is used for matching against
  236       the user input. It is called from within the getUsersPassword() method.
  237       @param rawPassword, the password as obtained from the database
  238       @return the argument rawPassword
  239       */
  240      protected String convertRawPassword(String rawPassword)
  241      {
  242         return rawPassword;
  243      }
  244   }

Save This Page
Home » jboss-5.0.0.CR1-src » org » jboss » security » auth » spi » [javadoc | source]