/*
 * 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;
  }


}