user_saml/lib/UserData.php

151 lines
4.4 KiB
PHP

<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020 Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @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 OCA\User_SAML\Exceptions\NoUserFoundException;
use OCP\IConfig;
class UserData {
private $uid;
/** @var array */
private $attributes;
/** @var UserResolver */
private $userResolver;
/** @var SAMLSettings */
private $samlSettings;
/** @var IConfig */
private $config;
public function __construct(UserResolver $userResolver, SAMLSettings $samlSettings, IConfig $config) {
$this->userResolver = $userResolver;
$this->samlSettings = $samlSettings;
$this->config = $config;
}
public function setAttributes(array $attributes): void {
$this->attributes = $attributes;
$this->uid = null; // clear the state in case
}
public function getAttributes(): array {
$this->assertIsInitialized();
return $this->attributes;
}
public function hasUidMappingAttribute(): bool {
$this->assertIsInitialized();
$prefix = $this->samlSettings->getPrefix();
$uidMapping = $this->config->getAppValue('user_saml', $prefix . 'general-uid_mapping');
return isset($this->attributes[$uidMapping]);
}
public function getOriginalUid(): string {
$this->assertIsInitialized();
return $this->extractSamlUserId();
}
public function getEffectiveUid(): string {
if($this->uid !== null) {
return $this->uid;
}
$this->assertIsInitialized();
try {
$uid = $this->extractSamlUserId();
$uid = $this->testEncodedObjectGUID($uid);
$uid = $this->userResolver->findExistingUserId($uid, true);
$this->uid = $uid;
} catch (NoUserFoundException $e) {
return '';
}
return $uid;
}
protected function extractSamlUserId(): string {
$prefix = $this->samlSettings->getPrefix();
$uidMapping = $this->config->getAppValue('user_saml', $prefix . 'general-uid_mapping');
if(isset($this->attributes[$uidMapping])) {
if (is_array($this->attributes[$uidMapping])) {
return trim($this->attributes[$uidMapping][0]);
} else {
return trim($this->attributes[$uidMapping]);
}
}
return '';
}
/**
* 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, true);
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): string {
$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);
}
protected function assertIsInitialized() {
if($this->attributes === null) {
throw new \LogicException('UserData have to be initialized with setAttributes first');
}
}
}