Skip to content
Open
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
1 change: 0 additions & 1 deletion lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ public function register(IRegistrationContext $context): void {

public function boot(IBootContext $context): void {
$context->injectFn(\Closure::fromCallable([$this->backend, 'injectSession']));
$context->injectFn(\Closure::fromCallable([$this, 'checkLoginToken']));
/** @var IUserSession $userSession */
$userSession = $this->getContainer()->get(IUserSession::class);
if ($userSession->isLoggedIn()) {
Expand Down
21 changes: 21 additions & 0 deletions lib/Controller/LoginController.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use OCA\UserOIDC\Service\LdapService;
use OCA\UserOIDC\Service\OIDCService;
use OCA\UserOIDC\Service\ProviderService;
use OCA\UserOIDC\Service\ProvisioningDeniedException;
use OCA\UserOIDC\Service\ProvisioningService;
use OCA\UserOIDC\Service\SettingsService;
use OCA\UserOIDC\Service\TokenService;
Expand Down Expand Up @@ -554,6 +555,25 @@
}

if ($autoProvisionAllowed) {
// TODO: (proposal) refactor all provisioning strategies into event handlers
$user = null;

try {
// use potential user from other backend, create it in our backend if it does not exist
$user = $this->provisioningService->provisionUser($userId, $providerId, $idTokenPayload, $userFromOtherBackend);

Check failure on line 563 in lib/Controller/LoginController.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

UndefinedVariable

lib/Controller/LoginController.php:563:94: UndefinedVariable: Cannot find referenced variable $userFromOtherBackend (see https://psalm.dev/024)
} catch (ProvisioningDeniedException $denied) {
// TODO: MagentaCLOUD should upstream the exception handling
$redirectUrl = $denied->getRedirectUrl();
if ($redirectUrl === null) {
$message = $this->l10n->t('Failed to provision user');
return $this->build403TemplateResponse($message, Http::STATUS_BAD_REQUEST, ['reason' => $denied->getMessage()]);
} else {
// error response is a redirect, e.g. to a booking site
// so that you can immediately get the registration page
return new RedirectResponse($redirectUrl);
}
}

if (!$softAutoProvisionAllowed && $existingUser !== null && $existingUser->getBackendClassName() !== Application::APP_ID) {
// if soft auto-provisioning is disabled,
// we refuse login for a user that already exists in another backend
Expand Down Expand Up @@ -890,6 +910,7 @@
string $error,
string $description,
array $throttleMetadata = [],
?bool $throttle = null,
): JSONResponse {
$this->logger->debug('Backchannel logout error. ' . $error . ' ; ' . $description);
return new JSONResponse(
Expand Down
4 changes: 4 additions & 0 deletions lib/Db/UserMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,25 @@
use OCP\Cache\CappedMemoryCache;
use OCP\IConfig;
use OCP\IDBConnection;
use Psr\Log\LoggerInterface;

/**
* @extends QBMapper<User>
*/
class UserMapper extends QBMapper {

private CappedMemoryCache $userCache;
private LoggerInterface $logger;

public function __construct(
IDBConnection $db,
LoggerInterface $logger,
private LocalIdService $idService,
private IConfig $config,
) {
parent::__construct($db, 'user_oidc', User::class);
$this->userCache = new CappedMemoryCache();
$this->logger = $logger;
}

/**
Expand Down
87 changes: 87 additions & 0 deletions lib/Event/UserAccountChangeEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php
/*
* @copyright Copyright (c) 2023 T-Systems International
*
* @author B. Rederlechner <bernd.rederlechner@t-systems.com>
*
* @license GNU AGPL version 3 or any later version
*
*/

declare(strict_types=1);

namespace OCA\UserOIDC\Event;

use OCP\EventDispatcher\Event;

/**
* Event to provide custom mapping logic based on the OIDC token data
* In order to avoid further processing the event propagation should be stopped
* in the listener after processing as the value might get overwritten afterwards
* by other listeners through $event->stopPropagation();
*/
class UserAccountChangeEvent extends Event {
private $uid;
private $displayname;
private $mainEmail;
private $quota;
private $claims;
private $result;


public function __construct(string $uid, ?string $displayname, ?string $mainEmail, ?string $quota, object $claims, bool $accessAllowed = false) {
parent::__construct();
$this->uid = $uid;
$this->displayname = $displayname;
$this->mainEmail = $mainEmail;
$this->quota = $quota;
$this->claims = $claims;
$this->result = new UserAccountChangeResult($accessAllowed, 'default');
}

/**
* @return get event username (uid)

Check failure on line 43 in lib/Event/UserAccountChangeEvent.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

UndefinedDocblockClass

lib/Event/UserAccountChangeEvent.php:43:13: UndefinedDocblockClass: Docblock-defined class, interface or enum named OCA\UserOIDC\Event\get does not exist (see https://psalm.dev/200)
*/
public function getUid(): string {
return $this->uid;
}

/**
* @return get event displayname

Check failure on line 50 in lib/Event/UserAccountChangeEvent.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

UndefinedDocblockClass

lib/Event/UserAccountChangeEvent.php:50:13: UndefinedDocblockClass: Docblock-defined class, interface or enum named OCA\UserOIDC\Event\get does not exist (see https://psalm.dev/200)
*/
public function getDisplayName(): ?string {
return $this->displayname;
}

/**
* @return get event main email

Check failure on line 57 in lib/Event/UserAccountChangeEvent.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

UndefinedDocblockClass

lib/Event/UserAccountChangeEvent.php:57:13: UndefinedDocblockClass: Docblock-defined class, interface or enum named OCA\UserOIDC\Event\get does not exist (see https://psalm.dev/200)
*/
public function getMainEmail(): ?string {
return $this->mainEmail;
}

/**
* @return get event quota

Check failure on line 64 in lib/Event/UserAccountChangeEvent.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

UndefinedDocblockClass

lib/Event/UserAccountChangeEvent.php:64:13: UndefinedDocblockClass: Docblock-defined class, interface or enum named OCA\UserOIDC\Event\get does not exist (see https://psalm.dev/200)
*/
public function getQuota(): ?string {
return $this->quota;
}

/**
* @return array the array of claim values associated with the event

Check failure on line 71 in lib/Event/UserAccountChangeEvent.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

MismatchingDocblockReturnType

lib/Event/UserAccountChangeEvent.php:71:13: MismatchingDocblockReturnType: Docblock has incorrect return type 'array<array-key, mixed>', should be 'object' (see https://psalm.dev/142)
*/
public function getClaims(): object {
return $this->claims;
}

/**
* @return value for the logged in user attribute

Check failure on line 78 in lib/Event/UserAccountChangeEvent.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

UndefinedDocblockClass

lib/Event/UserAccountChangeEvent.php:78:13: UndefinedDocblockClass: Docblock-defined class, interface or enum named OCA\UserOIDC\Event\value does not exist (see https://psalm.dev/200)
*/
public function getResult(): UserAccountChangeResult {
return $this->result;
}

public function setResult(bool $accessAllowed, string $reason = '', ?string $redirectUrl = null) : void {
$this->result = new UserAccountChangeResult($accessAllowed, $reason, $redirectUrl);
}
}
74 changes: 74 additions & 0 deletions lib/Event/UserAccountChangeResult.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php
/*
* @copyright Copyright (c) 2023 T-Systems International
*
* @author B. Rederlechner <bernd.rederlechner@t-systems.com>
*
* @license GNU AGPL version 3 or any later version
*
*/

declare(strict_types=1);

namespace OCA\UserOIDC\Event;

/**
* Event to provide custom mapping logic based on the OIDC token data
* In order to avoid further processing the event propagation should be stopped
* in the listener after processing as the value might get overwritten afterwards
* by other listeners through $event->stopPropagation();
*/
class UserAccountChangeResult {

/** @var bool */
private $accessAllowed;
/** @var string */
private $reason;
/** @var string */
private $redirectUrl;

public function __construct(bool $accessAllowed, string $reason = '', ?string $redirectUrl = null) {
$this->accessAllowed = $accessAllowed;
$this->redirectUrl = $redirectUrl;
$this->reason = $reason;
}

/**
* @return value for the logged in user attribute

Check failure on line 37 in lib/Event/UserAccountChangeResult.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

UndefinedDocblockClass

lib/Event/UserAccountChangeResult.php:37:13: UndefinedDocblockClass: Docblock-defined class, interface or enum named OCA\UserOIDC\Event\value does not exist (see https://psalm.dev/200)
*/
public function isAccessAllowed(): bool {
return $this->accessAllowed;
}

public function setAccessAllowed(bool $accessAllowed): void {
$this->accessAllowed = $accessAllowed;
}

/**
* @return get optional alternate redirect address

Check failure on line 48 in lib/Event/UserAccountChangeResult.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

UndefinedDocblockClass

lib/Event/UserAccountChangeResult.php:48:13: UndefinedDocblockClass: Docblock-defined class, interface or enum named OCA\UserOIDC\Event\get does not exist (see https://psalm.dev/200)
*/
public function getRedirectUrl(): ?string {
return $this->redirectUrl;
}

/**
* @return set optional alternate redirect address

Check failure on line 55 in lib/Event/UserAccountChangeResult.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

UndefinedDocblockClass

lib/Event/UserAccountChangeResult.php:55:13: UndefinedDocblockClass: Docblock-defined class, interface or enum named OCA\UserOIDC\Event\set does not exist (see https://psalm.dev/200)
*/
public function setRedirectUrl(?string $redirectUrl): void {
$this->redirectUrl = $redirectUrl;
}

/**
* @return get decision reason
*/
public function getReason(): string {
return $this->reason;
}

/**
* @return set decision reason
*/
public function setReason(string $reason): void {
$this->reason = $reason;
}
}
69 changes: 69 additions & 0 deletions lib/Service/ProvisioningDeniedException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

declare(strict_types=1);

/**
* @copyright Copyright (c) 2023, T-Systems International
*
* @author B. Rederlechner <bernd.rederlechner@t-Systems.com>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/

namespace OCA\UserOIDC\Service;

/**
* Exception if the precondition of the config update method isn't met
* @since 1.4.0
*/
class ProvisioningDeniedException extends \Exception {
private $redirectUrl;

/**
* Exception constructor including an option redirect url.
*
* @param string $message The error message. It will be not revealed to the
* the user (unless the hint is empty) and thus
* should be not translated.
* @param string $hint A useful message that is presented to the end
* user. It should be translated, but must not
* contain sensitive data.
* @param int $code Set default to 403 (Forbidden)
* @param \Exception|null $previous
*/
public function __construct(string $message, ?string $redirectUrl = null, int $code = 403, ?\Exception $previous = null) {
parent::__construct($message, $code, $previous);
$this->redirectUrl = $redirectUrl;
}

/**
* Read optional failure redirect if available
* @return string|null
*/
public function getRedirectUrl(): ?string {
return $this->redirectUrl;
}

/**
* Include redirect in string serialisation.
*
* @return string
*/
public function __toString(): string {
$redirect = $this->redirectUrl ?? '<no redirect>';
return __CLASS__ . ": [{$this->code}]: {$this->message} ({$redirect})\n";
}
}
Loading
Loading