Merge pull request #30 from lightswitch05/master

Added optional LDAP authentication
This commit is contained in:
Claude 2012-10-01 12:02:39 -07:00
commit 84d3dbc757
8 changed files with 500 additions and 1 deletions

View File

@ -0,0 +1,59 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/*
* This file is part of Auth_Ldap.
Auth_Ldap is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Auth_Ldap is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Auth_Ldap. If not, see <http://www.gnu.org/licenses/>.
*
*/
/**
* @author Greg Wojtak <gwojtak@techrockdo.com>
* @copyright Copyright © 2010,2011 by Greg Wojtak <gwojtak@techrockdo.com>
* @package Auth_Ldap
* @subpackage configuration
* @license GNU Lesser General Public License
*/
/**
* Array Index - Usage
* hosts - Array of ldap servers to try to authenticate against
* ports - The remote port on the ldap server to connect to
* basedn - The base dn of your ldap data store
* login_attribute - LDAP attribute used to check usernames against
* proxy_user - Distinguised name of a proxy user if your LDAP server does not allow anonymous binds
* proxy pass - Password to use with above
* roles - An array of role names to use within your app. The values are arbitrary.
* The keys themselves represent the
* "security level," ie
* if( $security_level >= 3 ) {
* // Is a power user
* echo display_info_for_power_users_or_admins();
* }
* member_attribute - Attribute to search to determine allowance after successful authentication
* auditlog - Location to log auditable events. Needs to be writeable
* by the web server
*/
$config['hosts'] = array('ldap.mycompany.com');
$config['ports'] = array(389);
$config['basedn'] = 'dc=mycompany,dc=com';
$config['login_attribute'] = 'uid';
$config['proxy_user'] = '';
$config['proxy_pass'] = '';
$config['roles'] = array(1 => 'User',
3 => 'Power User',
5 => 'Administrator');
$config['member_attribute'] = 'memberUid';
$config['auditlog'] = 'application/logs/audit.log'; // Some place to log attempted logins (separate from message log)
?>

View File

@ -104,6 +104,15 @@ $config['unknown_poster'] = 'random';
**/
$config['unknown_title'] = 'Untitled';
/**
* To require LDAP authentication or not.
*
* Weather to require LDAP authenticaiton or not.
* Set to either 'true' to require authentication or 'false' not to.
* NOTE: if changed, set LDAP settings in auth_ldap.php
**/
$config['require_auth'] = false;
/**
*
*

View File

@ -0,0 +1,85 @@
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
/*
* This file is part of Auth_Ldap.
Auth_Ldap is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Auth_Ldap is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Auth_Ldap. If not, see <http://www.gnu.org/licenses/>.
*
*/
/**
* @author Greg Wojtak <gwojtak@techrockdo.com>
* @copyright Copyright © 2010,2011 by Greg Wojtak <gwojtak@techrockdo.com>
* @package Auth_Ldap
* @subpackage auth demo
* @license GNU Lesser General Public License
*/
class Auth extends CI_Controller {
function __construct() {
parent::__construct();
$this->load->helper('form');
$this->load->library('Form_validation');
$this->load->library('auth_ldap');
$this->load->helper('url');
$this->load->library('table');
}
function index() {
$this->db_session->keep_flashdata('tried_to');
$this->login();
}
function login($errorMsg = NULL){
$this->db_session->keep_flashdata('tried_to');
if(!$this->auth_ldap->is_authenticated()) {
// Set up rules for form validation
$rules = $this->form_validation;
$rules->set_rules('username', 'Username', 'required|alpha_dash');
$rules->set_rules('password', 'Password', 'required');
// Do the login...
if($rules->run() && $this->auth_ldap->login(
$rules->set_value('username'),
$rules->set_value('password'))) {
// Login WIN!
if($this->db_session->flashdata('tried_to')) {
redirect($this->db_session->flashdata('tried_to'));
}else {
redirect('/');
}
}else {
// Login FAIL
$this->db_session->set_flashdata('login_error', 'Incorrect username or password.');
$this->load->view('auth/login_form');
}
}else {
// Already logged in...
redirect('/');
}
}
function logout() {
if($this->db_session->userdata('logged_in')) {
$data['name'] = $this->db_session->userdata('cn');
$data['username'] = $this->db_session->userdata('username');
$data['logged_in'] = TRUE;
$this->auth_ldap->logout();
} else {
$data['logged_in'] = FALSE;
}
redirect('/');
}
}
?>

