Basic layout and login functioning

This commit is contained in:
Deon George
2012-06-19 12:50:42 +10:00
parent e084621082
commit caf89ff4e5
49 changed files with 571 additions and 697 deletions

View File

@@ -0,0 +1,79 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* LDAP Auth driver.
*
* @package PLA
* @subpackage Auth/LDAP
* @category Helpers
* @author Deon George
* @copyright (c) phpLDAPadmin Development Team
* @license http://dev.phpldapadmin.org/license.html
*/
class PLA_Auth_Ldap extends Auth {
// Unnused required abstract functions
public function password($username) {}
public function check_password($password) {}
// Overrides
public function hash($str) {
// Since this is used automatically to encrypted a password, we need to suppress that for LDAP
if (! $this->_config['hash_key'])
return $str;
else
return parent::hash($str);
}
/**
* Logs a user in.
*
* @param string username
* @param string password
* @param boolean enable autologin (not supported)
* @return boolean
*/
protected function _login($user, $password, $remember) {
if ( ! is_object($user)) {
$username = $user;
// Load the user
// @todo Get the server ID
$sid = 'default';
$user = Database_LDAP::instance($sid)->select_db('user')->connect();
$user->bind($username,$password);
}
// @todo Implement conditional logging based on memberships to groups or other criteria.
// @todo This check of user being logged in needs to be better
if (! $user->noconnect) {
/*
// @todo To implement
if ($remember === TRUE) {
// Token data
$data = array(
'user_id'=>$user->id,
'expires'=>time()+$this->_config['lifetime'],
'user_agent'=>sha1(Request::$user_agent),
);
// Create a new autologin token
$token = ORM::factory('user_token')
->values($data)
->create();
// Set the autologin cookie
Cookie::set('authautologin', $token->token, $this->_config['lifetime']);
}
*/
// Finish the login
$this->complete_login($user);
return TRUE;
}
// Login failed
return FALSE;
}
}
?>

View File

@@ -0,0 +1,65 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class is for rendering HTML body blocks (left, center, right).
*
* It will provide a header, body and footer.
*
* @package PLA
* @subpackage Page
* @category Helpers
* @author Deon George
* @copyright (c) phpLDAPadmin Development Team
* @license http://dev.phpldapadmin.org/license.html
* @uses Style
*/
abstract class PLA_Block extends HTMLRender {
protected static $_data = array();
protected static $_required_keys = array('body');
/**
* Add a block to be rendered
*
* @param array Block attributes
*/
public static function add($block,$prepend=FALSE) {
parent::add($block);
// Detect any style sheets.
if (! empty($block['style']) && is_array($block['style']))
foreach ($block['style'] as $data=>$media)
Style::add(array(
'type'=>'file',
'data'=>$data,
'media'=>$media,
));
}
/**
* Return an instance of this class
*
* @return Block
*/
public static function factory() {
return new Block;
}
/**
* Render this block
*
* @see HTMLRender::render()
*/
protected function render() {
$output = '';
foreach (static::$_data as $value)
$output .= View::factory(Kohana::Config('config.theme').'/block')
->set('title',empty($value['title']) ? '' : $value['title'])
->set('subtitle',empty($value['subtitle']) ? '' : $value['subtitle'])
->set('body',empty($value['body']) ? '' : $value['body'])
->set('footer',empty($value['footer']) ? '' : $value['footer']);
return $output;
}
}
?>

View File

