Skip to content

Commit db84df7

Browse files
committed
Merge #25 [V33] Event based provisioning
2 parents ae03ffb + cf3fc5b commit db84df7

File tree

9 files changed

+1085
-1
lines changed

9 files changed

+1085
-1
lines changed

lib/AppInfo/Application.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public function register(IRegistrationContext $context): void {
7474

7575
public function boot(IBootContext $context): void {
7676
$context->injectFn(\Closure::fromCallable([$this->backend, 'injectSession']));
77-
$context->injectFn(\Closure::fromCallable([$this, 'checkLoginToken']));
77+
// $context->injectFn(\Closure::fromCallable([$this, 'checkLoginToken']));
7878
/** @var IUserSession $userSession */
7979
$userSession = $this->getContainer()->get(IUserSession::class);
8080
if ($userSession->isLoggedIn()) {

lib/Controller/LoginController.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use OCA\UserOIDC\Service\LdapService;
2525
use OCA\UserOIDC\Service\OIDCService;
2626
use OCA\UserOIDC\Service\ProviderService;
27+
use OCA\UserOIDC\Service\ProvisioningDeniedException;
2728
use OCA\UserOIDC\Service\ProvisioningService;
2829
use OCA\UserOIDC\Service\SettingsService;
2930
use OCA\UserOIDC\Service\TokenService;
@@ -559,6 +560,24 @@ public function code(string $state = '', string $code = '', string $scope = '',
559560
}
560561

561562
if ($autoProvisionAllowed) {
563+
$user = null;
564+
565+
try {
566+
// use potential user from other backend, create it in our backend if it does not exist
567+
$user = $this->provisioningService->provisionUser($userId, $providerId, $idTokenPayload, $existingUser);
568+
} catch (ProvisioningDeniedException $denied) {
569+
// TODO: MagentaCLOUD should upstream the exception handling
570+
$redirectUrl = $denied->getRedirectUrl();
571+
if ($redirectUrl === null) {
572+
$message = $this->l10n->t('Failed to provision user');
573+
return $this->build403TemplateResponse($message, Http::STATUS_BAD_REQUEST, ['reason' => $denied->getMessage()]);
574+
} else {
575+
// error response is a redirect, e.g. to a booking site
576+
// so that you can immediately get the registration page
577+
return new RedirectResponse($redirectUrl);
578+
}
579+
}
580+
562581
if (!$softAutoProvisionAllowed && $existingUser !== null && $existingUser->getBackendClassName() !== Application::APP_ID) {
563582
// if soft auto-provisioning is disabled,
564583
// we refuse login for a user that already exists in another backend
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
/*
3+
* @copyright Copyright (c) 2023 T-Systems International
4+
*
5+
* @author B. Rederlechner <bernd.rederlechner@t-systems.com>
6+
*
7+
* @license GNU AGPL version 3 or any later version
8+
*
9+
*/
10+
11+
declare(strict_types=1);
12+
13+
namespace OCA\UserOIDC\Event;
14+
15+
use OCP\EventDispatcher\Event;
16+
17+
/**
18+
* Event to provide custom mapping logic based on the OIDC token data
19+
* In order to avoid further processing the event propagation should be stopped
20+
* in the listener after processing as the value might get overwritten afterwards
21+
* by other listeners through $event->stopPropagation();
22+
*/
23+
class UserAccountChangeEvent extends Event {
24+
private $uid;
25+
private $displayname;
26+
private $mainEmail;
27+
private $quota;
28+
private $claims;
29+
private $result;
30+
31+
32+
public function __construct(string $uid, ?string $displayname, ?string $mainEmail, ?string $quota, object $claims, bool $accessAllowed = false) {
33+
parent::__construct();
34+
$this->uid = $uid;
35+
$this->displayname = $displayname;
36+
$this->mainEmail = $mainEmail;
37+
$this->quota = $quota;
38+
$this->claims = $claims;
39+
$this->result = new UserAccountChangeResult($accessAllowed, 'default');
40+
}
41+
42+
/**
43+
* @return get event username (uid)
44+
*/
45+
public function getUid(): string {
46+
return $this->uid;
47+
}
48+
49+
/**
50+
* @return get event displayname
51+
*/
52+
public function getDisplayName(): ?string {
53+
return $this->displayname;
54+
}
55+
56+
/**
57+
* @return get event main email
58+
*/
59+
public function getMainEmail(): ?string {
60+
return $this->mainEmail;
61+
}
62+
63+
/**
64+
* @return get event quota
65+
*/
66+
public function getQuota(): ?string {
67+
return $this->quota;
68+
}
69+
70+
/**
71+
* @return array the array of claim values associated with the event
72+
*/
73+
public function getClaims(): object {
74+
return $this->claims;
75+
}
76+
77+
/**
78+
* @return value for the logged in user attribute
79+
*/
80+
public function getResult(): UserAccountChangeResult {
81+
return $this->result;
82+
}
83+
84+
public function setResult(bool $accessAllowed, string $reason = '', ?string $redirectUrl = null) : void {
85+
$this->result = new UserAccountChangeResult($accessAllowed, $reason, $redirectUrl);
86+
}
87+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
/*
3+
* @copyright Copyright (c) 2023 T-Systems International
4+
*
5+
* @author B. Rederlechner <bernd.rederlechner@t-systems.com>
6+
*
7+
* @license GNU AGPL version 3 or any later version
8+
*
9+
*/
10+
11+
declare(strict_types=1);
12+
13+
namespace OCA\UserOIDC\Event;
14+
15+
/**
16+
* Event to provide custom mapping logic based on the OIDC token data
17+
* In order to avoid further processing the event propagation should be stopped
18+
* in the listener after processing as the value might get overwritten afterwards
19+
* by other listeners through $event->stopPropagation();
20+
*/
21+
class UserAccountChangeResult {
22+
23+
/** @var bool */
24+
private $accessAllowed;
25+
/** @var string */
26+
private $reason;
27+
/** @var string */
28+
private $redirectUrl;
29+
30+
public function __construct(bool $accessAllowed, string $reason = '', ?string $redirectUrl = null) {
31+
$this->accessAllowed = $accessAllowed;
32+
$this->redirectUrl = $redirectUrl;
33+
$this->reason = $reason;
34+
}
35+
36+
/**
37+
* @return value for the logged in user attribute
38+
*/
39+
public function isAccessAllowed(): bool {
40+
return $this->accessAllowed;
41+
}
42+
43+
public function setAccessAllowed(bool $accessAllowed): void {
44+
$this->accessAllowed = $accessAllowed;
45+
}
46+
47+
/**
48+
* @return get optional alternate redirect address
49+
*/
50+
public function getRedirectUrl(): ?string {
51+
return $this->redirectUrl;
52+
}
53+
54+
/**
55+
* @return set optional alternate redirect address
56+
*/
57+
public function setRedirectUrl(?string $redirectUrl): void {
58+
$this->redirectUrl = $redirectUrl;
59+
}
60+
61+
/**
62+
* @return get decision reason
63+
*/
64+
public function getReason(): string {
65+
return $this->reason;
66+
}
67+
68+
/**
69+
* @return set decision reason
70+
*/
71+
public function setReason(string $reason): void {
72+
$this->reason = $reason;
73+
}
74+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* @copyright Copyright (c) 2023, T-Systems International
7+
*
8+
* @author B. Rederlechner <bernd.rederlechner@t-Systems.com>
9+
*
10+
* @license AGPL-3.0
11+
*
12+
* This code is free software: you can redistribute it and/or modify
13+
* it under the terms of the GNU Affero General Public License, version 3,
14+
* as published by the Free Software Foundation.
15+
*
16+
* This program is distributed in the hope that it will be useful,
17+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
* GNU Affero General Public License for more details.
20+
*
21+
* You should have received a copy of the GNU Affero General Public License, version 3,
22+
* along with this program. If not, see <http://www.gnu.org/licenses/>
23+
*
24+
*/
25+
26+
namespace OCA\UserOIDC\Service;
27+
28+
/**
29+
* Exception if the precondition of the config update method isn't met
30+
* @since 1.4.0
31+
*/
32+
class ProvisioningDeniedException extends \Exception {
33+
private $redirectUrl;
34+
35+
/**
36+
* Exception constructor including an option redirect url.
37+
*
38+
* @param string $message The error message. It will be not revealed to the
39+
* the user (unless the hint is empty) and thus
40+
* should be not translated.
41+
* @param string $hint A useful message that is presented to the end
42+
* user. It should be translated, but must not
43+
* contain sensitive data.
44+
* @param int $code Set default to 403 (Forbidden)
45+
* @param \Exception|null $previous
46+
*/
47+
public function __construct(string $message, ?string $redirectUrl = null, int $code = 403, ?\Exception $previous = null) {
48+
parent::__construct($message, $code, $previous);
49+
$this->redirectUrl = $redirectUrl;
50+
}
51+
52+
/**
53+
* Read optional failure redirect if available
54+
* @return string|null
55+
*/
56+
public function getRedirectUrl(): ?string {
57+
return $this->redirectUrl;
58+
}
59+
60+
/**
61+
* Include redirect in string serialisation.
62+
*
63+
* @return string
64+
*/
65+
public function __toString(): string {
66+
$redirect = $this->redirectUrl ?? '<no redirect>';
67+
return __CLASS__ . ": [{$this->code}]: {$this->message} ({$redirect})\n";
68+
}
69+
}

0 commit comments

Comments
 (0)