Xataface  2.0alpha2
Xataface Application Framework
 All Data Structures Namespaces Files Functions Variables Groups Pages
AuthenticationTool.php
Go to the documentation of this file.
1 <?php
2 /*-------------------------------------------------------------------------------
3  * Xataface Web Application Framework
4  * Copyright (C) 2005-2008 Web Lite Solutions Corp (shannah@sfu.ca)
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *-------------------------------------------------------------------------------
20  */
29 import('Dataface/Table.php');
31 
32  var $authType = 'basic';
33 
34  var $conf;
38  var $delegate;
39 
44 
49 
54 
59 
60  private $emailColumn=null;
61 
65  var $authEnabled = true;
66 
67  public static function &getInstance($params=array()){
68  static $instance = 0;
69  if ( $instance === 0 ){
70  $instance = new Dataface_AuthenticationTool($params);
71  if ( !defined('DATAFACE_AUTHENTICATIONTOOL_LOADED') ){
72  define('DATAFACE_AUTHENTICATIONTOOL_LOADED', true);
73  }
74  }
75 
76  return $instance;
77  }
78 
79  function Dataface_AuthenticationTool($params=array()){
80  $this->conf = $params;
81  $this->usersTable = ( isset($params['users_table']) ? $params['users_table'] : null);
82  $this->usernameColumn = ( isset($params['username_column']) ? $params['username_column'] : null);
83  $this->passwordColumn = (isset( $params['password_column']) ? $params['password_column'] : null);
84  $this->userLevelColumn = (isset( $params['user_level_column']) ? $params['user_level_column'] : null);
85 
86  $this->setAuthType(@$params['auth_type']);
87  }
88 
89  function setAuthType($type){
90  if ( isset( $type ) and $type != $this->authType ){
91  $this->authType = $type;
92  $this->delegate = null;
93  // It is possible to define a delegate to this tool by adding the
94  // auth_type option to the conf.ini file _auth section.
95  $module = basename($type);
96  $module_path = array(
97  DATAFACE_SITE_PATH.'/modules/Auth/'.$module.'/'.$module.'.php',
98  DATAFACE_PATH.'/modules/Auth/'.$module.'/'.$module.'.php'
99  );
100  foreach ( $module_path as $path ){
101  if ( is_readable($path) ){
102  import($path);
103  $classname = 'dataface_modules_'.$module;
104  $this->delegate = new $classname;
105  break;
106  }
107  }
108 
109  }
110  }
111 
112  function getCredentials(){
113 
114  if ( isset($this->delegate) and method_exists($this->delegate, 'getCredentials') ){
115  return $this->delegate->getCredentials();
116  } else {
117  $username = (isset($_REQUEST['UserName']) ? $_REQUEST['UserName'] : null);
118  $password = (isset($_REQUEST['Password']) ? $_REQUEST['Password'] : null);
119  return array('UserName'=>$username, 'Password'=>$password);
120  }
121  }
122 
123  function checkCredentials(){
125  if ( !$this->authEnabled ) return true;
126  if ( isset($this->delegate) and method_exists($this->delegate, 'checkCredentials') ){
127  return $this->delegate->checkCredentials();
128  } else {
129  // The user is attempting to log in.
130  $creds = $this->getCredentials();
131  if ( !isset( $creds['UserName'] ) || !isset($creds['Password']) ){
132  // The user did not submit a username of password for login.. trigger error.
133  //throw new Exception("Username or Password Not specified", E_USER_ERROR);
134  return false;
135  }
136  import('Dataface/Serializer.php');
137  $serializer = new Dataface_Serializer($this->usersTable);
138  //$res = mysql_query(
139  $sql = "SELECT `".$this->usernameColumn."` FROM `".$this->usersTable."`
140  WHERE `".$this->usernameColumn."`='".addslashes(
141  $serializer->serialize($this->usernameColumn, $creds['UserName'])
142  )."'
143  AND `".$this->passwordColumn."`=".
144  $serializer->encrypt(
145  $this->passwordColumn,
146  "'".addslashes($serializer->serialize($this->passwordColumn, $creds['Password']))."'"
147  );
148  $res = mysql_query($sql, $app->db());
149  if ( !$res ) throw new Exception(mysql_error($app->db()), E_USER_ERROR);
150 
151  if ( mysql_num_rows($res) === 0 ){
152  return false;
153  }
154  $found = false;
155  while ( $row = mysql_fetch_row($res) ){
156  if ( strcmp($row[0], $creds['UserName'])===0 ){
157  $found=true;
158  break;
159  }
160  }
161  @mysql_free_result($res);
162  return $found;
163  }
164 
165  }
166 
167 
168  function setPassword($password){
170  if ( isset($this->delegate) and method_exists($this->delegate, 'setPassword') ){
171  return $this->delegate->setPassword($username, $password);
172  } else {
173 
174  $user = $this->getLoggedInUser();
175  if ( !$user ){
176 
177  throw new Exception("Failed to set password because there is no logged in user.");
178  }
179 
180  $user->setValue($this->passwordColumn, $password);
181  $res = $user->save();
182  if ( PEAR::isError($res) ){
183  throw new Exception($res->getMessage());
184  }
185  return true;
186  }
187  }
188 
189  function authenticate(){
191  if ( !$this->authEnabled ) return true;
192 
193 
194  if ( $app->sessionEnabled() or $app->autoSession ){
195  $app->startSession($this->conf);
196  }
197  $appdel =& $app->getDelegate();
198 
199  // Fire a trigger before we authenticate
200  if ( isset($appdel) and method_exists($appdel, 'before_authenticate') ){
201  $appdel->before_authenticate();
202  }
203 
204  if ( isset( $_REQUEST['-action'] ) and $_REQUEST['-action'] == 'logout' ){
205  $app->startSession();
206  // the user has invoked a logout request.
207 
208  if ( isset($appdel) and method_exists($appdel, 'before_action_logout' ) ){
209  $res = $appdel->before_action_logout();
210  if ( PEAR::isError($res) ) return $res;
211  }
212  $username = @$_SESSION['UserName'];
213  session_destroy();
214 
215  import('Dataface/Utilities.php');
216 
217  Dataface_Utilities::fireEvent('after_action_logout', array('UserName'=>$username));
218 
219 
220  if ( isset($this->delegate) and method_exists($this->delegate, 'logout') ){
221  $this->delegate->logout();
222  }
223  if ( isset($_REQUEST['-redirect']) and !empty($_REQUEST['-redirect']) ){
224  $app->redirect($_REQUEST['-redirect']);
225  } else if ( isset($_SESSION['-redirect']) ){
226  $redirect = $_SESSION['-redirect'];
227  unset($_SESSION['-redirect']);
228  $app->redirect($redirect);
229 
230 
231  } else {
232  $app->redirect(DATAFACE_SITE_HREF);
233  }
234 
235  }
236 
237  if ( isset( $_REQUEST['-action'] ) and $_REQUEST['-action'] == 'login' ){
238  $app->startSession();
239  if ( $this->isLoggedIn() ){
240  $app->redirect(DATAFACE_SITE_HREF.'?--msg='.urlencode("You are logged in"));
241 
242  }
243 
244  if ( $this->isLockedOut() ){
245  $app->redirect(DATAFACE_SITE_HREF.'?--msg='.urlencode("Sorry, you are currently locked out of the site due to failed login attempts. Please try again later, or contact a system administrator for help."));
246 
247  }
248  // The user is attempting to log in.
249  $creds = $this->getCredentials();
250  $approved = $this->checkCredentials();
251 
252  if ( isset($creds['UserName']) and !$approved ){
253 
254  $this->flagFailedAttempt($creds);
255 
256  return PEAR::raiseError(
257  df_translate('Incorrect Password',
258  'Sorry, you have entered an incorrect username /password combination. Please try again.'
259  ),
261  );
262  } else if ( !$approved ){
263 
264  $this->showLoginPrompt();
265  exit;
266  }
267 
268  $this->clearFailedAttempts();
269 
270  // If we are this far, then the login worked.. We will store the
271  // userid in the session.
272  $_SESSION['UserName'] = $creds['UserName'];
273 
274  import('Dataface/Utilities.php');
275 
276  Dataface_Utilities::fireEvent('after_action_login', array('UserName'=>$_SESSION['UserName']));
277  $msg = df_translate('You are now logged in','You are now logged in');
278  if ( isset( $_REQUEST['-redirect'] ) and !empty($_REQUEST['-redirect']) ){
279 
280  $redirect = df_append_query($_REQUEST['-redirect'], array('--msg'=>$msg));
281  //$app->redirect($redirect);
282 
283  } else if ( isset($_SESSION['-redirect']) ){
284  $redirect = $_SESSION['-redirect'];
285  unset($_SESSION['-redirect']);
286  $redirect = df_append_query($redirect, array('--msg'=>$msg));
287  //$app->redirect($redirect);
288 
289  } else {
290  // Now we forward to the homepage:
291  $redirect = $_SERVER['HOST_URI'].DATAFACE_SITE_HREF;
292  }
293 
294  $redirect = preg_replace('/-action=login_prompt/', '', $redirect);
295  $app->redirect($redirect);
296 
297  }
298 
299  if ( isset($this->delegate) and method_exists($this->delegate, 'authenticate') ){
300  $res = $this->delegate->authenticate();
301  if ( PEAR::isError($res) and $res->getCode() == DATAFACE_E_REQUEST_NOT_HANDLED ){
302  // we just pass the buck
303  } else {
304  return $res;
305  }
306  }
307 
308  if ( isset($this->conf['pre_auth_types']) ){
309  $pauthtypes = explode(',',$this->conf['pre_auth_types']);
310  if ( $pauthtypes ){
311  $oldType = $this->authType;
312  foreach ($pauthtypes as $pauthtype){
313  $this->setAuthType($pauthtype);
314  if ( isset($this->delegate) and method_exists($this->delegate, 'authenticate') ){
315  $res = $this->delegate->authenticate();
316  if ( PEAR::isError($res) and $res->getCode() == DATAFACE_E_REQUEST_NOT_HANDLED) {
317  // pass the buck
318  } else {
319  return $res;
320  }
321  }
322  }
323  $this->setAuthType($oldType);
324  }
325  }
326 
327 
328  }
329 
333  function isLoggedIn(){
334  if ( !$this->authEnabled ) return true;
335  if ( isset($this->delegate) and method_exists($this->delegate, 'isLoggedIn') ){
336  return $this->delegate->isLoggedIn();
337  }
338 
339  return (isset($_SESSION['UserName']) and $_SESSION['UserName']);
340  }
341 
346  function showLoginPrompt($msg=''){
347 
348  if ( !$this->authEnabled ) return true;
350  $query =& $app->getQuery();
351 
352  if ( @$query['--no-prompt'] ){
353  header("HTTP/1.0 401 Please Log In");
354  echo "<html><body>Please Log In</body></html>";
355  exit;
356  }
357 
358  if ( isset($this->delegate) and method_exists($this->delegate, 'showLoginPrompt') ){
359  return $this->delegate->showLoginPrompt($msg);
360  }
361  header("HTTP/1.1 401 Please Log In");
362 
363  $url = $app->url('-action=login_prompt');
364 
365  if ( $msg ) $msgarray = array($msg);
366  else $msgarray = array();
367  if ( isset($query['--msg']) ){
368  $msgarray[] = $query['--msg'];
369  }
370  $msg = trim(implode('<br>',$msgarray));
371  if ( $msg ) $url .= '&--msg='.urlencode($msg);
372  if ( $query['-action'] != 'login' and $query['-action'] != 'login_prompt' ) $_SESSION['-redirect'] = (isset($_SERVER['REQUEST_URI'])?$_SERVER['REQUEST_URI']:$app->url(''));
373  else {
374  $referer = @$_SERVER['HTTP_REFERER'];
375  if ( !@$_SESSION['-redirect'] and $referer and strpos($referer, df_absolute_url(DATAFACE_SITE_URL)) === 0 ){
376  $_SESSION['-redirect'] = $referer;
377  }
378  }
379  header("Location: $url");
380  exit;
381  //df_display(array('msg'=>$msg, 'redirect'=>@$_REQUEST['-redirect']), 'Dataface_Login_Prompt.html');
382 
383  }
384 
389  function &getLoggedInUser(){
390  $null = null;
391  if ( !$this->authEnabled ) return $null;
392  if ( isset($this->delegate) and method_exists($this->delegate, 'getLoggedInUser') ){
393  $user =& $this->delegate->getLoggedInUser();
394  return $user;
395  }
396  if ( !$this->isLoggedIn() ) return $null;
397  static $user = 0;
398  if ( $user === 0 ){
399  $user = df_get_record($this->usersTable, array($this->usernameColumn => '='.$_SESSION['UserName']));
400  if ( !$user ){
401  $user = new Dataface_Record($this->usersTable, array($this->usernameColumn => $_SESSION['UserName']));
402  }
403  }
404  return $user;
405 
406  }
407 
409  $null = null;
410  if ( !$this->authEnabled ) return $null;
411  if ( isset($this->delegate) and method_exists($this->delegate, 'getLoggedInUsername') ){
412  return $this->delegate->getLoggedInUsername();
413  }
414 
415  $user =& $this->getLoggedInUser();
416  if ( isset($user) ){
417  return $user->strval($this->usernameColumn);
418  }
419 
420  return $null;
421 
422  }
424  $res = mysql_query("create table if not exists `dataface__failed_logins` (
425  `attempt_id` int(11) not null auto_increment primary key,
426  `ip_address` varchar(32) not null,
427  `username` varchar(32) not null,
428  `time_of_attempt` int(11) not null
429  )", df_db());
430  if ( !$res ) throw new Exception(mysql_error(df_db()), E_USER_ERROR);
431  }
432 
433  function flagFailedAttempt($credentials){
435  $del = $app->getDelegate();
436  $method = 'loginFailed';
437  if ( isset($del) and method_exists($del, $method) ){
438  $del->$method($credentials['UserName'], $_SERVER['REMOTE_ADDR'], time() );
439  }
440  $this->_createFailedLoginsTable();
441  $res = mysql_query("insert into `dataface__failed_logins` (ip_address,username,time_of_attempt) values (
442  '".addslashes($_SERVER['REMOTE_ADDR'])."',
443  '".addslashes($credentials['UserName'])."',
444  '".addslashes(time())."'
445  )", df_db());
446  if ( !$res ) throw new Exception(mysql_error(df_db()), E_USER_ERROR);
447 
448 
449  }
450 
452  $this->_createFailedLoginsTable();
453  $res = mysql_query("delete from `dataface__failed_logins` where ip_address='".addslashes($_SERVER['REMOTE_ADDR'])."'", df_db());
454  if ( !$res ) throw new Exception(mysql_error(df_db()));
455  }
456 
457  function isLockedOut(){
458  $this->_createFailedLoginsTable();
459  $res = mysql_query("delete from `dataface__failed_logins` where `time_of_attempt` < ".(time()-(60*30)), df_db());
460  if ( !$res ) throw new Exception(mysql_error(df_db()), E_USER_ERROR);
461  $res = mysql_query("select count(*) from `dataface__failed_logins` where `ip_address`='".addslashes($_SERVER['REMOTE_ADDR'])."'", df_db());
462  if ( !$res ) throw new Exception(mysql_error(df_db()), E_USER_ERROR);
463  list($num) = mysql_fetch_row($res);
464  @mysql_free_result($res);
465  return ($num > 20);
466  }
467 
468  function getEmailColumn(){
469  if ( !isset($this->emailColumn) ){
470  import('Dataface/Ontology.php');
471  Dataface_Ontology::registerType('Person', 'Dataface/Ontology/Person.php', 'Dataface_Ontology_Person');
472  $ontology = Dataface_Ontology::newOntology('Person', $this->usersTable);
473  if ( isset($this->conf['email_column']) ) $this->emailColumn = $this->conf['email_column'];
474  else $this->emailColumn = $ontology->getFieldname('email');
475  }
476  return $this->emailColumn;
477  }
478 
479 
480 }
481