From 395d435e227d0ef689a428bb6b740943efc63573 Mon Sep 17 00:00:00 2001 From: Jared Hancock <jared@osticket.com> Date: Wed, 26 Mar 2014 10:54:33 -0500 Subject: [PATCH] Implement a remote user import process This adds a feature for remote authentication methods for clients, such as LDAP, which will, after successful authentication, yield a ClientCreateRequest rather than an AuthenticatedUser. The ClientCreateRequest represents a successful authentication and user information lookup for a remote client. The client is then presented with a registration page where their information for their account in the local system can be reviewed prior to the account creation. Once created, the client account is confirmed without an email confirmation and is logged in immediately without reentering a password. --- account.php | 24 +++++++++++--- include/class.auth.php | 57 +++++++++++++++++++++++++++++++-- include/class.client.php | 6 ++++ include/client/register.inc.php | 46 ++++++++++++++++++++++---- login.php | 16 ++++++++- 5 files changed, 135 insertions(+), 14 deletions(-) diff --git a/account.php b/account.php index d9c0793f9..7b8c7fa28 100644 --- a/account.php +++ b/account.php @@ -55,15 +55,13 @@ elseif ($_POST) { $user_form = UserForm::getUserForm()->getForm($_POST); if (!$user_form->isValid(function($f) { return !$f->get('internal'); })) $errors['err'] = 'Incomplete client information'; - elseif (!$_POST['passwd1']) + elseif (!$_POST['backend'] && !$_POST['passwd1']) $errors['passwd1'] = 'New password required'; - elseif ($_POST['passwd2'] != $_POST['passwd1']) + elseif (!$_POST['backend'] && $_POST['passwd2'] != $_POST['passwd1']) $errors['passwd1'] = 'Passwords do not match'; // XXX: The email will always be in use already if a guest is logged in // and is registering for an account. Instead, - elseif (!($user = $thisclient ?: User::fromForm($user_form))) - $errors['err'] = 'Unable to register account. See messages below'; elseif (($addr = $user_form->getField('email')->getClean()) && ClientAccount::lookupByUsername($addr)) { $user_form->getField('email')->addError( @@ -71,6 +69,12 @@ elseif ($_POST) { .urlencode($addr).'" style="color:inherit"><strong>sign in</strong></a>?'); $errors['err'] = 'Unable to register account. See messages below'; } + // Users created from ClientCreateRequest + elseif (isset($_POST['backend']) && !($user = User::fromVars($user_form->getClean()))) + $errors['err'] = 'Unable to create local account. See messages below'; + // New users and users registering from a ticket access link + elseif (!$user && !($user = $thisclient ?: User::fromForm($user_form))) + $errors['err'] = 'Unable to register account. See messages below'; else { if (!($acct = ClientAccount::createForUser($user))) $errors['err'] = 'Internal error. Unable to create new account'; @@ -84,6 +88,18 @@ elseif ($_POST) { $content = Page::lookup(Page::getIdByType('registration-confirm')); $inc = 'register.confirm.inc.php'; $acct->sendResetEmail('registration-client'); + break; + case 'import': + foreach (UserAuthenticationBackend::allRegistered() as $bk) { + if ($bk::$id == $_POST['backend']) { + $cl = new ClientSession(new EndUser($user)); + $acct->confirm(); + if ($user = $bk->login($cl, $bk)) + Http::redirect('tickets.php'); + break; + } + } + break; } } diff --git a/include/class.auth.php b/include/class.auth.php index 31459cd01..a1b8c5220 100644 --- a/include/class.auth.php +++ b/include/class.auth.php @@ -50,6 +50,44 @@ interface AuthDirectorySearch { 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; + } +} + /** * Authentication backend * @@ -133,6 +171,9 @@ abstract class AuthenticationBackend { if ($result instanceof AuthenticatedUser && ($bk->login($result, $bk))) return $result; + elseif ($result instanceof ClientCreateRequest + && $bk instanceof UserAuthenticationBackend) + return $result; elseif ($result instanceof AccessDenied) { break; } @@ -407,8 +448,20 @@ abstract class UserAuthenticationBackend extends AuthenticationBackend { } function getAllowedBackends($userid) { - // White listing backends for specific user not supported. - return array(); + $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) { diff --git a/include/class.client.php b/include/class.client.php index 8dfb211a8..7d1dfbcb7 100644 --- a/include/class.client.php +++ b/include/class.client.php @@ -474,6 +474,12 @@ class ClientAccount extends ClientAccountModel { $this->set('timezone_id', $vars['timezone_id']); $this->set('dst', isset($vars['dst']) ? 1 : 0); + if ($vars['backend']) { + $this->set('backend', $vars['backend']); + if ($vars['username']) + $this->set('username', $vars['username']); + } + if ($vars['passwd1']) { $this->set('passwd', Passwd::hash($vars['passwd1'])); $info = array('password' => $vars['passwd1']); diff --git a/include/client/register.inc.php b/include/client/register.inc.php index 71e54eb80..0a5676781 100644 --- a/include/client/register.inc.php +++ b/include/client/register.inc.php @@ -1,18 +1,31 @@ <?php -$info = $_POST ?: array( - 'timezone_id' => $cfg->getDefaultTimezoneId(), - 'dst' => $cfg->observeDaylightSaving(), -); +$info = $_POST; +if (!isset($info['timezone_id'])) + $info += array( + 'timezone_id' => $cfg->getDefaultTimezoneId(), + 'dst' => $cfg->observeDaylightSaving(), + 'backend' => null, + ); +if (isset($user) && $user instanceof ClientCreateRequest) { + $bk = $user->getBackend(); + $info = array_merge($info, array( + 'backend' => $bk::$id, + 'username' => $user->getUsername(), + )); +} + ?> <h1>Account Registration</h1> <p> -Use the forms below to update the information we have on file for your -account +Use the forms below to create or update the information we have on file for +your account </p> <form action="account.php" method="post"> <?php csrf_token(); ?> - <input type="hidden" name="do" value="<?php echo $_REQUEST['do'] ?: 'create'; ?>" /> + <input type="hidden" name="do" value="<?php echo $_REQUEST['do'] + ?: ($info['backend'] ? 'import' :'create'); ?>" /> <table width="800" class="padded"> +<tbody> <?php $cf = $user_form ?: UserForm::getInstance(); $cf->render(false); @@ -54,6 +67,23 @@ account <div><hr><h3>Access Credentials</h3></div> </td> </tr> +<?php if ($info['backend']) { ?> +<tr> + <td width="180"> + Login With: + </td> + <td> + <input type="hidden" name="backend" value="<?php echo $info['backend']; ?>"/> + <input type="hidden" name="username" value="<?php echo $info['username']; ?>"/> +<?php foreach (UserAuthenticationBackend::allRegistered() as $bk) { + if ($bk::$id == $info['backend']) { + echo $bk::$name; + break; + } +} ?> + </td> +</tr> +<?php } else { ?> <tr> <td width="180"> Create a Password: @@ -72,6 +102,8 @@ account <span class="error"> <?php echo $errors['passwd2']; ?></span> </td> </tr> +<?php } ?> +</tbody> </table> <hr> <p style="text-align: center;"> diff --git a/login.php b/login.php index 4d9bda78c..41658e483 100644 --- a/login.php +++ b/login.php @@ -35,7 +35,21 @@ if ($_POST && isset($_POST['luser'])) { $errors['err'] = 'Valid username or email address is required'; elseif (($user = UserAuthenticationBackend::process($_POST['luser'], $_POST['lpasswd'], $errors))) { - Http::redirect($_SESSION['_client']['auth']['dest'] ?: 'tickets.php'); + if ($user instanceof ClientCreateRequest) { + if ($cfg && $cfg->isClientRegistrationEnabled()) { + $inc = 'register.inc.php'; + $user_form = UserForm::getUserForm()->getForm($user->getInfo()); + } + else { + $errors['err'] = 'Access Denied. Contact your help desk + administrator to have an account registered for you'; + // fall through to show login page again + } + } + else { + Http::redirect($_SESSION['_client']['auth']['dest'] + ?: 'tickets.php'); + } } elseif(!$errors['err']) { $errors['err'] = 'Invalid username or password - try again!'; } -- GitLab