1414use OCA \Files \Event \LoadAdditionalScriptsEvent ;
1515use OCA \UserOIDC \Db \ProviderMapper ;
1616use OCA \UserOIDC \Listener \TimezoneHandlingListener ;
17+ use OCA \UserOIDC \MagentaBearer \MBackend ;
1718use OCA \UserOIDC \Service \ID4MeService ;
19+ use OCA \UserOIDC \Service \ProvisioningEventService ;
20+ use OCA \UserOIDC \Service \ProvisioningService ;
1821use OCA \UserOIDC \Service \SettingsService ;
19- use OCA \UserOIDC \User \Backend ;
2022use OCP \AppFramework \App ;
2123use OCP \AppFramework \Bootstrap \IBootContext ;
2224use OCP \AppFramework \Bootstrap \IBootstrap ;
2729use OCP \IUserManager ;
2830use OCP \IUserSession ;
2931use Throwable ;
32+ use Psr \Container \ContainerInterface ;
33+
34+ // this is needed only for the special, shortened client login flow
35+ use OCP \Security \ISecureRandom ;
36+ use OCP \ISession ;
3037
3138class Application extends App implements IBootstrap {
3239 public const APP_ID = 'user_oidc ' ;
@@ -40,14 +47,23 @@ public function __construct(array $urlParams = []) {
4047 }
4148
4249 public function register (IRegistrationContext $ context ): void {
50+ // Register the composer autoloader required for the added jwt-token libs
51+ include_once __DIR__ . '/../../vendor/autoload.php ' ;
52+
53+ // override registration of provisioning srevice to use event-based solution
54+ $ this ->getContainer ()->registerService (ProvisioningService::class, function (ContainerInterface $ c ): ProvisioningService {
55+ return $ c ->get (ProvisioningEventService::class);
56+ });
57+
4358 /** @var IUserManager $userManager */
4459 $ userManager = $ this ->getContainer ()->get (IUserManager::class);
4560
4661 /* Register our own user backend */
47- $ this ->backend = $ this ->getContainer ()->get (Backend::class);
62+ // $this->backend = $this->getContainer()->get(Backend::class);
63+ $ this ->backend = $ this ->getContainer ()->get (MBackend::class);
4864 // this was done before but OC_User::useBackend calls OC::$server->getUserManager()->registerBackend anyway
4965 // so the backend was registered twice, leading to wrong user count (double)
50- // $userManager->registerBackend($this->backend);
66+ $ userManager ->registerBackend ($ this ->backend );
5167 // TODO check if it can be replaced by $userManager->registerBackend($this->backend); in our case
5268 OC_User::useBackend ($ this ->backend );
5369
@@ -64,11 +80,72 @@ public function boot(IBootContext $context): void {
6480
6581 try {
6682 $ context ->injectFn (\Closure::fromCallable ([$ this , 'registerRedirect ' ]));
67- $ context ->injectFn (\Closure::fromCallable ([$ this , 'registerLogin ' ]));
83+ $ context ->injectFn (\Closure::fromCallable ([$ this , 'registerLogin ' ]));
84+ // this is the custom auto-redirect for MagentaCLOUD client access
85+ $ context ->injectFn (\Closure::fromCallable ([$ this , 'registerNmcClientFlow ' ]));
6886 } catch (Throwable $ e ) {
6987 }
7088 }
7189
90+ /**
91+ * This is the automatic redirect exclusively for Nextcloud/Magentacloud clients completely skipping consent layer
92+ */
93+ private function registerNmcClientFlow (IRequest $ request ,
94+ IURLGenerator $ urlGenerator ,
95+ ProviderMapper $ providerMapper ,
96+ ISession $ session ,
97+ ISecureRandom $ random ): void {
98+ $ providers = $ this ->getCachedProviders ($ providerMapper );
99+
100+ // Handle immediate redirect on client first-time login
101+ $ isClientLoginFlow = false ;
102+
103+ try {
104+ $ isClientLoginFlow = $ request ->getPathInfo () === '/login/flow ' ;
105+ } catch (Exception $ e ) {
106+ // in case any errors happen when checking for the path do not apply redirect logic as it is only needed for the login
107+ }
108+
109+ if ($ isClientLoginFlow ) {
110+ // only redirect if Telekom provider registered
111+ $ tproviders = array_values (array_filter ($ providers , function ($ p ) {
112+ return strtolower ($ p ->getIdentifier ()) === "telekom " ;
113+ }));
114+
115+ if (count ($ tproviders ) == 0 ) {
116+ // always show normal login flow as error fallback
117+ return ;
118+ }
119+
120+ $ stateToken = $ random ->generate (64 , ISecureRandom::CHAR_LOWER .ISecureRandom::CHAR_UPPER .ISecureRandom::CHAR_DIGITS );
121+ $ session ->set ('client.flow.state.token ' , $ stateToken );
122+
123+ // call the service to get the params, but suppress the template
124+ // compute grant redirect Url to go directly to Telekom login
125+ $ redirectUrl = $ urlGenerator ->linkToRoute ('core.ClientFlowLogin.grantPage ' , [
126+ 'stateToken ' => $ stateToken ,
127+ // grantPage service operation is deriving oauth2 client name (again),
128+ // so we simply pass on clientIdentifier or empty string
129+ 'clientIdentifier ' => $ request ->getParam ('clientIdentifier ' , '' ),
130+ 'direct ' => $ request ->getParam ('direct ' , '0 ' )
131+ ]);
132+
133+ if ($ redirectUrl === null ) {
134+ // always show normal login flow as error fallback
135+ return ;
136+ }
137+
138+ // direct login, consent layer later
139+ $ targetUrl = $ urlGenerator ->linkToRoute (self ::APP_ID . '.login.login ' , [
140+ 'providerId ' => $ tproviders [0 ]->getId (),
141+ 'redirectUrl ' => $ redirectUrl
142+ ]);
143+
144+ header ('Location: ' . $ targetUrl );
145+ exit ();
146+ }
147+ }
148+
72149 private function registerRedirect (IRequest $ request , IURLGenerator $ urlGenerator , SettingsService $ settings , ProviderMapper $ providerMapper ): void {
73150 $ providers = $ this ->getCachedProviders ($ providerMapper );
74151 $ redirectUrl = $ request ->getParam ('redirect_url ' );
0 commit comments