@@ -36,86 +36,86 @@ discard block |
||
36 | 36 | |
37 | 37 | $application = new Application(); |
38 | 38 | $application->registerRoutes($this, [ |
39 | - 'routes' => [ |
|
40 | - ['name' => 'lost#email', 'url' => '/lostpassword/email', 'verb' => 'POST'], |
|
41 | - ['name' => 'lost#resetform', 'url' => '/lostpassword/reset/form/{token}/{userId}', 'verb' => 'GET'], |
|
42 | - ['name' => 'lost#setPassword', 'url' => '/lostpassword/set/{token}/{userId}', 'verb' => 'POST'], |
|
43 | - ['name' => 'user#getDisplayNames', 'url' => '/displaynames', 'verb' => 'POST'], |
|
44 | - ['name' => 'avatar#getAvatar', 'url' => '/avatar/{userId}/{size}', 'verb' => 'GET'], |
|
45 | - ['name' => 'avatar#deleteAvatar', 'url' => '/avatar/', 'verb' => 'DELETE'], |
|
46 | - ['name' => 'avatar#postCroppedAvatar', 'url' => '/avatar/cropped', 'verb' => 'POST'], |
|
47 | - ['name' => 'avatar#getTmpAvatar', 'url' => '/avatar/tmp', 'verb' => 'GET'], |
|
48 | - ['name' => 'avatar#postAvatar', 'url' => '/avatar/', 'verb' => 'POST'], |
|
49 | - ['name' => 'GuestAvatar#getAvatar', 'url' => '/avatar/guest/{guestName}/{size}', 'verb' => 'GET'], |
|
50 | - ['name' => 'CSRFToken#index', 'url' => '/csrftoken', 'verb' => 'GET'], |
|
51 | - ['name' => 'login#tryLogin', 'url' => '/login', 'verb' => 'POST'], |
|
52 | - ['name' => 'login#confirmPassword', 'url' => '/login/confirm', 'verb' => 'POST'], |
|
53 | - ['name' => 'login#showLoginForm', 'url' => '/login', 'verb' => 'GET'], |
|
54 | - ['name' => 'login#logout', 'url' => '/logout', 'verb' => 'GET'], |
|
55 | - // Original login flow used by all clients |
|
56 | - ['name' => 'ClientFlowLogin#showAuthPickerPage', 'url' => '/login/flow', 'verb' => 'GET'], |
|
57 | - ['name' => 'ClientFlowLogin#generateAppPassword', 'url' => '/login/flow', 'verb' => 'POST'], |
|
58 | - ['name' => 'ClientFlowLogin#grantPage', 'url' => '/login/flow/grant', 'verb' => 'GET'], |
|
59 | - ['name' => 'ClientFlowLogin#apptokenRedirect', 'url' => '/login/flow/apptoken', 'verb' => 'POST'], |
|
60 | - // NG login flow used by desktop client in case of Kerberos/fancy 2fa (smart cards for example) |
|
61 | - ['name' => 'ClientFlowLoginV2#poll', 'url' => '/login/v2/poll', 'verb' => 'POST'], |
|
62 | - ['name' => 'ClientFlowLoginV2#showAuthPickerPage', 'url' => '/login/v2/flow', 'verb' => 'GET'], |
|
63 | - ['name' => 'ClientFlowLoginV2#landing', 'url' => '/login/v2/flow/{token}', 'verb' => 'GET'], |
|
64 | - ['name' => 'ClientFlowLoginV2#grantPage', 'url' => '/login/v2/grant', 'verb' => 'GET'], |
|
65 | - ['name' => 'ClientFlowLoginV2#generateAppPassword', 'url' => '/login/v2/grant', 'verb' => 'POST'], |
|
66 | - ['name' => 'ClientFlowLoginV2#init', 'url' => '/login/v2', 'verb' => 'POST'], |
|
67 | - ['name' => 'TwoFactorChallenge#selectChallenge', 'url' => '/login/selectchallenge', 'verb' => 'GET'], |
|
68 | - ['name' => 'TwoFactorChallenge#showChallenge', 'url' => '/login/challenge/{challengeProviderId}', 'verb' => 'GET'], |
|
69 | - ['name' => 'TwoFactorChallenge#solveChallenge', 'url' => '/login/challenge/{challengeProviderId}', 'verb' => 'POST'], |
|
70 | - ['name' => 'TwoFactorChallenge#setupProviders', 'url' => 'login/setupchallenge', 'verb' => 'GET'], |
|
71 | - ['name' => 'TwoFactorChallenge#setupProvider', 'url' => 'login/setupchallenge/{providerId}', 'verb' => 'GET'], |
|
72 | - ['name' => 'TwoFactorChallenge#confirmProviderSetup', 'url' => 'login/setupchallenge/{providerId}', 'verb' => 'POST'], |
|
73 | - ['name' => 'OCJS#getConfig', 'url' => '/core/js/oc.js', 'verb' => 'GET'], |
|
74 | - ['name' => 'Preview#getPreviewByFileId', 'url' => '/core/preview', 'verb' => 'GET'], |
|
75 | - ['name' => 'Preview#getPreview', 'url' => '/core/preview.png', 'verb' => 'GET'], |
|
76 | - ['name' => 'Svg#getSvgFromCore', 'url' => '/svg/core/{folder}/{fileName}', 'verb' => 'GET'], |
|
77 | - ['name' => 'Svg#getSvgFromApp', 'url' => '/svg/{app}/{fileName}', 'verb' => 'GET'], |
|
78 | - ['name' => 'Css#getCss', 'url' => '/css/{appName}/{fileName}', 'verb' => 'GET'], |
|
79 | - ['name' => 'Js#getJs', 'url' => '/js/{appName}/{fileName}', 'verb' => 'GET'], |
|
80 | - ['name' => 'contactsMenu#index', 'url' => '/contactsmenu/contacts', 'verb' => 'POST'], |
|
81 | - ['name' => 'contactsMenu#findOne', 'url' => '/contactsmenu/findOne', 'verb' => 'POST'], |
|
82 | - ['name' => 'WalledGarden#get', 'url' => '/204', 'verb' => 'GET'], |
|
83 | - ['name' => 'Search#search', 'url' => '/core/search', 'verb' => 'GET'], |
|
39 | + 'routes' => [ |
|
40 | + ['name' => 'lost#email', 'url' => '/lostpassword/email', 'verb' => 'POST'], |
|
41 | + ['name' => 'lost#resetform', 'url' => '/lostpassword/reset/form/{token}/{userId}', 'verb' => 'GET'], |
|
42 | + ['name' => 'lost#setPassword', 'url' => '/lostpassword/set/{token}/{userId}', 'verb' => 'POST'], |
|
43 | + ['name' => 'user#getDisplayNames', 'url' => '/displaynames', 'verb' => 'POST'], |
|
44 | + ['name' => 'avatar#getAvatar', 'url' => '/avatar/{userId}/{size}', 'verb' => 'GET'], |
|
45 | + ['name' => 'avatar#deleteAvatar', 'url' => '/avatar/', 'verb' => 'DELETE'], |
|
46 | + ['name' => 'avatar#postCroppedAvatar', 'url' => '/avatar/cropped', 'verb' => 'POST'], |
|
47 | + ['name' => 'avatar#getTmpAvatar', 'url' => '/avatar/tmp', 'verb' => 'GET'], |
|
48 | + ['name' => 'avatar#postAvatar', 'url' => '/avatar/', 'verb' => 'POST'], |
|
49 | + ['name' => 'GuestAvatar#getAvatar', 'url' => '/avatar/guest/{guestName}/{size}', 'verb' => 'GET'], |
|
50 | + ['name' => 'CSRFToken#index', 'url' => '/csrftoken', 'verb' => 'GET'], |
|
51 | + ['name' => 'login#tryLogin', 'url' => '/login', 'verb' => 'POST'], |
|
52 | + ['name' => 'login#confirmPassword', 'url' => '/login/confirm', 'verb' => 'POST'], |
|
53 | + ['name' => 'login#showLoginForm', 'url' => '/login', 'verb' => 'GET'], |
|
54 | + ['name' => 'login#logout', 'url' => '/logout', 'verb' => 'GET'], |
|
55 | + // Original login flow used by all clients |
|
56 | + ['name' => 'ClientFlowLogin#showAuthPickerPage', 'url' => '/login/flow', 'verb' => 'GET'], |
|
57 | + ['name' => 'ClientFlowLogin#generateAppPassword', 'url' => '/login/flow', 'verb' => 'POST'], |
|
58 | + ['name' => 'ClientFlowLogin#grantPage', 'url' => '/login/flow/grant', 'verb' => 'GET'], |
|
59 | + ['name' => 'ClientFlowLogin#apptokenRedirect', 'url' => '/login/flow/apptoken', 'verb' => 'POST'], |
|
60 | + // NG login flow used by desktop client in case of Kerberos/fancy 2fa (smart cards for example) |
|
61 | + ['name' => 'ClientFlowLoginV2#poll', 'url' => '/login/v2/poll', 'verb' => 'POST'], |
|
62 | + ['name' => 'ClientFlowLoginV2#showAuthPickerPage', 'url' => '/login/v2/flow', 'verb' => 'GET'], |
|
63 | + ['name' => 'ClientFlowLoginV2#landing', 'url' => '/login/v2/flow/{token}', 'verb' => 'GET'], |
|
64 | + ['name' => 'ClientFlowLoginV2#grantPage', 'url' => '/login/v2/grant', 'verb' => 'GET'], |
|
65 | + ['name' => 'ClientFlowLoginV2#generateAppPassword', 'url' => '/login/v2/grant', 'verb' => 'POST'], |
|
66 | + ['name' => 'ClientFlowLoginV2#init', 'url' => '/login/v2', 'verb' => 'POST'], |
|
67 | + ['name' => 'TwoFactorChallenge#selectChallenge', 'url' => '/login/selectchallenge', 'verb' => 'GET'], |
|
68 | + ['name' => 'TwoFactorChallenge#showChallenge', 'url' => '/login/challenge/{challengeProviderId}', 'verb' => 'GET'], |
|
69 | + ['name' => 'TwoFactorChallenge#solveChallenge', 'url' => '/login/challenge/{challengeProviderId}', 'verb' => 'POST'], |
|
70 | + ['name' => 'TwoFactorChallenge#setupProviders', 'url' => 'login/setupchallenge', 'verb' => 'GET'], |
|
71 | + ['name' => 'TwoFactorChallenge#setupProvider', 'url' => 'login/setupchallenge/{providerId}', 'verb' => 'GET'], |
|
72 | + ['name' => 'TwoFactorChallenge#confirmProviderSetup', 'url' => 'login/setupchallenge/{providerId}', 'verb' => 'POST'], |
|
73 | + ['name' => 'OCJS#getConfig', 'url' => '/core/js/oc.js', 'verb' => 'GET'], |
|
74 | + ['name' => 'Preview#getPreviewByFileId', 'url' => '/core/preview', 'verb' => 'GET'], |
|
75 | + ['name' => 'Preview#getPreview', 'url' => '/core/preview.png', 'verb' => 'GET'], |
|
76 | + ['name' => 'Svg#getSvgFromCore', 'url' => '/svg/core/{folder}/{fileName}', 'verb' => 'GET'], |
|
77 | + ['name' => 'Svg#getSvgFromApp', 'url' => '/svg/{app}/{fileName}', 'verb' => 'GET'], |
|
78 | + ['name' => 'Css#getCss', 'url' => '/css/{appName}/{fileName}', 'verb' => 'GET'], |
|
79 | + ['name' => 'Js#getJs', 'url' => '/js/{appName}/{fileName}', 'verb' => 'GET'], |
|
80 | + ['name' => 'contactsMenu#index', 'url' => '/contactsmenu/contacts', 'verb' => 'POST'], |
|
81 | + ['name' => 'contactsMenu#findOne', 'url' => '/contactsmenu/findOne', 'verb' => 'POST'], |
|
82 | + ['name' => 'WalledGarden#get', 'url' => '/204', 'verb' => 'GET'], |
|
83 | + ['name' => 'Search#search', 'url' => '/core/search', 'verb' => 'GET'], |
|
84 | 84 | |
85 | - // Legacy routes that need to be globally available while they are handled by an app |
|
86 | - ['name' => 'viewcontroller#showFile', 'url' => '/f/{fileid}', 'verb' => 'GET', 'app' => 'files'], |
|
87 | - ['name' => 'sharecontroller#showShare', 'url' => '/s/{token}', 'verb' => 'GET', 'app' => 'files_sharing'], |
|
88 | - ['name' => 'sharecontroller#showAuthenticate', 'url' => '/s/{token}/authenticate/{redirect}', 'verb' => 'GET', 'app' => 'files_sharing'], |
|
89 | - ['name' => 'sharecontroller#authenticate', 'url' => '/s/{token}/authenticate/{redirect}', 'verb' => 'POST', 'app' => 'files_sharing'], |
|
90 | - ['name' => 'sharecontroller#downloadShare', 'url' => '/s/{token}/download', 'verb' => 'GET', 'app' => 'files_sharing'], |
|
91 | - ['name' => 'publicpreview#directLink', 'url' => '/s/{token}/preview', 'verb' => 'GET', 'app' => 'files_sharing'], |
|
92 | - ['name' => 'requesthandlercontroller#addShare', 'url' => '/ocm/shares', 'verb' => 'POST', 'app' => 'cloud_federation_api'], |
|
93 | - ['name' => 'requesthandlercontroller#receiveNotification', 'url' => '/ocm/notifications', 'verb' => 'POST', 'app' => 'cloud_federation_api'], |
|
94 | - ['name' => 'pagecontroller#showCall', 'url' => '/call/{token}', 'verb' => 'GET', 'app' => 'spreed'], |
|
95 | - ['name' => 'pagecontroller#authenticatePassword', 'url' => '/call/{token}', 'verb' => 'POST', 'app' => 'spreed'], |
|
96 | - ], |
|
97 | - 'ocs' => [ |
|
98 | - ['root' => '/cloud', 'name' => 'OCS#getCapabilities', 'url' => '/capabilities', 'verb' => 'GET'], |
|
99 | - ['root' => '', 'name' => 'OCS#getConfig', 'url' => '/config', 'verb' => 'GET'], |
|
100 | - ['root' => '/person', 'name' => 'OCS#personCheck', 'url' => '/check', 'verb' => 'POST'], |
|
101 | - ['root' => '/identityproof', 'name' => 'OCS#getIdentityProof', 'url' => '/key/{cloudId}', 'verb' => 'GET'], |
|
102 | - ['root' => '/core', 'name' => 'Navigation#getAppsNavigation', 'url' => '/navigation/apps', 'verb' => 'GET'], |
|
103 | - ['root' => '/core', 'name' => 'Navigation#getSettingsNavigation', 'url' => '/navigation/settings', 'verb' => 'GET'], |
|
104 | - ['root' => '/core', 'name' => 'AutoComplete#get', 'url' => '/autocomplete/get', 'verb' => 'GET'], |
|
105 | - ['root' => '/core', 'name' => 'WhatsNew#get', 'url' => '/whatsnew', 'verb' => 'GET'], |
|
106 | - ['root' => '/core', 'name' => 'WhatsNew#dismiss', 'url' => '/whatsnew', 'verb' => 'POST'], |
|
107 | - ['root' => '/core', 'name' => 'AppPassword#getAppPassword', 'url' => '/getapppassword', 'verb' => 'GET'], |
|
108 | - ['root' => '/core', 'name' => 'AppPassword#deleteAppPassword', 'url' => '/apppassword', 'verb' => 'DELETE'], |
|
85 | + // Legacy routes that need to be globally available while they are handled by an app |
|
86 | + ['name' => 'viewcontroller#showFile', 'url' => '/f/{fileid}', 'verb' => 'GET', 'app' => 'files'], |
|
87 | + ['name' => 'sharecontroller#showShare', 'url' => '/s/{token}', 'verb' => 'GET', 'app' => 'files_sharing'], |
|
88 | + ['name' => 'sharecontroller#showAuthenticate', 'url' => '/s/{token}/authenticate/{redirect}', 'verb' => 'GET', 'app' => 'files_sharing'], |
|
89 | + ['name' => 'sharecontroller#authenticate', 'url' => '/s/{token}/authenticate/{redirect}', 'verb' => 'POST', 'app' => 'files_sharing'], |
|
90 | + ['name' => 'sharecontroller#downloadShare', 'url' => '/s/{token}/download', 'verb' => 'GET', 'app' => 'files_sharing'], |
|
91 | + ['name' => 'publicpreview#directLink', 'url' => '/s/{token}/preview', 'verb' => 'GET', 'app' => 'files_sharing'], |
|
92 | + ['name' => 'requesthandlercontroller#addShare', 'url' => '/ocm/shares', 'verb' => 'POST', 'app' => 'cloud_federation_api'], |
|
93 | + ['name' => 'requesthandlercontroller#receiveNotification', 'url' => '/ocm/notifications', 'verb' => 'POST', 'app' => 'cloud_federation_api'], |
|
94 | + ['name' => 'pagecontroller#showCall', 'url' => '/call/{token}', 'verb' => 'GET', 'app' => 'spreed'], |
|
95 | + ['name' => 'pagecontroller#authenticatePassword', 'url' => '/call/{token}', 'verb' => 'POST', 'app' => 'spreed'], |
|
96 | + ], |
|
97 | + 'ocs' => [ |
|
98 | + ['root' => '/cloud', 'name' => 'OCS#getCapabilities', 'url' => '/capabilities', 'verb' => 'GET'], |
|
99 | + ['root' => '', 'name' => 'OCS#getConfig', 'url' => '/config', 'verb' => 'GET'], |
|
100 | + ['root' => '/person', 'name' => 'OCS#personCheck', 'url' => '/check', 'verb' => 'POST'], |
|
101 | + ['root' => '/identityproof', 'name' => 'OCS#getIdentityProof', 'url' => '/key/{cloudId}', 'verb' => 'GET'], |
|
102 | + ['root' => '/core', 'name' => 'Navigation#getAppsNavigation', 'url' => '/navigation/apps', 'verb' => 'GET'], |
|
103 | + ['root' => '/core', 'name' => 'Navigation#getSettingsNavigation', 'url' => '/navigation/settings', 'verb' => 'GET'], |
|
104 | + ['root' => '/core', 'name' => 'AutoComplete#get', 'url' => '/autocomplete/get', 'verb' => 'GET'], |
|
105 | + ['root' => '/core', 'name' => 'WhatsNew#get', 'url' => '/whatsnew', 'verb' => 'GET'], |
|
106 | + ['root' => '/core', 'name' => 'WhatsNew#dismiss', 'url' => '/whatsnew', 'verb' => 'POST'], |
|
107 | + ['root' => '/core', 'name' => 'AppPassword#getAppPassword', 'url' => '/getapppassword', 'verb' => 'GET'], |
|
108 | + ['root' => '/core', 'name' => 'AppPassword#deleteAppPassword', 'url' => '/apppassword', 'verb' => 'DELETE'], |
|
109 | 109 | |
110 | - ['root' => '/collaboration', 'name' => 'CollaborationResources#searchCollections', 'url' => '/resources/collections/search/{filter}', 'verb' => 'GET'], |
|
111 | - ['root' => '/collaboration', 'name' => 'CollaborationResources#listCollection', 'url' => '/resources/collections/{collectionId}', 'verb' => 'GET'], |
|
112 | - ['root' => '/collaboration', 'name' => 'CollaborationResources#renameCollection', 'url' => '/resources/collections/{collectionId}', 'verb' => 'PUT'], |
|
113 | - ['root' => '/collaboration', 'name' => 'CollaborationResources#addResource', 'url' => '/resources/collections/{collectionId}', 'verb' => 'POST'], |
|
110 | + ['root' => '/collaboration', 'name' => 'CollaborationResources#searchCollections', 'url' => '/resources/collections/search/{filter}', 'verb' => 'GET'], |
|
111 | + ['root' => '/collaboration', 'name' => 'CollaborationResources#listCollection', 'url' => '/resources/collections/{collectionId}', 'verb' => 'GET'], |
|
112 | + ['root' => '/collaboration', 'name' => 'CollaborationResources#renameCollection', 'url' => '/resources/collections/{collectionId}', 'verb' => 'PUT'], |
|
113 | + ['root' => '/collaboration', 'name' => 'CollaborationResources#addResource', 'url' => '/resources/collections/{collectionId}', 'verb' => 'POST'], |
|
114 | 114 | |
115 | - ['root' => '/collaboration', 'name' => 'CollaborationResources#removeResource', 'url' => '/resources/collections/{collectionId}', 'verb' => 'DELETE'], |
|
116 | - ['root' => '/collaboration', 'name' => 'CollaborationResources#getCollectionsByResource', 'url' => '/resources/{resourceType}/{resourceId}', 'verb' => 'GET'], |
|
117 | - ['root' => '/collaboration', 'name' => 'CollaborationResources#createCollectionOnResource', 'url' => '/resources/{baseResourceType}/{baseResourceId}', 'verb' => 'POST'], |
|
118 | - ], |
|
115 | + ['root' => '/collaboration', 'name' => 'CollaborationResources#removeResource', 'url' => '/resources/collections/{collectionId}', 'verb' => 'DELETE'], |
|
116 | + ['root' => '/collaboration', 'name' => 'CollaborationResources#getCollectionsByResource', 'url' => '/resources/{resourceType}/{resourceId}', 'verb' => 'GET'], |
|
117 | + ['root' => '/collaboration', 'name' => 'CollaborationResources#createCollectionOnResource', 'url' => '/resources/{baseResourceType}/{baseResourceId}', 'verb' => 'POST'], |
|
118 | + ], |
|
119 | 119 | ]); |
120 | 120 | |
121 | 121 | // Post installation check |
@@ -124,4 +124,4 @@ discard block |
||
124 | 124 | // Core ajax actions |
125 | 125 | // Routing |
126 | 126 | $this->create('core_ajax_update', '/core/ajax/update.php') |
127 | - ->actionInclude('core/ajax/update.php'); |
|
127 | + ->actionInclude('core/ajax/update.php'); |
@@ -20,10 +20,10 @@ discard block |
||
20 | 20 | <?php } else { ?> |
21 | 21 | <strong><?php p($l->t('Two-factor authentication is enforced but has not been configured on your account. Please continue to setup two-factor authentication.')) ?></strong> |
22 | 22 | <a class="button primary two-factor-primary" href="<?php p(\OC::$server->getURLGenerator()->linkToRoute('core.TwoFactorChallenge.setupProviders', |
23 | - [ |
|
24 | - 'redirect_url' => $_['redirect_url'], |
|
25 | - ] |
|
26 | - )) ?>"> |
|
23 | + [ |
|
24 | + 'redirect_url' => $_['redirect_url'], |
|
25 | + ] |
|
26 | + )) ?>"> |
|
27 | 27 | <?php p($l->t('Set up two-factor authentication')) ?> |
28 | 28 | </a> |
29 | 29 | <?php } ?> |
@@ -37,18 +37,18 @@ discard block |
||
37 | 37 | <li> |
38 | 38 | <a class="two-factor-provider" |
39 | 39 | href="<?php p(\OC::$server->getURLGenerator()->linkToRoute('core.TwoFactorChallenge.showChallenge', |
40 | - [ |
|
41 | - 'challengeProviderId' => $provider->getId(), |
|
42 | - 'redirect_url' => $_['redirect_url'], |
|
43 | - ] |
|
44 | - )) ?>"> |
|
40 | + [ |
|
41 | + 'challengeProviderId' => $provider->getId(), |
|
42 | + 'redirect_url' => $_['redirect_url'], |
|
43 | + ] |
|
44 | + )) ?>"> |
|
45 | 45 | <?php |
46 | - if ($provider instanceof \OCP\Authentication\TwoFactorAuth\IProvidesIcons) { |
|
47 | - $icon = $provider->getLightIcon(); |
|
48 | - } else { |
|
49 | - $icon = image_path('core', 'actions/password-white.svg'); |
|
50 | - } |
|
51 | - ?> |
|
46 | + if ($provider instanceof \OCP\Authentication\TwoFactorAuth\IProvidesIcons) { |
|
47 | + $icon = $provider->getLightIcon(); |
|
48 | + } else { |
|
49 | + $icon = image_path('core', 'actions/password-white.svg'); |
|
50 | + } |
|
51 | + ?> |
|
52 | 52 | <img src="<?php p($icon) ?>" alt="" /> |
53 | 53 | <div> |
54 | 54 | <h3><?php p($provider->getDisplayName()) ?></h3> |
@@ -62,11 +62,11 @@ discard block |
||
62 | 62 | <?php if (!is_null($_['backupProvider'])): ?> |
63 | 63 | <p> |
64 | 64 | <a class="<?php if($noProviders): ?>button primary two-factor-primary<?php else: ?>two-factor-secondary<?php endif ?>" href="<?php p(\OC::$server->getURLGenerator()->linkToRoute('core.TwoFactorChallenge.showChallenge', |
65 | - [ |
|
66 | - 'challengeProviderId' => $_['backupProvider']->getId(), |
|
67 | - 'redirect_url' => $_['redirect_url'], |
|
68 | - ] |
|
69 | - )) ?>"> |
|
65 | + [ |
|
66 | + 'challengeProviderId' => $_['backupProvider']->getId(), |
|
67 | + 'redirect_url' => $_['redirect_url'], |
|
68 | + ] |
|
69 | + )) ?>"> |
|
70 | 70 | <?php p($l->t('Use backup code')) ?> |
71 | 71 | </a> |
72 | 72 | </p> |
@@ -27,11 +27,15 @@ discard block |
||
27 | 27 | <?php p($l->t('Set up two-factor authentication')) ?> |
28 | 28 | </a> |
29 | 29 | <?php } ?> |
30 | - <?php else: ?> |
|
30 | + <?php else { |
|
31 | + : ?> |
|
31 | 32 | <strong><?php p($l->t('Two-factor authentication is enforced but has not been configured on your account. Use one of your backup codes to log in or contact your admin for assistance.')) ?></strong> |
32 | - <?php endif; ?> |
|
33 | + <?php endif; |
|
34 | +} |
|
35 | +?> |
|
33 | 36 | </p> |
34 | - <?php else: ?> |
|
37 | + <?php else { |
|
38 | + : ?> |
|
35 | 39 | <ul> |
36 | 40 | <?php foreach ($_['providers'] as $provider): ?> |
37 | 41 | <li> |
@@ -45,6 +49,7 @@ discard block |
||
45 | 49 | <?php |
46 | 50 | if ($provider instanceof \OCP\Authentication\TwoFactorAuth\IProvidesIcons) { |
47 | 51 | $icon = $provider->getLightIcon(); |
52 | +} |
|
48 | 53 | } else { |
49 | 54 | $icon = image_path('core', 'actions/password-white.svg'); |
50 | 55 | } |
@@ -61,7 +66,8 @@ discard block |
||
61 | 66 | <?php endif ?> |
62 | 67 | <?php if (!is_null($_['backupProvider'])): ?> |
63 | 68 | <p> |
64 | - <a class="<?php if($noProviders): ?>button primary two-factor-primary<?php else: ?>two-factor-secondary<?php endif ?>" href="<?php p(\OC::$server->getURLGenerator()->linkToRoute('core.TwoFactorChallenge.showChallenge', |
|
69 | + <a class="<?php if($noProviders): ?>button primary two-factor-primary<?php else { |
|
70 | + : ?>two-factor-secondary<?php endif ?>" href="<?php p(\OC::$server->getURLGenerator()->linkToRoute('core.TwoFactorChallenge.showChallenge', |
|
65 | 71 | [ |
66 | 72 | 'challengeProviderId' => $_['backupProvider']->getId(), |
67 | 73 | 'redirect_url' => $_['redirect_url'], |
@@ -70,7 +76,9 @@ discard block |
||
70 | 76 | <?php p($l->t('Use backup code')) ?> |
71 | 77 | </a> |
72 | 78 | </p> |
73 | - <?php endif; ?> |
|
79 | + <?php endif; |
|
80 | +} |
|
81 | +?> |
|
74 | 82 | <p><a class="two-factor-secondary" href="<?php print_unescaped($_['logout_url']); ?>"> |
75 | 83 | <?php p($l->t('Cancel log in')) ?> |
76 | 84 | </a></p> |
@@ -31,18 +31,18 @@ |
||
31 | 31 | <li> |
32 | 32 | <a class="two-factor-provider" |
33 | 33 | href="<?php p(\OC::$server->getURLGenerator()->linkToRoute('core.TwoFactorChallenge.setupProvider', |
34 | - [ |
|
35 | - 'providerId' => $provider->getId(), |
|
36 | - 'redirect_url' => $_['redirect_url'], |
|
37 | - ] |
|
38 | - )) ?>"> |
|
34 | + [ |
|
35 | + 'providerId' => $provider->getId(), |
|
36 | + 'redirect_url' => $_['redirect_url'], |
|
37 | + ] |
|
38 | + )) ?>"> |
|
39 | 39 | <?php |
40 | - if ($provider instanceof \OCP\Authentication\TwoFactorAuth\IProvidesIcons) { |
|
41 | - $icon = $provider->getLightIcon(); |
|
42 | - } else { |
|
43 | - $icon = image_path('core', 'actions/password-white.svg'); |
|
44 | - } |
|
45 | - ?> |
|
40 | + if ($provider instanceof \OCP\Authentication\TwoFactorAuth\IProvidesIcons) { |
|
41 | + $icon = $provider->getLightIcon(); |
|
42 | + } else { |
|
43 | + $icon = image_path('core', 'actions/password-white.svg'); |
|
44 | + } |
|
45 | + ?> |
|
46 | 46 | <img src="<?php p($icon) ?>" alt="" /> |
47 | 47 | <div> |
48 | 48 | <h3><?php p($provider->getDisplayName()) ?></h3> |
@@ -44,106 +44,106 @@ |
||
44 | 44 | |
45 | 45 | class TwoFactorMiddleware extends Middleware { |
46 | 46 | |
47 | - /** @var Manager */ |
|
48 | - private $twoFactorManager; |
|
49 | - |
|
50 | - /** @var Session */ |
|
51 | - private $userSession; |
|
52 | - |
|
53 | - /** @var ISession */ |
|
54 | - private $session; |
|
55 | - |
|
56 | - /** @var IURLGenerator */ |
|
57 | - private $urlGenerator; |
|
58 | - |
|
59 | - /** @var IControllerMethodReflector */ |
|
60 | - private $reflector; |
|
61 | - |
|
62 | - /** @var IRequest */ |
|
63 | - private $request; |
|
64 | - |
|
65 | - /** |
|
66 | - * @param Manager $twoFactorManager |
|
67 | - * @param Session $userSession |
|
68 | - * @param ISession $session |
|
69 | - * @param IURLGenerator $urlGenerator |
|
70 | - */ |
|
71 | - public function __construct(Manager $twoFactorManager, Session $userSession, ISession $session, |
|
72 | - IURLGenerator $urlGenerator, IControllerMethodReflector $reflector, IRequest $request) { |
|
73 | - $this->twoFactorManager = $twoFactorManager; |
|
74 | - $this->userSession = $userSession; |
|
75 | - $this->session = $session; |
|
76 | - $this->urlGenerator = $urlGenerator; |
|
77 | - $this->reflector = $reflector; |
|
78 | - $this->request = $request; |
|
79 | - } |
|
80 | - |
|
81 | - /** |
|
82 | - * @param Controller $controller |
|
83 | - * @param string $methodName |
|
84 | - */ |
|
85 | - public function beforeController($controller, $methodName) { |
|
86 | - if ($this->reflector->hasAnnotation('PublicPage')) { |
|
87 | - // Don't block public pages |
|
88 | - return; |
|
89 | - } |
|
90 | - |
|
91 | - if ($controller instanceof ALoginSetupController |
|
92 | - && $this->userSession->getUser() !== null |
|
93 | - && $this->twoFactorManager->needsSecondFactor($this->userSession->getUser())) { |
|
94 | - return; |
|
95 | - } |
|
96 | - |
|
97 | - if ($controller instanceof LoginController && $methodName === 'logout') { |
|
98 | - // Don't block the logout page, to allow canceling the 2FA |
|
99 | - return; |
|
100 | - } |
|
101 | - |
|
102 | - if ($this->userSession->isLoggedIn()) { |
|
103 | - $user = $this->userSession->getUser(); |
|
104 | - |
|
105 | - if ($this->session->exists('app_password') || $this->twoFactorManager->isTwoFactorAuthenticated($user)) { |
|
106 | - $this->checkTwoFactor($controller, $methodName, $user); |
|
107 | - } else if ($controller instanceof TwoFactorChallengeController) { |
|
108 | - // Allow access to the two-factor controllers only if two-factor authentication |
|
109 | - // is in progress. |
|
110 | - throw new UserAlreadyLoggedInException(); |
|
111 | - } |
|
112 | - } |
|
113 | - // TODO: dont check/enforce 2FA if a auth token is used |
|
114 | - } |
|
115 | - |
|
116 | - private function checkTwoFactor(Controller $controller, $methodName, IUser $user) { |
|
117 | - // If two-factor auth is in progress disallow access to any controllers |
|
118 | - // defined within "LoginController". |
|
119 | - $needsSecondFactor = $this->twoFactorManager->needsSecondFactor($user); |
|
120 | - $twoFactor = $controller instanceof TwoFactorChallengeController; |
|
121 | - |
|
122 | - // Disallow access to any controller if 2FA needs to be checked |
|
123 | - if ($needsSecondFactor && !$twoFactor) { |
|
124 | - throw new TwoFactorAuthRequiredException(); |
|
125 | - } |
|
126 | - |
|
127 | - // Allow access to the two-factor controllers only if two-factor authentication |
|
128 | - // is in progress. |
|
129 | - if (!$needsSecondFactor && $twoFactor) { |
|
130 | - throw new UserAlreadyLoggedInException(); |
|
131 | - } |
|
132 | - } |
|
133 | - |
|
134 | - public function afterException($controller, $methodName, Exception $exception) { |
|
135 | - if ($exception instanceof TwoFactorAuthRequiredException) { |
|
136 | - $params = []; |
|
137 | - if (isset($this->request->server['REQUEST_URI'])) { |
|
138 | - $params['redirect_url'] = $this->request->server['REQUEST_URI']; |
|
139 | - } |
|
140 | - return new RedirectResponse($this->urlGenerator->linkToRoute('core.TwoFactorChallenge.selectChallenge', $params)); |
|
141 | - } |
|
142 | - if ($exception instanceof UserAlreadyLoggedInException) { |
|
143 | - return new RedirectResponse($this->urlGenerator->linkToRoute('files.view.index')); |
|
144 | - } |
|
145 | - |
|
146 | - throw $exception; |
|
147 | - } |
|
47 | + /** @var Manager */ |
|
48 | + private $twoFactorManager; |
|
49 | + |
|
50 | + /** @var Session */ |
|
51 | + private $userSession; |
|
52 | + |
|
53 | + /** @var ISession */ |
|
54 | + private $session; |
|
55 | + |
|
56 | + /** @var IURLGenerator */ |
|
57 | + private $urlGenerator; |
|
58 | + |
|
59 | + /** @var IControllerMethodReflector */ |
|
60 | + private $reflector; |
|
61 | + |
|
62 | + /** @var IRequest */ |
|
63 | + private $request; |
|
64 | + |
|
65 | + /** |
|
66 | + * @param Manager $twoFactorManager |
|
67 | + * @param Session $userSession |
|
68 | + * @param ISession $session |
|
69 | + * @param IURLGenerator $urlGenerator |
|
70 | + */ |
|
71 | + public function __construct(Manager $twoFactorManager, Session $userSession, ISession $session, |
|
72 | + IURLGenerator $urlGenerator, IControllerMethodReflector $reflector, IRequest $request) { |
|
73 | + $this->twoFactorManager = $twoFactorManager; |
|
74 | + $this->userSession = $userSession; |
|
75 | + $this->session = $session; |
|
76 | + $this->urlGenerator = $urlGenerator; |
|
77 | + $this->reflector = $reflector; |
|
78 | + $this->request = $request; |
|
79 | + } |
|
80 | + |
|
81 | + /** |
|
82 | + * @param Controller $controller |
|
83 | + * @param string $methodName |
|
84 | + */ |
|
85 | + public function beforeController($controller, $methodName) { |
|
86 | + if ($this->reflector->hasAnnotation('PublicPage')) { |
|
87 | + // Don't block public pages |
|
88 | + return; |
|
89 | + } |
|
90 | + |
|
91 | + if ($controller instanceof ALoginSetupController |
|
92 | + && $this->userSession->getUser() !== null |
|
93 | + && $this->twoFactorManager->needsSecondFactor($this->userSession->getUser())) { |
|
94 | + return; |
|
95 | + } |
|
96 | + |
|
97 | + if ($controller instanceof LoginController && $methodName === 'logout') { |
|
98 | + // Don't block the logout page, to allow canceling the 2FA |
|
99 | + return; |
|
100 | + } |
|
101 | + |
|
102 | + if ($this->userSession->isLoggedIn()) { |
|
103 | + $user = $this->userSession->getUser(); |
|
104 | + |
|
105 | + if ($this->session->exists('app_password') || $this->twoFactorManager->isTwoFactorAuthenticated($user)) { |
|
106 | + $this->checkTwoFactor($controller, $methodName, $user); |
|
107 | + } else if ($controller instanceof TwoFactorChallengeController) { |
|
108 | + // Allow access to the two-factor controllers only if two-factor authentication |
|
109 | + // is in progress. |
|
110 | + throw new UserAlreadyLoggedInException(); |
|
111 | + } |
|
112 | + } |
|
113 | + // TODO: dont check/enforce 2FA if a auth token is used |
|
114 | + } |
|
115 | + |
|
116 | + private function checkTwoFactor(Controller $controller, $methodName, IUser $user) { |
|
117 | + // If two-factor auth is in progress disallow access to any controllers |
|
118 | + // defined within "LoginController". |
|
119 | + $needsSecondFactor = $this->twoFactorManager->needsSecondFactor($user); |
|
120 | + $twoFactor = $controller instanceof TwoFactorChallengeController; |
|
121 | + |
|
122 | + // Disallow access to any controller if 2FA needs to be checked |
|
123 | + if ($needsSecondFactor && !$twoFactor) { |
|
124 | + throw new TwoFactorAuthRequiredException(); |
|
125 | + } |
|
126 | + |
|
127 | + // Allow access to the two-factor controllers only if two-factor authentication |
|
128 | + // is in progress. |
|
129 | + if (!$needsSecondFactor && $twoFactor) { |
|
130 | + throw new UserAlreadyLoggedInException(); |
|
131 | + } |
|
132 | + } |
|
133 | + |
|
134 | + public function afterException($controller, $methodName, Exception $exception) { |
|
135 | + if ($exception instanceof TwoFactorAuthRequiredException) { |
|
136 | + $params = []; |
|
137 | + if (isset($this->request->server['REQUEST_URI'])) { |
|
138 | + $params['redirect_url'] = $this->request->server['REQUEST_URI']; |
|
139 | + } |
|
140 | + return new RedirectResponse($this->urlGenerator->linkToRoute('core.TwoFactorChallenge.selectChallenge', $params)); |
|
141 | + } |
|
142 | + if ($exception instanceof UserAlreadyLoggedInException) { |
|
143 | + return new RedirectResponse($this->urlGenerator->linkToRoute('files.view.index')); |
|
144 | + } |
|
145 | + |
|
146 | + throw $exception; |
|
147 | + } |
|
148 | 148 | |
149 | 149 | } |
@@ -43,237 +43,237 @@ |
||
43 | 43 | |
44 | 44 | class TwoFactorChallengeController extends Controller { |
45 | 45 | |
46 | - /** @var Manager */ |
|
47 | - private $twoFactorManager; |
|
46 | + /** @var Manager */ |
|
47 | + private $twoFactorManager; |
|
48 | 48 | |
49 | - /** @var IUserSession */ |
|
50 | - private $userSession; |
|
49 | + /** @var IUserSession */ |
|
50 | + private $userSession; |
|
51 | 51 | |
52 | - /** @var ISession */ |
|
53 | - private $session; |
|
52 | + /** @var ISession */ |
|
53 | + private $session; |
|
54 | 54 | |
55 | - /** @var IURLGenerator */ |
|
56 | - private $urlGenerator; |
|
55 | + /** @var IURLGenerator */ |
|
56 | + private $urlGenerator; |
|
57 | 57 | |
58 | - /** |
|
59 | - * @param string $appName |
|
60 | - * @param IRequest $request |
|
61 | - * @param Manager $twoFactorManager |
|
62 | - * @param IUserSession $userSession |
|
63 | - * @param ISession $session |
|
64 | - * @param IURLGenerator $urlGenerator |
|
65 | - */ |
|
66 | - public function __construct($appName, IRequest $request, Manager $twoFactorManager, IUserSession $userSession, |
|
67 | - ISession $session, IURLGenerator $urlGenerator) { |
|
68 | - parent::__construct($appName, $request); |
|
69 | - $this->twoFactorManager = $twoFactorManager; |
|
70 | - $this->userSession = $userSession; |
|
71 | - $this->session = $session; |
|
72 | - $this->urlGenerator = $urlGenerator; |
|
73 | - } |
|
58 | + /** |
|
59 | + * @param string $appName |
|
60 | + * @param IRequest $request |
|
61 | + * @param Manager $twoFactorManager |
|
62 | + * @param IUserSession $userSession |
|
63 | + * @param ISession $session |
|
64 | + * @param IURLGenerator $urlGenerator |
|
65 | + */ |
|
66 | + public function __construct($appName, IRequest $request, Manager $twoFactorManager, IUserSession $userSession, |
|
67 | + ISession $session, IURLGenerator $urlGenerator) { |
|
68 | + parent::__construct($appName, $request); |
|
69 | + $this->twoFactorManager = $twoFactorManager; |
|
70 | + $this->userSession = $userSession; |
|
71 | + $this->session = $session; |
|
72 | + $this->urlGenerator = $urlGenerator; |
|
73 | + } |
|
74 | 74 | |
75 | - /** |
|
76 | - * @return string |
|
77 | - */ |
|
78 | - protected function getLogoutUrl() { |
|
79 | - return OC_User::getLogoutUrl($this->urlGenerator); |
|
80 | - } |
|
75 | + /** |
|
76 | + * @return string |
|
77 | + */ |
|
78 | + protected function getLogoutUrl() { |
|
79 | + return OC_User::getLogoutUrl($this->urlGenerator); |
|
80 | + } |
|
81 | 81 | |
82 | - /** |
|
83 | - * @param IProvider[] $providers |
|
84 | - */ |
|
85 | - private function splitProvidersAndBackupCodes(array $providers): array { |
|
86 | - $regular = []; |
|
87 | - $backup = null; |
|
88 | - foreach ($providers as $provider) { |
|
89 | - if ($provider->getId() === 'backup_codes') { |
|
90 | - $backup = $provider; |
|
91 | - } else { |
|
92 | - $regular[] = $provider; |
|
93 | - } |
|
94 | - } |
|
82 | + /** |
|
83 | + * @param IProvider[] $providers |
|
84 | + */ |
|
85 | + private function splitProvidersAndBackupCodes(array $providers): array { |
|
86 | + $regular = []; |
|
87 | + $backup = null; |
|
88 | + foreach ($providers as $provider) { |
|
89 | + if ($provider->getId() === 'backup_codes') { |
|
90 | + $backup = $provider; |
|
91 | + } else { |
|
92 | + $regular[] = $provider; |
|
93 | + } |
|
94 | + } |
|
95 | 95 | |
96 | - return [$regular, $backup]; |
|
97 | - } |
|
96 | + return [$regular, $backup]; |
|
97 | + } |
|
98 | 98 | |
99 | - /** |
|
100 | - * @NoAdminRequired |
|
101 | - * @NoCSRFRequired |
|
102 | - * |
|
103 | - * @param string $redirect_url |
|
104 | - * @return StandaloneTemplateResponse |
|
105 | - */ |
|
106 | - public function selectChallenge($redirect_url) { |
|
107 | - $user = $this->userSession->getUser(); |
|
108 | - $providerSet = $this->twoFactorManager->getProviderSet($user); |
|
109 | - $allProviders = $providerSet->getProviders(); |
|
110 | - list($providers, $backupProvider) = $this->splitProvidersAndBackupCodes($allProviders); |
|
111 | - $setupProviders = $this->twoFactorManager->getLoginSetupProviders($user); |
|
99 | + /** |
|
100 | + * @NoAdminRequired |
|
101 | + * @NoCSRFRequired |
|
102 | + * |
|
103 | + * @param string $redirect_url |
|
104 | + * @return StandaloneTemplateResponse |
|
105 | + */ |
|
106 | + public function selectChallenge($redirect_url) { |
|
107 | + $user = $this->userSession->getUser(); |
|
108 | + $providerSet = $this->twoFactorManager->getProviderSet($user); |
|
109 | + $allProviders = $providerSet->getProviders(); |
|
110 | + list($providers, $backupProvider) = $this->splitProvidersAndBackupCodes($allProviders); |
|
111 | + $setupProviders = $this->twoFactorManager->getLoginSetupProviders($user); |
|
112 | 112 | |
113 | - $data = [ |
|
114 | - 'providers' => $providers, |
|
115 | - 'backupProvider' => $backupProvider, |
|
116 | - 'providerMissing' => $providerSet->isProviderMissing(), |
|
117 | - 'redirect_url' => $redirect_url, |
|
118 | - 'logout_url' => $this->getLogoutUrl(), |
|
119 | - 'hasSetupProviders' => !empty($setupProviders), |
|
120 | - ]; |
|
121 | - return new StandaloneTemplateResponse($this->appName, 'twofactorselectchallenge', $data, 'guest'); |
|
122 | - } |
|
113 | + $data = [ |
|
114 | + 'providers' => $providers, |
|
115 | + 'backupProvider' => $backupProvider, |
|
116 | + 'providerMissing' => $providerSet->isProviderMissing(), |
|
117 | + 'redirect_url' => $redirect_url, |
|
118 | + 'logout_url' => $this->getLogoutUrl(), |
|
119 | + 'hasSetupProviders' => !empty($setupProviders), |
|
120 | + ]; |
|
121 | + return new StandaloneTemplateResponse($this->appName, 'twofactorselectchallenge', $data, 'guest'); |
|
122 | + } |
|
123 | 123 | |
124 | - /** |
|
125 | - * @NoAdminRequired |
|
126 | - * @NoCSRFRequired |
|
127 | - * @UseSession |
|
128 | - * |
|
129 | - * @param string $challengeProviderId |
|
130 | - * @param string $redirect_url |
|
131 | - * @return StandaloneTemplateResponse|RedirectResponse |
|
132 | - */ |
|
133 | - public function showChallenge($challengeProviderId, $redirect_url) { |
|
134 | - $user = $this->userSession->getUser(); |
|
135 | - $providerSet = $this->twoFactorManager->getProviderSet($user); |
|
136 | - $provider = $providerSet->getProvider($challengeProviderId); |
|
124 | + /** |
|
125 | + * @NoAdminRequired |
|
126 | + * @NoCSRFRequired |
|
127 | + * @UseSession |
|
128 | + * |
|
129 | + * @param string $challengeProviderId |
|
130 | + * @param string $redirect_url |
|
131 | + * @return StandaloneTemplateResponse|RedirectResponse |
|
132 | + */ |
|
133 | + public function showChallenge($challengeProviderId, $redirect_url) { |
|
134 | + $user = $this->userSession->getUser(); |
|
135 | + $providerSet = $this->twoFactorManager->getProviderSet($user); |
|
136 | + $provider = $providerSet->getProvider($challengeProviderId); |
|
137 | 137 | |
138 | - if (is_null($provider)) { |
|
139 | - return new RedirectResponse($this->urlGenerator->linkToRoute('core.TwoFactorChallenge.selectChallenge')); |
|
140 | - } |
|
138 | + if (is_null($provider)) { |
|
139 | + return new RedirectResponse($this->urlGenerator->linkToRoute('core.TwoFactorChallenge.selectChallenge')); |
|
140 | + } |
|
141 | 141 | |
142 | - $backupProvider = $providerSet->getProvider('backup_codes'); |
|
143 | - if (!is_null($backupProvider) && $backupProvider->getId() === $provider->getId()) { |
|
144 | - // Don't show the backup provider link if we're already showing that provider's challenge |
|
145 | - $backupProvider = null; |
|
146 | - } |
|
142 | + $backupProvider = $providerSet->getProvider('backup_codes'); |
|
143 | + if (!is_null($backupProvider) && $backupProvider->getId() === $provider->getId()) { |
|
144 | + // Don't show the backup provider link if we're already showing that provider's challenge |
|
145 | + $backupProvider = null; |
|
146 | + } |
|
147 | 147 | |
148 | - $errorMessage = ''; |
|
149 | - $error = false; |
|
150 | - if ($this->session->exists('two_factor_auth_error')) { |
|
151 | - $this->session->remove('two_factor_auth_error'); |
|
152 | - $error = true; |
|
153 | - $errorMessage = $this->session->get("two_factor_auth_error_message"); |
|
154 | - $this->session->remove('two_factor_auth_error_message'); |
|
155 | - } |
|
156 | - $tmpl = $provider->getTemplate($user); |
|
157 | - $tmpl->assign('redirect_url', $redirect_url); |
|
158 | - $data = [ |
|
159 | - 'error' => $error, |
|
160 | - 'error_message' => $errorMessage, |
|
161 | - 'provider' => $provider, |
|
162 | - 'backupProvider' => $backupProvider, |
|
163 | - 'logout_url' => $this->getLogoutUrl(), |
|
164 | - 'redirect_url' => $redirect_url, |
|
165 | - 'template' => $tmpl->fetchPage(), |
|
166 | - ]; |
|
167 | - $response = new StandaloneTemplateResponse($this->appName, 'twofactorshowchallenge', $data, 'guest'); |
|
168 | - if ($provider instanceof IProvidesCustomCSP) { |
|
169 | - $response->setContentSecurityPolicy($provider->getCSP()); |
|
170 | - } |
|
171 | - return $response; |
|
172 | - } |
|
148 | + $errorMessage = ''; |
|
149 | + $error = false; |
|
150 | + if ($this->session->exists('two_factor_auth_error')) { |
|
151 | + $this->session->remove('two_factor_auth_error'); |
|
152 | + $error = true; |
|
153 | + $errorMessage = $this->session->get("two_factor_auth_error_message"); |
|
154 | + $this->session->remove('two_factor_auth_error_message'); |
|
155 | + } |
|
156 | + $tmpl = $provider->getTemplate($user); |
|
157 | + $tmpl->assign('redirect_url', $redirect_url); |
|
158 | + $data = [ |
|
159 | + 'error' => $error, |
|
160 | + 'error_message' => $errorMessage, |
|
161 | + 'provider' => $provider, |
|
162 | + 'backupProvider' => $backupProvider, |
|
163 | + 'logout_url' => $this->getLogoutUrl(), |
|
164 | + 'redirect_url' => $redirect_url, |
|
165 | + 'template' => $tmpl->fetchPage(), |
|
166 | + ]; |
|
167 | + $response = new StandaloneTemplateResponse($this->appName, 'twofactorshowchallenge', $data, 'guest'); |
|
168 | + if ($provider instanceof IProvidesCustomCSP) { |
|
169 | + $response->setContentSecurityPolicy($provider->getCSP()); |
|
170 | + } |
|
171 | + return $response; |
|
172 | + } |
|
173 | 173 | |
174 | - /** |
|
175 | - * @NoAdminRequired |
|
176 | - * @NoCSRFRequired |
|
177 | - * @UseSession |
|
178 | - * |
|
179 | - * @UserRateThrottle(limit=5, period=100) |
|
180 | - * |
|
181 | - * @param string $challengeProviderId |
|
182 | - * @param string $challenge |
|
183 | - * @param string $redirect_url |
|
184 | - * @return RedirectResponse |
|
185 | - */ |
|
186 | - public function solveChallenge($challengeProviderId, $challenge, $redirect_url = null) { |
|
187 | - $user = $this->userSession->getUser(); |
|
188 | - $provider = $this->twoFactorManager->getProvider($user, $challengeProviderId); |
|
189 | - if (is_null($provider)) { |
|
190 | - return new RedirectResponse($this->urlGenerator->linkToRoute('core.TwoFactorChallenge.selectChallenge')); |
|
191 | - } |
|
174 | + /** |
|
175 | + * @NoAdminRequired |
|
176 | + * @NoCSRFRequired |
|
177 | + * @UseSession |
|
178 | + * |
|
179 | + * @UserRateThrottle(limit=5, period=100) |
|
180 | + * |
|
181 | + * @param string $challengeProviderId |
|
182 | + * @param string $challenge |
|
183 | + * @param string $redirect_url |
|
184 | + * @return RedirectResponse |
|
185 | + */ |
|
186 | + public function solveChallenge($challengeProviderId, $challenge, $redirect_url = null) { |
|
187 | + $user = $this->userSession->getUser(); |
|
188 | + $provider = $this->twoFactorManager->getProvider($user, $challengeProviderId); |
|
189 | + if (is_null($provider)) { |
|
190 | + return new RedirectResponse($this->urlGenerator->linkToRoute('core.TwoFactorChallenge.selectChallenge')); |
|
191 | + } |
|
192 | 192 | |
193 | - try { |
|
194 | - if ($this->twoFactorManager->verifyChallenge($challengeProviderId, $user, $challenge)) { |
|
195 | - if (!is_null($redirect_url)) { |
|
196 | - return new RedirectResponse($this->urlGenerator->getAbsoluteURL(urldecode($redirect_url))); |
|
197 | - } |
|
198 | - return new RedirectResponse(OC_Util::getDefaultPageUrl()); |
|
199 | - } |
|
200 | - } catch (TwoFactorException $e) { |
|
201 | - /* |
|
193 | + try { |
|
194 | + if ($this->twoFactorManager->verifyChallenge($challengeProviderId, $user, $challenge)) { |
|
195 | + if (!is_null($redirect_url)) { |
|
196 | + return new RedirectResponse($this->urlGenerator->getAbsoluteURL(urldecode($redirect_url))); |
|
197 | + } |
|
198 | + return new RedirectResponse(OC_Util::getDefaultPageUrl()); |
|
199 | + } |
|
200 | + } catch (TwoFactorException $e) { |
|
201 | + /* |
|
202 | 202 | * The 2FA App threw an TwoFactorException. Now we display more |
203 | 203 | * information to the user. The exception text is stored in the |
204 | 204 | * session to be used in showChallenge() |
205 | 205 | */ |
206 | - $this->session->set('two_factor_auth_error_message', $e->getMessage()); |
|
207 | - } |
|
206 | + $this->session->set('two_factor_auth_error_message', $e->getMessage()); |
|
207 | + } |
|
208 | 208 | |
209 | - $this->session->set('two_factor_auth_error', true); |
|
210 | - return new RedirectResponse($this->urlGenerator->linkToRoute('core.TwoFactorChallenge.showChallenge', [ |
|
211 | - 'challengeProviderId' => $provider->getId(), |
|
212 | - 'redirect_url' => $redirect_url, |
|
213 | - ])); |
|
214 | - } |
|
209 | + $this->session->set('two_factor_auth_error', true); |
|
210 | + return new RedirectResponse($this->urlGenerator->linkToRoute('core.TwoFactorChallenge.showChallenge', [ |
|
211 | + 'challengeProviderId' => $provider->getId(), |
|
212 | + 'redirect_url' => $redirect_url, |
|
213 | + ])); |
|
214 | + } |
|
215 | 215 | |
216 | - /** |
|
217 | - * @NoAdminRequired |
|
218 | - * @NoCSRFRequired |
|
219 | - */ |
|
220 | - public function setupProviders() { |
|
221 | - $user = $this->userSession->getUser(); |
|
222 | - $setupProviders = $this->twoFactorManager->getLoginSetupProviders($user); |
|
216 | + /** |
|
217 | + * @NoAdminRequired |
|
218 | + * @NoCSRFRequired |
|
219 | + */ |
|
220 | + public function setupProviders() { |
|
221 | + $user = $this->userSession->getUser(); |
|
222 | + $setupProviders = $this->twoFactorManager->getLoginSetupProviders($user); |
|
223 | 223 | |
224 | - $data = [ |
|
225 | - 'providers' => $setupProviders, |
|
226 | - 'logout_url' => $this->getLogoutUrl(), |
|
227 | - ]; |
|
224 | + $data = [ |
|
225 | + 'providers' => $setupProviders, |
|
226 | + 'logout_url' => $this->getLogoutUrl(), |
|
227 | + ]; |
|
228 | 228 | |
229 | - $response = new StandaloneTemplateResponse($this->appName, 'twofactorsetupselection', $data, 'guest'); |
|
230 | - return $response; |
|
231 | - } |
|
229 | + $response = new StandaloneTemplateResponse($this->appName, 'twofactorsetupselection', $data, 'guest'); |
|
230 | + return $response; |
|
231 | + } |
|
232 | 232 | |
233 | - /** |
|
234 | - * @NoAdminRequired |
|
235 | - * @NoCSRFRequired |
|
236 | - */ |
|
237 | - public function setupProvider(string $providerId) { |
|
238 | - $user = $this->userSession->getUser(); |
|
239 | - $providers = $this->twoFactorManager->getLoginSetupProviders($user); |
|
233 | + /** |
|
234 | + * @NoAdminRequired |
|
235 | + * @NoCSRFRequired |
|
236 | + */ |
|
237 | + public function setupProvider(string $providerId) { |
|
238 | + $user = $this->userSession->getUser(); |
|
239 | + $providers = $this->twoFactorManager->getLoginSetupProviders($user); |
|
240 | 240 | |
241 | - $provider = null; |
|
242 | - foreach ($providers as $p) { |
|
243 | - if ($p->getId() === $providerId) { |
|
244 | - $provider = $p; |
|
245 | - break; |
|
246 | - } |
|
247 | - } |
|
241 | + $provider = null; |
|
242 | + foreach ($providers as $p) { |
|
243 | + if ($p->getId() === $providerId) { |
|
244 | + $provider = $p; |
|
245 | + break; |
|
246 | + } |
|
247 | + } |
|
248 | 248 | |
249 | - if ($provider === null) { |
|
250 | - return new RedirectResponse($this->urlGenerator->linkToRoute('core.TwoFactorChallenge.selectChallenge')); |
|
251 | - } |
|
249 | + if ($provider === null) { |
|
250 | + return new RedirectResponse($this->urlGenerator->linkToRoute('core.TwoFactorChallenge.selectChallenge')); |
|
251 | + } |
|
252 | 252 | |
253 | - /** @var IActivatableAtLogin $provider */ |
|
254 | - $tmpl = $provider->getLoginSetup($user)->getBody(); |
|
255 | - $data = [ |
|
256 | - 'provider' => $provider, |
|
257 | - 'logout_url' => $this->getLogoutUrl(), |
|
258 | - 'template' => $tmpl->fetchPage(), |
|
259 | - ]; |
|
260 | - $response = new StandaloneTemplateResponse($this->appName, 'twofactorsetupchallenge', $data, 'guest'); |
|
261 | - return $response; |
|
262 | - } |
|
253 | + /** @var IActivatableAtLogin $provider */ |
|
254 | + $tmpl = $provider->getLoginSetup($user)->getBody(); |
|
255 | + $data = [ |
|
256 | + 'provider' => $provider, |
|
257 | + 'logout_url' => $this->getLogoutUrl(), |
|
258 | + 'template' => $tmpl->fetchPage(), |
|
259 | + ]; |
|
260 | + $response = new StandaloneTemplateResponse($this->appName, 'twofactorsetupchallenge', $data, 'guest'); |
|
261 | + return $response; |
|
262 | + } |
|
263 | 263 | |
264 | - /** |
|
265 | - * @NoAdminRequired |
|
266 | - * @NoCSRFRequired |
|
267 | - * |
|
268 | - * @todo handle the extreme edge case of an invalid provider ID and redirect to the provider selection page |
|
269 | - */ |
|
270 | - public function confirmProviderSetup(string $providerId) { |
|
271 | - return new RedirectResponse($this->urlGenerator->linkToRoute( |
|
272 | - 'core.TwoFactorChallenge.showChallenge', |
|
273 | - [ |
|
274 | - 'challengeProviderId' => $providerId, |
|
275 | - ] |
|
276 | - )); |
|
277 | - } |
|
264 | + /** |
|
265 | + * @NoAdminRequired |
|
266 | + * @NoCSRFRequired |
|
267 | + * |
|
268 | + * @todo handle the extreme edge case of an invalid provider ID and redirect to the provider selection page |
|
269 | + */ |
|
270 | + public function confirmProviderSetup(string $providerId) { |
|
271 | + return new RedirectResponse($this->urlGenerator->linkToRoute( |
|
272 | + 'core.TwoFactorChallenge.showChallenge', |
|
273 | + [ |
|
274 | + 'challengeProviderId' => $providerId, |
|
275 | + ] |
|
276 | + )); |
|
277 | + } |
|
278 | 278 | |
279 | 279 | } |
@@ -31,13 +31,13 @@ |
||
31 | 31 | */ |
32 | 32 | interface IActivatableAtLogin extends IProvider { |
33 | 33 | |
34 | - /** |
|
35 | - * @param IUser $user |
|
36 | - * |
|
37 | - * @return ILoginSetupProvider |
|
38 | - * |
|
39 | - * @since 17.0.0 |
|
40 | - */ |
|
41 | - public function getLoginSetup(IUser $user): ILoginSetupProvider; |
|
34 | + /** |
|
35 | + * @param IUser $user |
|
36 | + * |
|
37 | + * @return ILoginSetupProvider |
|
38 | + * |
|
39 | + * @since 17.0.0 |
|
40 | + */ |
|
41 | + public function getLoginSetup(IUser $user): ILoginSetupProvider; |
|
42 | 42 | |
43 | 43 | } |
@@ -31,11 +31,11 @@ |
||
31 | 31 | */ |
32 | 32 | interface ILoginSetupProvider { |
33 | 33 | |
34 | - /** |
|
35 | - * @return Template |
|
36 | - * |
|
37 | - * @since 17.0.0 |
|
38 | - */ |
|
39 | - public function getBody(): Template; |
|
34 | + /** |
|
35 | + * @return Template |
|
36 | + * |
|
37 | + * @since 17.0.0 |
|
38 | + */ |
|
39 | + public function getBody(): Template; |
|
40 | 40 | |
41 | 41 | } |
@@ -49,342 +49,342 @@ |
||
49 | 49 | |
50 | 50 | class Manager { |
51 | 51 | |
52 | - const SESSION_UID_KEY = 'two_factor_auth_uid'; |
|
53 | - const SESSION_UID_DONE = 'two_factor_auth_passed'; |
|
54 | - const REMEMBER_LOGIN = 'two_factor_remember_login'; |
|
55 | - const BACKUP_CODES_PROVIDER_ID = 'backup_codes'; |
|
56 | - |
|
57 | - /** @var ProviderLoader */ |
|
58 | - private $providerLoader; |
|
59 | - |
|
60 | - /** @var IRegistry */ |
|
61 | - private $providerRegistry; |
|
62 | - |
|
63 | - /** @var MandatoryTwoFactor */ |
|
64 | - private $mandatoryTwoFactor; |
|
65 | - |
|
66 | - /** @var ISession */ |
|
67 | - private $session; |
|
68 | - |
|
69 | - /** @var IConfig */ |
|
70 | - private $config; |
|
71 | - |
|
72 | - /** @var IManager */ |
|
73 | - private $activityManager; |
|
74 | - |
|
75 | - /** @var ILogger */ |
|
76 | - private $logger; |
|
77 | - |
|
78 | - /** @var TokenProvider */ |
|
79 | - private $tokenProvider; |
|
80 | - |
|
81 | - /** @var ITimeFactory */ |
|
82 | - private $timeFactory; |
|
83 | - |
|
84 | - /** @var EventDispatcherInterface */ |
|
85 | - private $dispatcher; |
|
86 | - |
|
87 | - public function __construct(ProviderLoader $providerLoader, |
|
88 | - IRegistry $providerRegistry, |
|
89 | - MandatoryTwoFactor $mandatoryTwoFactor, |
|
90 | - ISession $session, IConfig $config, |
|
91 | - IManager $activityManager, ILogger $logger, TokenProvider $tokenProvider, |
|
92 | - ITimeFactory $timeFactory, EventDispatcherInterface $eventDispatcher) { |
|
93 | - $this->providerLoader = $providerLoader; |
|
94 | - $this->providerRegistry = $providerRegistry; |
|
95 | - $this->mandatoryTwoFactor = $mandatoryTwoFactor; |
|
96 | - $this->session = $session; |
|
97 | - $this->config = $config; |
|
98 | - $this->activityManager = $activityManager; |
|
99 | - $this->logger = $logger; |
|
100 | - $this->tokenProvider = $tokenProvider; |
|
101 | - $this->timeFactory = $timeFactory; |
|
102 | - $this->dispatcher = $eventDispatcher; |
|
103 | - } |
|
104 | - |
|
105 | - /** |
|
106 | - * Determine whether the user must provide a second factor challenge |
|
107 | - * |
|
108 | - * @param IUser $user |
|
109 | - * @return boolean |
|
110 | - */ |
|
111 | - public function isTwoFactorAuthenticated(IUser $user): bool { |
|
112 | - if ($this->mandatoryTwoFactor->isEnforcedFor($user)) { |
|
113 | - return true; |
|
114 | - } |
|
115 | - |
|
116 | - $providerStates = $this->providerRegistry->getProviderStates($user); |
|
117 | - $providers = $this->providerLoader->getProviders($user); |
|
118 | - $fixedStates = $this->fixMissingProviderStates($providerStates, $providers, $user); |
|
119 | - $enabled = array_filter($fixedStates); |
|
120 | - $providerIds = array_keys($enabled); |
|
121 | - $providerIdsWithoutBackupCodes = array_diff($providerIds, [self::BACKUP_CODES_PROVIDER_ID]); |
|
122 | - |
|
123 | - return !empty($providerIdsWithoutBackupCodes); |
|
124 | - } |
|
125 | - |
|
126 | - /** |
|
127 | - * Get a 2FA provider by its ID |
|
128 | - * |
|
129 | - * @param IUser $user |
|
130 | - * @param string $challengeProviderId |
|
131 | - * @return IProvider|null |
|
132 | - */ |
|
133 | - public function getProvider(IUser $user, string $challengeProviderId) { |
|
134 | - $providers = $this->getProviderSet($user)->getProviders(); |
|
135 | - return $providers[$challengeProviderId] ?? null; |
|
136 | - } |
|
137 | - |
|
138 | - /** |
|
139 | - * @param IUser $user |
|
140 | - * @return IActivatableAtLogin[] |
|
141 | - * @throws Exception |
|
142 | - */ |
|
143 | - public function getLoginSetupProviders(IUser $user): array { |
|
144 | - $providers = $this->providerLoader->getProviders($user); |
|
145 | - return array_filter($providers, function(IProvider $provider) { |
|
146 | - return ($provider instanceof IActivatableAtLogin); |
|
147 | - }); |
|
148 | - } |
|
149 | - |
|
150 | - /** |
|
151 | - * Check if the persistant mapping of enabled/disabled state of each available |
|
152 | - * provider is missing an entry and add it to the registry in that case. |
|
153 | - * |
|
154 | - * @todo remove in Nextcloud 17 as by then all providers should have been updated |
|
155 | - * |
|
156 | - * @param string[] $providerStates |
|
157 | - * @param IProvider[] $providers |
|
158 | - * @param IUser $user |
|
159 | - * @return string[] the updated $providerStates variable |
|
160 | - */ |
|
161 | - private function fixMissingProviderStates(array $providerStates, |
|
162 | - array $providers, IUser $user): array { |
|
163 | - |
|
164 | - foreach ($providers as $provider) { |
|
165 | - if (isset($providerStates[$provider->getId()])) { |
|
166 | - // All good |
|
167 | - continue; |
|
168 | - } |
|
169 | - |
|
170 | - $enabled = $provider->isTwoFactorAuthEnabledForUser($user); |
|
171 | - if ($enabled) { |
|
172 | - $this->providerRegistry->enableProviderFor($provider, $user); |
|
173 | - } else { |
|
174 | - $this->providerRegistry->disableProviderFor($provider, $user); |
|
175 | - } |
|
176 | - $providerStates[$provider->getId()] = $enabled; |
|
177 | - } |
|
178 | - |
|
179 | - return $providerStates; |
|
180 | - } |
|
181 | - |
|
182 | - /** |
|
183 | - * @param array $states |
|
184 | - * @param IProvider $providers |
|
185 | - */ |
|
186 | - private function isProviderMissing(array $states, array $providers): bool { |
|
187 | - $indexed = []; |
|
188 | - foreach ($providers as $provider) { |
|
189 | - $indexed[$provider->getId()] = $provider; |
|
190 | - } |
|
191 | - |
|
192 | - $missing = []; |
|
193 | - foreach ($states as $providerId => $enabled) { |
|
194 | - if (!$enabled) { |
|
195 | - // Don't care |
|
196 | - continue; |
|
197 | - } |
|
198 | - |
|
199 | - if (!isset($indexed[$providerId])) { |
|
200 | - $missing[] = $providerId; |
|
201 | - $this->logger->alert("two-factor auth provider '$providerId' failed to load", |
|
202 | - [ |
|
203 | - 'app' => 'core', |
|
204 | - ]); |
|
205 | - } |
|
206 | - } |
|
207 | - |
|
208 | - if (!empty($missing)) { |
|
209 | - // There was at least one provider missing |
|
210 | - $this->logger->alert(count($missing) . " two-factor auth providers failed to load", ['app' => 'core']); |
|
211 | - |
|
212 | - return true; |
|
213 | - } |
|
214 | - |
|
215 | - // If we reach this, there was not a single provider missing |
|
216 | - return false; |
|
217 | - } |
|
218 | - |
|
219 | - /** |
|
220 | - * Get the list of 2FA providers for the given user |
|
221 | - * |
|
222 | - * @param IUser $user |
|
223 | - * @throws Exception |
|
224 | - */ |
|
225 | - public function getProviderSet(IUser $user): ProviderSet { |
|
226 | - $providerStates = $this->providerRegistry->getProviderStates($user); |
|
227 | - $providers = $this->providerLoader->getProviders($user); |
|
228 | - |
|
229 | - $fixedStates = $this->fixMissingProviderStates($providerStates, $providers, $user); |
|
230 | - $isProviderMissing = $this->isProviderMissing($fixedStates, $providers); |
|
231 | - |
|
232 | - $enabled = array_filter($providers, function (IProvider $provider) use ($fixedStates) { |
|
233 | - return $fixedStates[$provider->getId()]; |
|
234 | - }); |
|
235 | - return new ProviderSet($enabled, $isProviderMissing); |
|
236 | - } |
|
237 | - |
|
238 | - /** |
|
239 | - * Verify the given challenge |
|
240 | - * |
|
241 | - * @param string $providerId |
|
242 | - * @param IUser $user |
|
243 | - * @param string $challenge |
|
244 | - * @return boolean |
|
245 | - */ |
|
246 | - public function verifyChallenge(string $providerId, IUser $user, string $challenge): bool { |
|
247 | - $provider = $this->getProvider($user, $providerId); |
|
248 | - if ($provider === null) { |
|
249 | - return false; |
|
250 | - } |
|
251 | - |
|
252 | - $passed = $provider->verifyChallenge($user, $challenge); |
|
253 | - if ($passed) { |
|
254 | - if ($this->session->get(self::REMEMBER_LOGIN) === true) { |
|
255 | - // TODO: resolve cyclic dependency and use DI |
|
256 | - \OC::$server->getUserSession()->createRememberMeToken($user); |
|
257 | - } |
|
258 | - $this->session->remove(self::SESSION_UID_KEY); |
|
259 | - $this->session->remove(self::REMEMBER_LOGIN); |
|
260 | - $this->session->set(self::SESSION_UID_DONE, $user->getUID()); |
|
261 | - |
|
262 | - // Clear token from db |
|
263 | - $sessionId = $this->session->getId(); |
|
264 | - $token = $this->tokenProvider->getToken($sessionId); |
|
265 | - $tokenId = $token->getId(); |
|
266 | - $this->config->deleteUserValue($user->getUID(), 'login_token_2fa', $tokenId); |
|
267 | - |
|
268 | - $dispatchEvent = new GenericEvent($user, ['provider' => $provider->getDisplayName()]); |
|
269 | - $this->dispatcher->dispatch(IProvider::EVENT_SUCCESS, $dispatchEvent); |
|
270 | - |
|
271 | - $this->publishEvent($user, 'twofactor_success', [ |
|
272 | - 'provider' => $provider->getDisplayName(), |
|
273 | - ]); |
|
274 | - } else { |
|
275 | - $dispatchEvent = new GenericEvent($user, ['provider' => $provider->getDisplayName()]); |
|
276 | - $this->dispatcher->dispatch(IProvider::EVENT_FAILED, $dispatchEvent); |
|
277 | - |
|
278 | - $this->publishEvent($user, 'twofactor_failed', [ |
|
279 | - 'provider' => $provider->getDisplayName(), |
|
280 | - ]); |
|
281 | - } |
|
282 | - return $passed; |
|
283 | - } |
|
284 | - |
|
285 | - /** |
|
286 | - * Push a 2fa event the user's activity stream |
|
287 | - * |
|
288 | - * @param IUser $user |
|
289 | - * @param string $event |
|
290 | - * @param array $params |
|
291 | - */ |
|
292 | - private function publishEvent(IUser $user, string $event, array $params) { |
|
293 | - $activity = $this->activityManager->generateEvent(); |
|
294 | - $activity->setApp('core') |
|
295 | - ->setType('security') |
|
296 | - ->setAuthor($user->getUID()) |
|
297 | - ->setAffectedUser($user->getUID()) |
|
298 | - ->setSubject($event, $params); |
|
299 | - try { |
|
300 | - $this->activityManager->publish($activity); |
|
301 | - } catch (BadMethodCallException $e) { |
|
302 | - $this->logger->warning('could not publish activity', ['app' => 'core']); |
|
303 | - $this->logger->logException($e, ['app' => 'core']); |
|
304 | - } |
|
305 | - } |
|
306 | - |
|
307 | - /** |
|
308 | - * Check if the currently logged in user needs to pass 2FA |
|
309 | - * |
|
310 | - * @param IUser $user the currently logged in user |
|
311 | - * @return boolean |
|
312 | - */ |
|
313 | - public function needsSecondFactor(IUser $user = null): bool { |
|
314 | - if ($user === null) { |
|
315 | - return false; |
|
316 | - } |
|
317 | - |
|
318 | - // If we are authenticated using an app password skip all this |
|
319 | - if ($this->session->exists('app_password')) { |
|
320 | - return false; |
|
321 | - } |
|
322 | - |
|
323 | - // First check if the session tells us we should do 2FA (99% case) |
|
324 | - if (!$this->session->exists(self::SESSION_UID_KEY)) { |
|
325 | - |
|
326 | - // Check if the session tells us it is 2FA authenticated already |
|
327 | - if ($this->session->exists(self::SESSION_UID_DONE) && |
|
328 | - $this->session->get(self::SESSION_UID_DONE) === $user->getUID()) { |
|
329 | - return false; |
|
330 | - } |
|
331 | - |
|
332 | - /* |
|
52 | + const SESSION_UID_KEY = 'two_factor_auth_uid'; |
|
53 | + const SESSION_UID_DONE = 'two_factor_auth_passed'; |
|
54 | + const REMEMBER_LOGIN = 'two_factor_remember_login'; |
|
55 | + const BACKUP_CODES_PROVIDER_ID = 'backup_codes'; |
|
56 | + |
|
57 | + /** @var ProviderLoader */ |
|
58 | + private $providerLoader; |
|
59 | + |
|
60 | + /** @var IRegistry */ |
|
61 | + private $providerRegistry; |
|
62 | + |
|
63 | + /** @var MandatoryTwoFactor */ |
|
64 | + private $mandatoryTwoFactor; |
|
65 | + |
|
66 | + /** @var ISession */ |
|
67 | + private $session; |
|
68 | + |
|
69 | + /** @var IConfig */ |
|
70 | + private $config; |
|
71 | + |
|
72 | + /** @var IManager */ |
|
73 | + private $activityManager; |
|
74 | + |
|
75 | + /** @var ILogger */ |
|
76 | + private $logger; |
|
77 | + |
|
78 | + /** @var TokenProvider */ |
|
79 | + private $tokenProvider; |
|
80 | + |
|
81 | + /** @var ITimeFactory */ |
|
82 | + private $timeFactory; |
|
83 | + |
|
84 | + /** @var EventDispatcherInterface */ |
|
85 | + private $dispatcher; |
|
86 | + |
|
87 | + public function __construct(ProviderLoader $providerLoader, |
|
88 | + IRegistry $providerRegistry, |
|
89 | + MandatoryTwoFactor $mandatoryTwoFactor, |
|
90 | + ISession $session, IConfig $config, |
|
91 | + IManager $activityManager, ILogger $logger, TokenProvider $tokenProvider, |
|
92 | + ITimeFactory $timeFactory, EventDispatcherInterface $eventDispatcher) { |
|
93 | + $this->providerLoader = $providerLoader; |
|
94 | + $this->providerRegistry = $providerRegistry; |
|
95 | + $this->mandatoryTwoFactor = $mandatoryTwoFactor; |
|
96 | + $this->session = $session; |
|
97 | + $this->config = $config; |
|
98 | + $this->activityManager = $activityManager; |
|
99 | + $this->logger = $logger; |
|
100 | + $this->tokenProvider = $tokenProvider; |
|
101 | + $this->timeFactory = $timeFactory; |
|
102 | + $this->dispatcher = $eventDispatcher; |
|
103 | + } |
|
104 | + |
|
105 | + /** |
|
106 | + * Determine whether the user must provide a second factor challenge |
|
107 | + * |
|
108 | + * @param IUser $user |
|
109 | + * @return boolean |
|
110 | + */ |
|
111 | + public function isTwoFactorAuthenticated(IUser $user): bool { |
|
112 | + if ($this->mandatoryTwoFactor->isEnforcedFor($user)) { |
|
113 | + return true; |
|
114 | + } |
|
115 | + |
|
116 | + $providerStates = $this->providerRegistry->getProviderStates($user); |
|
117 | + $providers = $this->providerLoader->getProviders($user); |
|
118 | + $fixedStates = $this->fixMissingProviderStates($providerStates, $providers, $user); |
|
119 | + $enabled = array_filter($fixedStates); |
|
120 | + $providerIds = array_keys($enabled); |
|
121 | + $providerIdsWithoutBackupCodes = array_diff($providerIds, [self::BACKUP_CODES_PROVIDER_ID]); |
|
122 | + |
|
123 | + return !empty($providerIdsWithoutBackupCodes); |
|
124 | + } |
|
125 | + |
|
126 | + /** |
|
127 | + * Get a 2FA provider by its ID |
|
128 | + * |
|
129 | + * @param IUser $user |
|
130 | + * @param string $challengeProviderId |
|
131 | + * @return IProvider|null |
|
132 | + */ |
|
133 | + public function getProvider(IUser $user, string $challengeProviderId) { |
|
134 | + $providers = $this->getProviderSet($user)->getProviders(); |
|
135 | + return $providers[$challengeProviderId] ?? null; |
|
136 | + } |
|
137 | + |
|
138 | + /** |
|
139 | + * @param IUser $user |
|
140 | + * @return IActivatableAtLogin[] |
|
141 | + * @throws Exception |
|
142 | + */ |
|
143 | + public function getLoginSetupProviders(IUser $user): array { |
|
144 | + $providers = $this->providerLoader->getProviders($user); |
|
145 | + return array_filter($providers, function(IProvider $provider) { |
|
146 | + return ($provider instanceof IActivatableAtLogin); |
|
147 | + }); |
|
148 | + } |
|
149 | + |
|
150 | + /** |
|
151 | + * Check if the persistant mapping of enabled/disabled state of each available |
|
152 | + * provider is missing an entry and add it to the registry in that case. |
|
153 | + * |
|
154 | + * @todo remove in Nextcloud 17 as by then all providers should have been updated |
|
155 | + * |
|
156 | + * @param string[] $providerStates |
|
157 | + * @param IProvider[] $providers |
|
158 | + * @param IUser $user |
|
159 | + * @return string[] the updated $providerStates variable |
|
160 | + */ |
|
161 | + private function fixMissingProviderStates(array $providerStates, |
|
162 | + array $providers, IUser $user): array { |
|
163 | + |
|
164 | + foreach ($providers as $provider) { |
|
165 | + if (isset($providerStates[$provider->getId()])) { |
|
166 | + // All good |
|
167 | + continue; |
|
168 | + } |
|
169 | + |
|
170 | + $enabled = $provider->isTwoFactorAuthEnabledForUser($user); |
|
171 | + if ($enabled) { |
|
172 | + $this->providerRegistry->enableProviderFor($provider, $user); |
|
173 | + } else { |
|
174 | + $this->providerRegistry->disableProviderFor($provider, $user); |
|
175 | + } |
|
176 | + $providerStates[$provider->getId()] = $enabled; |
|
177 | + } |
|
178 | + |
|
179 | + return $providerStates; |
|
180 | + } |
|
181 | + |
|
182 | + /** |
|
183 | + * @param array $states |
|
184 | + * @param IProvider $providers |
|
185 | + */ |
|
186 | + private function isProviderMissing(array $states, array $providers): bool { |
|
187 | + $indexed = []; |
|
188 | + foreach ($providers as $provider) { |
|
189 | + $indexed[$provider->getId()] = $provider; |
|
190 | + } |
|
191 | + |
|
192 | + $missing = []; |
|
193 | + foreach ($states as $providerId => $enabled) { |
|
194 | + if (!$enabled) { |
|
195 | + // Don't care |
|
196 | + continue; |
|
197 | + } |
|
198 | + |
|
199 | + if (!isset($indexed[$providerId])) { |
|
200 | + $missing[] = $providerId; |
|
201 | + $this->logger->alert("two-factor auth provider '$providerId' failed to load", |
|
202 | + [ |
|
203 | + 'app' => 'core', |
|
204 | + ]); |
|
205 | + } |
|
206 | + } |
|
207 | + |
|
208 | + if (!empty($missing)) { |
|
209 | + // There was at least one provider missing |
|
210 | + $this->logger->alert(count($missing) . " two-factor auth providers failed to load", ['app' => 'core']); |
|
211 | + |
|
212 | + return true; |
|
213 | + } |
|
214 | + |
|
215 | + // If we reach this, there was not a single provider missing |
|
216 | + return false; |
|
217 | + } |
|
218 | + |
|
219 | + /** |
|
220 | + * Get the list of 2FA providers for the given user |
|
221 | + * |
|
222 | + * @param IUser $user |
|
223 | + * @throws Exception |
|
224 | + */ |
|
225 | + public function getProviderSet(IUser $user): ProviderSet { |
|
226 | + $providerStates = $this->providerRegistry->getProviderStates($user); |
|
227 | + $providers = $this->providerLoader->getProviders($user); |
|
228 | + |
|
229 | + $fixedStates = $this->fixMissingProviderStates($providerStates, $providers, $user); |
|
230 | + $isProviderMissing = $this->isProviderMissing($fixedStates, $providers); |
|
231 | + |
|
232 | + $enabled = array_filter($providers, function (IProvider $provider) use ($fixedStates) { |
|
233 | + return $fixedStates[$provider->getId()]; |
|
234 | + }); |
|
235 | + return new ProviderSet($enabled, $isProviderMissing); |
|
236 | + } |
|
237 | + |
|
238 | + /** |
|
239 | + * Verify the given challenge |
|
240 | + * |
|
241 | + * @param string $providerId |
|
242 | + * @param IUser $user |
|
243 | + * @param string $challenge |
|
244 | + * @return boolean |
|
245 | + */ |
|
246 | + public function verifyChallenge(string $providerId, IUser $user, string $challenge): bool { |
|
247 | + $provider = $this->getProvider($user, $providerId); |
|
248 | + if ($provider === null) { |
|
249 | + return false; |
|
250 | + } |
|
251 | + |
|
252 | + $passed = $provider->verifyChallenge($user, $challenge); |
|
253 | + if ($passed) { |
|
254 | + if ($this->session->get(self::REMEMBER_LOGIN) === true) { |
|
255 | + // TODO: resolve cyclic dependency and use DI |
|
256 | + \OC::$server->getUserSession()->createRememberMeToken($user); |
|
257 | + } |
|
258 | + $this->session->remove(self::SESSION_UID_KEY); |
|
259 | + $this->session->remove(self::REMEMBER_LOGIN); |
|
260 | + $this->session->set(self::SESSION_UID_DONE, $user->getUID()); |
|
261 | + |
|
262 | + // Clear token from db |
|
263 | + $sessionId = $this->session->getId(); |
|
264 | + $token = $this->tokenProvider->getToken($sessionId); |
|
265 | + $tokenId = $token->getId(); |
|
266 | + $this->config->deleteUserValue($user->getUID(), 'login_token_2fa', $tokenId); |
|
267 | + |
|
268 | + $dispatchEvent = new GenericEvent($user, ['provider' => $provider->getDisplayName()]); |
|
269 | + $this->dispatcher->dispatch(IProvider::EVENT_SUCCESS, $dispatchEvent); |
|
270 | + |
|
271 | + $this->publishEvent($user, 'twofactor_success', [ |
|
272 | + 'provider' => $provider->getDisplayName(), |
|
273 | + ]); |
|
274 | + } else { |
|
275 | + $dispatchEvent = new GenericEvent($user, ['provider' => $provider->getDisplayName()]); |
|
276 | + $this->dispatcher->dispatch(IProvider::EVENT_FAILED, $dispatchEvent); |
|
277 | + |
|
278 | + $this->publishEvent($user, 'twofactor_failed', [ |
|
279 | + 'provider' => $provider->getDisplayName(), |
|
280 | + ]); |
|
281 | + } |
|
282 | + return $passed; |
|
283 | + } |
|
284 | + |
|
285 | + /** |
|
286 | + * Push a 2fa event the user's activity stream |
|
287 | + * |
|
288 | + * @param IUser $user |
|
289 | + * @param string $event |
|
290 | + * @param array $params |
|
291 | + */ |
|
292 | + private function publishEvent(IUser $user, string $event, array $params) { |
|
293 | + $activity = $this->activityManager->generateEvent(); |
|
294 | + $activity->setApp('core') |
|
295 | + ->setType('security') |
|
296 | + ->setAuthor($user->getUID()) |
|
297 | + ->setAffectedUser($user->getUID()) |
|
298 | + ->setSubject($event, $params); |
|
299 | + try { |
|
300 | + $this->activityManager->publish($activity); |
|
301 | + } catch (BadMethodCallException $e) { |
|
302 | + $this->logger->warning('could not publish activity', ['app' => 'core']); |
|
303 | + $this->logger->logException($e, ['app' => 'core']); |
|
304 | + } |
|
305 | + } |
|
306 | + |
|
307 | + /** |
|
308 | + * Check if the currently logged in user needs to pass 2FA |
|
309 | + * |
|
310 | + * @param IUser $user the currently logged in user |
|
311 | + * @return boolean |
|
312 | + */ |
|
313 | + public function needsSecondFactor(IUser $user = null): bool { |
|
314 | + if ($user === null) { |
|
315 | + return false; |
|
316 | + } |
|
317 | + |
|
318 | + // If we are authenticated using an app password skip all this |
|
319 | + if ($this->session->exists('app_password')) { |
|
320 | + return false; |
|
321 | + } |
|
322 | + |
|
323 | + // First check if the session tells us we should do 2FA (99% case) |
|
324 | + if (!$this->session->exists(self::SESSION_UID_KEY)) { |
|
325 | + |
|
326 | + // Check if the session tells us it is 2FA authenticated already |
|
327 | + if ($this->session->exists(self::SESSION_UID_DONE) && |
|
328 | + $this->session->get(self::SESSION_UID_DONE) === $user->getUID()) { |
|
329 | + return false; |
|
330 | + } |
|
331 | + |
|
332 | + /* |
|
333 | 333 | * If the session is expired check if we are not logged in by a token |
334 | 334 | * that still needs 2FA auth |
335 | 335 | */ |
336 | - try { |
|
337 | - $sessionId = $this->session->getId(); |
|
338 | - $token = $this->tokenProvider->getToken($sessionId); |
|
339 | - $tokenId = $token->getId(); |
|
340 | - $tokensNeeding2FA = $this->config->getUserKeys($user->getUID(), 'login_token_2fa'); |
|
341 | - |
|
342 | - if (!\in_array($tokenId, $tokensNeeding2FA, true)) { |
|
343 | - $this->session->set(self::SESSION_UID_DONE, $user->getUID()); |
|
344 | - return false; |
|
345 | - } |
|
346 | - } catch (InvalidTokenException $e) { |
|
347 | - } |
|
348 | - } |
|
349 | - |
|
350 | - if (!$this->isTwoFactorAuthenticated($user)) { |
|
351 | - // There is no second factor any more -> let the user pass |
|
352 | - // This prevents infinite redirect loops when a user is about |
|
353 | - // to solve the 2FA challenge, and the provider app is |
|
354 | - // disabled the same time |
|
355 | - $this->session->remove(self::SESSION_UID_KEY); |
|
356 | - |
|
357 | - $keys = $this->config->getUserKeys($user->getUID(), 'login_token_2fa'); |
|
358 | - foreach ($keys as $key) { |
|
359 | - $this->config->deleteUserValue($user->getUID(), 'login_token_2fa', $key); |
|
360 | - } |
|
361 | - return false; |
|
362 | - } |
|
363 | - |
|
364 | - return true; |
|
365 | - } |
|
366 | - |
|
367 | - /** |
|
368 | - * Prepare the 2FA login |
|
369 | - * |
|
370 | - * @param IUser $user |
|
371 | - * @param boolean $rememberMe |
|
372 | - */ |
|
373 | - public function prepareTwoFactorLogin(IUser $user, bool $rememberMe) { |
|
374 | - $this->session->set(self::SESSION_UID_KEY, $user->getUID()); |
|
375 | - $this->session->set(self::REMEMBER_LOGIN, $rememberMe); |
|
376 | - |
|
377 | - $id = $this->session->getId(); |
|
378 | - $token = $this->tokenProvider->getToken($id); |
|
379 | - $this->config->setUserValue($user->getUID(), 'login_token_2fa', $token->getId(), $this->timeFactory->getTime()); |
|
380 | - } |
|
381 | - |
|
382 | - public function clearTwoFactorPending(string $userId) { |
|
383 | - $tokensNeeding2FA = $this->config->getUserKeys($userId, 'login_token_2fa'); |
|
384 | - |
|
385 | - foreach ($tokensNeeding2FA as $tokenId) { |
|
386 | - $this->tokenProvider->invalidateTokenById($userId, $tokenId); |
|
387 | - } |
|
388 | - } |
|
336 | + try { |
|
337 | + $sessionId = $this->session->getId(); |
|
338 | + $token = $this->tokenProvider->getToken($sessionId); |
|
339 | + $tokenId = $token->getId(); |
|
340 | + $tokensNeeding2FA = $this->config->getUserKeys($user->getUID(), 'login_token_2fa'); |
|
341 | + |
|
342 | + if (!\in_array($tokenId, $tokensNeeding2FA, true)) { |
|
343 | + $this->session->set(self::SESSION_UID_DONE, $user->getUID()); |
|
344 | + return false; |
|
345 | + } |
|
346 | + } catch (InvalidTokenException $e) { |
|
347 | + } |
|
348 | + } |
|
349 | + |
|
350 | + if (!$this->isTwoFactorAuthenticated($user)) { |
|
351 | + // There is no second factor any more -> let the user pass |
|
352 | + // This prevents infinite redirect loops when a user is about |
|
353 | + // to solve the 2FA challenge, and the provider app is |
|
354 | + // disabled the same time |
|
355 | + $this->session->remove(self::SESSION_UID_KEY); |
|
356 | + |
|
357 | + $keys = $this->config->getUserKeys($user->getUID(), 'login_token_2fa'); |
|
358 | + foreach ($keys as $key) { |
|
359 | + $this->config->deleteUserValue($user->getUID(), 'login_token_2fa', $key); |
|
360 | + } |
|
361 | + return false; |
|
362 | + } |
|
363 | + |
|
364 | + return true; |
|
365 | + } |
|
366 | + |
|
367 | + /** |
|
368 | + * Prepare the 2FA login |
|
369 | + * |
|
370 | + * @param IUser $user |
|
371 | + * @param boolean $rememberMe |
|
372 | + */ |
|
373 | + public function prepareTwoFactorLogin(IUser $user, bool $rememberMe) { |
|
374 | + $this->session->set(self::SESSION_UID_KEY, $user->getUID()); |
|
375 | + $this->session->set(self::REMEMBER_LOGIN, $rememberMe); |
|
376 | + |
|
377 | + $id = $this->session->getId(); |
|
378 | + $token = $this->tokenProvider->getToken($id); |
|
379 | + $this->config->setUserValue($user->getUID(), 'login_token_2fa', $token->getId(), $this->timeFactory->getTime()); |
|
380 | + } |
|
381 | + |
|
382 | + public function clearTwoFactorPending(string $userId) { |
|
383 | + $tokensNeeding2FA = $this->config->getUserKeys($userId, 'login_token_2fa'); |
|
384 | + |
|
385 | + foreach ($tokensNeeding2FA as $tokenId) { |
|
386 | + $this->tokenProvider->invalidateTokenById($userId, $tokenId); |
|
387 | + } |
|
388 | + } |
|
389 | 389 | |
390 | 390 | } |
@@ -34,61 +34,61 @@ |
||
34 | 34 | |
35 | 35 | class TwoFactorCommand extends ALoginCommand { |
36 | 36 | |
37 | - /** @var Manager */ |
|
38 | - private $twoFactorManager; |
|
37 | + /** @var Manager */ |
|
38 | + private $twoFactorManager; |
|
39 | 39 | |
40 | - /** @var MandatoryTwoFactor */ |
|
41 | - private $mandatoryTwoFactor; |
|
40 | + /** @var MandatoryTwoFactor */ |
|
41 | + private $mandatoryTwoFactor; |
|
42 | 42 | |
43 | - /** @var IURLGenerator */ |
|
44 | - private $urlGenerator; |
|
43 | + /** @var IURLGenerator */ |
|
44 | + private $urlGenerator; |
|
45 | 45 | |
46 | - public function __construct(Manager $twoFactorManager, |
|
47 | - MandatoryTwoFactor $mandatoryTwoFactor, |
|
48 | - IURLGenerator $urlGenerator) { |
|
49 | - $this->twoFactorManager = $twoFactorManager; |
|
50 | - $this->mandatoryTwoFactor = $mandatoryTwoFactor; |
|
51 | - $this->urlGenerator = $urlGenerator; |
|
52 | - } |
|
46 | + public function __construct(Manager $twoFactorManager, |
|
47 | + MandatoryTwoFactor $mandatoryTwoFactor, |
|
48 | + IURLGenerator $urlGenerator) { |
|
49 | + $this->twoFactorManager = $twoFactorManager; |
|
50 | + $this->mandatoryTwoFactor = $mandatoryTwoFactor; |
|
51 | + $this->urlGenerator = $urlGenerator; |
|
52 | + } |
|
53 | 53 | |
54 | - public function process(LoginData $loginData): LoginResult { |
|
55 | - if (!$this->twoFactorManager->isTwoFactorAuthenticated($loginData->getUser())) { |
|
56 | - return $this->processNextOrFinishSuccessfully($loginData); |
|
57 | - } |
|
54 | + public function process(LoginData $loginData): LoginResult { |
|
55 | + if (!$this->twoFactorManager->isTwoFactorAuthenticated($loginData->getUser())) { |
|
56 | + return $this->processNextOrFinishSuccessfully($loginData); |
|
57 | + } |
|
58 | 58 | |
59 | - $this->twoFactorManager->prepareTwoFactorLogin($loginData->getUser(), $loginData->isRememberLogin()); |
|
59 | + $this->twoFactorManager->prepareTwoFactorLogin($loginData->getUser(), $loginData->isRememberLogin()); |
|
60 | 60 | |
61 | - $providerSet = $this->twoFactorManager->getProviderSet($loginData->getUser()); |
|
62 | - $loginProviders = $this->twoFactorManager->getLoginSetupProviders($loginData->getUser()); |
|
63 | - $providers = $providerSet->getPrimaryProviders(); |
|
64 | - if (empty($providers) |
|
65 | - && !$providerSet->isProviderMissing() |
|
66 | - && !empty($loginProviders) |
|
67 | - && $this->mandatoryTwoFactor->isEnforcedFor($loginData->getUser())) { |
|
68 | - // No providers set up, but 2FA is enforced and setup providers are available |
|
69 | - $url = 'core.TwoFactorChallenge.setupProviders'; |
|
70 | - $urlParams = []; |
|
71 | - } else if (!$providerSet->isProviderMissing() && count($providers) === 1) { |
|
72 | - // Single provider (and no missing ones), hence we can redirect to that provider's challenge page directly |
|
73 | - /* @var $provider IProvider */ |
|
74 | - $provider = array_pop($providers); |
|
75 | - $url = 'core.TwoFactorChallenge.showChallenge'; |
|
76 | - $urlParams = [ |
|
77 | - 'challengeProviderId' => $provider->getId(), |
|
78 | - ]; |
|
79 | - } else { |
|
80 | - $url = 'core.TwoFactorChallenge.selectChallenge'; |
|
81 | - $urlParams = []; |
|
82 | - } |
|
61 | + $providerSet = $this->twoFactorManager->getProviderSet($loginData->getUser()); |
|
62 | + $loginProviders = $this->twoFactorManager->getLoginSetupProviders($loginData->getUser()); |
|
63 | + $providers = $providerSet->getPrimaryProviders(); |
|
64 | + if (empty($providers) |
|
65 | + && !$providerSet->isProviderMissing() |
|
66 | + && !empty($loginProviders) |
|
67 | + && $this->mandatoryTwoFactor->isEnforcedFor($loginData->getUser())) { |
|
68 | + // No providers set up, but 2FA is enforced and setup providers are available |
|
69 | + $url = 'core.TwoFactorChallenge.setupProviders'; |
|
70 | + $urlParams = []; |
|
71 | + } else if (!$providerSet->isProviderMissing() && count($providers) === 1) { |
|
72 | + // Single provider (and no missing ones), hence we can redirect to that provider's challenge page directly |
|
73 | + /* @var $provider IProvider */ |
|
74 | + $provider = array_pop($providers); |
|
75 | + $url = 'core.TwoFactorChallenge.showChallenge'; |
|
76 | + $urlParams = [ |
|
77 | + 'challengeProviderId' => $provider->getId(), |
|
78 | + ]; |
|
79 | + } else { |
|
80 | + $url = 'core.TwoFactorChallenge.selectChallenge'; |
|
81 | + $urlParams = []; |
|
82 | + } |
|
83 | 83 | |
84 | - if ($loginData->getRedirectUrl() !== null) { |
|
85 | - $urlParams['redirect_url'] = $loginData->getRedirectUrl(); |
|
86 | - } |
|
84 | + if ($loginData->getRedirectUrl() !== null) { |
|
85 | + $urlParams['redirect_url'] = $loginData->getRedirectUrl(); |
|
86 | + } |
|
87 | 87 | |
88 | - return LoginResult::success( |
|
89 | - $loginData, |
|
90 | - $this->urlGenerator->linkToRoute($url, $urlParams) |
|
91 | - ); |
|
92 | - } |
|
88 | + return LoginResult::success( |
|
89 | + $loginData, |
|
90 | + $this->urlGenerator->linkToRoute($url, $urlParams) |
|
91 | + ); |
|
92 | + } |
|
93 | 93 | |
94 | 94 | } |