@@ -0,0 +1,117 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides the default template controller for rendering pages.
*
* @package PLA
* @subpackage Page/Template
* @category Controllers
* @author Deon George
* @copyright (c) phpLDAPadmin Development Team
* @license http://dev.phpldapadmin.org/license.html
*/
abstract class PLA_Controller_Template extends Kohana_Controller_Template {
// @var object meta object information as per [meta]
private $meta;
public function __construct(Request $request, Response $response) {
$this->template = Kohana::$config->load('config')->theme;
return parent::__construct($request,$response);
}
public function before() {
// Do not template media files
if ($this->request->action() === 'media') {
$this->auto_render = FALSE;
return;
}
parent::before();
// For AJAX calls, we dont need to render the complete page.
if ($this->request->is_ajax()) {
$this->auto_render = FALSE;
return;
}
$this->template->content = '';
// Setup the page template
$this->meta = new Meta;
View::bind_global('meta',$this->meta);
}
public function after() {
if ($this->auto_render === TRUE) {
// Application Title
$this->meta->title = Kohana::$config->load('config')->appname;
// Language
// @todo
$this->meta->language = '';
// Description
$this->meta->description = sprintf('%s::%s',$this->request->controller(),$this->request->action());
// Control Line
// @todo
$this->template->control = '';
// System Messages line
// @todo
$this->template->sysmsg = '';
// Left Item
// @todo
$this->template->left = '';
$this->template->right = '';
$this->template->center = '';
if (! $this->response->body())
$this->response->body((string)Block::factory());
if (empty($this->template->content))
$this->template->content = $this->response->body();
// Footer
// @todo
$this->template->footer = '';
// Our default script(s)
foreach (array('file'=>array_reverse(array(
))) as $type => $datas) {
foreach ($datas as $data) {
Script::add(array(
'type'=>$type,
'data'=>$data,
),TRUE);
}
}
// For any ajax rendered actions, we'll need to capture the content and put it in the response
// @todo
} elseif ($this->request->is_ajax() && isset($this->template->content) && ! $this->response->body()) {
// @todo move this formatting to a view?
if ($s = $this->_sysmsg() AND (string)$s)
$this->response->body(sprintf('<table class="sysmsg"><tr><td>%s</td></tr></table>',$s));
// Since we are ajax, we should re-render the breadcrumb
Session::instance()->set('breadcrumb',(string)Breadcrumb::factory());
$this->response->bodyadd(Script::add(array('type'=>'stdin','data'=>'$().ready($("#ajCONTROL").load("'.URL::site('welcome/breadcrumb').'",null,function(x,s,r) {}));')));
// In case there any javascript for this render.
$this->response->bodyadd(Script::factory());
// Get the response body
$this->response->bodyadd(sprintf('<table class="content"><tr><td>%s</td></tr></table>',$this->template->content));
}
parent::after();
// Generate and check the ETag for this file
if (Kohana::$environment === Kohana::PRODUCTION)
$this->response->check_cache(NULL,$this->request);
}
}

View File

