Skip to content

Commit f5fba4d

Browse files
committed
Merge #13 Central customization setup V31
2 parents a744cc8 + ae86fd7 commit f5fba4d

File tree

5 files changed

+146
-8
lines changed

5 files changed

+146
-8
lines changed

.github/README.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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+
19+
class Application extends App implements IBootstrap {
20+
...
21+
public function register(IRegistrationContext $context): void {
22+
$context->registerEventListener(AttributeMappedEvent::class, MyUserAttributeListener::class);
23+
$context->registerEventListener(UserAccountChangeEvent::class, MyUserAccountChangeListener::class);
24+
}
25+
...
26+
}
27+
```
28+
The provisioning handler should return a `OCA\UserOIDC\Event\UserAccountChangeResult` object
29+
30+
## Feature: Telekom-specific bearer token
31+
32+
Due to historic reason, Telekom bearer tokens have a close to standard structure, but
33+
require special security implementation in detail. The customisation overrides te standard
34+
35+
36+
### Requiring web-token libraries
37+
The central configuration branch `nmc/2372-central-setup` automatic merge will frequently fail if composer
38+
upstream
39+
40+
The fast and easy way to bring it back to sync with upstream is:
41+
```
42+
git checkout nmc/2372-central-setup
43+
git rebase --onto main nmc/2372-central-setup
44+
# manually take over everything from upstream for composer.lock (TODO: automate that)
45+
46+
# ALWAYS update web-token dependencies in composer.lock
47+
# to avoid upstream conflicts. The lock file diff should only contain adds to upstream state!
48+
composer update "web-token/jwt-*"
49+
```
50+
51+
52+
### Configuring an additional Bearer preshared secret with provider
53+
TODO
54+
55+
### Testing Bearer secrets
56+
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: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,12 @@
1717
['name' => 'login#singleLogoutService', 'url' => '/sls', 'verb' => 'GET'],
1818
['name' => 'login#backChannelLogout', 'url' => '/backchannel-logout/{providerIdentifier}', 'verb' => 'POST'],
1919

20-
['name' => 'api#createUser', 'url' => '/user', 'verb' => 'POST'],
21-
['name' => 'api#deleteUser', 'url' => '/user/{userId}', 'verb' => 'DELETE'],
20+
// compatibility with NMC V24 until reconfig on SAM
21+
['name' => 'login#telekomBackChannelLogout', 'url' => '/logout', 'verb' => 'POST'],
22+
23+
// this is a security problem combined with Telekom provisioning, so we have to disable the endpoint
24+
// ['name' => 'api#createUser', 'url' => '/user', 'verb' => 'POST'],
25+
// ['name' => 'api#deleteUser', 'url' => '/user/{userId}', 'verb' => 'DELETE'],
2226

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

lib/AppInfo/Application.php

Lines changed: 78 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,25 @@
1616
use OCA\UserOIDC\Event\ExchangedTokenRequestedEvent;
1717
use OCA\UserOIDC\Listener\ExchangedTokenRequestedListener;
1818
use OCA\UserOIDC\Listener\TimezoneHandlingListener;
19+
use OCA\UserOIDC\MagentaBearer\MBackend;
1920
use OCA\UserOIDC\Service\ID4MeService;
21+
use OCA\UserOIDC\Service\ProvisioningEventService;
22+
use OCA\UserOIDC\Service\ProvisioningService;
2023
use OCA\UserOIDC\Service\SettingsService;
21-
use OCA\UserOIDC\Service\TokenService;
22-
use OCA\UserOIDC\User\Backend;
2324
use OCP\AppFramework\App;
2425
use OCP\AppFramework\Bootstrap\IBootContext;
2526
use OCP\AppFramework\Bootstrap\IBootstrap;
2627
use OCP\AppFramework\Bootstrap\IRegistrationContext;
2728
use OCP\IL10N;
2829
use OCP\IRequest;
30+
use OCP\ISession;
2931
use OCP\IURLGenerator;
3032
use OCP\IUserManager;
3133
use OCP\IUserSession;
34+
use OCP\Security\ISecureRandom;
35+
36+
// this is needed only for the special, shortened client login flow
37+
use Psr\Container\ContainerInterface;
3238
use Throwable;
3339

3440
class Application extends App implements IBootstrap {
@@ -43,14 +49,23 @@ public function __construct(array $urlParams = []) {
4349
}
4450

4551
public function register(IRegistrationContext $context): void {
52+
// Register the composer autoloader required for the added jwt-token libs
53+
include_once __DIR__ . '/../../vendor/autoload.php';
54+
55+
// override registration of provisioning srevice to use event-based solution
56+
$this->getContainer()->registerService(ProvisioningService::class, function (ContainerInterface $c): ProvisioningService {
57+
return $c->get(ProvisioningEventService::class);
58+
});
59+
4660
/** @var IUserManager $userManager */
4761
$userManager = $this->getContainer()->get(IUserManager::class);
4862

4963
/* Register our own user backend */
50-
$this->backend = $this->getContainer()->get(Backend::class);
64+
// $this->backend = $this->getContainer()->get(Backend::class);
65+
$this->backend = $this->getContainer()->get(MBackend::class);
5166
// this was done before but OC_User::useBackend calls OC::$server->getUserManager()->registerBackend anyway
5267
// so the backend was registered twice, leading to wrong user count (double)
53-
// $userManager->registerBackend($this->backend);
68+
$userManager->registerBackend($this->backend);
5469
// TODO check if it can be replaced by $userManager->registerBackend($this->backend); in our case
5570
OC_User::useBackend($this->backend);
5671

@@ -70,12 +85,69 @@ public function boot(IBootContext $context): void {
7085
try {
7186
$context->injectFn(\Closure::fromCallable([$this, 'registerRedirect']));
7287
$context->injectFn(\Closure::fromCallable([$this, 'registerLogin']));
88+
// this is the custom auto-redirect for MagentaCLOUD client access
89+
$context->injectFn(\Closure::fromCallable([$this, 'registerNmcClientFlow']));
7390
} catch (Throwable $e) {
7491
}
7592
}
7693

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

81153
private function registerRedirect(IRequest $request, IURLGenerator $urlGenerator, SettingsService $settings, ProviderMapper $providerMapper): void {

tests/bootstrap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
require_once __DIR__ . '/../vendor/autoload.php';
1212

1313
\OC::$loader->addValidRoot(OC::$SERVERROOT . '/tests');
14+
\OC::$composerAutoloader->addPsr4('OCA\\UserOIDC\\BaseTest\\', dirname(__FILE__) . '/unit/MagentaCloud/', true);
1415
\OC_App::loadApp('user_oidc');
1516

1617
OC_Hook::clear();

0 commit comments

Comments
 (0)