2016-06-29 00:04:23 +02:00
|
|
|
<?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;
|
|
|
|
|
2021-11-09 02:45:45 +01:00
|
|
|
use InvalidArgumentException;
|
|
|
|
use OCA\User_SAML\Db\ConfigurationsMapper;
|
|
|
|
use OCP\DB\Exception;
|
2016-06-29 00:04:23 +02:00
|
|
|
use OCP\IConfig;
|
2017-08-01 17:17:32 +02:00
|
|
|
use OCP\IRequest;
|
2018-07-11 12:22:45 +02:00
|
|
|
use OCP\ISession;
|
2016-06-29 00:04:23 +02:00
|
|
|
use OCP\IURLGenerator;
|
2019-05-10 10:14:46 +02:00
|
|
|
use OneLogin\Saml2\Constants;
|
2016-06-29 00:04:23 +02:00
|
|
|
|
|
|
|
class SAMLSettings {
|
2021-11-09 02:45:45 +01:00
|
|
|
private const LOADED_NONE = 0;
|
|
|
|
private const LOADED_CHOSEN = 1;
|
|
|
|
private const LOADED_ALL = 2;
|
|
|
|
|
|
|
|
public const IDP_CONFIG_KEYS = [
|
|
|
|
'general-idp0_display_name',
|
|
|
|
'general-uid_mapping',
|
|
|
|
'idp-entityId',
|
|
|
|
'idp-singleLogoutService.responseUrl',
|
|
|
|
'idp-singleLogoutService.url',
|
|
|
|
'idp-singleSignOnService.url',
|
|
|
|
'idp-x509cert',
|
|
|
|
'security-authnRequestsSigned',
|
|
|
|
'security-general',
|
|
|
|
'security-logoutRequestSigned',
|
|
|
|
'security-logoutResponseSigned',
|
|
|
|
'security-lowercaseUrlencoding',
|
|
|
|
'security-nameIdEncrypted',
|
|
|
|
'security-offer',
|
|
|
|
'security-required',
|
|
|
|
'security-signatureAlgorithm',
|
|
|
|
'security-signMetadata',
|
|
|
|
'security-sloWebServerDecode',
|
|
|
|
'security-wantAssertionsEncrypted',
|
|
|
|
'security-wantAssertionsSigned',
|
|
|
|
'security-wantMessagesSigned',
|
|
|
|
'security-wantNameId',
|
|
|
|
'security-wantNameIdEncrypted',
|
|
|
|
'security-wantXMLValidation',
|
|
|
|
'saml-attribute-mapping-displayName_mapping',
|
|
|
|
'saml-attribute-mapping-email_mapping',
|
|
|
|
'saml-attribute-mapping-group_mapping',
|
|
|
|
'saml-attribute-mapping-home_mapping',
|
|
|
|
'saml-attribute-mapping-quota_mapping',
|
|
|
|
'sp-x509cert',
|
|
|
|
'sp-name-id-format',
|
|
|
|
'sp-privateKey',
|
|
|
|
];
|
|
|
|
|
2016-06-29 00:04:23 +02:00
|
|
|
/** @var IURLGenerator */
|
|
|
|
private $urlGenerator;
|
|
|
|
/** @var IConfig */
|
|
|
|
private $config;
|
2018-07-11 12:22:45 +02:00
|
|
|
/** @var ISession */
|
|
|
|
private $session;
|
|
|
|
/** @var array list of global settings which are valid for every idp */
|
|
|
|
private $globalSettings = ['general-require_provisioned_account', 'general-allow_multiple_user_back_ends', 'general-use_saml_auth_for_desktop'];
|
2021-11-09 02:45:45 +01:00
|
|
|
/** @var array<int, array<string, string>> */
|
|
|
|
private $configurations;
|
|
|
|
/** @var int */
|
|
|
|
private $configurationsLoadedState = self::LOADED_NONE;
|
|
|
|
/** @var ConfigurationsMapper */
|
|
|
|
private $mapper;
|
2016-06-29 00:04:23 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param IURLGenerator $urlGenerator
|
|
|
|
* @param IConfig $config
|
2017-08-01 17:17:32 +02:00
|
|
|
* @param IRequest $request
|
2018-07-11 12:22:45 +02:00
|
|
|
* @param ISession $session
|
2016-06-29 00:04:23 +02:00
|
|
|
*/
|
2021-11-09 02:45:45 +01:00
|
|
|
public function __construct(
|
|
|
|
IURLGenerator $urlGenerator,
|
|
|
|
IConfig $config,
|
|
|
|
ISession $session,
|
|
|
|
ConfigurationsMapper $mapper
|
|
|
|
) {
|
2016-06-29 00:04:23 +02:00
|
|
|
$this->urlGenerator = $urlGenerator;
|
|
|
|
$this->config = $config;
|
2018-07-11 12:22:45 +02:00
|
|
|
$this->session = $session;
|
2021-11-09 02:45:45 +01:00
|
|
|
$this->mapper = $mapper;
|
2016-06-29 00:04:23 +02:00
|
|
|
}
|
|
|
|
|
2018-07-10 17:33:51 +02:00
|
|
|
/**
|
2022-01-04 16:50:09 +01:00
|
|
|
* Get list of the configured IDPs
|
2018-07-10 17:33:51 +02:00
|
|
|
*
|
2021-11-09 02:45:45 +01:00
|
|
|
* @return array<int, string>
|
|
|
|
* @throws Exception
|
2018-07-10 17:33:51 +02:00
|
|
|
*/
|
2021-11-09 02:45:45 +01:00
|
|
|
public function getListOfIdps(): array {
|
|
|
|
$this->ensureConfigurationsLoaded();
|
2018-07-10 17:33:51 +02:00
|
|
|
|
2021-11-09 02:45:45 +01:00
|
|
|
$result = [];
|
|
|
|
foreach ($this->configurations as $configID => $config) {
|
|
|
|
// no fancy array_* method, because there might be thousands
|
|
|
|
$result[$configID] = $config['general-idp0_display_name'] ?? '';
|
2018-07-10 17:33:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2022-01-04 16:50:09 +01:00
|
|
|
* Check if multiple user back ends are allowed
|
2018-07-10 17:33:51 +02:00
|
|
|
*/
|
2021-11-09 02:45:45 +01:00
|
|
|
public function allowMultipleUserBackEnds(): bool {
|
2018-08-15 12:52:17 +02:00
|
|
|
$type = $this->config->getAppValue('user_saml', 'type');
|
2018-07-10 17:33:51 +02:00
|
|
|
$setting = $this->config->getAppValue('user_saml', 'general-allow_multiple_user_back_ends', '0');
|
2021-11-09 02:45:45 +01:00
|
|
|
return ($setting === '1' && $type === 'saml');
|
2018-07-10 17:33:51 +02:00
|
|
|
}
|
|
|
|
|
2022-02-25 19:17:30 +01:00
|
|
|
public function usesSloWebServerDecode(int $idp): bool {
|
|
|
|
$config = $this->get($idp);
|
|
|
|
return ($config['security-sloWebServerDecode'] ?? false) === '1';
|
2021-05-04 17:28:58 +02:00
|
|
|
}
|
|
|
|
|
2018-07-10 18:07:34 +02:00
|
|
|
/**
|
2022-01-04 16:50:09 +01:00
|
|
|
* Get config for given IDP
|
2018-07-10 18:07:34 +02:00
|
|
|
*
|
2021-11-09 02:45:45 +01:00
|
|
|
* @throws Exception
|
2018-07-10 18:07:34 +02:00
|
|
|
*/
|
2021-11-09 02:45:45 +01:00
|
|
|
public function getOneLoginSettingsArray(int $idp): array {
|
|
|
|
$this->ensureConfigurationsLoaded($idp);
|
2018-07-10 17:33:51 +02:00
|
|
|
|
2016-06-29 00:04:23 +02:00
|
|
|
$settings = [
|
2017-01-12 21:43:40 +01:00
|
|
|
'strict' => true,
|
2017-02-03 12:30:10 +01:00
|
|
|
'debug' => $this->config->getSystemValue('debug', false),
|
2018-08-14 18:03:37 +02:00
|
|
|
'baseurl' => $this->urlGenerator->linkToRouteAbsolute('user_saml.SAML.base'),
|
2016-06-29 00:04:23 +02:00
|
|
|
'security' => [
|
2021-11-09 02:45:45 +01:00
|
|
|
'nameIdEncrypted' => ($this->configurations[$idp]['security-nameIdEncrypted'] ?? '0') === '1',
|
|
|
|
'authnRequestsSigned' => ($this->configurations[$idp]['security-authnRequestsSigned'] ?? '0') === '1',
|
|
|
|
'logoutRequestSigned' => ($this->configurations[$idp]['security-logoutRequestSigned'] ?? '0') === '1',
|
|
|
|
'logoutResponseSigned' => ($this->configurations[$idp]['security-logoutResponseSigned'] ?? '0') === '1',
|
|
|
|
'signMetadata' => ($this->configurations[$idp]['security-signMetadata'] ?? '0') === '1',
|
|
|
|
'wantMessagesSigned' => ($this->configurations[$idp]['security-wantMessagesSigned'] ?? '0') === '1',
|
|
|
|
'wantAssertionsSigned' => ($this->configurations[$idp]['security-wantAssertionsSigned'] ?? '0') === '1',
|
|
|
|
'wantAssertionsEncrypted' => ($this->configurations[$idp]['security-wantAssertionsEncrypted'] ?? '0') === '1',
|
|
|
|
'wantNameId' => ($this->configurations[$idp]['security-wantNameId'] ?? '0') === '1',
|
|
|
|
'wantNameIdEncrypted' => ($this->configurations[$idp]['security-wantNameIdEncrypted'] ?? '0') === '1',
|
|
|
|
'wantXMLValidation' => ($this->configurations[$idp]['security-wantXMLValidation'] ?? '0') === '1',
|
2016-11-14 11:54:03 +01:00
|
|
|
'requestedAuthnContext' => false,
|
2021-11-09 02:45:45 +01:00
|
|
|
'lowercaseUrlencoding' => ($this->configurations[$idp]['security-lowercaseUrlencoding'] ?? '0') === '1',
|
|
|
|
'signatureAlgorithm' => $this->configurations[$idp]['security-signatureAlgorithm'] ?? null,
|
2022-02-25 19:17:30 +01:00
|
|
|
// "sloWebServerDecode" is not expected to be passed to the OneLogin class
|
2016-06-29 00:04:23 +02:00
|
|
|
],
|
|
|
|
'sp' => [
|
|
|
|
'entityId' => $this->urlGenerator->linkToRouteAbsolute('user_saml.SAML.getMetadata'),
|
|
|
|
'assertionConsumerService' => [
|
|
|
|
'url' => $this->urlGenerator->linkToRouteAbsolute('user_saml.SAML.assertionConsumerService'),
|
|
|
|
],
|
2021-11-09 02:45:45 +01:00
|
|
|
'NameIDFormat' => $this->configurations[$idp]['sp-name-id-format'] ?? Constants::NAMEID_UNSPECIFIED,
|
|
|
|
'x509cert' => $this->configurations[$idp]['sp-x509cert'] ?? '',
|
|
|
|
'privateKey' => $this->configurations[$idp]['sp-privateKey'] ?? '',
|
2016-06-29 00:04:23 +02:00
|
|
|
],
|
|
|
|
'idp' => [
|
2021-11-09 02:45:45 +01:00
|
|
|
'entityId' => $this->configurations[$idp]['idp-entityId'] ?? '',
|
2016-06-29 00:04:23 +02:00
|
|
|
'singleSignOnService' => [
|
2021-11-09 02:45:45 +01:00
|
|
|
'url' => $this->configurations[$idp]['idp-singleSignOnService.url'] ?? '',
|
2016-06-29 00:04:23 +02:00
|
|
|
],
|
2021-11-09 02:45:45 +01:00
|
|
|
'x509cert' => $this->configurations[$idp]['idp-x509cert'] ?? '',
|
2016-06-29 00:04:23 +02:00
|
|
|
],
|
|
|
|
];
|
|
|
|
|
2021-11-09 02:45:45 +01:00
|
|
|
// must be added only if configured
|
|
|
|
if (($this->configurations[$idp]['idp-singleLogoutService.url'] ?? '') !== '') {
|
|
|
|
$settings['idp']['singleLogoutService'] = ['url' => $this->configurations[$idp]['idp-singleLogoutService.url']];
|
|
|
|
$settings['sp']['singleLogoutService'] = ['url' => $this->urlGenerator->linkToRouteAbsolute('user_saml.SAML.singleLogoutService')];
|
|
|
|
if (($this->configurations[$idp]['idp-singleLogoutService.responseUrl'] ?? '') !== '') {
|
|
|
|
$settings['idp']['singleLogoutService']['responseUrl'] = $this->configurations[$idp]['idp-singleLogoutService.responseUrl'];
|
|
|
|
}
|
2016-06-29 00:04:23 +02:00
|
|
|
}
|
|
|
|
|
2021-11-09 02:45:45 +01:00
|
|
|
return $settings;
|
|
|
|
}
|
2016-06-29 00:04:23 +02:00
|
|
|
|
2021-11-09 02:45:45 +01:00
|
|
|
public function getProviderId(): int {
|
|
|
|
// defaults to 1, needed for env-mode
|
|
|
|
return (int)($this->session->get('user_saml.Idp') ?? 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getNewProviderId(): int {
|
|
|
|
return $this->mapper->reserveId();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @throws Exception
|
|
|
|
*/
|
|
|
|
public function get(int $id): array {
|
|
|
|
$this->ensureConfigurationsLoaded($id);
|
|
|
|
return $this->configurations[$id] ?? [];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @throws InvalidArgumentException
|
|
|
|
*/
|
|
|
|
public function set(int $id, array $settings): void {
|
|
|
|
foreach (array_keys($settings) as $configKey) {
|
|
|
|
if (!in_array($configKey, self::IDP_CONFIG_KEYS)) {
|
|
|
|
throw new InvalidArgumentException('Invalid config key');
|
2020-06-22 18:21:30 +02:00
|
|
|
}
|
2016-06-29 11:10:59 +02:00
|
|
|
}
|
2016-06-29 10:25:09 +02:00
|
|
|
|
2021-11-09 02:45:45 +01:00
|
|
|
$this->mapper->set($id, $settings);
|
2016-06-29 00:04:23 +02:00
|
|
|
}
|
|
|
|
|
2018-07-11 12:22:45 +02:00
|
|
|
/**
|
2021-11-09 02:45:45 +01:00
|
|
|
* @throws Exception
|
2018-07-11 12:22:45 +02:00
|
|
|
*/
|
2021-11-09 02:45:45 +01:00
|
|
|
public function delete(int $id): void {
|
|
|
|
$this->mapper->deleteById($id);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @throws Exception
|
|
|
|
*/
|
|
|
|
protected function ensureConfigurationsLoaded(int $idp = -1): void {
|
|
|
|
if (self::LOADED_ALL === $this->configurationsLoadedState
|
|
|
|
|| (self::LOADED_CHOSEN === $this->configurationsLoadedState
|
|
|
|
&& isset($this->configurations[$idp])
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
return;
|
2018-07-11 12:22:45 +02:00
|
|
|
}
|
|
|
|
|
2021-12-10 08:16:06 +01:00
|
|
|
if ($idp !== -1) {
|
2021-11-09 02:45:45 +01:00
|
|
|
$this->configurations[$idp] = $this->mapper->get($idp);
|
|
|
|
} else {
|
|
|
|
$configs = $this->mapper->getAll();
|
|
|
|
foreach ($configs as $id => $config) {
|
|
|
|
$this->configurations[$id] = $config;
|
|
|
|
}
|
2018-07-11 12:22:45 +02:00
|
|
|
}
|
|
|
|
|
2021-11-09 02:45:45 +01:00
|
|
|
$this->configurationsLoadedState = $idp === -1 ? self::LOADED_ALL : self::LOADED_CHOSEN;
|
2018-07-11 12:22:45 +02:00
|
|
|
}
|
|
|
|
}
|