@@ -0,0 +1,186 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class takes care of communicating with LDAP
*
* @package PLA
* @subpackage LDAP
* @category Helpers
* @author Deon George
* @copyright (c) phpLDAPadmin Development Team
* @license http://dev.phpldapadmin.org/license.html
*/
abstract class PLA_Database_LDAP extends Database {
// Our required abstract functions
public function set_charset($charset) {}
public function query($type, $sql, $as_object = FALSE, array $params = NULL) {}
public function begin($mode = NULL) {}
public function commit() {}
public function rollback() {}
public function list_tables($like = NULL) {}
public function list_columns($table, $like = NULL, $add_prefix = TRUE) {}
public function escape($value) { return $value;}
// Overrides
public function quote_column($column) {
return $column;
}
// This function will enable us to have multiple resource contexts
// @todo To Implement
public function select_db($x) {
return $this;
}
private function _connect() {
/*
// @todo To implement
# No identifiable connection exists, lets create a new one.
if (DEBUG_ENABLED)
debug_log('Creating NEW connection [%s] for index [%s]',16,0,__FILE__,__LINE__,__METHOD__,
$method,$this->index);
*/
/*
// @todo To implement
if (function_exists('run_hook'))
run_hook('pre_connect',array('server_id'=>$this->index,'method'=>$method));
*/
if (! empty($this->_config['port']))
$r = ldap_connect($this->_config['connection']['hostname'],$this->_config['port']);
else
$r = ldap_connect($this->_config['connection']['hostname']);
/*
// @todo To implement
if (DEBUG_ENABLED)
debug_log('LDAP Resource [%s], Host [%s], Port [%s]',16,0,__FILE__,__LINE__,__METHOD__,
$this->_r,$this->getValue('server','host'),$this->getValue('server','port'));
*/
if (! is_resource($r))
throw Kohana_Exception('UNHANDLED, $r is not a resource');
// Go with LDAP version 3 if possible (needed for renaming and Novell schema fetching)
ldap_set_option($r,LDAP_OPT_PROTOCOL_VERSION,3);
/* Disabling this makes it possible to browse the tree for Active Directory, and seems
* to not affect other LDAP servers (tested with OpenLDAP) as phpLDAPadmin explicitly
* specifies deref behavior for each ldap_search operation. */
ldap_set_option($r,LDAP_OPT_REFERRALS,0);
/*
// @todo To implement
# Try to fire up TLS is specified in the config
if ($this->isTLSEnabled())
$this->startTLS($this->_r);
*/
return $r;
}
private function _bind($r,$u,$p) {
if (! is_resource($r))
throw Kohana_Exception('UNHANDLED, $r is not a resource');
/*
// @todo To implement
# If SASL has been configured for binding, then start it now.
if ($this->isSASLEnabled())
$br = $this->startSASL($this->_r,$method);
# Normal bind...
else
*/
$br = @ldap_bind($r,$u,$p);
/*
if ($debug)
debug_dump(array('method'=>$method,'bind'=>$bind,'USER'=>$_SESSION['USER']));
if (DEBUG_ENABLED)
debug_log('Resource [%s], Bind Result [%s]',16,0,__FILE__,__LINE__,__METHOD__,$this->_r,$bind);
*/
if (! $br) {
/*
if (DEBUG_ENABLED)
debug_log('Leaving with FALSE, bind FAILed',16,0,__FILE__,__LINE__,__METHOD__);
*/
$this->noconnect = true;
/*
// @todo To implement
system_message(array(
'title'=>sprintf('%s %s',_('Unable to connect to LDAP server'),$this->getName()),
'body'=>sprintf('<b>%s</b>: %s (%s) for <b>%s</b>',_('Error'),$this->getErrorMessage($method),$this->getErrorNum($method),$method),
'type'=>'error'));
*/
} else {
$this->noconnect = false;
/*
// @todo To implement
# If this is a proxy session, we need to switch to the proxy user
if ($this->isProxyEnabled() && $bind['id'] && $method != 'anon')
if (! $this->startProxy($this->_r,$method)) {
$this->noconnect = true;
$CACHE[$this->index][$method] = null;
}
*/
}
/*
// @todo To implement
if (function_exists('run_hook'))
run_hook('post_connect',array('server_id'=>$this->index,'method'=>$method,'id'=>$bind['id']));
*/
/*
// @todo To implement
if ($debug)
debug_dump(array($method=>$CACHE[$this->index][$method]));
*/
return $br;
}
public function connect() {
if ($this->_r = $this->_connect())
return $this;
else
throw Kohana_Exception('Unable to connect to LDAP Server?');
}
public function bind($user,$pass) {
// If this is an anon query, then we return
// Do we need to do an anon search to find the DN
if (! empty($this->_config['login_attr']) AND strtoupper($this->_config['login_attr']) != 'DN') {
$u = $this->search()
->scope('sub')
->where($this->_config['login_attr'],'=',$user)
->run();
if (! $u)
throw new Kohana_Exception('Unable to find user :user',array(':user'=>$user));
$u = array_shift($u);
$user = $u['dn'];
}
// Bind
if ($this->_bind($this->_r,$user,$pass))
return $this;
else
throw new Kohana_Exception('Unable to bind');
}
public function search() {
return new Database_LDAP_Search($this->_r);
}
}
?>

View File

