Skip to content

Commit f2979a6

Browse files
committed
Merge #14 Add event based provisioning V31
2 parents 34b4a36 + 610aec0 commit f2979a6

File tree

8 files changed

+1053
-6
lines changed

8 files changed

+1053
-6
lines changed

lib/Controller/LoginController.php

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use OCA\UserOIDC\Service\DiscoveryService;
2424
use OCA\UserOIDC\Service\LdapService;
2525
use OCA\UserOIDC\Service\ProviderService;
26+
use OCA\UserOIDC\Service\ProvisioningDeniedException;
2627
use OCA\UserOIDC\Service\ProvisioningService;
2728
use OCA\UserOIDC\Vendor\Firebase\JWT\JWT;
2829
use OCP\AppFramework\Db\DoesNotExistException;
@@ -480,15 +481,35 @@ public function code(string $state = '', string $code = '', string $scope = '',
480481
}
481482

482483
if ($autoProvisionAllowed) {
483-
$softAutoProvisionAllowed = (!isset($oidcSystemConfig['soft_auto_provision']) || $oidcSystemConfig['soft_auto_provision']);
484-
if (!$softAutoProvisionAllowed && $userFromOtherBackend !== null) {
484+
// $softAutoProvisionAllowed = (!isset($oidcSystemConfig['soft_auto_provision']) || $oidcSystemConfig['soft_auto_provision']);
485+
// if (!$softAutoProvisionAllowed && $userFromOtherBackend !== null) {
485486
// if soft auto-provisioning is disabled,
486487
// we refuse login for a user that already exists in another backend
487-
$message = $this->l10n->t('User conflict');
488-
return $this->build403TemplateResponse($message, Http::STATUS_BAD_REQUEST, ['reason' => 'non-soft auto provision, user conflict'], false);
488+
// $message = $this->l10n->t('User conflict');
489+
// return $this->build403TemplateResponse($message, Http::STATUS_BAD_REQUEST, ['reason' => 'non-soft auto provision, user conflict'], false);
490+
// }
491+
492+
// TODO: (proposal) refactor all provisioning strategies into event handlers
493+
$user = null;
494+
495+
try {
496+
$user = $this->provisioningService->provisionUser($userId, $providerId, $idTokenPayload, $userFromOtherBackend);
497+
} catch (ProvisioningDeniedException $denied) {
498+
// TODO MagentaCLOUD should upstream the exception handling
499+
$redirectUrl = $denied->getRedirectUrl();
500+
if ($redirectUrl === null) {
501+
$message = $this->l10n->t('Failed to provision user');
502+
return $this->build403TemplateResponse($message, Http::STATUS_BAD_REQUEST, ['reason' => $denied->getMessage()]);
503+
} else {
504+
// error response is a redirect, e.g. to a booking site
505+
// so that you can immediately get the registration page
506+
return new RedirectResponse($redirectUrl);
507+
}
489508
}
509+
490510
// use potential user from other backend, create it in our backend if it does not exist
491-
$user = $this->provisioningService->provisionUser($userId, $providerId, $idTokenPayload, $userFromOtherBackend);
511+
// $user = $this->provisioningService->provisionUser($userId, $providerId, $idTokenPayload, $userFromOtherBackend);
512+
// no default exception handling to pass on unittest assertion failures
492513
} else {
493514
// when auto provision is disabled, we assume the user has been created by another user backend (or manually)
494515
$user = $userFromOtherBackend;
@@ -739,7 +760,7 @@ public function backChannelLogout(string $providerIdentifier, string $logout_tok
739760
* @return JSONResponse
740761
*/
741762
private function getBackchannelLogoutErrorResponse(
742-
string $error, string $description, array $throttleMetadata = [],
763+
string $error, string $description, array $throttleMetadata = [], ?bool $throttle = null,
743764
): JSONResponse {
744765
$this->logger->debug('Backchannel logout error. ' . $error . ' ; ' . $description);
745766
return new JSONResponse(
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)