<?php
// $Id: certificatelogin.module,v 1.7 2010/08/16 19:28:14 flamingvan Exp $

/**
 * @file
 * Lets users login to drupal by certificate
 *
 * Uses a unique identifier in a certificate to
 * either create a new account for a user and log
 * them in or log them in to an existing account
 */

/**
 * Implementation of hook_help().
 */
function certificatelogin_help($path, $arg) {
  switch ($path) {
    case 'admin/help#certificatelogin':
     $output = t("<p>Do you work for an organization where certificates are used for authentication? This module makes it possible for your users to register and login by certificate. When a drupal page is accessed via https the module checks for certain environmental variables that contain the user's unique information, such as an email address. It then creates a new account if one does not exist or logs the user in if the account exists already.</p>
<p>
Features:
<ul>
<li>Present login link on access denied (403)</li><li>
Automatic account creation</li><li>
Automatic role configuration</li><li>
Current login override</li>
</ul>
</p>
<p>This module makes no major changes to your database.</p>
");
      return $output;
      break;
    case 'admin/user/certificatelogin':
      $output = t('<p>Customize your login and registration system. More help can be found <a href="!url">here</a>.</p>', array('!url' => url('admin/help/certificatelogin')));

      return $output;
  }
}

/**
 * Implementation of hook_perm().
 */
function certificatelogin_perm() {
  return array('Administer Certificate Login');
}

/**
 * Implementation of hook_menu().
 */
function certificatelogin_menu() {
$items = array();

  // Settings page.
  $items['admin/settings/certificatelogin'] = array(
    'title' => 'Certificate Login Settings',
    'description' => 'Allow users to authenticate by certificate',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('certificatelogin_admin_settings'),
    'access arguments' => array('Administer Certificate Login'),
    'type' => MENU_NORMAL_ITEM
  );    

// Display the login form on a specified page
  $items['login'] = array(
    'access callback' => TRUE,
    'page callback' => 'certificatelogin_login',
    'title' => 'Certificate login',
    'type' => MENU_CALLBACK
  ); 

// Callback for handling access denied redirection
  $items['certificatelogin/denied'] = array(
    'page callback' => 'certificatelogin_denied',
    'access callback' => TRUE,
    'title' => 'Access denied',
    'type' => MENU_CALLBACK
  ); 

//JMP
   $items['user/%user/certificatelogin'] = array(
         'title' => 'Certificate login',
         'page callback' => 'certificatelogin_auth_user_identities',
         'page arguments' => array(1),
         'access callback' => 'certificatelogin_auth_access_user',
         'access arguments' => array(1),
         'type' => MENU_LOCAL_TASK,
   );
//JMP
   $items['user/%user/certificatelogin/delete'] = array(
         'title' => 'Delete Certificate',
         'page callback' => 'certificatelogin_auth_user_delete',
         'page arguments' => array(1),
         'access callback' => 'certificatelogin_auth_access_user',
         'access arguments' => array(1),
         'type' => MENU_CALLBACK
         //'file' => 'tracker.pages.inc',
   );
  return $items;
}

//JMP
//Check access to user/cacert
function certificatelogin_auth_access_user($account) {
   $access = (user_access('administer users') || $GLOBALS['user']->uid == $account->uid);
   return (bool) $access;
}

//JMP
function certificatelogin_auth_user_identities($account) {
   $header = array(t('Certificates'), t('Actions'));
   $rows = array();

   $result = db_query("SELECT * FROM {authmap} WHERE module='certificatelogin' AND uid=%d", $account->uid);
   while ($identity = db_fetch_object($result)) {
     list($uid) = explode('@', $identity->authname);
     $rows[] = array(
             $uid, 
             l(t('Delete'), 'user/'. $account->uid .'/certificatelogin/delete/'. $identity->aid)
         );
   }

   $output = theme('table', $header, $rows);

   return $output;
}

//JMP

function certificatelogin_auth_user_delete($account, $aid = 0) {
  db_query("DELETE FROM {authmap} WHERE uid=%d AND aid=%d AND module='certificatelogin'", $account->uid, $aid);
  if (db_affected_rows()) {
   drupal_set_message(t('Certificate Login association deleted.'));
  }
  drupal_goto('user/'. $account->uid .'/certificatelogin');
}

/**
 * Define the login page 
 */
function certificatelogin_login() {
  if(variable_get('certificatelogin_enabled', 0) == 1){
  $output = t(variable_get ('certificatelogin_loginmessage', '')).'<br />';
  $output .= drupal_get_form('certificate_login_form');
  } else {
    drupal_set_message(t('Certificate login is not enabled. Please contact the webmaster for more details.'));
  }
  return $output;
}

/**
 * Define the login form
 */
function certificate_login_form() {
  $form['#action'] = 'https://'.$_SERVER['HTTP_HOST'].'/'.$_SERVER['REQUEST_URI']; 
	$form['submit'] = array(
		  '#type' => 'submit',
		  '#value' => t('Login'));

  return $form;
}

/**
 * Define the settings form
 */

function certificatelogin_admin_settings(&$form_state) {

  $form['certificatelogin'] = array(
    '#type' => 'fieldset',
    '#title' => t('Certificate Login Settings.')
  );

  $form['certificatelogin']['certificatelogin_enabled'] = array(
    '#type' => 'radios',
    '#title' => t('Enable/Disable'),
    '#options' => array('1' => t('Enabled'), '0' => t('Disabled')),
    '#default_value' => variable_get('certificatelogin_enabled', 0),
    '#description' => t('Enable the Certificate Login module. Users will be able to login by visiting the \login path')
  );

/*
  $form['certificatelogin']['certificatelogin_loginpath'] = array(
    '#type' => 'textfield',
    '#title' => t('Automatic Login Path'),
    '#default_value' => variable_get('certificatelogin_loginpath', ''),
    '#description' => t('Path for automatic authentication.  The URL must be accessed with HTTPS. If the field is left blank and the module is enabled then accessing any HTTPS will attempt a login.')
  );
*/

  $form['certificatelogin']['certificatelogin_accountcreation'] = array(
    '#type' => 'radios',
    '#title' => t('Account Creation'),
    '#options' => array('1' => t('Enabled'), '0' => t('Disabled')),
    '#default_value' => variable_get('certificatelogin_accountcreation', 0),
    '#description' => t('Automatically create accounts when they do not exist.  Account names must be unique so using email addresses is a good idea!')
  );

  $form['certificatelogin']['certificatelogin_role'] = array(
    '#type' => 'select',
    '#title' => t('Account Creation Role'),
    '#options' => array(user_roles()),
    '#default_value' => variable_get('certificatelogin_role', '1'),
    '#description' => t('When a new account is created the user will be automatically assigned to this role.')
  );

  $form['certificatelogin']['certificatelogin_loginoverride'] = array(
    '#type' => 'radios',
    '#title' => t('Current Login Override'),
    '#options' => array('1' => t('Enabled'), '0' => t('Disabled')),
    '#default_value' => variable_get('certificatelogin_loginoverride', 0),
    '#description' => t('If the user is currently logged in and this option is enabled they will be logged out and logged in as the user corresponding to their certificate.  If it is disabled they will only be logged in if they are not currently logged in.')
  );

  $site403 = variable_get('site_403', '');
  if ($site403 == 'certificatelogin/denied'){
    $disabled = '';
  }
  else {
    $disabled = $site403;
  }
  $options = array($disabled => 'disabled', 'certificatelogin/denied' => 'enabled');

  $form['certificatelogin']['site_403'] = array(
    '#type' => 'radios',
    '#title' => t('Present login link on access denied (403)'),
    '#options' => array('certificatelogin/denied' => t('Enabled'), '0' => t('Disabled')),
    '#default_value' => $site403,
    '#description' => t('Anonymous users will be presented with a login link along with an access denied message.')
  );

  $form['certificatelogin']['certificatelogin_logintitle'] = array(
    '#type' => 'textfield',
    '#title' => t('Custom login title'),
    '#default_value' => variable_get('certificatelogin_logintitle', 'Login using certificates'),
    '#description' => t('Display a custom title on login page')
  );

  $form['certificatelogin']['certificatelogin_loginmessage'] = array(
    '#type' => 'textarea',
    '#title' => t('Custom login message'),
    '#default_value' => variable_get('certificatelogin_loginmessage', 'To connect you will need a valid X.509 certificate.'),
    '#description' => t('Display a custom message on login page')
  );

  $form['certificatelogin']['certificatelogin_message'] = array(
    '#type' => 'textfield',
    '#title' => t('Access Denied Custom Message'),
    '#default_value' => variable_get('certificatelogin_message', ''),
    '#description' => t('Display a custom message on access denied pages.')
  );



  $form['array_filter'] = array('#type' => 'hidden');
  return system_settings_form($form);
}

/**
 * Force https to attempt login 
 */
function certificate_login_form_submit($form_id, $form_values){
  //if https is enabled

  if (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on')){
   //set some variables
    GLOBAL $user;
    $loginSuccess = 0; 
    //the username from the variable set under admin

		if($_SERVER['SSL_CLIENT_VERIFY'] == "SUCCESS") 
      {
			if($_SERVER['SSL_SERVER_I_DN_OU'] == "http://www.CAcert.org")
			{
        $authid=$_SERVER["SSL_CLIENT_S_DN_Email_1"];
        $authname=$_SERVER["SSL_CLIENT_S_DN_CN"];
        if ($authname=="CAcert WoT User") $authname = md5($authid)." (change me)";
        $authemail=$_SERVER["SSL_CLIENT_S_DN_Email"];
			}
    }

    if( $user->uid != 0 && variable_get('certificatelogin_loginoverride', 0) == 0) 
      {
        // The user is logged in and the override is disabled
        drupal_set_message('A login was attempted but failed because you are already logged in as '.$user->name.'.', 'error');
        return drupal_goto('<front>');     
      }
    if( $user->uid == 0 || variable_get('certificatelogin_loginoverride', 0) == 1)
      { 
      //if the user is not logged in or the override is enabled 
      //determine if the account exists
      //$query = "SELECT uid FROM {users} WHERE mail ILIKE '".$authemail."'";
      $query = "SELECT uid FROM {authmap} WHERE module='certificatelogin' AND authname = $authid";
      $userSearch = db_fetch_object(db_query($query));
      if($userSearch->uid == 0 && variable_get('certificatelogin_accountcreation', '') == 1){  //if the account does not exist then create the account and login (if enabled)
        $password = user_password();
        $roles = variable_get('certificatelogin_role', '2');
        $roleSet[$roles] = $roles; 
        $userArray = array('name' => $authname, 'pass' => $password, 'status' => 1, 'mail' => $authemail, 'authname_certificatelogin' => $authid,  'roles' => $roleSet );
        $newAccountSuccess = user_save($account, $userArray); 
        if(!newAccountSuccess){
          drupal_set_message("A new account could not be created for $authuser", 'error');
          return drupal_goto('<front>');
        }
        else{
          drupal_set_message("A new account has been created for $authuser with username $authid. It is recommended to later change your username.", 'status');
          $query = "SELECT uid FROM {authmap} WHERE module='certificatelogin' AND authname = $authid";
          $userSearch = db_fetch_object(db_query($query));
        }
      }
      if($userSearch->uid == 0 && variable_get('certificatelogin_accountcreation', '') == 0){  //if the account does not exist and cannot be created then return
        drupal_set_message('A login was attempted but failed because an account does not exist.  You are currently an anonymous user.', 'error');
        return drupal_goto('<front>');    
      }
      //now log in
      $userID = $userSearch;
      $account = user_load(array('uid' => $userSearch->uid, 'status' => 1));
      if ($account != false) {
        $user = $account;
        $loginSuccess = 1;
      }
    }
    if($loginSuccess){
        drupal_set_message('You have been automatically logged in as '.$user->name, 'status');
				return drupal_goto('<front>');  
    }
    else{
      drupal_set_message('A login was attempted but failed.  You are currently an anonymous user', 'error');
			return drupal_goto('<front>');  
    }
  }
}


/**
 * Implementation of hook_form_alter()
 *
 */

//function certificatelogin_form_alter($form_id, &$form) {
// JMP

function certificatelogin_form_alter($form_id, &$form) {
  switch ($form_id) {
    case 'system_modules':
      $form['#validate']['certificatelogin_site_403_validate'] = array();
      break;
  }
}

/**
 * Resets the the site 403 variable to the default if the module is disabled
 * and the toboggan redirect on access denied is enabled.
 */
function certificatelogin_site_403_validate($form_id, $form_values) {
  // The check order is important:
  //   1. Is the module disabled?  Skip if so.
  //   2. Has the module been selected to be disabled?
  //   3. Is the current site 403 toboggan/denied?
  // Only reset the site 403 variable if 2 and 3 are true.
  if (!isset($form_values['disabled_modules']['certificatelogin']) && !$form_values['status']['certificatelogin'] && (variable_get('site_403', '') == 'certificatelogin/denied')) {
  }
    variable_set('site_403', '');
}

function certificatelogin_denied() {
  global $user;
  if ($user->uid == 0 && variable_get('certificatelogin_enabled', '0')) {

    global $certificatelogin_denied;
    $certificatelogin_denied = TRUE;

    // build the user menu item as the 403 page content, adjust the page title appropriately, and warn
    // the user that they were denied access.
    menu_set_active_item(variable_get('certificatelogin_loginpath', ''));
    $return = menu_execute_active_handler();
    drupal_set_title(t('Access Denied'));
    drupal_set_message(variable_get('certificatelogin_message', ''), 'error');
  }
  else {
    drupal_set_title(t('Access Denied'));
    $return = theme('lt_access_denied');
  }
  return $return;

function _user_has_role($role, $user = NULL) {
  if ($user == NULL) {
    global $user;
  }

  if (is_array($user->roles) && in_array($role, array_values($user->roles))) {
    return TRUE;
  }

  return FALSE;
}
}