@@ -0,0 +1,270 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class takes care of searching within LDAP
*
* @package PLA
* @subpackage LDAP/Search
* @category Helpers
* @author Deon George
* @copyright (c) phpLDAPadmin Development Team
* @license http://dev.phpldapadmin.org/license.html
*/
abstract class PLA_Database_LDAP_Search {
private $_r; // Our LDAP Server to query
private $_scope = 'base'; // LDAP Search Scope
private $_filter = '(objectclass=*)'; // LDAP Search Scope
private $_attrs = array('*','+'); // LDAP Attributes to Return
private $_base = ''; // LDAP Base to Search
private $_db_pending = array(); // LDAP Query Filter to compile
/**
* Callable database methods
* @var array
*/
protected static $_db_methods = array(
'where', 'and_where', 'or_where', 'where_open', 'and_where_open', 'or_where_open', 'where_close',
'and_where_close', 'or_where_close',
);
/**
* Members that have access methods
* @var array
*/
protected static $_properties = array(
);
public function __construct($resource) {
$this->_r = $resource;
}
/**
* Handles pass-through to database methods. Calls to query methods
* (query, get, insert, update) are not allowed. Query builder methods
* are chainable.
*
* @param string $method Method name
* @param array $args Method arguments
* @return mixed
*/
public function __call($method,array $args) {
if (in_array($method,Database_LDAP_Search::$_properties)) {
/*
// @todo To Implement
if ($method === 'validation')
{
if ( ! isset($this->_validation))
{
// Initialize the validation object
$this->_validation();
}
}
*/
// Return the property
return $this->{'_'.$method};
}
elseif (in_array($method,Database_LDAP_Search::$_db_methods))
{
// Add pending database call which is executed after query type is determined
$this->_db_pending[] = array('name' => $method,'args' => $args);
return $this;
}
else
{
throw new Kohana_Exception('Invalid method :method called in :class',
array(':method' => $method,':class' => get_class($this)));
}
}
private function _build() {
$s = Database_LDAP_Search::Search();
// Process pending database method calls
foreach ($this->_db_pending as $method) {
$name = $method['name'];
$args = $method['args'];
$this->_db_applied[$name] = $name;
call_user_func_array(array($s,$name),$args);
}
return $s;
}
public static function Search($columns = NULL) {
return new Database_LDAP_Search_Builder_Query(func_get_args());
}
public static function instance($resource) {
return new Database_LDAP_Search($resource);
}
public function scope($val) {
switch ($val) {
case 'base':
case 'sub':
case 'one': $this->_scope = $val;
break;
default:
throw new Kohana_Exception('Unknown search scope :scope',array(':scope',$val));
}
return $this;
}
/**
* Search the LDAP database
*/
public function run() {
$query = array();
// Compile our query
if ($this->_db_pending)
$this->_filter = $this->_build();
/*
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
*/
$attrs_only = 0;
$this->_base = 'o=Simpsons';
/*
// @todo To implement
if (! isset($query['base'])) {
$bases = $this->getBaseDN();
$query['base'] = array_shift($bases);
}
*/
/*
// @todo To implement
if (! isset($query['deref']))
$query['deref'] = $_SESSION[APPCONFIG]->getValue('deref','search');
*/
if (! isset($query['size_limit']))
$query['size_limit'] = 0;
if (! isset($query['time_limit']))
$query['time_limit'] = 0;
/*
if ($query['scope'] == 'base' && ! isset($query['baseok']))
system_message(array(
'title'=>sprintf('Dont call %s',__METHOD__),
'body'=>sprintf('Use getDNAttrValues for base queries [%s]',$query['base']),
'type'=>'info'));
*/
/*
if (is_array($query['base'])) {
system_message(array(
'title'=>_('Invalid BASE for query'),
'body'=>_('The query was cancelled because of an invalid base.'),
'type'=>'error'));
return array();
}
*/
/*
if (DEBUG_ENABLED)
debug_log('%s search PREPARE.',16,0,__FILE__,__LINE__,__METHOD__,$query['scope']);
*/
/*
if ($debug)
debug_dump(array('query'=>$query,'server'=>$this->getIndex(),'con'=>$this->connect($method)));
*/
//$resource = $this->connect($method,$debug);
switch ($this->_scope) {
case 'base':
$search = @ldap_read($this->_r,$this->_base,$this->_filter,$this->_attrs,$attrs_only,$query['size_limit'],$query['time_limit'],$query['deref']);
break;
case 'one':
$search = @ldap_list($this->_r,$this->_base,$this->_filter,$this->_attrs,$attrs_only,$query['size_limit'],$query['time_limit'],$query['deref']);
break;
case 'sub':
default:
$search = @ldap_search($this->_r,$this->_base,$this->_filter,$this->_attrs,$attrs_only,$query['size_limit'],$query['time_limit'],$query['deref']);
break;
}
/*
if ($debug)
debug_dump(array('method'=>$method,'search'=>$search,'error'=>$this->getErrorMessage()));
*/
/*
if (DEBUG_ENABLED)
debug_log('Search scope [%s] base [%s] filter [%s] attrs [%s] COMPLETE (%s).',16,0,__FILE__,__LINE__,__METHOD__,
$query['scope'],$query['base'],$query['filter'],$query['attrs'],is_null($search));
*/
if (! $search)
return array();
$return = array();
// Get the first entry identifier
if ($entries = ldap_get_entries($this->_r,$search)) {
# Remove the count
if (isset($entries['count']))
unset($entries['count']);
// Iterate over the entries
foreach ($entries as $a => $entry) {
/*
if (! isset($entry['dn']))
debug_dump_backtrace('No DN?',1);
*/
// Remove the none entry references.
if (! is_array($entry)) {
unset($entries[$a]);
continue;
}
$dn = $entry['dn'];
unset($entry['dn']);
// Iterate over the attributes
foreach ($entry as $b => $attrs) {
// Remove the none entry references.
if (! is_array($attrs)) {
unset($entry[$b]);
continue;
}
// Remove the count
if (isset($entry[$b]['count']))
unset($entry[$b]['count']);
}
// Our queries always include the DN (the only value not an array).
$entry['dn'] = $dn;
$return[$dn] = $entry;
}
// Sort our results
foreach ($return as $key => $values)
ksort($return[$key]);
}
/*
if (DEBUG_ENABLED)
debug_log('Returning (%s)',17,0,__FILE__,__LINE__,__METHOD__,$return);
*/
return $return;
}
}
?>

