Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/Controller/SAMLController.php
Original file line number Diff line number Diff line change
Expand Up @@ -392,8 +392,8 @@ public function assertionConsumerService(): Http\RedirectResponse {
if ($firstLogin) {
$this->userBackend->initializeHomeDir($user->getUID());
}
} catch (NoUserFoundException) {
throw new \InvalidArgumentException('User "' . $this->userBackend->getCurrentUserId() . '" is not valid');
} catch (NoUserFoundException $e) {
throw new \InvalidArgumentException('User "' . $this->userBackend->getCurrentUserId() . '" is not valid.', previous: $e);
} catch (Exception $e) {
$this->logger->critical($e->getMessage(), ['exception' => $e, 'app' => $this->appName]);
$response = new Http\RedirectResponse($this->urlGenerator->linkToRouteAbsolute('user_saml.SAML.notProvisioned'));
Expand Down
1 change: 1 addition & 0 deletions lib/SAMLSettings.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class SAMLSettings {
'saml-attribute-mapping-home_mapping',
'saml-attribute-mapping-quota_mapping',
'saml-attribute-mapping-mfa_mapping',
'saml-attribute-mapping-user_id_ldap_mapping',
'saml-attribute-mapping-group_mapping_prefix',
'saml-user-filter-reject_groups',
'saml-user-filter-require_groups',
Expand Down
11 changes: 11 additions & 0 deletions lib/Settings/Admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use OCP\AppFramework\Services\IAppConfig;
use OCP\AppFramework\Services\IInitialState;
use OCP\Defaults;
use OCP\IConfig;
use OCP\IL10N;
use OCP\Settings\IDelegatedSettings;
use OneLogin\Saml2\Constants;
Expand All @@ -24,6 +25,7 @@ public function __construct(
private readonly IL10N $l10n,
private readonly Defaults $defaults,
private readonly IAppConfig $appConfig,
private readonly IConfig $config,
private readonly SAMLSettings $samlSettings,
private readonly IInitialState $initialState,
) {
Expand Down Expand Up @@ -148,13 +150,22 @@ public function getForm(): TemplateResponse {
'type' => 'line',
'required' => false,
],
'user_id_ldap_mapping' => [
'text' => $this->l10n->t('Attribute to map the users to an existing LDAP user'),
'type' => 'line',
'required' => false,
],
'group_mapping_prefix' => [
'text' => $this->l10n->t('Group Mapping Prefix, default: %s', [SAMLSettings::DEFAULT_GROUP_PREFIX]),
'type' => 'line',
'required' => false,
],
];

if (version_compare($this->config->getSystemValueString('version', '0.0.0'), '34.0.0', '<')) {
unset($attributeMappingSettings['user_id_ldap_mapping']);
}

$userFilterSettings = [
'reject_groups' => [
'text' => $this->l10n->t('Reject members of these groups. This setting has precedence over required memberships.'),
Expand Down
2 changes: 1 addition & 1 deletion lib/UserData.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public function getEffectiveUid(): string {
try {
$providedUid = $this->extractSamlUserId();
$uid = $this->testEncodedObjectGUID($providedUid);
$uid = $this->userResolver->findExistingUserId($uid, true, $providedUid !== $uid);
$uid = $this->userResolver->findExistingUserId($uid, $this->getProviderSettings(), true, $providedUid !== $uid);
$this->uid = $uid;
} catch (NoUserFoundException) {
return '';
Expand Down
48 changes: 42 additions & 6 deletions lib/UserResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,55 @@
namespace OCA\User_SAML;

use OCA\User_SAML\Exceptions\NoUserFoundException;
use OCP\IConfig;
use OCP\IUser;
use OCP\IUserManager;
use OCP\LDAP\Exceptions\MultipleUsersReturnedException;
use OCP\LDAP\ILDAPProviderFactory;
use OCP\Server;

class UserResolver {
public function __construct(
private IUserManager $userManager,
private readonly IUserManager $userManager,
private readonly ILDAPProviderFactory $ldapProviderFactory,
private readonly IConfig $config,
) {
}

/**
* @throws NoUserFoundException
*/
public function findExistingUserId(string $rawUidCandidate, bool $force = false, bool $isActiveDirectory = false): string {
public function findExistingUserId(string $rawUidCandidate, ?array $idpSettings = null, bool $force = false, bool $isActiveDirectory = false): string {
// If configured, find the user based on a different LDAP attribute.
if ($idpSettings !== null
&& version_compare($this->config->getSystemValueString('version', '0.0.0'), '34.0.0', '>=')
&& $this->ldapProviderFactory->isAvailable()
&& isset($idpSettings['saml-attribute-mapping-user_id_ldap_mapping'])
&& $idpSettings['saml-attribute-mapping-user_id_ldap_mapping'] !== null
&& $idpSettings['saml-attribute-mapping-user_id_ldap_mapping'] !== '') {
$userIdLdapMapping = $idpSettings['saml-attribute-mapping-user_id_ldap_mapping'];
/** @psalm-suppress UndefinedClass only in NC 34 or above */
try {
if ($isActiveDirectory) {
$rawUidCandidate = $this->formatGuid2ForFilterUser($rawUidCandidate);
}

if ($rawUidCandidate === '') {
throw new NoUserFoundException('User id is empty');
}

/** @psalm-suppress UndefinedInterfaceMethod only in NC 34 or above */
$user = $this->ldapProviderFactory->getLDAPProvider()->findOneUserByAttributeValue($userIdLdapMapping, $rawUidCandidate);
} catch (MultipleUsersReturnedException $e) {
return '';
}
if ($user !== null) {
return $user->getUID();
}

// continue normal workflow
}

if ($force) {
if ($isActiveDirectory) {
$this->ensureUser($this->formatGuid2ForFilterUser($rawUidCandidate));
Expand All @@ -40,7 +76,7 @@ public function findExistingUserId(string $rawUidCandidate, bool $force = false,
if ($this->userManager->userExists($sanitized)) {
return $sanitized;
}
throw new NoUserFoundException('User' . $rawUidCandidate . ' not valid or not found');
throw new NoUserFoundException('User ' . $rawUidCandidate . ' not valid or not found');
}

/**
Expand Down Expand Up @@ -86,14 +122,14 @@ public function findExistingUser(string $rawUidCandidate): IUser {
$uid = $this->findExistingUserId($rawUidCandidate);
$user = $this->userManager->get($uid);
if ($user === null) {
throw new NoUserFoundException('User' . $rawUidCandidate . ' not valid or not found');
throw new NoUserFoundException('User ' . $rawUidCandidate . ' not valid or not found.');
}
return $user;
}

public function userExists(string $uid, bool $force = false): bool {
public function userExists(string $uid, array $idpSettings, bool $force = false): bool {
try {
$this->findExistingUserId($uid, $force);
$this->findExistingUserId($uid, $idpSettings, $force);
return true;
} catch (NoUserFoundException) {
return false;
Expand Down
12 changes: 12 additions & 0 deletions tests/unit/Settings/AdminTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use OCP\AppFramework\Services\IAppConfig;
use OCP\AppFramework\Services\IInitialState;
use OCP\Defaults;
use OCP\IConfig;
use OCP\IL10N;
use OneLogin\Saml2\Constants;
use Override;
Expand All @@ -26,20 +27,23 @@ class AdminTest extends \Test\TestCase {
private IL10N&MockObject $l10n;
private Defaults&MockObject $defaults;
private IAppConfig&MockObject $appConfig;
private IConfig&MockObject $config;
private IInitialState&MockObject $initialState;

#[Override]
protected function setUp(): void {
$this->l10n = $this->createMock(IL10N::class);
$this->defaults = $this->createMock(Defaults::class);
$this->appConfig = $this->createMock(IAppConfig::class);
$this->config = $this->createMock(IConfig::class);
$this->settings = $this->createMock(SAMLSettings::class);
$this->initialState = $this->createMock(IInitialState::class);

$this->admin = new Admin(
$this->l10n,
$this->defaults,
$this->appConfig,
$this->config,
$this->settings,
$this->initialState,
);
Expand All @@ -48,6 +52,9 @@ protected function setUp(): void {
}

public function formDataProvider(): array {
$this->config->method('getSystemValueString')->with('version', '0.0.0')
->willReturn('34.0.0');

$this->l10n
->expects($this->any())
->method('t')
Expand Down Expand Up @@ -161,6 +168,11 @@ public function formDataProvider(): array {
'type' => 'line',
'required' => false,
],
'user_id_ldap_mapping' => [
'text' => $this->l10n->t('Attribute to map the users to an existing LDAP user'),
'type' => 'line',
'required' => false,
],
'group_mapping_prefix' => [
'text' => $this->l10n->t('Group Mapping Prefix, default: SAML_'),
'type' => 'line',
Expand Down
Loading