mirror of
https://github.com/netzbegruenung/user_saml.git
synced 2024-05-04 18:13:41 +02:00
9672ed6ca5
Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
755 lines
21 KiB
PHP
755 lines
21 KiB
PHP
<?php
|
|
/**
|
|
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
|
|
*
|
|
* @license GNU AGPL version 3 or any later version
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
namespace OCA\User_SAML;
|
|
|
|
use OCP\Authentication\IApacheBackend;
|
|
use OCP\DB\QueryBuilder\IQueryBuilder;
|
|
use OCP\Files\NotPermittedException;
|
|
use OCP\IDBConnection;
|
|
use OCP\ILogger;
|
|
use OCP\IUser;
|
|
use OCP\IUserManager;
|
|
use OCP\IGroupManager;
|
|
use OCP\UserInterface;
|
|
use OCP\IUserBackend;
|
|
use OCP\IConfig;
|
|
use OCP\IURLGenerator;
|
|
use OCP\ISession;
|
|
use Symfony\Component\EventDispatcher\GenericEvent;
|
|
use function base64_decode;
|
|
|
|
class UserBackend implements IApacheBackend, UserInterface, IUserBackend {
|
|
/** @var IConfig */
|
|
private $config;
|
|
/** @var IURLGenerator */
|
|
private $urlGenerator;
|
|
/** @var ISession */
|
|
private $session;
|
|
/** @var IDBConnection */
|
|
private $db;
|
|
/** @var IUserManager */
|
|
private $userManager;
|
|
/** @var IGroupManager */
|
|
private $groupManager;
|
|
/** @var \OCP\UserInterface[] */
|
|
private static $backends = [];
|
|
/** @var SAMLSettings */
|
|
private $settings;
|
|
/** @var ILogger */
|
|
private $logger;
|
|
|
|
/**
|
|
* @param IConfig $config
|
|
* @param IURLGenerator $urlGenerator
|
|
* @param ISession $session
|
|
* @param IDBConnection $db
|
|
* @param IUserManager $userManager
|
|
* @param IGroupManager $groupManager
|
|
* @param SAMLSettings $settings
|
|
* @param ILogger $logger
|
|
*/
|
|
public function __construct(IConfig $config,
|
|
IURLGenerator $urlGenerator,
|
|
ISession $session,
|
|
IDBConnection $db,
|
|
IUserManager $userManager,
|
|
IGroupManager $groupManager,
|
|
SAMLSettings $settings,
|
|
ILogger $logger) {
|
|
$this->config = $config;
|
|
$this->urlGenerator = $urlGenerator;
|
|
$this->session = $session;
|
|
$this->db = $db;
|
|
$this->userManager = $userManager;
|
|
$this->groupManager = $groupManager;
|
|
$this->settings = $settings;
|
|
$this->logger = $logger;
|
|
}
|
|
|
|
/**
|
|
* Whether $uid exists in the database
|
|
*
|
|
* @param string $uid
|
|
* @return bool
|
|
*/
|
|
protected function userExistsInDatabase($uid) {
|
|
/* @var $qb IQueryBuilder */
|
|
$qb = $this->db->getQueryBuilder();
|
|
$qb->select('uid')
|
|
->from('user_saml_users')
|
|
->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
|
|
->setMaxResults(1);
|
|
$result = $qb->execute();
|
|
$users = $result->fetchAll();
|
|
$result->closeCursor();
|
|
|
|
return !empty($users);
|
|
}
|
|
|
|
/**
|
|
* Creates a user if it does not exist. In case home directory mapping
|
|
* is enabled we also set up the user's home from $attributes.
|
|
*
|
|
* @param string $uid
|
|
* @param array $attributes
|
|
*/
|
|
public function createUserIfNotExists($uid, array $attributes = array()) {
|
|
if(!$this->userExistsInDatabase($uid)) {
|
|
$values = [
|
|
'uid' => $uid,
|
|
];
|
|
|
|
// Try to get the mapped home directory of the user
|
|
try {
|
|
$home = $this->getAttributeValue('saml-attribute-mapping-home_mapping', $attributes);
|
|
} catch (\InvalidArgumentException $e) {
|
|
$home = '';
|
|
}
|
|
|
|
if ($home !== '') {
|
|
//if attribute's value is an absolute path take this, otherwise append it to data dir
|
|
//check for / at the beginning or pattern c:\ resp. c:/
|
|
if( '/' !== $home[0]
|
|
&& !(3 < strlen($home) && ctype_alpha($home[0])
|
|
&& $home[1] === ':' && ('\\' === $home[2] || '/' === $home[2]))
|
|
) {
|
|
$home = $this->config->getSystemValue('datadirectory',
|
|
\OC::$SERVERROOT.'/data' ) . '/' . $home;
|
|
}
|
|
|
|
$values['home'] = $home;
|
|
}
|
|
|
|
/* @var $qb IQueryBuilder */
|
|
$qb = $this->db->getQueryBuilder();
|
|
$qb->insert('user_saml_users');
|
|
foreach($values as $column => $value) {
|
|
$qb->setValue($column, $qb->createNamedParameter($value));
|
|
}
|
|
$qb->execute();
|
|
|
|
$this->initializeHomeDir($uid);
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string $uid
|
|
* @throws \OCP\Files\NotFoundException
|
|
*/
|
|
public function initializeHomeDir($uid) {
|
|
### Code taken from lib/private/User/Session.php - function prepareUserLogin() ###
|
|
//trigger creation of user home and /files folder
|
|
$userFolder = \OC::$server->getUserFolder($uid);
|
|
try {
|
|
// copy skeleton
|
|
\OC_Util::copySkeleton($uid, $userFolder);
|
|
} catch (NotPermittedException $ex) {
|
|
// read only uses
|
|
}
|
|
// trigger any other initialization
|
|
$user = $this->userManager->get($uid);
|
|
\OC::$server->getEventDispatcher()->dispatch(IUser::class . '::firstLogin', new GenericEvent($user));
|
|
}
|
|
|
|
/**
|
|
* Check if backend implements actions
|
|
* @param int $actions bitwise-or'ed actions
|
|
* @return boolean
|
|
*
|
|
* Returns the supported actions as int to be
|
|
* compared with \OC\User\Backend::CREATE_USER etc.
|
|
* @since 4.5.0
|
|
*/
|
|
public function implementsActions($actions) {
|
|
$availableActions = \OC\User\Backend::CHECK_PASSWORD;
|
|
$availableActions |= \OC\User\Backend::GET_DISPLAYNAME;
|
|
$availableActions |= \OC\User\Backend::GET_HOME;
|
|
$availableActions |= \OC\User\Backend::COUNT_USERS;
|
|
return (bool)($availableActions & $actions);
|
|
}
|
|
|
|
/**
|
|
* Check if the provided token is correct
|
|
* @param string $uid The username
|
|
* @param string $password The password
|
|
* @return string
|
|
*
|
|
* Check if the password is correct without logging in the user
|
|
* returns the user id or false
|
|
*/
|
|
public function checkPassword($uid, $password) {
|
|
/* @var $qb IQueryBuilder */
|
|
$qb = $this->db->getQueryBuilder();
|
|
$qb->select('token')
|
|
->from('user_saml_auth_token')
|
|
->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
|
|
->setMaxResults(1000);
|
|
$result = $qb->execute();
|
|
$data = $result->fetchAll();
|
|
$result->closeCursor();
|
|
|
|
foreach($data as $passwords) {
|
|
if(password_verify($password, $passwords['token'])) {
|
|
return $uid;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* delete a user
|
|
* @param string $uid The username of the user to delete
|
|
* @return bool
|
|
* @since 4.5.0
|
|
*/
|
|
public function deleteUser($uid) {
|
|
if($this->userExistsInDatabase($uid)) {
|
|
/* @var $qb IQueryBuilder */
|
|
$qb = $this->db->getQueryBuilder();
|
|
$qb->delete('user_saml_users')
|
|
->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
|
|
->execute();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns the user's home directory, if home directory mapping is set up.
|
|
*
|
|
* @param string $uid the username
|
|
* @return string
|
|
*/
|
|
public function getHome($uid) {
|
|
if($this->userExistsInDatabase($uid)) {
|
|
$qb = $this->db->getQueryBuilder();
|
|
$qb->select('home')
|
|
->from('user_saml_users')
|
|
->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
|
|
->setMaxResults(1);
|
|
$result = $qb->execute();
|
|
$users = $result->fetchAll();
|
|
if (isset($users[0]['home'])) {
|
|
return $users[0]['home'];
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get a list of all users
|
|
*
|
|
* @param string $search
|
|
* @param null|int $limit
|
|
* @param null|int $offset
|
|
* @return string[] an array of all uids
|
|
* @since 4.5.0
|
|
*/
|
|
public function getUsers($search = '', $limit = null, $offset = null) {
|
|
// shamelessly duplicated from \OC\User\Database
|
|
$users = $this->getDisplayNames($search, $limit, $offset);
|
|
$userIds = array_map(function ($uid) {
|
|
return (string)$uid;
|
|
}, array_keys($users));
|
|
sort($userIds, SORT_STRING | SORT_FLAG_CASE);
|
|
return $userIds;
|
|
}
|
|
|
|
/**
|
|
* check if a user exists
|
|
* @param string $uid the username
|
|
* @return boolean
|
|
* @since 4.5.0
|
|
*/
|
|
public function userExists($uid) {
|
|
if($backend = $this->getActualUserBackend($uid)) {
|
|
return $backend->userExists($uid);
|
|
} else {
|
|
return $this->userExistsInDatabase($uid);
|
|
}
|
|
}
|
|
|
|
public function setDisplayName($uid, $displayName) {
|
|
if($backend = $this->getActualUserBackend($uid)) {
|
|
return $backend->setDisplayName($uid, $displayName);
|
|
}
|
|
|
|
if ($this->userExistsInDatabase($uid)) {
|
|
$qb = $this->db->getQueryBuilder();
|
|
$qb->update('user_saml_users')
|
|
->set('displayname', $qb->createNamedParameter($displayName))
|
|
->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
|
|
->execute();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Get display name of the user
|
|
*
|
|
* @param string $uid user ID of the user
|
|
* @return string display name
|
|
* @since 4.5.0
|
|
*/
|
|
public function getDisplayName($uid) {
|
|
if($backend = $this->getActualUserBackend($uid)) {
|
|
return $backend->getDisplayName($uid);
|
|
} else {
|
|
if($this->userExistsInDatabase($uid)) {
|
|
$qb = $this->db->getQueryBuilder();
|
|
$qb->select('displayname')
|
|
->from('user_saml_users')
|
|
->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
|
|
->setMaxResults(1);
|
|
$result = $qb->execute();
|
|
$users = $result->fetchAll();
|
|
if (isset($users[0]['displayname'])) {
|
|
return $users[0]['displayname'];
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Get a list of all display names and user ids.
|
|
*
|
|
* @param string $search
|
|
* @param string|null $limit
|
|
* @param string|null $offset
|
|
* @return array an array of all displayNames (value) and the corresponding uids (key)
|
|
* @since 4.5.0
|
|
*/
|
|
public function getDisplayNames($search = '', $limit = null, $offset = null) {
|
|
// shamelessly duplicate from \OC\User\Database
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
$query->select('uid', 'displayname')
|
|
->from('user_saml_users', 'u')
|
|
->leftJoin('u', 'preferences', 'p', $query->expr()->andX(
|
|
$query->expr()->eq('userid', 'uid'),
|
|
$query->expr()->eq('appid', $query->expr()->literal('settings')),
|
|
$query->expr()->eq('configkey', $query->expr()->literal('email')))
|
|
)
|
|
// sqlite doesn't like re-using a single named parameter here
|
|
->where($query->expr()->iLike('uid', $query->createPositionalParameter('%' . $this->db->escapeLikeParameter($search) . '%')))
|
|
->orWhere($query->expr()->iLike('displayname', $query->createPositionalParameter('%' . $this->db->escapeLikeParameter($search) . '%')))
|
|
->orWhere($query->expr()->iLike('configvalue', $query->createPositionalParameter('%' . $this->db->escapeLikeParameter($search) . '%')))
|
|
->orderBy($query->func()->lower('displayname'), 'ASC')
|
|
->orderBy('uid', 'ASC')
|
|
->setMaxResults($limit)
|
|
->setFirstResult($offset);
|
|
|
|
$result = $query->execute();
|
|
$displayNames = [];
|
|
while ($row = $result->fetch()) {
|
|
$displayNames[(string)$row['uid']] = (string)$row['displayname'];
|
|
}
|
|
$result->closeCursor();
|
|
|
|
return $displayNames;
|
|
}
|
|
|
|
/**
|
|
* Check if a user list is available or not
|
|
* @return boolean if users can be listed or not
|
|
* @since 4.5.0
|
|
*/
|
|
public function hasUserListings() {
|
|
if($this->autoprovisionAllowed()) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* In case the user has been authenticated by Apache true is returned.
|
|
*
|
|
* @return boolean whether Apache reports a user as currently logged in.
|
|
* @since 6.0.0
|
|
*/
|
|
public function isSessionActive() {
|
|
if($this->getCurrentUserId() !== '') {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* {@inheritdoc}
|
|
*/
|
|
public function getLogoutUrl() {
|
|
$prefix = $this->settings->getPrefix();
|
|
$slo = $this->config->getAppValue('user_saml', $prefix . 'idp-singleLogoutService.url', '');
|
|
if($slo === '') {
|
|
return '';
|
|
}
|
|
|
|
return $this->urlGenerator->linkToRouteAbsolute(
|
|
'user_saml.SAML.singleLogoutService',
|
|
[
|
|
'requesttoken' => \OC::$server->getCsrfTokenManager()->getToken()->getEncryptedValue(),
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Logout attribute for Nextcloud < 12.0.3
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getLogoutAttribute() {
|
|
return 'style="display:none;"';
|
|
}
|
|
|
|
/**
|
|
* return user data from the idp
|
|
*
|
|
* @return mixed
|
|
*/
|
|
public function getUserData() {
|
|
$userData = $this->session->get('user_saml.samlUserData');
|
|
$userData = $this->formatUserData($userData);
|
|
|
|
// make sure that a valid UID is given
|
|
if (empty($userData['formatted']['uid'])) {
|
|
$this->logger->error('No valid uid given, please check your attribute mapping. Got uid: {uid}', ['app' => $this->appName, 'uid' => $userData['uid']]);
|
|
throw new \InvalidArgumentException('No valid uid given, please check your attribute mapping. Got uid: ' . $userData['uid']);
|
|
}
|
|
|
|
|
|
return $userData;
|
|
|
|
}
|
|
|
|
/**
|
|
* format user data and map them to the configured attributes
|
|
*
|
|
* @param $attributes
|
|
* @return array
|
|
*/
|
|
private function formatUserData($attributes) {
|
|
|
|
$result = ['formatted' => [], 'raw' => $attributes];
|
|
|
|
try {
|
|
$result['formatted']['email'] = $this->getAttributeValue('saml-attribute-mapping-email_mapping', $attributes);
|
|
} catch (\InvalidArgumentException $e) {
|
|
$result['formatted']['email'] = null;
|
|
}
|
|
try {
|
|
$result['formatted']['displayName'] = $this->getAttributeValue('saml-attribute-mapping-displayName_mapping', $attributes);
|
|
} catch (\InvalidArgumentException $e) {
|
|
$result['formatted']['displayName'] = null;
|
|
}
|
|
try {
|
|
$result['formatted']['quota'] = $this->getAttributeValue('saml-attribute-mapping-quota_mapping', $attributes);
|
|
if ($result['formatted']['quota'] === '') {
|
|
$result['formatted']['quota'] = 'default';
|
|
}
|
|
} catch (\InvalidArgumentException $e) {
|
|
$result['formatted']['quota'] = null;
|
|
}
|
|
|
|
try {
|
|
$result['formatted']['groups'] = $this->getAttributeArrayValue('saml-attribute-mapping-group_mapping', $attributes);
|
|
} catch (\InvalidArgumentException $e) {
|
|
$result['formatted']['groups'] = null;
|
|
}
|
|
|
|
$prefix = $this->settings->getPrefix();
|
|
$uidMapping = $this->config->getAppValue('user_saml', $prefix . 'general-uid_mapping');
|
|
$result['formatted']['uid'] = '';
|
|
if (isset($attributes[$uidMapping])) {
|
|
$result['formatted']['uid'] = $attributes[$uidMapping][0];
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Return the id of the current user
|
|
* @return string
|
|
* @since 6.0.0
|
|
*/
|
|
public function getCurrentUserId() {
|
|
$samlData = $this->session->get('user_saml.samlUserData');
|
|
$prefix = $this->settings->getPrefix();
|
|
$uidMapping = $this->config->getAppValue('user_saml', $prefix . 'general-uid_mapping', '');
|
|
|
|
if($uidMapping !== '' && isset($samlData[$uidMapping])) {
|
|
if(is_array($samlData[$uidMapping])) {
|
|
$uid = $samlData[$uidMapping][0];
|
|
} else {
|
|
$uid = $samlData[$uidMapping];
|
|
}
|
|
$uid = $this->testEncodedObjectGUID($uid);
|
|
|
|
if($this->userExists($uid)) {
|
|
$this->session->set('last-password-confirm', strtotime('+4 year', time()));
|
|
return $uid;
|
|
}
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
|
|
/**
|
|
* Backend name to be shown in user management
|
|
* @return string the name of the backend to be shown
|
|
* @since 8.0.0
|
|
*/
|
|
public function getBackendName() {
|
|
return 'user_saml';
|
|
}
|
|
|
|
/**
|
|
* Whether autoprovisioning is enabled or not
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function autoprovisionAllowed() {
|
|
return $this->config->getAppValue('user_saml', 'general-require_provisioned_account', '0') === '0';
|
|
}
|
|
|
|
/**
|
|
* Gets the actual user backend of the user
|
|
*
|
|
* @param string $uid
|
|
* @return null|UserInterface
|
|
*/
|
|
public function getActualUserBackend($uid) {
|
|
foreach(self::$backends as $backend) {
|
|
if($backend->userExists($uid)) {
|
|
return $backend;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Registers the used backends, used later to get the actual user backend
|
|
* of the user.
|
|
*
|
|
* @param \OCP\UserInterface[] $backends
|
|
*/
|
|
public function registerBackends(array $backends) {
|
|
self::$backends = $backends;
|
|
}
|
|
|
|
private function getAttributeKeys($name)
|
|
{
|
|
$prefix = $this->settings->getPrefix($name);
|
|
$keys = explode(' ', $this->config->getAppValue('user_saml', $prefix . $name, ''));
|
|
|
|
if (count($keys) === 1 && $keys[0] === '') {
|
|
throw new \InvalidArgumentException('Attribute is not configured');
|
|
}
|
|
return $keys;
|
|
}
|
|
|
|
private function getAttributeValue($name, array $attributes) {
|
|
$keys = $this->getAttributeKeys($name);
|
|
|
|
$value = '';
|
|
foreach($keys as $key) {
|
|
if (isset($attributes[$key])) {
|
|
if (is_array($attributes[$key])) {
|
|
foreach ($attributes[$key] as $attribute_part_value) {
|
|
if($value !== '') {
|
|
$value .= ' ';
|
|
}
|
|
$value .= $attribute_part_value;
|
|
}
|
|
} else {
|
|
if($value !== '') {
|
|
$value .= ' ';
|
|
}
|
|
$value .= $attributes[$key];
|
|
}
|
|
}
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
private function getAttributeArrayValue($name, array $attributes) {
|
|
$keys = $this->getAttributeKeys($name);
|
|
|
|
$value = array();
|
|
foreach($keys as $key) {
|
|
if (isset($attributes[$key])) {
|
|
if (is_array($attributes[$key])) {
|
|
$value = array_merge($value, array_values($attributes[$key]));
|
|
} else {
|
|
$value[] = $attributes[$key];
|
|
}
|
|
}
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
public function updateAttributes($uid,
|
|
array $attributes) {
|
|
$user = $this->userManager->get($uid);
|
|
try {
|
|
$newEmail = $this->getAttributeValue('saml-attribute-mapping-email_mapping', $attributes);
|
|
$this->logger->debug('Email attribute content: {email}', ['app' => 'user_saml', 'email' => $newEmail]);
|
|
} catch (\InvalidArgumentException $e) {
|
|
$this->logger->debug('Failed to fetch email attribute: {exception}', ['app' => 'user_saml', 'exception' => $e->getMessage()]);
|
|
$newEmail = null;
|
|
}
|
|
try {
|
|
$newDisplayname = $this->getAttributeValue('saml-attribute-mapping-displayName_mapping', $attributes);
|
|
$this->logger->debug('Display name attribute content: {displayName}', ['app' => 'user_saml', 'displayName' => $newDisplayname]);
|
|
} catch (\InvalidArgumentException $e) {
|
|
$this->logger->debug('Failed to fetch display name attribute: {exception}', ['app' => 'user_saml', 'exception' => $e->getMessage()]);
|
|
$newDisplayname = null;
|
|
}
|
|
try {
|
|
$newQuota = $this->getAttributeValue('saml-attribute-mapping-quota_mapping', $attributes);
|
|
$this->logger->debug('Quota attribute content: {quota}', ['app' => 'user_saml', 'quota' => $newQuota]);
|
|
if ($newQuota === '') {
|
|
$newQuota = 'default';
|
|
}
|
|
} catch (\InvalidArgumentException $e) {
|
|
$this->logger->debug('Failed to fetch quota attribute: {exception}', ['app' => 'user_saml', 'exception' => $e->getMessage()]);
|
|
$newQuota = null;
|
|
}
|
|
|
|
try {
|
|
$newGroups = $this->getAttributeArrayValue('saml-attribute-mapping-group_mapping', $attributes);
|
|
$this->logger->debug('Group attribute content: {groups}', ['app' => 'user_saml', 'groups' => json_encode($newGroups)]);
|
|
} catch (\InvalidArgumentException $e) {
|
|
$this->logger->debug('Failed to fetch group attribute: {exception}', ['app' => 'user_saml', 'exception' => $e->getMessage()]);
|
|
$newGroups = null;
|
|
}
|
|
|
|
if ($user !== null) {
|
|
$currentEmail = (string)$user->getEMailAddress();
|
|
if ($newEmail !== null
|
|
&& $currentEmail !== $newEmail) {
|
|
$user->setEMailAddress($newEmail);
|
|
}
|
|
$currentDisplayname = (string)$this->getDisplayName($uid);
|
|
if ($newDisplayname !== null
|
|
&& $currentDisplayname !== $newDisplayname) {
|
|
\OC_Hook::emit('OC_User', 'changeUser',
|
|
[
|
|
'user' => $user,
|
|
'feature' => 'displayName',
|
|
'value' => $newDisplayname
|
|
]
|
|
);
|
|
$this->setDisplayName($uid, $newDisplayname);
|
|
}
|
|
|
|
if ($newQuota !== null) {
|
|
$user->setQuota($newQuota);
|
|
}
|
|
|
|
if ($newGroups !== null) {
|
|
$groupManager = $this->groupManager;
|
|
$oldGroups = $groupManager->getUserGroupIds($user);
|
|
|
|
$groupsToAdd = array_unique(array_diff($newGroups, $oldGroups));
|
|
$groupsToRemove = array_diff($oldGroups, $newGroups);
|
|
|
|
foreach ($groupsToAdd as $group) {
|
|
if (!($groupManager->groupExists($group))) {
|
|
$groupManager->createGroup($group);
|
|
}
|
|
$groupManager->get($group)->addUser($user);
|
|
}
|
|
|
|
foreach ($groupsToRemove as $group) {
|
|
$groupManager->get($group)->removeUser($user);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* returns the plain text UUID if the provided $uid string is a
|
|
* base64-encoded binary string representing e.g. the objectGUID. Otherwise
|
|
*
|
|
*/
|
|
public function testEncodedObjectGUID(string $uid): string {
|
|
if (preg_match('/[^a-zA-Z0-9=+\/]/', $uid) !== 0) {
|
|
// certainly not encoded
|
|
return $uid;
|
|
}
|
|
|
|
$candidate = base64_decode($uid, false);
|
|
if($candidate === false) {
|
|
return $uid;
|
|
}
|
|
$candidate = $this->convertObjectGUID2Str($candidate);
|
|
// the regex only matches the structure of the UUID, not its semantic
|
|
// (i.e. version or variant) simply to be future compatible
|
|
if(preg_match('/^[a-f0-9]{8}(-[a-f0-9]{4}){4}[a-f0-9]{8}$/i', $candidate) === 1) {
|
|
$uid = $candidate;
|
|
}
|
|
return $uid;
|
|
}
|
|
|
|
/**
|
|
* @see \OCA\User_LDAP\Access::convertObjectGUID2Str
|
|
*/
|
|
protected function convertObjectGUID2Str($oguid) {
|
|
$hex_guid = bin2hex($oguid);
|
|
$hex_guid_to_guid_str = '';
|
|
for($k = 1; $k <= 4; ++$k) {
|
|
$hex_guid_to_guid_str .= substr($hex_guid, 8 - 2 * $k, 2);
|
|
}
|
|
$hex_guid_to_guid_str .= '-';
|
|
for($k = 1; $k <= 2; ++$k) {
|
|
$hex_guid_to_guid_str .= substr($hex_guid, 12 - 2 * $k, 2);
|
|
}
|
|
$hex_guid_to_guid_str .= '-';
|
|
for($k = 1; $k <= 2; ++$k) {
|
|
$hex_guid_to_guid_str .= substr($hex_guid, 16 - 2 * $k, 2);
|
|
}
|
|
$hex_guid_to_guid_str .= '-' . substr($hex_guid, 16, 4);
|
|
$hex_guid_to_guid_str .= '-' . substr($hex_guid, 20);
|
|
|
|
return strtoupper($hex_guid_to_guid_str);
|
|
}
|
|
|
|
public function countUsers() {
|
|
$query = $this->db->getQueryBuilder();
|
|
$query->select($query->func()->count('uid'))
|
|
->from('user_saml_users');
|
|
$result = $query->execute();
|
|
|
|
return $result->fetchColumn();
|
|
}
|
|
}
|