golden hour
/home/doctorbruno/public_html/taeionline.com/support/include
⬆️ Go Up
Upload
File/Folder
Size
Actions
.MANIFEST
194.97 KB
Del
OK
.htaccess
14 B
Del
OK
JSON.php
33.13 KB
Del
OK
PasswordHash.php
6.92 KB
Del
OK
Spyc.php
31.74 KB
Del
OK
UniversalClassLoader.php
8.61 KB
Del
OK
ajax.admin.php
7.29 KB
Del
OK
ajax.config.php
4.61 KB
Del
OK
ajax.content.php
9.76 KB
Del
OK
ajax.draft.php
13.03 KB
Del
OK
ajax.email.php
2.33 KB
Del
OK
ajax.export.php
1006 B
Del
OK
ajax.filter.php
874 B
Del
OK
ajax.forms.php
13.84 KB
Del
OK
ajax.i18n.php
5.1 KB
Del
OK
ajax.kbase.php
2.93 KB
Del
OK
ajax.note.php
2.05 KB
Del
OK
ajax.orgs.php
11.73 KB
Del
OK
ajax.plugins.php
2.15 KB
Del
OK
ajax.schedule.php
4.29 KB
Del
OK
ajax.search.php
12.72 KB
Del
OK
ajax.sequence.php
3.2 KB
Del
OK
ajax.staff.php
11.62 KB
Del
OK
ajax.tasks.php
33.87 KB
Del
OK
ajax.thread.php
8.81 KB
Del
OK
ajax.tickets.php
79.7 KB
Del
OK
ajax.tips.php
1.66 KB
Del
OK
ajax.upgrader.php
2.24 KB
Del
OK
ajax.users.php
18.33 KB
Del
OK
api.cron.php
1.06 KB
Del
OK
api.tickets.php
10.36 KB
Del
OK
class.2fa.php
7.76 KB
Del
OK
class.ajax.php
1.45 KB
Del
OK
class.api.php
15.31 KB
Del
OK
class.app.php
1.49 KB
Del
OK
class.attachment.php
6.95 KB
Del
OK
class.auth.php
50.73 KB
Del
OK
class.avatar.php
6.44 KB
Del
OK
class.banlist.php
2.58 KB
Del
OK
class.base32.php
4.07 KB
Del
OK
class.businesshours.php
7.08 KB
Del
OK
class.canned.php
9.05 KB
Del
OK
class.captcha.php
1.73 KB
Del
OK
class.category.php
11.14 KB
Del
OK
class.charset.php
3.41 KB
Del
OK
class.cli.php
9.48 KB
Del
OK
class.client.php
15.39 KB
Del
OK
class.collaborator.php
5.65 KB
Del
OK
class.company.php
2.65 KB
Del
OK
class.config.php
60.36 KB
Del
OK
class.controller.php
1.15 KB
Del
OK
class.cron.php
3.6 KB
Del
OK
class.crypto.php
18.92 KB
Del
OK
class.csrf.php
2.38 KB
Del
OK
class.dept.php
33.21 KB
Del
OK
class.dispatcher.php
7.12 KB
Del
OK
class.draft.php
6.23 KB
Del
OK
class.dynamic_forms.php
64.98 KB
Del
OK
class.email.php
49 KB
Del
OK
class.error.php
1.69 KB
Del
OK
class.export.php
32.92 KB
Del
OK
class.faq.php
15.05 KB
Del
OK
class.file.php
34.59 KB
Del
OK
class.filter.php
31.9 KB
Del
OK
class.filter_action.php
23.67 KB
Del
OK
class.format.php
44.3 KB
Del
OK
class.forms.php
195.58 KB
Del
OK
class.http.php
7.16 KB
Del
OK
class.i18n.php
24.28 KB
Del
OK
class.import.php
6.9 KB
Del
OK
class.json.php
2.71 KB
Del
OK
class.knowledgebase.php
5.8 KB
Del
OK
class.list.php
42 KB
Del
OK
class.lock.php
4.05 KB
Del
OK
class.log.php
1.55 KB
Del
OK
class.mail.php
35.76 KB
Del
OK
class.mailer.php
25.75 KB
Del
OK
class.mailfetch.php
9.72 KB
Del
OK
class.mailparse.php
30.99 KB
Del
OK
class.message.php
6.42 KB
Del
OK
class.migrater.php
5.2 KB
Del
OK
class.misc.php
7.5 KB
Del
OK
class.model.php
2.3 KB
Del
OK
class.nav.php
14.15 KB
Del
OK
class.note.php
2.39 KB
Del
OK
class.oauth2.php
4.04 KB
Del
OK
class.organization.php
22.45 KB
Del
OK
class.orm.php
120.9 KB
Del
OK
class.osticket.php
20.64 KB
Del
OK
class.ostsession.php
21.86 KB
Del
OK
class.page.php
10.59 KB
Del
OK
class.pagenate.php
5.53 KB
Del
OK
class.passwd.php
1.21 KB
Del
OK
class.pdf.php
3.83 KB
Del
OK
class.plugin.php
36.13 KB
Del
OK
class.priority.php
1.81 KB
Del
OK
class.queue.php
103.51 KB
Del
OK
class.report.php
11.76 KB
Del
OK
class.role.php
11.27 KB
Del
OK
class.schedule.php
46.28 KB
Del
OK
class.search.php
61.66 KB
Del
OK
class.sequence.php
7.27 KB
Del
OK
class.session.php
19.25 KB
Del
OK
class.setup.php
3.55 KB
Del
OK
class.signal.php
4.16 KB
Del
OK
class.sla.php
9.11 KB
Del
OK
class.staff.php
60.21 KB
Del
OK
class.task.php
56.21 KB
Del
OK
class.team.php
12.31 KB
Del
OK
class.template.php
23.45 KB
Del
OK
class.thread.php
108.96 KB
Del
OK
class.thread_actions.php
17.08 KB
Del
OK
class.ticket.php
167.39 KB
Del
OK
class.timezone.php
21.94 KB
Del
OK
class.topic.php
20.06 KB
Del
OK
class.translation.php
34.79 KB
Del
OK
class.upgrader.php
13.76 KB
Del
OK
class.user.php
43.4 KB
Del
OK
class.usersession.php
7.55 KB
Del
OK
class.util.php
10.07 KB
Del
OK
class.validator.php
12.94 KB
Del
OK
class.variable.php
11.93 KB
Del
OK
class.xml.php
3.23 KB
Del
OK
class.yaml.php
1.15 KB
Del
OK
cli
-
Del
OK
client
-
Del
OK
config
-
Del
OK
fpdf
-
Del
OK
htmLawed.php
53.53 KB
Del
OK
html2text.php
33.71 KB
Del
OK
i18n
-
Del
OK
index.php
37 B
Del
OK
laminas-mail
-
Del
OK
mpdf
-
Del
OK
mysqli.php
9.55 KB
Del
OK
ost-config.php
5.63 KB
Del
OK
ost-sampleconfig.php
6.24 KB
Del
OK
pear
-
Del
OK
plugins
-
Del
OK
staff
-
Del
OK
tnef_decoder.php
19.82 KB
Del
OK
upgrader
-
Del
OK
Edit: class.auth.php
<?php require_once(INCLUDE_DIR.'class.2fa.php'); interface AuthenticatedUser { // Get basic information function getId(); function getUsername(); function getUserType(); // Get password reset timestamp function getPasswdResetTimestamp(); //Backend used to authenticate the user function getAuthBackend(); // Get 2FA Backend function get2FABackend(); //Authentication key function setAuthKey($key); function getAuthKey(); // logOut the user function logOut(); // Signal method to allow performing extra things when a user is logged // into the sysem function onLogin($bk); } abstract class BaseAuthenticatedUser implements AuthenticatedUser { //Authorization key returned by the backend used to authorize the user private $authkey; // Get basic information abstract function getId(); abstract function getUsername(); abstract function getUserType(); // Get password reset timestamp function getPasswdResetTimestamp() { return null; } //Backend used to authenticate the user abstract function getAuthBackend(); // Get 2FA Backend abstract function get2FABackend(); //Authentication key function setAuthKey($key) { $this->authkey = $key; } function getAuthKey() { return $this->authkey; } // logOut the user function logOut() { if ($bk = $this->getAuthBackend()) return $bk->signOut($this); return false; } // Signal method to allow performing extra things when a user is logged // into the sysem function onLogin($bk) {} } require_once(INCLUDE_DIR.'class.ostsession.php'); require_once(INCLUDE_DIR.'class.usersession.php'); interface AuthDirectorySearch { /** * Indicates if the backend can be used to search for user information. * Lookup is performed to find user information based on a unique * identifier. */ function lookup($id); /** * Indicates if the backend supports searching for usernames. This is * distinct from information lookup in that lookup is intended to lookup * information based on a unique identifier */ function search($query); } /** * Class: ClientCreateRequest * * Simple container to represent a remote authentication success for a * client which should be imported into the local database. The class will * provide access to the backend that authenticated the user, the username * that the user entered when logging in, and any other information about * the user that the backend was able to lookup. Generally, this extra * information would be the same information retrieved from calling the * AuthDirectorySearch::lookup() method. */ class ClientCreateRequest { var $backend; var $username; var $info; function __construct($backend, $username, $info=array()) { $this->backend = $backend; $this->username = $username; $this->info = $info; } function getBackend() { return $this->backend; } function setBackend($what) { $this->backend = $what; } function getUsername() { return $this->username; } function getInfo() { return $this->info; } function attemptAutoRegister() { global $cfg; if (!$cfg || $cfg->isClientRegistrationMode(['disabled'])) return false; // Attempt to automatically register $this_form = UserForm::getUserForm()->getForm($this->getInfo()); $bk = $this->getBackend(); $defaults = array( 'timezone' => $cfg->getDefaultTimezone(), 'username' => $this->getUsername(), ); if ($bk->supportsInteractiveAuthentication()) // User can only be authenticated against this backend $defaults['backend'] = $bk->getBkId(); if ($this_form->isValid(function($f) { return !$f->isVisibleToUsers(); }) && ($U = User::fromVars($this_form->getClean())) && ($acct = ClientAccount::createForUser($U, $defaults)) // Confirm and save the account && $acct->confirm() // Login, since `tickets.php` will not attempt SSO && ($cl = new ClientSession(new EndUser($U))) && ($bk->login($cl, $bk))) return $cl; } } /** * Authorization backend * * Abstract service registry to provide authorization backends. */ abstract class OAuth2Backend extends ServiceRegistry { static $name; static $id; protected $config; static function register($bk) { if (is_string($bk) && class_exists($bk)) $bk = new $bk(); if (!is_object($bk) || !($bk instanceof OAuth2Backend)) return false; static::$registry[$bk->getBkId()] = $bk; } static function allRegistered() { return self::getRegistry(); } static function getBackend($id) { if ($id && ($registry = static::allRegistered()) && isset($registry[$id])) return $registry[$id]; } } abstract class OAuth2AuthorizationBackend extends OAuth2Backend { static protected $registry = array(); static function allRegistered() { return array_merge(self::$registry, parent::allRegistered()); } static function getBackend($id) { if (($bk=parent::getBackend($id))) return $bk; // Overloaded backend load. list($auth, $a, $i) = self::parseId($id); if ($auth && ($bk=parent::getBackend($auth))) return $bk; } static function parseId($id) { return preg_split('/(?(?<=oauth2):(?!\w+)|:)/', $id); } abstract function getConfigForm($configId); abstract function triggerEmailAuth($id); abstract function callback($resp, $ref); } abstract class OAuth2AuthenticationBackend extends OAuth2Backend { static protected $registry = array(); static function allRegistered() { return array_merge(self::$registry, parent::allRegistered()); } static function getBackend($id) { if (($bk=parent::getBackend($id))) return $bk; } } /** * Authentication backend * * Authentication provides the basis of abstracting the link between the * login page with a username and password and the staff member, * administrator, or client using the system. * * The system works by allowing the AUTH_BACKENDS setting from * ost-config.php to determine the list of authentication backends or * providers and also specify the order they should be evaluated in. * * The authentication backend should define a authenticate() method which * receives a username and optional password. If the authentication * succeeds, an instance deriving from <User> should be returned. */ abstract class AuthenticationBackend extends ServiceRegistry { static $name; static $id; protected $config; static function register($bk) { if (is_string($bk) && class_exists($bk)) $bk = new $bk(); if (!is_object($bk) || !($bk instanceof AuthenticationBackend)) return false; static::$registry[$bk->getBkId()] = $bk; } static function allRegistered() { return self::getRegistry(); } static function getInteractive() { $backends = []; foreach (static::allRegistered() as $bk) if ($bk->isInteractive()) $backends[] = $bk; return $backends; } static function getExternal() { $backends = []; foreach (static::allRegistered() as $bk) if ($bk->isExternal()) $backends[] = $bk; return $backends; } static function getBackend($id) { if ($id && ($backends = static::allRegistered()) && isset($backends[$id])) return $backends[$id]; } static function lookupBackend($id, $class=null) { // Lookup both registries if (!($bk = StaffAuthenticationBackend::getBackend($id))) $bk = UserAuthenticationBackend::getBackend($id); // See if specific type is requesred. if ($class && !($bk instanceof $class)) return null; return $bk; } static function getSearchDirectoryBackend($id) { if ($id && ($backends = static::getSearchDirectories()) && isset($backends[$id])) return $backends[$id]; } /* * Allow the backend to do login audit depending on the result * This is mainly used to track failed login attempts */ static function authAudit($result, $credentials=null) { if (!$result) return; foreach (static::allRegistered() as $bk) $bk->audit($result, $credentials); } static function process($username, $password=null, &$errors=array()) { if (!$username) return false; $backends = static::getAllowedBackends($username); foreach (static::allRegistered() as $bk) { if ($backends //Allowed backends && $bk->supportsInteractiveAuthentication() && !in_array($bk->getBkId(), $backends)) // User cannot be authenticated against this backend continue; // All backends are queried here, even if they don't support // authentication so that extensions like lockouts and audits // can be supported. try { $result = $bk->authenticate($username, $password); if ($result instanceof AuthenticatedUser && ($bk->login($result, $bk))) return $result; elseif ($result instanceof ClientCreateRequest && $bk instanceof UserAuthenticationBackend) return $result; elseif ($result instanceof AccessDenied) { break; } } catch (AccessDenied $e) { $result = $e; break; } } if (!$result) $result = new AccessDenied(__('Access denied')); if ($result && $result instanceof AccessDenied) $errors['err'] = $result->reason; $info = array('username' => $username, 'password' => $password); Signal::send('auth.login.failed', null, $info); self::authAudit($result, $info); } /* * Attempt to process non-interactive sign-on e.g HTTP-Passthrough * * $forcedAuth - indicate if authentication is required. * */ static function processSignOn(&$errors, $forcedAuth=true) { foreach (static::allRegistered() as $bk) { // All backends are queried here, even if they don't support // authentication so that extensions like lockouts and audits // can be supported. try { $result = $bk->signOn(); if ($result instanceof AuthenticatedUser) { //Perform further Object specific checks and the actual login if (!$bk->login($result, $bk)) continue; return $result; } elseif ($result instanceof ClientCreateRequest && $bk instanceof UserAuthenticationBackend) return $result; elseif ($result instanceof AccessDenied) { break; } } catch (AccessDenied $e) { $result = $e; break; } } if (!$result && $forcedAuth) $result = new AccessDenied(__('Unknown user')); if ($result && $result instanceof AccessDenied) $errors['err'] = $result->reason; self::authAudit($result); } static function getSearchDirectories() { $backends = array(); foreach (StaffAuthenticationBackend::allRegistered() as $bk) if ($bk instanceof AuthDirectorySearch) $backends[$bk->getBkId()] = $bk; foreach (UserAuthenticationBackend::allRegistered() as $bk) if ($bk instanceof AuthDirectorySearch) $backends[$bk->getBkId()] = $bk; return array_unique($backends, SORT_REGULAR); } static function searchUsers($query) { $users = array(); foreach (static::getSearchDirectories() as $bk) $users = array_merge($users, $bk->search($query)); return $users; } /** * Indicates if the backed supports authentication. Useful if the * backend is used for logging or lockout only */ function supportsInteractiveAuthentication() { return true; } /** * Alias for supportsInteractiveAuthentication() * */ function isInteractive() { return $this->supportsInteractiveAuthentication(); } /** * Indicates of the backend is External Authentication */ function isExternal() { return ($this instanceof ExternalAuthentication); } /** * Indicates if the backend supports changing a user's password. This * would be done in two fashions. Either the currently-logged in user * want to change its own password or a user requests to have their * password reset. This requires an administrative privilege which this * backend might not possess, so it's defined in supportsPasswordReset() */ function supportsPasswordChange() { return false; } /** * Get supported password policies for the backend. * */ function getPasswordPolicies($user=null) { return PasswordPolicy::allActivePolicies(); } /** * Request the backend to update the password for a user. This method is * the main entry for password updates so that password policies can be * applied to the new password before passing the new password to the * backend for updating. * * Throws: * BadPassword — if password does not meet policy requirement * PasswordUpdateFailed — if backend failed to update the password */ function setPassword($user, $password, $current=false) { foreach ($this->getPasswordPolicies($user) as $P) $P->onSet($password, $current); $rv = $this->syncPassword($user, $password); if ($rv) { $info = array('password' => $password, 'current' => $current); Signal::send('auth.pwchange', $user, $info); } return $rv; } /* * Request the backend to check the policies for a just logged * in user. * Throws: BadPassword & ExpiredPassword - for password related failures */ function checkPolicies($user, $password) { // Password policies foreach ($this->getPasswordPolicies($user) as $P) $P->onLogin($user, $password); } /** * Request the backend to update the user's password with the password * given. This method should only be used if the backend advertises * supported password updates with the supportsPasswordChange() method. * * Returns: * true if the password was successfully updated and false otherwise. */ protected function syncPassword($user, $password) { return false; } function supportsPasswordReset() { return false; } function signOn() { return null; } protected function validate($auth) { return null; } protected function audit($result, $credentials) { return null; } abstract function authenticate($username, $password); abstract function login($user, $bk); abstract static function getUser(); //Validates authenticated users. abstract static function getAllowedBackends($userid); abstract protected function getAuthKey($user); abstract static function signOut($user); } /** * ExternalAuthenticationBackend * * External authentication backends are backends such as Google+ which * require a redirect to a remote site and a redirect back to osTicket in * order for a user to be authenticated. For such backends, neither the * username and password fields nor single sign on alone can be used to * authenticate the user. */ interface ExternalAuthentication { /** * Requests the backend to render an external link box. When the user * clicks this box, the backend will be prompted to redirect the user to * the remote site for authentication there. */ function renderExternalLink(); /** * Function: getServiceName * * Called to get the service name displayed on login page. */ function getServiceName(); /** * Function: triggerAuth * * Called when a user clicks the button rendered in the * ::renderExternalLink() function. This method should initiate the * remote authentication mechanism. */ function triggerAuth(); } abstract class StaffAuthenticationBackend extends AuthenticationBackend { static protected $registry = array(); static function allRegistered() { return array_merge(self::$registry, parent::allRegistered()); } static function isBackendAllowed($staff, $bk) { if (!($backends=self::getAllowedBackends($staff->getId()))) return true; //No restrictions return in_array($bk->getBkId(), array_map('strtolower', $backends)); } function getPasswordPolicies($user=null) { global $cfg; $policies = PasswordPolicy::allActivePolicies(); if ($cfg && ($policy = $cfg->getStaffPasswordPolicy())) { foreach ($policies as $P) if ($policy == $P->getBkId()) return array($P); } return $policies; } static function getAllowedBackends($userid) { $backends =array(); //XXX: Only one backend can be specified at the moment. $sql = 'SELECT backend FROM '.STAFF_TABLE .' WHERE backend IS NOT NULL '; if (is_numeric($userid)) $sql.= ' AND staff_id='.db_input($userid); else { $sql.= ' AND (username='.db_input($userid) .' OR email='.db_input($userid).')'; } if (($res=db_query($sql, false)) && db_num_rows($res)) $backends[] = db_result($res); return array_filter($backends); } function login($staff, $bk) { global $ost; if (!$bk || !($staff instanceof Staff)) return false; // Ensure staff is allowed for realz to be authenticated via the backend. if (!static::isBackendAllowed($staff, $bk) || !($authkey=$bk->getAuthKey($staff))) return false; //Log debug info. $ost->logDebug(_S('Agent Login'), sprintf(_S("%s logged in [%s], via %s"), $staff->getUserName(), $_SERVER['REMOTE_ADDR'], get_class($bk))); //Debug. $agent = Staff::lookup($staff->getId()); $type = array('type' => 'login'); Signal::send('person.login', $agent, $type); // Check if the agent has 2fa enabled $auth2fa = null; if (($_2fa = $staff->get2FABackend()) && ($token=$_2fa->send($staff))) { $auth2fa = sprintf('%s:%s:%s', $_2fa->getId(), md5($token.$staff->getId()), time()); } // Tag the authkey. $authkey = $bk->getBkId().':'.$authkey; // Now set session crap and lets roll baby! $authsession = &$_SESSION['_auth']['staff']; $authsession = array(); //clear. $authsession['id'] = $staff->getId(); $authsession['key'] = $authkey; $authsession['2fa'] = $auth2fa; // Set TIME_BOMB to regenerate the session 10 seconds after login $_SESSION['TIME_BOMB'] = time() + 10; // Set session token $staff->setSessionToken(); // Set Auth Key $staff->setAuthKey($authkey); Signal::send('auth.login.succeeded', $staff); if ($bk->supportsInteractiveAuthentication()) $staff->cancelResetTokens(); // Update last-used language, login time, etc $staff->onLogin($bk); return true; } /* Base signOut * * Backend should extend the signout and perform any additional signout * it requires. */ static function signOut($staff) { global $ost; $_SESSION['_auth']['staff'] = array(); unset($_SESSION[':token']['staff']); $ost->logDebug(_S('Agent logout'), sprintf(_S("%s logged out [%s]"), $staff->getUserName(), $_SERVER['REMOTE_ADDR'])); //Debug. $agent = Staff::lookup($staff->getId()); $type = array('type' => 'logout'); Signal::send('person.logout', $agent, $type); Signal::send('auth.logout', $staff); } // Called to get authenticated user (if any) static function getUser() { if (!isset($_SESSION['_auth']['staff']) || !$_SESSION['_auth']['staff']['key']) return null; list($id, $auth) = explode(':', $_SESSION['_auth']['staff']['key']); if (!($bk=static::getBackend($id)) //get the backend || !($staff = $bk->validate($auth)) //Get AuthicatedUser || !($staff instanceof Staff) || !$staff->isActive() || $staff->getId() != $_SESSION['_auth']['staff']['id'] // check ID ) return null; $staff->setAuthKey($_SESSION['_auth']['staff']['key']); return $staff; } function authenticate($username, $password) { return false; } // Generic authentication key for staff's backend is the username protected function getAuthKey($staff) { if(!($staff instanceof Staff)) return null; return $staff->getUsername(); } protected function validate($authkey) { if (($staff = StaffSession::lookup($authkey)) && $staff->getId() && $staff->isActive()) return $staff; } } abstract class ExternalStaffAuthenticationBackend extends StaffAuthenticationBackend implements ExternalAuthentication { static $fa_icon = "signin"; static $sign_in_image_url = false; static $service_name = "External"; function getServiceName() { return __(static::$service_name); } function renderExternalLink() { $service = sprintf('%s %s', __('Sign in with'), $this->getServiceName()); ?> <a class="external-sign-in" title="<?php echo $service; ?>" href="login.php?do=ext&bk=<?php echo urlencode($this->getBkId()); ?>"> <?php if (static::$sign_in_image_url) { ?> <img class="sign-in-image" src="<?php echo static::$sign_in_image_url; ?>" alt="<?php echo $service; ?>"/> <?php } else { ?> <div class="external-auth-box"> <span class="external-auth-icon"> <i class="icon-<?php echo static::$fa_icon; ?> icon-large icon-fixed-with"></i> </span> <span class="external-auth-name"> <?php echo $service; ?> </span> </div> <?php } ?> </a><?php } function triggerAuth() { $_SESSION['ext:bk:id'] = $this->getBkId(); // For legacy plugins prior to v1.17 $_SESSION['ext:bk:class'] = get_class($this); } } Signal::connect('api', function($dispatcher) { $dispatcher->append( url('^/auth/ext$', function() { if ($id = $_SESSION['ext:bk:id']) { $bk = StaffAuthenticationBackend::getBackend($id) ?: UserAuthenticationBackend::getBackend($id); if ($bk instanceof ExternalAuthentication) $bk->triggerAuth(); } }) ); }); abstract class UserAuthenticationBackend extends AuthenticationBackend { static protected $registry = array(); static function allRegistered() { return array_merge(self::$registry, parent::allRegistered()); } function getPasswordPolicies($user=null) { global $cfg; $policies = PasswordPolicy::allActivePolicies(); if ($cfg && ($policy = $cfg->getClientPasswordPolicy())) { foreach ($policies as $P) if ($policy == $P->getId()) return array($P); } return $policies; } static function getAllowedBackends($userid) { $backends = array(); $sql = 'SELECT A1.backend FROM '.USER_ACCOUNT_TABLE .' A1 INNER JOIN '.USER_EMAIL_TABLE.' A2 ON (A2.user_id = A1.user_id)' .' WHERE backend IS NOT NULL ' .' AND (A1.username='.db_input($userid) .' OR A2.`address`='.db_input($userid).')'; if (!($res=db_query($sql, false))) return $backends; while (list($bk) = db_fetch_row($res)) $backends[] = $bk; return array_filter($backends); } function login($user, $bk) { global $ost; if (!$user || !$bk || !$bk->getBkId() //Must have ID || !($authkey = $bk->getAuthKey($user))) return false; $acct = $user->getAccount(); if ($acct) { if (!$acct->isConfirmed()) throw new AccessDenied(__('Account confirmation required')); elseif ($acct->isLocked()) throw new AccessDenied(__('Account is administratively locked')); } // Tag the user and associated ticket in the SESSION $this->setAuthKey($user, $bk, $authkey); // Set Session Token $user->setSessionToken(); //The backend used decides the format of the auth key. // XXX: encrypt to hide the bk?? $user->setAuthKey($authkey); //Log login info... $msg=sprintf(_S('%1$s (%2$s) logged in [%3$s]' /* Tokens are <username>, <id>, and <ip> */), $user->getUserName(), $user->getId(), $_SERVER['REMOTE_ADDR']); $ost->logDebug(_S('User login'), $msg); $u = $user->getSessionUser()->getUser(); $type = array('type' => 'login'); Signal::send('person.login', $u, $type); // Set TIME_BOMB to regenerate the session 10 seconds after login $_SESSION['TIME_BOMB'] = time() + 10; if ($bk->supportsInteractiveAuthentication() && ($acct=$user->getAccount())) $acct->cancelResetTokens(); // Update last-used language, login time, etc $user->onLogin($bk); return true; } function setAuthKey($user, $bk, $key=false) { $authkey = $key ?: $bk->getAuthKey($user); //Tag the authkey. $authkey = $bk->getBkId().':'.$authkey; //Set the session goodies $authsession = &$_SESSION['_auth']['user']; $authsession = array(); //clear. $authsession['id'] = $user->getId(); $authsession['key'] = $authkey; } function authenticate($username, $password) { return false; } static function signOut($user) { global $ost; $_SESSION['_auth']['user'] = array(); unset($_SESSION[':token']['client']); $ost->logDebug(_S('User logout'), sprintf(_S("%s logged out [%s]" /* Tokens are <username> and <ip> */), $user->getUserName(), $_SERVER['REMOTE_ADDR'])); $u = $user->getSessionUser()->getUser(); $type = array('type' => 'logout'); Signal::send('person.logout', $u, $type); } protected function getAuthKey($user) { return $user->getId(); } static function getUser() { if (!isset($_SESSION['_auth']['user']) || !$_SESSION['_auth']['user']['key']) return null; list($id, $auth) = explode(':', $_SESSION['_auth']['user']['key']); if (!($bk=static::getBackend($id)) //get the backend || !($user=$bk->validate($auth)) //Get AuthicatedUser || !($user instanceof AuthenticatedUser) // Make sure it user || $user->getId() != $_SESSION['_auth']['user']['id'] // check ID ) return null; if (($account=$user->getAccount()) && !$account->isActive()) return null; $user->setAuthKey($_SESSION['_auth']['user']['key']); return $user; } protected function validate($userid) { if (!($user = User::lookup($userid))) return false; elseif (!($account=$user->getAccount())) return false; elseif (!$account->isActive()) return false; return new ClientSession(new EndUser($user)); } } abstract class ExternalUserAuthenticationBackend extends UserAuthenticationBackend implements ExternalAuthentication { static $fa_icon = "signin"; static $sign_in_image_url = false; static $service_name = "External"; function getServiceName() { return __(static::$service_name); } function renderExternalLink() { $service = sprintf('%s %s', __('Sign in with'), $this->getServiceName()); ?> <a class="external-sign-in" title="<?php echo $service; ?>" href="login.php?do=ext&bk=<?php echo urlencode($this->getBkId()); ?>"> <?php if (static::$sign_in_image_url) { ?> <img class="sign-in-image" src="<?php echo static::$sign_in_image_url; ?>" alt="<?php echo $service; ?>"/> <?php } else { ?> <div class="external-auth-box"> <span class="external-auth-icon"> <i class="icon-<?php echo static::$fa_icon; ?> icon-large icon-fixed-with"></i> </span> <span class="external-auth-name"> <?php echo $service; ?> </span> </div> <?php } ?> </a><?php } function triggerAuth() { $_SESSION['ext:bk:id'] = $this->getBkId(); // Legacy for plugins prior to v1.17 $_SESSION['ext:bk:class'] = get_class($this); } } /** * This will be an exception in later versions of PHP */ class AccessDenied extends Exception { function __construct($reason) { $this->reason = $reason; parent::__construct($reason); } } /** * Simple authentication backend which will lock the login form after a * configurable number of attempts */ abstract class AuthStrikeBackend extends AuthenticationBackend { function authenticate($username, $password=null) { return static::authTimeout(); } function signOn() { return static::authTimeout(); } static function signOut($user) { return false; } function login($user, $bk) { return false; } static function getUser() { return null; } function supportsInteractiveAuthentication() { return false; } static function getAllowedBackends($userid) { return array(); } function getAuthKey($user) { return null; } //Provides audit facility for logins attempts function audit($result, $credentials) { //Count failed login attempts as a strike. if ($result instanceof AccessDenied) return static::authStrike($credentials); } abstract static function authStrike($credentials); abstract static function authTimeout(); } /* * Backend to monitor staff's failed login attempts */ class StaffAuthStrikeBackend extends AuthStrikeBackend { static $id = "authstrike.staff"; static function authTimeout() { global $ost; $cfg = $ost->getConfig(); $authsession = &$_SESSION['_auth']['staff']; if (!isset($authsession['laststrike'])) return; //Veto login due to excessive login attempts. if((time()-$authsession['laststrike'])<$cfg->getStaffLoginTimeout()) { $authsession['laststrike'] = time(); //reset timer. return new AccessDenied(__('Maximum failed login attempts reached')); } //Timeout is over. //Reset the counter for next round of attempts after the timeout. $authsession['laststrike']=null; $authsession['strikes']=0; } static function authstrike($credentials) { global $ost; $cfg = $ost->getConfig(); $authsession = &$_SESSION['_auth']['staff']; $username = $credentials['username']; $authsession['strikes']+=1; if($authsession['strikes']>$cfg->getStaffMaxLogins()) { $authsession['laststrike']=time(); $timeout = $cfg->getStaffLoginTimeout()/60; $alert=_S('Excessive login attempts by an agent?')."\n" ._S('Username').": $username\n" ._S('IP').": {$_SERVER['REMOTE_ADDR']}\n" ._S('Time').": ".date('M j, Y, g:i a T')."\n\n" ._S('Attempts').": {$authsession['strikes']}\n" ._S('Timeout').": ".sprintf(_N('%d minute', '%d minutes', $timeout), $timeout)."\n\n"; $admin_alert = ($cfg->alertONLoginError() == 1) ? TRUE : FALSE; $ost->logWarning(sprintf(_S('Excessive login attempts (%s)'),$username), $alert, $admin_alert); if ($username) { $agent = Staff::lookup($username); $type = array('type' => 'login', 'msg' => sprintf('Excessive login attempts (%s)', $authsession['strikes'])); Signal::send('person.login', $agent, $type); } return new AccessDenied(__('Forgot your login info? Contact Admin.')); //Log every other third failed login attempt as a warning. } elseif($authsession['strikes']%3==0) { $alert=_S('Username').": {$username}\n" ._S('IP').": {$_SERVER['REMOTE_ADDR']}\n" ._S('Time').": ".date('M j, Y, g:i a T')."\n\n" ._S('Attempts').": {$authsession['strikes']}"; $ost->logWarning(sprintf(_S('Failed agent login attempt (%s)'),$username), $alert, false); } } } StaffAuthenticationBackend::register('StaffAuthStrikeBackend'); /* * Backend to monitor user's failed login attempts */ class UserAuthStrikeBackend extends AuthStrikeBackend { static $id = "authstrike.user"; static function authTimeout() { global $ost; $cfg = $ost->getConfig(); $authsession = &$_SESSION['_auth']['user']; if (!$authsession['laststrike']) return; //Veto login due to excessive login attempts. if ((time()-$authsession['laststrike']) < $cfg->getStaffLoginTimeout()) { $authsession['laststrike'] = time(); //reset timer. return new AccessDenied(__("You've reached maximum failed login attempts allowed.")); } //Timeout is over. //Reset the counter for next round of attempts after the timeout. $authsession['laststrike']=null; $authsession['strikes']=0; } static function authstrike($credentials) { global $ost; $cfg = $ost->getConfig(); $authsession = &$_SESSION['_auth']['user']; $username = $credentials['username']; $password = $credentials['password']; $authsession['strikes']+=1; if($authsession['strikes']>$cfg->getClientMaxLogins()) { $authsession['laststrike'] = time(); $alert=_S('Excessive login attempts by a user.')."\n". _S('Username').": {$username}\n". _S('IP').": {$_SERVER['REMOTE_ADDR']}\n". _S('Time').": ".date('M j, Y, g:i a T')."\n\n". _S('Attempts').": {$authsession['strikes']}"; $admin_alert = ($cfg->alertONLoginError() == 1 ? TRUE : FALSE); $ost->logError(_S('Excessive login attempts (user)'), $alert, $admin_alert); if ($username) { $account = UserAccount::lookupByUsername($username); $id = UserEmailModel::getIdByEmail($username); if ($account) $user = User::lookup($account->user_id); elseif ($id) $user = User::lookup($id); if ($user) { $type = array('type' => 'login', 'msg' => sprintf('Excessive login attempts (%s)', $authsession['strikes'])); Signal::send('person.login', $user, $type); } } return new AccessDenied(__('Access denied')); } elseif($authsession['strikes']%3==0) { //Log every third failed login attempt as a warning. $alert=_S('Username').": {$username}\n". _S('IP').": {$_SERVER['REMOTE_ADDR']}\n". _S('Time').": ".date('M j, Y, g:i a T')."\n\n". _S('Attempts').": {$authsession['strikes']}"; $ost->logWarning(_S('Failed login attempt (user)'), $alert, false); } } } UserAuthenticationBackend::register('UserAuthStrikeBackend'); class osTicketStaffAuthentication extends StaffAuthenticationBackend { static $name = "Local Authentication"; static $id = "local"; function authenticate($username, $password) { if (($user = StaffSession::lookup($username)) && $user->getId() && $user->check_passwd($password)) { try { $this->checkPolicies($user, $password); } catch (BadPassword | ExpiredPassword $ex) { $user->change_passwd = 1; } return $user; } } function supportsPasswordChange() { return true; } function syncPassword($staff, $password) { $staff->passwd = Passwd::hash($password); } static function checkPassword($new, $current) { PasswordPolicy::checkPassword($new, $current, new self()); } } StaffAuthenticationBackend::register('osTicketStaffAuthentication'); class PasswordResetTokenBackend extends StaffAuthenticationBackend { static $id = "pwreset.staff"; function supportsInteractiveAuthentication() { return false; } function signOn($errors=array()) { global $ost; if (!isset($_POST['userid']) || !isset($_POST['token'])) return false; elseif (!($_config = new Config('pwreset'))) return false; $staff = StaffSession::lookup($_POST['userid']); if (!$staff || !$staff->getId()) $errors['msg'] = __('Invalid user-id given'); elseif (!($id = $_config->get($_POST['token'])) || $id != $staff->getId()) $errors['msg'] = __('Invalid reset token'); elseif (!($ts = $_config->lastModified($_POST['token'])) && ($ost->getConfig()->getPwResetWindow() < (time() - strtotime($ts)))) $errors['msg'] = __('Invalid reset token'); elseif (!$staff->forcePasswdRest()) $errors['msg'] = __('Unable to reset password'); else return $staff; } function login($staff, $bk) { $_SESSION['_staff']['reset-token'] = $_POST['token']; Signal::send('auth.pwreset.login', $staff); return parent::login($staff, $bk); } } StaffAuthenticationBackend::register('PasswordResetTokenBackend'); /* * AuthToken Authentication Backend * * Provides auto-login facility for end users with valid link * * Ticket used to loggin is tracked durring the session this is * important in the future when auto-logins will be * limited to single ticket view. */ class AuthTokenAuthentication extends UserAuthenticationBackend { static $name = "Auth Token Authentication"; static $id = "authtoken"; function signOn() { global $cfg; if (!$cfg || !$cfg->isAuthTokenEnabled()) return null; $user = null; if ($_GET['auth']) { if (($u = TicketUser::lookupByToken($_GET['auth']))) $user = new ClientSession($u); } // Support old ticket based tokens. elseif ($_GET['t'] && $_GET['e'] && $_GET['a']) { if (($ticket = Ticket::lookupByNumber($_GET['t'], $_GET['e'])) // Using old ticket auth code algo - hardcoded here because it // will be removed in ticket class in the upcoming rewrite && strcasecmp((string) $_GET['a'], md5($ticket->getId() . strtolower($_GET['e']) . SECRET_SALT)) === 0 && ($owner = $ticket->getOwner())) $user = new ClientSession($owner); } return $user; } function supportsInteractiveAuthentication() { return false; } protected function getAuthKey($user) { if (!$user) return null; //Generate authkey based the type of ticket user // It's required to validate users going forward. $authkey = sprintf('%s%dt%dh%s', //XXX: Placeholder ($user->isOwner() ? 'o':'c'), $user->getId(), $user->getTicketId(), md5($user->getId().$this->id)); return $authkey; } protected function validate($authkey) { $regex = '/^(?P<type>\w{1})(?P<id>\d+)t(?P<tid>\d+)h(?P<hash>.*)$/i'; $matches = array(); if (!preg_match($regex, $authkey, $matches)) return false; $user = null; switch ($matches['type']) { case 'c': //Collaborator $criteria = array( 'user_id' => $matches['id'], 'thread__ticket__ticket_id' => $matches['tid'] ); if (($c = Collaborator::lookup($criteria)) && ($c->getTicketId() == $matches['tid'])) $user = new ClientSession($c); break; case 'o': //Ticket owner if (($ticket = Ticket::lookup($matches['tid'])) && ($o = $ticket->getOwner()) && ($o->getId() == $matches['id'])) $user = new ClientSession($o); break; } //Make sure the authkey matches. if (!$user || strcmp($this->getAuthKey($user), $authkey)) return null; $user->flagGuest(); return $user; } } UserAuthenticationBackend::register('AuthTokenAuthentication'); //Simple ticket lookup backend used to recover ticket access link. // We're using authentication backend so we can guard aganist brute force // attempts (which doesn't buy much since the link is emailed) class AccessLinkAuthentication extends UserAuthenticationBackend { static $name = "Ticket Access Link Authentication"; static $id = "authlink"; function authenticate($email, $number) { if (!($ticket = Ticket::lookupByNumber($number)) || !($user=User::lookup(array('emails__address' => $email)))) return false; if (!($user = $this->_getTicketUser($ticket, $user))) return false; $_SESSION['_auth']['user-ticket'] = $number; return new ClientSession($user); } function _getTicketUser($ticket, $user) { // Ticket owner? if ($ticket->getUserId() == $user->getId()) $user = $ticket->getOwner(); // Collaborator? elseif (!($user = Collaborator::lookup(array( 'user_id' => $user->getId(), 'thread__ticket__ticket_id' => $ticket->getId()) ))) return false; //Bro, we don't know you! return $user; } // We are not actually logging in the user.... function login($user, $bk) { global $cfg; if (!$cfg->isClientEmailVerificationRequired()) { return parent::login($user, $bk); } return true; } protected function validate($userid) { $number = $_SESSION['_auth']['user-ticket']; if (!($ticket = Ticket::lookupByNumber($number))) return false; if (!($user = User::lookup($userid))) return false; if (!($user = $this->_getTicketUser($ticket, $user))) return false; $user = new ClientSession($user); $user->flagGuest(); return $user; } function supportsInteractiveAuthentication() { return false; } } UserAuthenticationBackend::register('AccessLinkAuthentication'); class osTicketClientAuthentication extends UserAuthenticationBackend { static $name = "Local Client Authentication"; static $id = "client"; function authenticate($username, $password) { if (!($acct = ClientAccount::lookupByUsername($username))) return; if (($client = new ClientSession(new EndUser($acct->getUser()))) && !$client->getId()) return false; elseif (!$acct->check_passwd($password)) return false; else return $client; } static function checkPassword($new, $current) { PasswordPolicy::checkPassword($new, $current, new self()); } } UserAuthenticationBackend::register('osTicketClientAuthentication'); class ClientPasswordResetTokenBackend extends UserAuthenticationBackend { static $id = "pwreset.client"; function supportsInteractiveAuthentication() { return false; } function signOn($errors=array()) { global $ost; if (!isset($_POST['userid']) || !isset($_POST['token'])) return false; elseif (!($_config = new Config('pwreset'))) return false; elseif (!($acct = ClientAccount::lookupByUsername($_POST['userid'])) || !$acct->getId() || !($client = new ClientSession(new EndUser($acct->getUser())))) $errors['msg'] = __('Invalid user-id given'); elseif (!($id = $_config->get($_POST['token'])) || $id != 'c'.$client->getId()) $errors['msg'] = __('Invalid reset token'); elseif (!($ts = $_config->lastModified($_POST['token'])) && ($ost->getConfig()->getPwResetWindow() < (time() - strtotime($ts)))) $errors['msg'] = __('Invalid reset token'); elseif (!$acct->forcePasswdReset()) $errors['msg'] = __('Unable to reset password'); else return $client; } function login($client, $bk) { $_SESSION['_client']['reset-token'] = $_POST['token']; Signal::send('auth.pwreset.login', $client); return parent::login($client, $bk); } } UserAuthenticationBackend::register('ClientPasswordResetTokenBackend'); class ClientAcctConfirmationTokenBackend extends UserAuthenticationBackend { static $id = "confirm.client"; function supportsInteractiveAuthentication() { return false; } function signOn($errors=array()) { global $ost; if (!isset($_GET['token'])) return false; elseif (!($_config = new Config('pwreset'))) return false; elseif (!($id = $_config->get($_GET['token']))) return false; elseif (!($acct = ClientAccount::lookup(array('user_id'=>substr($id,1)))) || !$acct->getId() || $id != 'c'.$acct->getUserId() || !($client = new ClientSession(new EndUser($acct->getUser())))) return false; else return $client; } } UserAuthenticationBackend::register('ClientAcctConfirmationTokenBackend'); // ----- Password Policy -------------------------------------- class BadPassword extends Exception {} class ExpiredPassword extends Exception {} class PasswordUpdateFailed extends Exception {} abstract class PasswordPolicy extends ServiceRegistry { static $id; static $name; /** * Check a password and throw BadPassword with a meaningful message if * the password cannot be accepted. */ abstract function onset($new, $current); /* * Called on login to enforce policies & check for expired passwords */ abstract function onLogin($user, $password); /* * Check a password aganist all available policies */ static function checkPassword($new, $current, $bk=null) { if ($bk && is_a($bk, 'AuthenticationBackend')) $policies = $bk->getPasswordPolicies(); else $policies = self::allActivePolicies(); foreach ($policies as $P) $P->onSet($new, $current); } static function allActivePolicies() { $policies = array(); foreach (array_reverse(self::getRegistry()) as $P) { if (is_string($P) && class_exists($P)) $P = new $P(); if ($P instanceof PasswordPolicy) $policies[] = $P; } return $policies; } static function cleanSessions($model, $user=null) { $criteria = array(); switch (true) { case ($model instanceof Staff): $criteria['user_id'] = $model->getId(); if ($user && ($model->getId() == $user->getId())) array_push($criteria, Q::not(array('session_id' => $user->session->session_id))); break; case ($model instanceof User): $regexp = '_auth\|.*"user";[a-z]+:[0-9]+:\{[a-z]+:[0-9]+:"id";[a-z]+:'.$model->getId(); $criteria['user_id'] = 0; $criteria['session_data__regex'] = $regexp; if ($user) array_push($criteria, Q::not(array('session_id' => $user->session->session_id))); break; default: return false; } return DatabaseSessionRecord::objects()->filter($criteria)->delete(); } } Signal::connect('auth.clean', array('PasswordPolicy', 'cleanSessions')); /* * Basic default password policy that ships with osTicket. * */ class osTicketPasswordPolicy extends PasswordPolicy { static $id = "basic"; static $name = /* @trans */ "Default Basic Policy"; function onLogin($user, $password) { global $cfg; // Check for possible password expiration // Check is only here for legacy reasons - password management // policies are now done via plugins. if ($cfg && $user && ($period=$cfg->getPasswdResetPeriod()) && ($time=$user->getPasswdResetTimestamp()) && $time < time()-($period*2629800)) throw new ExpiredPassword(__('Expired password')); } function onSet($passwd, $current) { $pwdlen = strlen($passwd); if ($pwdlen < 6) { throw new BadPassword( __('Password must be at least 6 characters')); } elseif ($pwdlen > 128) { throw new BadPassword( sprintf('%s - %s', __('Password is too long'), __('maximum of 128 characters allowed'))); } // XXX: Changing case is technicall changing the password if (0 === strcasecmp($passwd, $current)) { throw new BadPassword( __('New password MUST be different from the current password!')); } } } PasswordPolicy::register('osTicketPasswordPolicy'); ?>
Save