dev@glassfish.java.net

Re: custom Realm implementation

From: Ashish Sahni <Ashish.Sahni_at_Sun.COM>
Date: Thu, 17 Nov 2005 08:06:21 -0800

Allen,
I'll take a look into this and get back to you.

Thanks
Ashish


Allen Gilliland wrote:

>I am trying to make use of a custom security realm which works against a
>sql database via jdbc. I have the LoginModule working properly, but for
>some reason the authorization realm is not working.
>
>My realm is defined like this and it is set as the default realm ...
>
><auth-realm classname="com.sun.wpe.jdbcrealm.JDBCRealm"
>name="wpejdbc">
> <property name="dbusername" value="roller"/>
> <property name="dbpasswd" value="roller"/>
> <property name="userpasswdcol" value="passphrase"/>
> <property name="jaas-context" value="JDBCRealm"/>
> <property name="usernamecol" value="username"/>
> <property name="usertable" value="rolleruser"/>
> <property name="grouptable" value="userrole"/>
> <property name="usergroupcol" value="rolename"/>
> <property name="dbdrivername" value="com.mysql.jdbc.Driver"/>
> <property name="dburl"
>value="jdbc:mysql://localhost:3306/roller_dev?autoReconnect=true"/>
> </auth-realm>
>
>I have all the right security constraint definitions in my web.xml file
>and the login module is defined in the login.conf file. When I access a
>protected resource in my application I am properly presented with my
>login page, then after submitting my name/password I can see in the
>server.log that the authentication succeeded. After that I simply get a
>403 response from the server.
>
>It appears as if my JDBCRealm.getGroupNames(username) method is never
>being called because I never get any debugging from that method.
>shouldn't that method be called on each request for a protected
>resource?
>
>I've attached my source files.
>
>Thanks in advance for any help.
>
>-- Allen
>
>
>
>------------------------------------------------------------------------
>
>/*
> * Copyright 2001, 2002 Sun Microsystems, Inc. All Rights Reserved.
> *
> * This software is the proprietary information of Sun Microsystems, Inc.
> * Use is subject to license terms.
> */
>
>package com.sun.wpe.jdbcrealm;
>
>import java.util.*;
>import java.io.IOException;
>
>import java.sql.Connection;
>import java.sql.PreparedStatement;
>import java.sql.ResultSet;
>import java.sql.SQLException;
>import java.sql.Driver;
>import java.sql.DriverManager;
>
>import javax.security.auth.login.LoginException;
>import com.iplanet.ias.security.auth.login.PasswordLoginModule;
>
>import com.sun.enterprise.security.auth.AuthenticationStatus;
>import com.sun.enterprise.security.auth.realm.Realm;
>
>import com.sun.wpe.jdbcrealm.JDBCRealm;
>
>
>
>/**
> * JDBCRealm login module.
> *
> * <P>This login module provides a sample implementation of a custom realm.
> * You may use this sample as a template for creating alternate custom
> * authentication realm implementations to suit your applications needs.
> *
> * <P>In order to plug in a realm into the server you need to implement
> * both a login module (as shown by this class) which performs the
> * authentication and a realm (see JDBCRealm for an example) which is used
> * to manage other realm operations.
> *
> * <P>The PasswordLoginModule class is a JAAS LoginModule and must be
> * extended by this class. PasswordLoginModule provides internal
> * implementations for all the LoginModule methods (such as login(),
> * commit()). This class should not override these methods.
> *
> * <P>This class is only required to implement the authenticate() method as
> * shown below. The following rules need to be followed in the implementation
> * of this method:
> *
> * <ul>
> * <li>Your code should obtain the user and password to authenticate from
> * _username and _password fields, respectively.
> * <li>The authenticate method must finish with this call:
> * return commitAuthentication(_username, _password, _currentRealm,
> * grpList);
> * <li>The grpList parameter is a String[] which can optionally be
> * populated to contain the list of groups this user belongs to
> * </ul>
> *
> * <P>The PasswordLoginModule, AuthenticationStatus and other classes and
> * fields referenced in the sample code should be treated as opaque
> * undocumented interfaces.
> *
> * <P>Sample setting in server.xml for JDBCLoginModule
> * <pre>
> * <auth-realm name="jdbc" classname="samples.security.jdbcrealm.JDBCRealm">
> * <property name="dbdrivername" value="com.pointbase.jdbc.jdbcUniversalDriver"/>
> * <property name="dburl" value="jdbc:pointbase:server://localhost:9092/sample"/>
> * <property name="dbusername" value="public"/>
> * <property name="dbpasswd" value="public"/>
> * <property name="usertable" value="user_tbl"/>
> * <property name="usernamecol" value="uid"/>
> * <property name="userpasswdcol" value="passwd"/>
> * <property name="usergroupcol" value="groups"/>
> * <property name="jaas-context" value="jdbcRealm"/>
> * </auth-realm>
> * </pre>
> */
>public class JDBCLoginModule extends PasswordLoginModule {
>
> static final String PARAMS_DBSOURCE = "dbdatasource";
> static final String PARAMS_DBDRIVERNAME= "dbdrivername";
> static final String PARAMS_DBURL = "dburl";
> static final String PARAMS_DBUSERNAME = "dbusername";
> static final String PARAMS_DBPASSWD = "dbpasswd";
>
> static final String PARAMS_USERTABLE = "usertable";
> static final String PARAMS_USERNAMECOL = "usernamecol";
> static final String PARAMS_USERPASSWDCOL= "userpasswdcol";
> static final String PARAMS_USERGROUPCOL = "usergroupcol";
>
> static final String PARAMS_GRPTABLE = "grouptable";
>
> static Driver _dbdriver = null;
> static Connection _dbConnection = null;
>
>
> /**
> * Perform authentication.
> */
> protected AuthenticationStatus authenticate()
> throws LoginException {
>
> if (!(_currentRealm instanceof JDBCRealm)) {
> throw new LoginException("JDBCLoginModule requires JDBCRealm.");
> }
>
> String[] grpList = this.authenticate(_username, _password);
> if (grpList == null) { // JAAS behavior
> throw new LoginException("Failed JDBC login: " + _username);
> }
>
> return commitAuthentication(_username, _password,
> _currentRealm, grpList);
> }
>
>
> /**
> * Return the user group associated with the specified username and
> * credentials, if there is one; otherwise return <code>null</code>.
> *
> * @param username the user's id
> * @param passwd the user's clear password
> */
> private String[] authenticate(String username, String passwd) {
>
> System.out.println("Trying auth for "+username);
>
> // Look up the user's credentials
> String dbCredential = null;
> Vector grps = null;
>
> JDBCRealm jdbcRealm = (JDBCRealm) _currentRealm;
> String usertable = jdbcRealm.getRealmProperty(PARAMS_USERTABLE);
> String grptable = jdbcRealm.getRealmProperty(PARAMS_GRPTABLE);
> String usernamecol = jdbcRealm.getRealmProperty(PARAMS_USERNAMECOL);
> String userpasswdcol= jdbcRealm.getRealmProperty(PARAMS_USERPASSWDCOL);
> String usergroupcol = jdbcRealm.getRealmProperty(PARAMS_USERGROUPCOL);
>
>
> // setup our sql query differently if we are using a separate
> // table for group/role associations
> String sql = null;
> if(grptable != null) {
> sql = "SELECT " + usertable+"."+userpasswdcol +
> "," + grptable+"."+usergroupcol +
> " FROM " + usertable + " LEFT JOIN " + grptable +
> " on " + usertable+"."+usernamecol+"="+grptable+"."+usernamecol +
> " WHERE " + usertable+"."+usernamecol + " =?";
>
> }
> else {
> sql = "SELECT " + userpasswdcol + "," + usergroupcol +
> " FROM " + usertable +
> " WHERE " + usernamecol + " =?";
> }
>
> System.out.println(sql);
>
> PreparedStatement ps = null;
> try {
> grps = new Vector();
> Connection dbcon = jdbcRealm.getConnection();
> ps = dbcon.prepareStatement(sql);
> ps.setString(1, username);
> ResultSet rs = ps.executeQuery();
> while (rs.next()) {
> dbCredential = rs.getString(1).trim();
> grps.add(rs.getString(2).trim());
> }
> } catch (SQLException e) {
> e.printStackTrace();
> } finally {
> try {
> ps.close();
> } catch (SQLException ignore) {
> ignore.printStackTrace();
> }
> }
>
> // convert groups to string array
> String[] dbGroups = null;
> if (grps.size()>0) {
> dbGroups = new String[grps.size()];
> grps.toArray(dbGroups);
> //jdbcRealm.setGroupNames(username,g);
> }
>
> System.out.println("grps vector is " + grps.size());
> for(int i=0; i < dbGroups.length; i++)
> System.out.println(dbGroups[i]);
>
> return dbGroups;
> }
>
>
>}
>
>
>
>------------------------------------------------------------------------
>
>/*
> * Copyright 2001, 2002 Sun Microsystems, Inc. All Rights Reserved.
> *
> * This software is the proprietary information of Sun Microsystems, Inc.
> * Use is subject to license terms.
> */
>
>package com.sun.wpe.jdbcrealm;
>
>import java.util.Properties;
>import java.util.Vector;
>import java.util.HashMap;
>
>import java.sql.Connection;
>import java.sql.Statement;
>import java.sql.ResultSet;
>import java.sql.SQLException;
>import java.sql.Driver;
>import java.sql.DriverManager;
>import javax.sql.DataSource;
>import javax.naming.InitialContext;
>
>import com.sun.enterprise.security.acl.RoleMapper;
>import com.sun.enterprise.security.auth.realm.BadRealmException;
>import com.sun.enterprise.security.auth.realm.NoSuchUserException;
>import com.sun.enterprise.security.auth.realm.NoSuchRealmException;
>import com.sun.enterprise.security.auth.realm.InvalidOperationException;
>
>import com.iplanet.ias.security.RealmConfig;
>import com.iplanet.ias.security.auth.realm.IASRealm;
>
>
>/**
> * JDBCRealm for supporting RDBMS authentication.
> *
> * <P>This login module provides a sample implementation of a custom realm.
> * You may use this sample as a template for creating alternate custom
> * authentication realm implementations to suit your applications needs.
> *
> * <P>In order to plug in a realm into the server you need to
> * implement both a login module (see JDBCLoginModule for an example)
> * which performs the authentication and a realm (as shown by this
> * class) which is used to manage other realm operations.
> *
> * <P>A custom realm should implement the following methods:
> *
> * <ul>
> * <li>init(props)
> * <li>getAuthType()
> * <li>getGroupNames(username)
> * </ul>
> *
> * <P>IASRealm and other classes and fields referenced in the sample
> * code should be treated as opaque undocumented interfaces.
> *
> */
>final public class JDBCRealm extends IASRealm {
>
> public static final String AUTH_TYPE = "JDBCRealm";
>
> private static DataSource _datasource = null;
>
> Properties _realmProperties = null;
> Vector _groups = new Vector();
>
> HashMap _groupCache = new HashMap();
> Vector _emptyVector;
>
>
> public JDBCRealm() {
> }
>
>
> /**
> * Initialize a realm. This method will be invoked once during server
> * startup.
> *
> * @param props Initialization parameters configured for this realm
> * in server.xml
> * @exception BadRealmException If the configuration parameters
> * identify a corrupt realm.
> * @exception NoSuchRealmException If the configuration parameters
> * specify a realm which doesn't exist.
> *
> */
> protected void init(Properties props)
> throws BadRealmException, NoSuchRealmException {
>
> System.out.print("JDBCRealm Init ... ");
>
> _realmProperties = props;
>
> String jaasCtx = props.getProperty(IASRealm.JAAS_CONTEXT_PARAM);
> this.setProperty(IASRealm.JAAS_CONTEXT_PARAM, jaasCtx);
>
> _emptyVector = new Vector();
>
> System.out.println("done!");
> System.out.println(_realmProperties.toString());
> }
>
>
> /**
> * Returns a short description of the kind of authentication which is
> * supported by this realm.
> *
> * @return Description of the kind of authentication that is directly
> * supported by this realm.
> */
> public String getAuthType()
> {
> return AUTH_TYPE;
> }
>
>
> /**
> * Returns the names of all the groups that this user belongs to.
> * This method is called during authorization when role mapping
> * to groups is processed. An implementation of this method can
> * cache the results for a given user or determine the membership
> * dynamically on every request, as desired.
> *
> * @param username Name of the user in this realm whose group listing
> * is needed.
> * @return Enumeration of group names (strings).
> * @exception InvalidOperationException thrown if the realm does not
> * support this operation - e.g. Certificate realm does not support
> * this operation.
> */
> public java.util.Enumeration getGroupNames (String username)
> throws InvalidOperationException, NoSuchUserException {
>
> System.out.println("looking up groups for ["+username+"]");
>
> Vector groups = new Vector();
>
> String uid = this.escape(username);
>
> String usertable = this.getRealmProperty(JDBCLoginModule.PARAMS_USERTABLE);
> String grptable = this.getRealmProperty(JDBCLoginModule.PARAMS_GRPTABLE);
> String usernamecol = this.getRealmProperty(JDBCLoginModule.PARAMS_USERNAMECOL);
> String usergroupcol = this.getRealmProperty(JDBCLoginModule.PARAMS_USERGROUPCOL);
>
>
> String sql = null;
> if(grptable != null)
> sql = "SELECT "+usergroupcol+" FROM "+grptable+" WHERE "+usernamecol+" = '"+uid+"'";
> else
> sql = "SELECT "+usergroupcol+" FROM "+usertable+" WHERE "+usernamecol+" = '"+uid+"'";
>
>
> try {
> Connection db = this.getConnection();
> Statement stmt = db.createStatement();
>
> ResultSet rs = stmt.executeQuery(sql);
>
> while(rs.next()) {
> groups.add(rs.getString(1));
> }
>
> stmt.close();
> db.close();
>
> } catch(SQLException se) {
> System.out.println(sql);
> se.printStackTrace();
> }
>
> for(int z=0; z < groups.size(); z++)
> System.out.println((String) groups.get(z));
>
> return groups.elements();
> }
>
>
> /**
> * Returns the property string for this realm defined in server.xml
> */
> public String getRealmProperty(String name) {
> return _realmProperties.getProperty(name);
> }
>
>
> /**
> * Get a connection to the db.
> **/
> protected Connection getConnection() throws SQLException {
> String ds = this.getRealmProperty(JDBCLoginModule.PARAMS_DBSOURCE);
> String driver = this.getRealmProperty(JDBCLoginModule.PARAMS_DBDRIVERNAME);
> String dburl = this.getRealmProperty(JDBCLoginModule.PARAMS_DBURL);
>
> if(_datasource == null && ds != null) {
> System.out.println("Initializing datasource "+ds);
>
> try {
> InitialContext ctx = new InitialContext();
>
> _datasource = (DataSource) ctx.lookup(ds);
>
> } catch (Exception e) {
> e.printStackTrace();
> }
> }
> else if(_datasource == null) {
> // we are not using a datasource, so do it manually
> try {
> Class.forName(driver);
> } catch(Exception e) {
> System.out.println("Couldn't load class ... "+driver);
> }
>
> Connection dbcon = DriverManager.getConnection(dburl);
>
> return (dbcon);
> }
> else {
> // Datasource already initialized ... Open a new connection
> Connection dbcon = _datasource.getConnection();
>
> return (dbcon);
> }
>
> return null;
> }
>
>
> /**
> * Escapes single quote marks(') in a String
> *
> * i.e. "it's" becomes "it\'s"
> **/
> protected String escape(String s) {
> if(s == null)
> return s;
>
> String retvalue = s;
> if ( s.indexOf ("'") != -1 ) {
>
> StringBuffer hold = new StringBuffer();
> char c;
> for ( int i = 0; i < s.length(); i++ ) {
> if ( (c=s.charAt(i)) == '\'' )
> hold.append ("''");
> else
> hold.append(c);
> }
>
> retvalue = hold.toString();
> }
>
> return retvalue;
> }
>
>
>}
>
>
>
>------------------------------------------------------------------------
>
>---------------------------------------------------------------------
>To unsubscribe, e-mail: dev-unsubscribe_at_glassfish.dev.java.net
>For additional commands, e-mail: dev-help_at_glassfish.dev.java.net
>
>