View File

@@ -0,0 +1,214 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class takes care of building an LDAP filter query
*
* @package PLA
* @subpackage LDAP/Search
* @category Helpers
* @author Deon George
* @copyright (c) phpLDAPadmin Development Team
* @license http://dev.phpldapadmin.org/license.html
*/
abstract class PLA_Database_LDAP_Search_Builder_Query extends Database_Query_Builder {
protected $_where = array();
// @todo Not implemented
public function reset() {}
public function __construct() {
parent::__construct(Database::SELECT,'ldap');
}
/**
* Alias of and_where()
*
* @param mixed column name or array($column, $alias) or object
* @param string logic operator
* @param mixed column value
* @return $this
*/
public function where($column,$op,$value) {
return $this->and_where($column,$op,$value);
}
/**
* Creates a new "AND WHERE" condition for the query.
*
* @param mixed column name or array($column,$alias) or object
* @param string logic operator
* @param mixed column value
* @return $this
*/
public function and_where($column,$op,$value) {
$this->_where[] = array('AND' => array($column,$op,$value));
return $this;
}
/**
* Creates a new "OR WHERE" condition for the query.
*
* @param mixed column name or array($column,$alias) or object
* @param string logic operator
* @param mixed column value
* @return $this
*/
public function or_where($column,$op,$value) {
$this->_where[] = array('OR' => array($column,$op,$value));
return $this;
}
/**
* Alias of and_where_open()
*
* @return $this
*/
public function where_open() {
return $this->and_where_open();
}
/**
* Opens a new "AND WHERE (...)" grouping.
*
* @return $this
*/
public function and_where_open() {
$this->_where[] = array('AND' => '(');
return $this;
}
/**
* Opens a new "OR WHERE (...)" grouping.
*
* @return $this
*/
public function or_where_open() {
$this->_where[] = array('OR' => '(');
return $this;
}
public function compile($db = NULL) {
$filter = '';
return $this->_compile_conditions($db,$this->_where);
}
/**
* Closes an open "AND WHERE (...)" grouping.
*
* @return $this
*/
public function where_close() {
return $this->and_where_close();
}
/**
* Closes an open "AND WHERE (...)" grouping.
*
* @return $this
*/
public function and_where_close() {
$this->_where[] = array('AND' => ')');
return $this;
}
/**
* Closes an open "OR WHERE (...)" grouping.
*
* @return $this
*/
public function or_where_close() {
$this->_where[] = array('OR' => ')');
return $this;
}
/**
* Compiles an array of conditions into an LDAP filter.
*
* @param object Database instance
* @param array condition statements
* @return string
*/
protected function _compile_conditions(Database $db,array $conditions,$index=0) {
$current_condition = $last_condition = NULL;
$filter = '';
$sub = 0;
foreach ($conditions as $key => $group) {
// If we have been called again, we need to skip ahead, or skip what has been processed
if ($key < $index OR $sub)
continue;
// Process groups of conditions
foreach ($group as $logic => $condition) {
if ($condition === '(') {
$filter .= $this->_compile_conditions($db,$conditions,$key+1);
$sub = 1;
} elseif ($condition === ')') {
if ($index) {
// As we return, we'll include our condition
switch ($current_condition) {
case 'AND':
return '(&'.$filter.')';
case 'OR':
return '(|'.$filter.')';
default:
throw new Kohana_Exception('Condition :condition not handled.',array(':condition'=>$condition));
}
}
$sub = 0;
} else {
// We currently cant handle when a condition changes, without brackets.
if ($filter AND $current_condition AND $current_condition != $logic)
throw new Kohana_Exception('Condition changed without brackets');
$current_condition = $logic;
// Split the condition
list($column,$op,$value) = $condition;
// Database operators are always uppercase
$op = strtoupper($op);
if ((is_string($value) AND array_key_exists($value,$this->_parameters)) === FALSE) {
// Quote the value, it is not a parameter
$value = $db->quote($value);
}
if ($column) {
// Apply proper quoting to the column
$column = $db->quote_column($column);
}
// Append the statement to the query
$filter .= trim('('.$column.$op.$value.')');
}
$last_condition = $condition;
}
}
switch ($current_condition) {
case 'AND':
return '(&'.$filter.')';
case 'OR':
return '(|'.$filter.')';
default:
throw new Kohana_Exception('Condition :condition not handled.',array(':condition'=>$condition));
}
}
}
?>