View File

@ -17,6 +17,7 @@
* - _valid_lang()
* - _valid_captcha()
* - _valid_ip()
* - _valid_authentication()
* - get_cm_js()
* - error_404()
* Classes list:
@ -30,6 +31,9 @@ class Main extends CI_Controller
{
parent::__construct();
$this->load->model('languages');
if ($this->config->item('require_auth') ){
$this->load->library('auth_ldap');
}
if (!$this->db->table_exists('ci_sessions'))
{
@ -235,6 +239,7 @@ class Main extends CI_Controller
function index()
{
$this->_valid_authentication();
$this->load->helper('json');
if (!$this->input->post('submit'))
@ -307,6 +312,7 @@ class Main extends CI_Controller
function raw()
{
$this->_valid_authentication();
$this->load->model('pastes');
$check = $this->pastes->checkPaste(3);
@ -323,6 +329,7 @@ class Main extends CI_Controller
function rss()
{
$this->_valid_authentication();
$this->load->model('pastes');
$check = $this->pastes->checkPaste(3);
@ -343,6 +350,7 @@ class Main extends CI_Controller
function embed()
{
$this->_valid_authentication();
$this->load->model('pastes');
$check = $this->pastes->checkPaste(3);
@ -359,6 +367,7 @@ class Main extends CI_Controller
function download()
{
$this->_valid_authentication();
$this->load->model('pastes');
$check = $this->pastes->checkPaste(3);
@ -375,7 +384,7 @@ class Main extends CI_Controller
function lists()
{
$this->_valid_authentication();
if ($this->config->item('private_only'))
{
show_404();
@ -403,6 +412,7 @@ class Main extends CI_Controller
function view()
{
$this->_valid_authentication();
$this->load->helper('json');
$this->load->model('pastes');
$check = $this->pastes->checkPaste(2);
@ -528,6 +538,16 @@ class Main extends CI_Controller
}
}
function _valid_authentication()
{
if ($this->config->item('require_auth') ){
if (!$this->auth_ldap->is_authenticated()){
$this->db_session->set_flashdata('tried_to', "/" . $this->uri->uri_string());
redirect('/auth');
}
}
}
function get_cm_js()
{
$lang = $this->uri->segment(3);

View File

@ -0,0 +1,273 @@
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
/*
* This file is part of Auth_Ldap.
Auth_Ldap is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Auth_Ldap is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Auth_Ldap. If not, see <http://www.gnu.org/licenses/>.
*
*/
/**
* Auth_Ldap Class
*
* Simple LDAP Authentication library for Code Igniter.
*
* @package Auth_Ldap
* @author Greg Wojtak <gwojtak@techrockdo.com>
* @version 0.6
* @link http://www.techrockdo.com/projects/auth_ldap
* @license GNU Lesser General Public License (LGPL)
* @copyright Copyright © 2010,2011 by Greg Wojtak <gwojtak@techrockdo.com>
* @todo Allow for privileges in groups of groups in AD
* @todo Rework roles system a little bit to a "auth level" paradigm
*/
class Auth_Ldap {
function __construct() {
$this->ci =& get_instance();
log_message('debug', 'Auth_Ldap initialization commencing...');
// Load the session library
$this->ci->load->library('db_session');
// Load the configuration
$this->ci->load->config('auth_ldap');
// Load the language file
// $this->ci->lang->load('auth_ldap');
$this->_init();
}
/**
* @access private
* @return void
*/
private function _init() {
// Verify that the LDAP extension has been loaded/built-in
// No sense continuing if we can't
if (! function_exists('ldap_connect')) {
show_error('LDAP functionality not present. Either load the module ldap php module or use a php with ldap support compiled in.');
log_message('error', 'LDAP functionality not present in php.');
}
$this->hosts = $this->ci->config->item('hosts');
$this->ports = $this->ci->config->item('ports');
$this->basedn = $this->ci->config->item('basedn');
$this->account_ou = $this->ci->config->item('account_ou');
$this->login_attribute = $this->ci->config->item('login_attribute');
$this->use_ad = $this->ci->config->item('use_ad');
$this->ad_domain = $this->ci->config->item('ad_domain');
$this->proxy_user = $this->ci->config->item('proxy_user');
$this->proxy_pass = $this->ci->config->item('proxy_pass');
$this->roles = $this->ci->config->item('roles');
$this->auditlog = $this->ci->config->item('auditlog');
$this->member_attribute = $this->ci->config->item('member_attribute');
}
/**
* @access public
* @param string $username
* @param string $password
* @return bool
*/
function login($username, $password) {
/*
* For now just pass this along to _authenticate. We could do
* something else here before hand in the future.
*/
$user_info = $this->_authenticate($username,$password);
if(empty($user_info['role'])) {
log_message('info', $username." has no role to play.");
//show_error($username.' succssfully authenticated, but is not allowed because the username was not found in an allowed access group.');
return FALSE;
}
// Record the login
$this->_audit("Successful login: ".$user_info['cn']."(".$username.") from ".$this->ci->input->ip_address());
// Set the session data
$customdata = array('username' => $username,
'cn' => $user_info['cn'],
'role' => $user_info['role'],
'logged_in' => TRUE);
$this->ci->db_session->set_userdata($customdata);
return TRUE;
}
/**
* @access public
* @return bool
*/
function is_authenticated() {
if($this->ci->db_session->userdata('logged_in')) {
return TRUE;
} else {
return FALSE;
}
}
/**
* @access public
*/
function logout() {
// Just set logged_in to FALSE and then destroy everything for good measure
$this->ci->db_session->set_userdata(array('logged_in' => FALSE));
$this->ci->db_session->sess_destroy();
}
/**
* @access private
* @param string $msg
* @return bool
*/
private function _audit($msg){
$date = date('Y/m/d H:i:s');
if( ! file_put_contents($this->auditlog, $date.": ".$msg."\n",FILE_APPEND)) {
log_message('info', 'Error opening audit log '.$this->auditlog);
return FALSE;
}
return TRUE;
}
/**
* @access private
* @param string $username
* @param string $password
* @return array
*/
private function _authenticate($username, $password) {
$needed_attrs = array('dn', 'cn', $this->login_attribute);
foreach($this->hosts as $host) {
$this->ldapconn = ldap_connect($host);
if($this->ldapconn) {
break;
}else {
log_message('info', 'Error connecting to '.$uri);
}
}
// At this point, $this->ldapconn should be set. If not... DOOM!
if(! $this->ldapconn) {
log_message('error', "Couldn't connect to any LDAP servers. Bailing...");
show_error('Error connecting to your LDAP server(s). Please check the connection and try again.');
}
// We've connected, now we can attempt the login...
// These to ldap_set_options are needed for binding to AD properly
// They should also work with any modern LDAP service.
ldap_set_option($this->ldapconn, LDAP_OPT_REFERRALS, 0);
ldap_set_option($this->ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3);
// Find the DN of the user we are binding as
// If proxy_user and proxy_pass are set, use those, else bind anonymously
if($this->proxy_user) {
$bind = ldap_bind($this->ldapconn, $this->proxy_user, $this->proxy_pass);
}else {
$bind = ldap_bind($this->ldapconn);
}
if(!$bind){
log_message('error', 'Unable to perform anonymous/proxy bind');
show_error('Unable to bind for user id lookup');
}
log_message('debug', 'Successfully bound to directory. Performing dn lookup for '.$username);
$filter = '('.$this->login_attribute.'='.$username.')';
$search = ldap_search($this->ldapconn, $this->basedn, $filter,
array('dn', $this->login_attribute, 'cn'));
$entries = ldap_get_entries($this->ldapconn, $search);
if(!isset($entries[0])){
//User either does not exist or has no permissions
$this->_audit("Failed login attempt: ".$username." from ".$_SERVER['REMOTE_ADDR']);
return FALSE;
}
$binddn = $entries[0]['dn'];
// Now actually try to bind as the user
$bind = ldap_bind($this->ldapconn, $binddn, $password);
if(! $bind) {
$this->_audit("Failed login attempt: ".$username." from ".$_SERVER['REMOTE_ADDR']);
return FALSE;
}
$cn = $entries[0]['cn'][0];
$dn = stripslashes($entries[0]['dn']);
$id = $entries[0][$this->login_attribute][0];
$get_role_arg = $id;
return array('cn' => $cn, 'dn' => $dn, 'id' => $id,
'role' => $this->_get_role($get_role_arg));
}
/**
* @access private
* @param string $str
* @param bool $for_dn
* @return string
*/
private function ldap_escape($str, $for_dn = false) {
/**
* This function courtesy of douglass_davis at earthlink dot net
* Posted in comments at
* http://php.net/manual/en/function.ldap-search.php on 2009/04/08
*/
// see:
// RFC2254
// http://msdn.microsoft.com/en-us/library/ms675768(VS.85).aspx
// http://www-03.ibm.com/systems/i/software/ldap/underdn.html
if ($for_dn)
$metaChars = array(',','=', '+', '<','>',';', '\\', '"', '#');
else
$metaChars = array('*', '(', ')', '\\', chr(0));
$quotedMetaChars = array();
foreach ($metaChars as $key => $value) $quotedMetaChars[$key] = '\\'.str_pad(dechex(ord($value)), 2, '0');
$str=str_replace($metaChars,$quotedMetaChars,$str); //replace them
return ($str);
}
/**
* @access private
* @param string $username
* @return int
*/
private function _get_role($username) {
$filter = '('.$this->member_attribute.'='.$username.')';
$search = ldap_search($this->ldapconn, $this->basedn, $filter, array('cn'));
if(! $search ) {
log_message('error', "Error searching for group:".ldap_error($this->ldapconn));
show_error('Couldn\'t find groups: '.ldap_error($this->ldapconn));
}
$results = ldap_get_entries($this->ldapconn, $search);
if($results['count'] != 0) {
for($i = 0; $i < $results['count']; $i++) {
$role = array_search($results[$i]['cn'][0], $this->roles);
if($role !== FALSE) {
return $role;
}
}
}
return false;
}
}
?>

View File

@ -0,0 +1,31 @@
<?php $this->load->view('defaults/header'); ?>
<?php
$message = $this->db_session->flashdata('login_error');
if ($message){
echo '<div class="login_error">';
echo $message;
echo '</div>';
}
?>
<div class="login">
<?php echo form_fieldset(); ?>
<?php echo validation_errors();?>
<?php echo form_open('auth/login', array('id' => 'loginform')); ?>
<?php
$table = array(array('', ''),
array(form_label('Username', 'username'),
form_input(array('name' => 'username', 'id' => 'username',
'class' => 'formfield'))),
array(form_label('Password', 'password'),
form_password(array('name' => 'password', 'id' => 'password',
'class' => 'formfield'))));
echo $this->table->generate($table);
?>
<?php echo form_submit('login', 'Login'); ?>
<?php echo form_close(); ?>
<?php echo form_fieldset_close(); ?>
</div>
<?php $this->load->view('defaults/footer'); ?>

View File

@ -53,6 +53,13 @@ $this->carabiner->display('css');
<?php } ?>
<li><a <?php if($l == "api"){ echo 'class="active"'; }?> href="<?php echo site_url('api'); ?>" title="API">API</a></li>
<li><a <?php if($l == "about"){ echo 'class="active"'; }?> href="<?php echo site_url('about'); ?>" title="About">About</a></li>
<?php
if ($this->config->item('require_auth') ){
if ($this->auth_ldap->is_authenticated()){
echo "<li><a href=" . site_url('auth/logout') . ' title="Logout">Logout</a></li>';
}
}
?>
</ul>
</div>

View File

@ -483,3 +483,18 @@ h4 {
color: inherit;
text-decoration: underline;
}
.login {
margin-left: 35%;
margin-right: auto;
}
.login_error {
border: 1px solid;
margin-left: 25%;
margin-right: 30%;
padding:15px 10px 15px 50px;
text-align: center;
background-color: #FFBABA;
color: #D8000C;
}