forked from extern/egroupware
* SAML: support joining a SAML account to an existing one, if configured in setup
notification of user does not yet work, as redirect on login page looses Api\Framework::message() :(
This commit is contained in:
parent
a993938134
commit
b7ed148371
@ -654,7 +654,7 @@ class Accounts
|
||||
}
|
||||
|
||||
// Include previous contact information to avoid blank history rows
|
||||
$contact = array_merge((array)$GLOBALS['egw']->contacts->read($data['person_id']), array(
|
||||
$contact = array_merge((array)$GLOBALS['egw']->contacts->read($data['person_id'], true), array(
|
||||
'n_given' => $data['account_firstname'],
|
||||
'n_family' => $data['account_lastname'],
|
||||
'email' => $data['account_email'],
|
||||
|
@ -76,10 +76,11 @@ class Auth
|
||||
*
|
||||
* Type will be stored in session, to automatic use the same type eg. for conditional use of SAML.
|
||||
*
|
||||
* @param Backend $type =null default is type from session / auth or login, or if not set config
|
||||
* @param string $type =null default is type from session / auth or login, or if not set config
|
||||
* @param bool $save_in_session default true, false: do not store backend
|
||||
* @return Auth\Backend|Auth\BackendSSO
|
||||
*/
|
||||
static function backend($type=null)
|
||||
static function backend($type=null, $save_in_session=true)
|
||||
{
|
||||
if (is_null($type))
|
||||
{
|
||||
@ -106,7 +107,7 @@ class Auth
|
||||
{
|
||||
throw new Exception\AssertionFailed("Auth backend class $backend_class is NO EGroupware\\Api\Auth\\Backend!");
|
||||
}
|
||||
Cache::setSession(__CLASS__, 'backend', $type);
|
||||
if ($save_in_session) Cache::setSession(__CLASS__, 'backend', $type);
|
||||
|
||||
return $backend;
|
||||
}
|
||||
|
@ -127,21 +127,153 @@ class Saml implements BackendSSO
|
||||
// check if user already exists
|
||||
if (!$GLOBALS['egw']->accounts->name2id($username, 'account_lid', 'u'))
|
||||
{
|
||||
// fail if auto-creation of authenticated users is NOT configured
|
||||
if (empty($GLOBALS['egw_info']['server']['auto_create_acct']))
|
||||
if (($existing = $this->checkJoin($_GET['login'], $_GET['passwd'], $username)) ||
|
||||
($existing = $this->checkReplaceUsername($username)))
|
||||
{
|
||||
return null;
|
||||
$username = $this->updateJoinedAccount($existing, $attrs);
|
||||
}
|
||||
else
|
||||
{
|
||||
// fail if auto-creation of authenticated users is NOT configured
|
||||
if (empty($GLOBALS['egw_info']['server']['auto_create_acct']))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
$GLOBALS['auto_create_acct'] = [
|
||||
'firstname' => $attrs[self::firstName][0],
|
||||
'lastname' => $attrs[self::lastName][0],
|
||||
'email' => $attrs[self::emailAddress][0],
|
||||
];
|
||||
}
|
||||
$GLOBALS['auto_create_acct'] = [
|
||||
'firstname' => $attrs[self::firstName][0],
|
||||
'lastname' => $attrs[self::lastName][0],
|
||||
'email' => $attrs[self::emailAddress][0],
|
||||
];
|
||||
}
|
||||
// return user session
|
||||
return $GLOBALS['egw']->session->create($username, null, null, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if joining a SAML account with an existing accounts is enabled and user specified correct credentials
|
||||
*
|
||||
* @param string $login login-name entered by user
|
||||
* @param string $password password entered by user
|
||||
* @param string $username SAML username
|
||||
* @return string|null|false existing user-name to join or
|
||||
* null if no joining configured or missing credentials or user does not exist or
|
||||
* false if authentication with given credentials failed
|
||||
*/
|
||||
private function checkJoin($login, $password, $username)
|
||||
{
|
||||
// check SAML username is stored in account_description and we have a matching account
|
||||
if ($GLOBALS['egw_info']['server']['saml_join'] === 'description' &&
|
||||
($account_id = $GLOBALS['egw']->accounts->name2id($username, 'account_description', 'u')))
|
||||
{
|
||||
return Api\Accounts::id2name($account_id);
|
||||
}
|
||||
|
||||
// check join configuration and if user specified credentials
|
||||
if (empty($GLOBALS['egw_info']['server']['saml_join']) || empty($login) || empty($password))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
$backend = Api\Auth::backend($GLOBALS['egw_info']['server']['auth_type'] ?: 'sql', false);
|
||||
if (!$backend->authenticate($login, $password))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return $login;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update joined account, if configured
|
||||
*
|
||||
* @param $account_lid existing account_lid
|
||||
* @param array $attrs saml attributes incl. SAML username
|
||||
* @return string username to use
|
||||
*/
|
||||
private function updateJoinedAccount($account_lid, array $attrs)
|
||||
{
|
||||
if (empty($GLOBALS['egw_info']['server']['saml_join']))
|
||||
{
|
||||
return $account_lid;
|
||||
}
|
||||
$account = $update = $GLOBALS['egw']->accounts->read($account_lid);
|
||||
|
||||
switch($GLOBALS['egw_info']['server']['saml_join'])
|
||||
{
|
||||
case 'usernameemail':
|
||||
if (!empty($attrs[self::emailAddress]))
|
||||
{
|
||||
unset($update['account_email']); // force email update
|
||||
}
|
||||
// fall through
|
||||
case 'username':
|
||||
$update['account_lid'] = $attrs[self::usernameOid()][0];
|
||||
break;
|
||||
|
||||
case 'description':
|
||||
$update['account_description'] = $attrs[self::usernameOid()][0];
|
||||
break;
|
||||
}
|
||||
// update other attributes
|
||||
foreach([
|
||||
'account_email' => self::emailAddress,
|
||||
'account_firstname' => self::firstName,
|
||||
'account_lastname' => self::lastName,
|
||||
] as $name => $oid)
|
||||
{
|
||||
if (!empty($attrs[$oid]) && ($name !== 'account_email' || empty($update['account_email'])))
|
||||
{
|
||||
$update[$name] = $attrs[$oid][0];
|
||||
}
|
||||
}
|
||||
// update account if necessary
|
||||
if ($account != $update)
|
||||
{
|
||||
// notify user about successful update of existing account and evtl. updated account-name
|
||||
if ($GLOBALS['egw']->accounts->save($update))
|
||||
{
|
||||
$msg = lang('Your account has been updated with new data from your identity provider.');
|
||||
if ($account['account_lid'] !== $update['account_lid'])
|
||||
{
|
||||
$msg .= "\n".lang("Please remember to use '%1' as username for local login's from now on!", $update['account_lid']);
|
||||
// rename home directory
|
||||
Api\Vfs::$is_root = true;
|
||||
Api\Vfs::rename('/home/'.$account['account_lid'], '/home/'.$update['account_lid']);
|
||||
Api\Vfs::$is_root = false;
|
||||
}
|
||||
Api\Framework::message($msg, 'notice');
|
||||
}
|
||||
else
|
||||
{
|
||||
Api\Framework::message(lang('Updating your account with new data from your identity provider failed!'), 'error');
|
||||
}
|
||||
}
|
||||
return $update['account_lid'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if some replacement is configured to match SAML usernames to existing ones
|
||||
*
|
||||
* @param string $username SAML username
|
||||
* @return string|null existing username or null if not found
|
||||
*/
|
||||
private function checkReplaceUsername($username)
|
||||
{
|
||||
if (empty($GLOBALS['egw_info']['server']['saml_replace']))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
$replace = $GLOBALS['egw_info']['server']['saml_replace'];
|
||||
$with = $GLOBALS['egw_info']['server']['saml_replace_with'] ?? '';
|
||||
$replaced = $replace[0] === '/' ? preg_replace($replaced, $with, $username) : str_replace($replace, $with, $username);
|
||||
|
||||
if (empty($replaced) || !$GLOBALS['egw']->accounts->name2id($replaced, 'account_lid', 'u'))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return $replaced;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logout SSO system
|
||||
*/
|
||||
|
32
login.php
32
login.php
@ -51,6 +51,22 @@ if(isset($GLOBALS['sitemgr_info']) && $GLOBALS['egw_info']['user']['userid'] ==
|
||||
}
|
||||
}
|
||||
|
||||
$forward = isset($_GET['phpgw_forward']) ? urldecode($_GET['phpgw_forward']) : @$_POST['phpgw_forward'];
|
||||
if (!$forward)
|
||||
{
|
||||
$extra_vars['cd'] = 'yes';
|
||||
$forward = '/index.php';
|
||||
}
|
||||
else
|
||||
{
|
||||
list($forward,$extra_vars) = explode('?',$forward,2);
|
||||
// only append cd=yes, if there is not already a cd value!
|
||||
if (strpos($extra_vars, 'cd=') === false)
|
||||
{
|
||||
$extra_vars .= ($extra_vars ? '&' : '').'cd=yes';
|
||||
}
|
||||
}
|
||||
|
||||
// SSO login: CAS, SAML, ...
|
||||
if (($GLOBALS['sessionid'] = Api\Auth::login()))
|
||||
{
|
||||
@ -241,22 +257,6 @@ else
|
||||
// check if new translations are available
|
||||
Api\Translation::check_invalidate_cache();
|
||||
|
||||
$forward = isset($_GET['phpgw_forward']) ? urldecode($_GET['phpgw_forward']) : @$_POST['phpgw_forward'];
|
||||
if (!$forward)
|
||||
{
|
||||
$extra_vars['cd'] = 'yes';
|
||||
$forward = '/index.php';
|
||||
}
|
||||
else
|
||||
{
|
||||
list($forward,$extra_vars) = explode('?',$forward,2);
|
||||
// only append cd=yes, if there is not already a cd value!
|
||||
if (strpos($extra_vars, 'cd=') === false)
|
||||
{
|
||||
$extra_vars .= ($extra_vars ? '&' : '').'cd=yes';
|
||||
}
|
||||
}
|
||||
|
||||
if(strpos($_SERVER['HTTP_REFERER'], $_SERVER['REQUEST_URI']) === false) {
|
||||
// login requuest does not come from login.php
|
||||
// redirect to referer on logout
|
||||
|
@ -521,6 +521,26 @@
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="row_on">
|
||||
<td>{lang_Allow_SAML_logins_to_join_existing_accounts}:<br/>({lang_Requires_SAML_optional_on_login_page_and_user_to_specify_username_and_password})</td>
|
||||
<td>
|
||||
<select name="newsettings[saml_join]">
|
||||
<option value="">{lang_No}</option>
|
||||
<option value="usernameemail"{selected_saml_join_usernameemail}>{lang_Replace_username_and_email}</option>
|
||||
<option value="username"{selected_saml_join_username}>{lang_Replace_username_and_keep_email}</option>
|
||||
<option value="description"{selected_saml_join_description}>{lang_Use_account_description_to_store_SAML_username}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="row_off">
|
||||
<td>{lang_Match_SAML_usernames_to_existing_ones_(use_strings_or_regular_expression)}:</td>
|
||||
<td>
|
||||
<input name="newsettings[saml_replace]" placeholder="{lang_replace}: '@uni-kl.de' {lang_or} '/@(uni-kl\.de)$/'" value="{value_saml_replace}" size="40"/>
|
||||
<input name="newsettings[saml_replace_with]" placeholder="{lang_with}: '@rhrk.uni-kl.de' {lang_or} '@rhrk.$1'" value="{value_saml_replace_with}" size="35"/>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="row_on" height="25">
|
||||
<td>{lang_Some_information_for_the_own_Service_Provider_metadata}:</td>
|
||||
<td><a href="{value_webserver_url}/saml/module.php/saml/sp/metadata.php/default-sp">{lang_Metadata_URL}</a></td>
|
||||
|
Loading…
Reference in New Issue
Block a user