View File

@@ -0,0 +1,31 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class extends the core Kohana exception handling
*
* @package PLA
* @category Exceptions
* @author Deon George
* @copyright (c) phpLDAPadmin Development Team
* @license http://dev.phpldapadmin.org/license.html
*/
class PLA_Exception extends Kohana_Exception {
public function __construct($message, array $variables = NULL, $code = 0) {
parent::__construct($message,$variables,$code);
switch ($code) {
case '400':
SystemMessage::add('warn',$message);
Request::current()->redirect('login');
break;
}
echo debug::vars(array('m'=>$message,'v'=>$variables,'c'=>$code,'t'=>$this));die();
}
public function __toString() {
echo __METHOD__;die();
}
}
?>

View File

@@ -0,0 +1,65 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class is for rendering PLA System Messages
*
* It will provide a header, body and footer.
*
* @package PLA
* @subpackage Page
* @category Helpers
* @author Deon George
* @copyright (c) phpLDAPadmin Development Team
* @license http://dev.phpldapadmin.org/license.html
* @uses Style
*/
abstract class PLA_SystemMessage extends HTMLRender {
protected static $_data = array();
protected static $_required_keys = array('body');
/**
* Add a block to be rendered
*
* @param array Block attributes
*/
public static function add($block,$prepend=FALSE) {
parent::add($block);
// Detect any style sheets.
if (! empty($block['style']) && is_array($block['style']))
foreach ($block['style'] as $data=>$media)
Style::add(array(
'type'=>'file',
'data'=>$data,
'media'=>$media,
));
}
/**
* Return an instance of this class
*
* @return Block
*/
public static function factory() {
return new SystemMessage;
}
/**
* Render this block
*
* @see HTMLRender::render()
*/
protected function render() {
$output = '';
foreach (static::$_data as $value)
$output .= View::factory(Kohana::Config('config.theme').'/block')
->set('title',empty($value['title']) ? '' : $value['title'])
->set('subtitle',empty($value['subtitle']) ? '' : $value['subtitle'])
->set('body',empty($value['body']) ? '' : $value['body'])
->set('footer',empty($value['footer']) ? '' : $value['footer']);
return $output;
}
}
?>