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


}