Skip to content

Commit 681472c

Browse files
committed
Merge #26 [V33] Central customization
2 parents d66c99c + 9ddcb52 commit 681472c

File tree

5 files changed

+147
-3
lines changed

5 files changed

+147
-3
lines changed

.github/README.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# MagentaCLOUD user_oidc
2+
3+
Customisation of the Nextcloud delivered OpenID connect app for MagentaCLOUD.
4+
5+
The app extends the standard `user_oidc` Nextcloud app,
6+
see [upstream configuration hints for basic setup](https://github.com/nextcloud/user_oidc/blob/main/README.md)
7+
8+
9+
## Feature: Event-based provisioning (upstream contribution candidate)
10+
The mechanism allows to implement custom puser provisioning logic in a separate Nextcloud app by
11+
registering and handling a attribute change and provisioning event:
12+
13+
```
14+
use OCP\AppFramework\App;
15+
use OCP\AppFramework\Bootstrap\IBootContext;
16+
use OCP\AppFramework\Bootstrap\IBootstrap;
17+
use OCP\AppFramework\Bootstrap\IRegistrationContext;
18+
class Application extends App implements IBootstrap {
19+
...
20+
public function register(IRegistrationContext $context): void {
21+
$context->registerEventListener(AttributeMappedEvent::class, MyUserAttributeListener::class);
22+
$context->registerEventListener(UserAccountChangeEvent::class, MyUserAccountChangeListener::class);
23+
}
24+
...
25+
}
26+
```
27+
The provisioning handler should return a `OCA\UserOIDC\Event\UserAccountChangeResult` object
28+
29+
## Feature: Telekom-specific bearer token
30+
31+
Due to historic reason, Telekom bearer tokens have a close to standard structure, but
32+
require special security implementation in detail. The customisation overrides te standard
33+
34+
35+
### Requiring web-token libraries
36+
The central configuration branch `nmc/2372-central-setup` automatic merge will frequently fail if composer
37+
upstream
38+
39+
The fast and easy way to bring it back to sync with upstream is:
40+
```
41+
git checkout nmc/2372-central-setup
42+
git rebase --onto main nmc/2372-central-setup
43+
# manually take over everything from upstream for composer.lock (TODO: automate that)
44+
# ALWAYS update web-token dependencies in composer.lock
45+
# to avoid upstream conflicts. The lock file diff should only contain adds to upstream state!
46+
composer update "web-token/jwt-*"
47+
```
48+
49+
50+
### Configuring an additional Bearer preshared secret with provider
51+
TODO
52+
53+
### Testing Bearer secrets
54+
TODO

COPYING.DTAG

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Although this Nextcloud app code is free and available under the AGPL3 license, Deutsche Telekom
2+
(including T-Systems) fully reserves all rights to the Telekom brand. To prevent users from getting confused about
3+
the source of a digital product or experience, there are stringent restrictions on using the Telekom brand and design,
4+
even when built into code that we provide. For any customization other than explicitly for Telekom or T-Systems, you must
5+
replace the Deutsche Telekom and T-Systems brand elements contained in the provided sources.

appinfo/routes.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@
1616
['name' => 'login#code', 'url' => '/code', 'verb' => 'GET'],
1717
['name' => 'login#singleLogoutService', 'url' => '/sls', 'verb' => 'GET'],
1818
['name' => 'login#backChannelLogout', 'url' => '/backchannel-logout/{providerIdentifier}', 'verb' => 'POST'],
19+
['name' => 'login#telekomBackChannelLogout', 'url' => '/logout', 'verb' => 'POST'],
1920

20-
['name' => 'api#createUser', 'url' => '/user', 'verb' => 'POST'],
21-
['name' => 'api#deleteUser', 'url' => '/user/{userId}', 'verb' => 'DELETE'],
21+
// ['name' => 'api#createUser', 'url' => '/user', 'verb' => 'POST'],
22+
// ['name' => 'api#deleteUser', 'url' => '/user/{userId}', 'verb' => 'DELETE'],
2223

2324
['name' => 'id4me#showLogin', 'url' => '/id4me', 'verb' => 'GET'],
2425
['name' => 'id4me#login', 'url' => '/id4me', 'verb' => 'POST'],

lib/AppInfo/Application.php

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020
use OCA\UserOIDC\Listener\InternalTokenRequestedListener;
2121
use OCA\UserOIDC\Listener\TimezoneHandlingListener;
2222
use OCA\UserOIDC\Listener\TokenInvalidatedListener;
23+
use OCA\UserOIDC\MagentaBearer\MBackend;
2324
use OCA\UserOIDC\Service\ID4MeService;
25+
use OCA\UserOIDC\Service\ProvisioningEventService;
26+
use OCA\UserOIDC\Service\ProvisioningService;
2427
use OCA\UserOIDC\Service\SettingsService;
2528
use OCA\UserOIDC\Service\TokenService;
2629
use OCA\UserOIDC\User\Backend;
@@ -31,9 +34,14 @@
3134
use OCP\IConfig;
3235
use OCP\IL10N;
3336
use OCP\IRequest;
37+
use OCP\ISession;
3438
use OCP\IURLGenerator;
3539
use OCP\IUserManager;
3640
use OCP\IUserSession;
41+
use OCP\Security\ISecureRandom;
42+
43+
// this is needed only for the special, shortened client login flow
44+
use Psr\Container\ContainerInterface;
3745
use Throwable;
3846

3947
class Application extends App implements IBootstrap {
@@ -48,11 +56,19 @@ public function __construct(array $urlParams = []) {
4856
}
4957

5058
public function register(IRegistrationContext $context): void {
59+
// Register the composer autoloader required for the added jwt-token libs
60+
include_once __DIR__ . '/../../vendor/autoload.php';
61+
62+
// override registration of provisioning srevice to use event-based solution
63+
$this->getContainer()->registerService(ProvisioningService::class, function (ContainerInterface $c): ProvisioningService {
64+
return $c->get(ProvisioningEventService::class);
65+
});
66+
5167
/** @var IUserManager $userManager */
5268
$userManager = $this->getContainer()->get(IUserManager::class);
5369

5470
/* Register our own user backend */
55-
$this->backend = $this->getContainer()->get(Backend::class);
71+
$this->backend = $this->getContainer()->get(MBackend::class);
5672

5773
$config = $this->getContainer()->get(IConfig::class);
5874
if (version_compare($config->getSystemValueString('version', '0.0.0'), '32.0.0', '>=')) {
@@ -84,10 +100,73 @@ public function boot(IBootContext $context): void {
84100
try {
85101
$context->injectFn(\Closure::fromCallable([$this, 'registerRedirect']));
86102
$context->injectFn(\Closure::fromCallable([$this, 'registerLogin']));
103+
104+
// this is the custom auto-redirect for MagentaCLOUD client access
105+
$context->injectFn(\Closure::fromCallable([$this, 'registerNmcClientFlow']));
87106
} catch (Throwable $e) {
88107
}
89108
}
90109

110+
/**
111+
* This is the automatic redirect exclusively for Nextcloud/Magentacloud clients completely skipping consent layer
112+
*/
113+
private function registerNmcClientFlow(IRequest $request,
114+
IURLGenerator $urlGenerator,
115+
ProviderMapper $providerMapper,
116+
ISession $session,
117+
ISecureRandom $random): void {
118+
$providers = $this->getCachedProviders($providerMapper);
119+
120+
// Handle immediate redirect on client first-time login
121+
$isClientLoginFlow = false;
122+
123+
try {
124+
$isClientLoginFlow = $request->getPathInfo() === '/login/flow';
125+
} catch (Exception $e) {
126+
// in case any errors happen when checking for the path do not apply redirect logic as it is only needed for the login
127+
}
128+
129+
if ($isClientLoginFlow) {
130+
// only redirect if Telekom provider registered
131+
$tproviders = array_values(array_filter($providers, function ($p) {
132+
return strtolower($p->getIdentifier()) === 'telekom';
133+
}));
134+
135+
if (count($tproviders) == 0) {
136+
// always show normal login flow as error fallback
137+
return;
138+
}
139+
140+
$stateToken = $random->generate(64, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS);
141+
$session->set('client.flow.state.token', $stateToken);
142+
143+
// call the service to get the params, but suppress the template
144+
// compute grant redirect Url to go directly to Telekom login
145+
$redirectUrl = $urlGenerator->linkToRoute('core.ClientFlowLogin.grantPage', [
146+
'stateToken' => $stateToken,
147+
// grantPage service operation is deriving oauth2 client name (again),
148+
// so we simply pass on clientIdentifier or empty string
149+
'clientIdentifier' => $request->getParam('clientIdentifier', ''),
150+
'direct' => $request->getParam('direct', '0')
151+
]);
152+
153+
if ($redirectUrl === null) {
154+
155+
// always show normal login flow as error fallback
156+
return;
157+
}
158+
159+
// direct login, consent layer later
160+
$targetUrl = $urlGenerator->linkToRoute(self::APP_ID . '.login.login', [
161+
'providerId' => $tproviders[0]->getId(),
162+
'redirectUrl' => $redirectUrl
163+
]);
164+
165+
header('Location: ' . $targetUrl);
166+
exit();
167+
}
168+
}
169+
91170
private function checkLoginToken(TokenService $tokenService): void {
92171
$tokenService->checkLoginToken();
93172
}

tests/bootstrap.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,9 @@
1818
require_once __DIR__ . '/../../../tests/autoload.php';
1919
require_once __DIR__ . '/../vendor/autoload.php';
2020

21+
\OC::$loader->addValidRoot(OC::$SERVERROOT . '/tests');
22+
\OC::$composerAutoloader->addPsr4('OCA\\UserOIDC\\BaseTest\\', dirname(__FILE__) . '/unit/MagentaCloud/', true);
23+
2124
Server::get(IAppManager::class)->loadApp('user_oidc');
25+
26+
OC_Hook::clear();

0 commit comments

Comments
 (0)