FusionDirectory
class_LoginMethod.inc
1 <?php
2 /*
3  This code is part of FusionDirectory (http://www.fusiondirectory.org/)
4  Copyright (C) 2003-2010 Cajus Pollmeier
5  Copyright (C) 2011-2018 FusionDirectory
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21 
28 {
29  static protected $username;
30  static protected $password;
31 
32  static function init ()
33  {
34  static::$username = NULL;
35  static::$password = NULL;
36  }
37 
39  static function runSchemaCheck (): bool
40  {
41  global $config;
42  if ($config->get_cfg_value('schemaCheck') != 'TRUE') {
43  return TRUE;
44  }
45  $cfg = [];
46  $cfg['admin'] = $config->current['ADMINDN'];
47  $cfg['password'] = $config->current['ADMINPASSWORD'];
48  $cfg['connection'] = $config->current['SERVER'];
49  $cfg['tls'] = ($config->get_cfg_value('ldapTLS') == 'TRUE');
50  $str = check_schema($cfg);
51  foreach ($str as $tr) {
52  if (!$tr['STATUS']) {
53  if ($tr['IS_MUST_HAVE']) {
54  throw new FusionDirectoryError(htmlescape(_('LDAP schema check reported errors:')).'<br/><br/><i>'.htmlescape($tr['MSG']).'</i>');
55  } else {
56  $warning = new FusionDirectoryWarning(nl2br(htmlescape(sprintf(_("LDAP schema error:\n%s"), $tr['MSG']))));
57  $warning->display();
58  }
59  }
60  }
61  return TRUE;
62  }
63 
65  static function checkForLockingBranch (): bool
66  {
67  global $config;
68  $ldap = $config->get_ldap_link();
69  $ldap->cat(get_ou('lockRDN').get_ou('fusiondirectoryRDN').$config->current['BASE'], ['dn']);
70  $attrs = $ldap->fetch();
71  if (!count($attrs)) {
72  $ldap->cd($config->current['BASE']);
73  try {
74  $ldap->create_missing_trees(get_ou('lockRDN').get_ou('fusiondirectoryRDN').$config->current['BASE']);
75  } catch (FusionDirectoryError $error) {
76  $error->display();
77  }
78  }
79 
80  return TRUE;
81  }
82 
85  static function validateUserInput (): bool
86  {
87  global $message, $smarty;
88  static::$username = trim(static::$username);
89  if (!preg_match('/^[@A-Za-z0-9_.-]+$/', static::$username)) {
90  $message = _('Please specify a valid username!');
91  return FALSE;
92  } elseif (mb_strlen(static::$password, 'UTF-8') == 0) {
93  $message = _('Please specify your password!');
94  $smarty->assign('focusfield', 'password');
95  return FALSE;
96  }
97  return TRUE;
98  }
99 
101  static function ldapLoginUser (): bool
102  {
103  global $ui, $config, $message, $smarty;
104  /* Login as user, initialize user ACL's */
105  try {
106  $ui = userinfo::loginUser(static::$username, static::$password);
107  } catch (LoginFailureException $e) {
108  /* Load plist to be able to log */
109  pluglist::load();
110  logging::log('security', 'login failure', static::$username, [], 'Authentication failed: '.$e->getMessage());
111  /* Show the same message whether the user exists or not to avoid information leak */
112  $message = $e->getMessage();
113  $smarty->assign('focusfield', 'password');
114  return FALSE;
115  }
116  return TRUE;
117  }
118 
120  static function loginAndCheckExpired (): bool
121  {
122  global $ui, $config, $plist, $message, $smarty;
123 
124  /* Remove all locks of this user */
125  Lock::deleteByUser($ui->dn);
126 
127  /* Save userinfo and plugin structure */
128  session::set('ui', $ui);
129 
130  /* User might have its own language, re-run initLanguage */
131  $plistReloaded = Language::init();
132 
133  /* We need a fully loaded plist and config to test account expiration */
134  if (!$plistReloaded) {
135  session::un_set('plist');
136  }
137  pluglist::load();
138 
139  /* Check that newly installed plugins have their configuration in the LDAP (will reload plist if needed) */
140  $config->checkLdapConfig();
141 
142  /* Check account expiration */
143  $expired = $ui->expired_status();
144 
145  if ($expired == POSIX_ACCOUNT_EXPIRED) {
146  logging::log('security', 'account', $ui->dn, [], 'Account for user "'.static::$username.'" has expired');
147  $message = _('Account locked. Please contact your system administrator!');
148  $smarty->assign('focusfield', 'username');
149  return FALSE;
150  }
151 
152  return TRUE;
153  }
154 
156  static function connect ()
157  {
158  global $config, $ui;
159 
160  $ui = session::get('ui');
161 
162  //Create new session ID to avoir session_fixation security issues after sucess login
163  session_regenerate_id();
164 
165  /* Not account expired or password forced change go to main page */
166  logging::log('security', 'login', $ui->uid, [], 'Logged in successfully');
167  session::set('connected', 1);
168  session::set('DEBUGLEVEL', $config->get_cfg_value('DEBUGLEVEL'));
169  }
170 
172  static function redirect ()
173  {
174  static::connect();
175  header('Location: main.php');
176  exit;
177  }
178 
180  static function runSteps ($steps)
181  {
182  try {
183  foreach ($steps as $step) {
184  $status = static::$step();
185  if (is_string($status)) {
186  /* Deprecated */
187  msg_dialog::display(_('LDAP error'), $status, LDAP_ERROR);
188  return FALSE;
189  } elseif ($status === FALSE) {
190  return FALSE;
191  }
192  }
193  } catch (FusionDirectoryError $e) {
194  $e->display();
195  return FALSE;
196  }
197  return TRUE;
198  }
199 
201  static function loginProcess ()
202  {
203  global $config, $smarty;
204 
205  $method = $config->get_cfg_value('LoginMethod', '');
206  if (empty($method)) {
207  // Try to detect configurations from FD<1.4
208  if ($config->get_cfg_value('httpAuthActivated') == 'TRUE') {
209  $method = 'LoginHTTPAuth';
210  } elseif ($config->get_cfg_value('casActivated') == 'TRUE') {
211  $method = 'LoginCAS';
212  } elseif ($config->get_cfg_value('httpHeaderAuthActivated') == 'TRUE') {
213  $method = 'LoginHTTPHeader';
214  } else {
215  $method = 'LoginPost';
216  }
217  }
218  try {
219  $method::loginProcess();
220  } catch (Exception $e) {
221  $lang = session::get('lang');
222 
223  $display = '<h1>'.htmlescape(_('An unrecoverable error occurred. Please contact your administator.')).'</h1><p>';
224  if (ini_get('display_errors') == 1) {
225  $display .= nl2br(htmlescape((string)$e));
226  } else {
227  $display .= 'Error detail display is turned off.';
228  }
229  $display .= '</p>'."\n";
230  $smarty->assign('headline', _('Fatal error!'));
231  $smarty->assign('headline_image', 'geticon.php?context=status&icon=dialog-error&size=32');
232  $smarty->assign('usePrototype', 'false');
233  $smarty->assign('date', date('l, dS F Y H:i:s O'));
234  $smarty->assign('lang', preg_replace('/_.*$/', '', $lang));
235  $smarty->assign('rtl', Language::isRTL($lang));
236 
237  $smarty->display(get_template_path('headers.tpl'));
238  echo $display;
239  exit();
240  }
241  }
242 
244  static function getLabel ()
245  {
246  return FALSE;
247  }
248 
249  static function getMethods ()
250  {
251  $methods = [
252  'LoginPost',
253  'LoginCAS',
254  'LoginHTTPAuth',
255  'LoginHTTPHeader',
256  ];
257  $return = [];
258  foreach ($methods as $method) {
259  $label = $method::getLabel();
260  if ($label) {
261  $return[$method] = $label;
262  }
263  }
264  return $return;
265  }
266 }
static loginProcess()
All login steps in the right order.
htmlescape(string $str)
Escape string for HTML output.
Definition: php_setup.inc:32
static runSchemaCheck()
Runs schemaCheck if activated in configuration.
get_ou($name)
Get the OU of a certain RDN.
Definition: functions.inc:391
static load()
Loads plist and load it in config object.
static redirect()
Final step of successful login: redirect to main.php.
get_template_path($filename='', $plugin=FALSE, $path='')
Return themed path for specified base file.
Definition: functions.inc:174
static get($name)
Accessor of a session var.
static log(string $action, string $objecttype, string $object, array $changes=[], string $result='')
logging method
static display($title, string $message, int $type=INFO_DIALOG, array $trace=[])
Display a message dialog.
static deleteByUser(string $userdn)
Remove all locks owned by a specific userdn.
Definition: class_Lock.inc:185
static set($name, $value)
Set a value in a session.
static checkForLockingBranch()
Check if locking LDAP branch is here or create it.
static init($lang=NULL)
Initialize language configuration.
check_schema(array $cfg)
Check if LDAP schema matches the requirements.
Definition: functions.inc:1138
Base class for login methods.
static loginUser(string $username, string $password)
Verify user login against LDAP directory.
static loginAndCheckExpired()
Called after successful login, return FALSE if account is expired.
static un_set($name)
Unset a session.
static connect()
Connect user.
static ldapLoginUser()
Performs an LDAP bind with $username and $password.
static validateUserInput()
Check username for invalid characters and check password is not empty Also trims username.
Parent class for all errors in FusionDirectory.
static isRTL($lang)
Returns TRUE if $lang is a right to left language.
static runSteps($steps)
Run each step in $steps, stop on errors.
static getLabel()
Displayed name for each login method. Returning FALSE disables a method.