Passed
Push — master ( 00bf7e...349da3 )
by Joas
14:20 queued 12s
created
lib/private/Authentication/LoginCredentials/Store.php 1 patch
Indentation   +81 added lines, -81 removed lines patch added patch discarded remove patch
@@ -40,85 +40,85 @@
 block discarded – undo
40 40
 
41 41
 class Store implements IStore {
42 42
 
43
-	/** @var ISession */
44
-	private $session;
45
-
46
-	/** @var LoggerInterface */
47
-	private $logger;
48
-
49
-	/** @var IProvider|null */
50
-	private $tokenProvider;
51
-
52
-	public function __construct(ISession $session,
53
-								LoggerInterface $logger,
54
-								IProvider $tokenProvider = null) {
55
-		$this->session = $session;
56
-		$this->logger = $logger;
57
-		$this->tokenProvider = $tokenProvider;
58
-
59
-		Util::connectHook('OC_User', 'post_login', $this, 'authenticate');
60
-	}
61
-
62
-	/**
63
-	 * Hook listener on post login
64
-	 *
65
-	 * @param array $params
66
-	 */
67
-	public function authenticate(array $params) {
68
-		$this->session->set('login_credentials', json_encode($params));
69
-	}
70
-
71
-	/**
72
-	 * Replace the session implementation
73
-	 *
74
-	 * @param ISession $session
75
-	 */
76
-	public function setSession(ISession $session) {
77
-		$this->session = $session;
78
-	}
79
-
80
-	/**
81
-	 * @since 12
82
-	 *
83
-	 * @return ICredentials the login credentials of the current user
84
-	 * @throws CredentialsUnavailableException
85
-	 */
86
-	public function getLoginCredentials(): ICredentials {
87
-		if ($this->tokenProvider === null) {
88
-			throw new CredentialsUnavailableException();
89
-		}
90
-
91
-		$trySession = false;
92
-		try {
93
-			$sessionId = $this->session->getId();
94
-			$token = $this->tokenProvider->getToken($sessionId);
95
-
96
-			$uid = $token->getUID();
97
-			$user = $token->getLoginName();
98
-			$password = $this->tokenProvider->getPassword($token, $sessionId);
99
-
100
-			return new Credentials($uid, $user, $password);
101
-		} catch (SessionNotAvailableException $ex) {
102
-			$this->logger->debug('could not get login credentials because session is unavailable', ['app' => 'core']);
103
-		} catch (InvalidTokenException $ex) {
104
-			$this->logger->debug('could not get login credentials because the token is invalid', ['app' => 'core']);
105
-			$trySession = true;
106
-		} catch (PasswordlessTokenException $ex) {
107
-			$this->logger->debug('could not get login credentials because the token has no password', ['app' => 'core']);
108
-			$trySession = true;
109
-		}
110
-
111
-		if ($trySession && $this->session->exists('login_credentials')) {
112
-			/** @var array $creds */
113
-			$creds = json_decode($this->session->get('login_credentials'), true);
114
-			return new Credentials(
115
-				$creds['uid'],
116
-				$creds['loginName'] ?? $this->session->get('loginname') ?? $creds['uid'], // Pre 20 didn't have a loginName property, hence fall back to the session value and then to the UID
117
-				$creds['password']
118
-			);
119
-		}
120
-
121
-		// If we reach this line, an exception was thrown.
122
-		throw new CredentialsUnavailableException();
123
-	}
43
+    /** @var ISession */
44
+    private $session;
45
+
46
+    /** @var LoggerInterface */
47
+    private $logger;
48
+
49
+    /** @var IProvider|null */
50
+    private $tokenProvider;
51
+
52
+    public function __construct(ISession $session,
53
+                                LoggerInterface $logger,
54
+                                IProvider $tokenProvider = null) {
55
+        $this->session = $session;
56
+        $this->logger = $logger;
57
+        $this->tokenProvider = $tokenProvider;
58
+
59
+        Util::connectHook('OC_User', 'post_login', $this, 'authenticate');
60
+    }
61
+
62
+    /**
63
+     * Hook listener on post login
64
+     *
65
+     * @param array $params
66
+     */
67
+    public function authenticate(array $params) {
68
+        $this->session->set('login_credentials', json_encode($params));
69
+    }
70
+
71
+    /**
72
+     * Replace the session implementation
73
+     *
74
+     * @param ISession $session
75
+     */
76
+    public function setSession(ISession $session) {
77
+        $this->session = $session;
78
+    }
79
+
80
+    /**
81
+     * @since 12
82
+     *
83
+     * @return ICredentials the login credentials of the current user
84
+     * @throws CredentialsUnavailableException
85
+     */
86
+    public function getLoginCredentials(): ICredentials {
87
+        if ($this->tokenProvider === null) {
88
+            throw new CredentialsUnavailableException();
89
+        }
90
+
91
+        $trySession = false;
92
+        try {
93
+            $sessionId = $this->session->getId();
94
+            $token = $this->tokenProvider->getToken($sessionId);
95
+
96
+            $uid = $token->getUID();
97
+            $user = $token->getLoginName();
98
+            $password = $this->tokenProvider->getPassword($token, $sessionId);
99
+
100
+            return new Credentials($uid, $user, $password);
101
+        } catch (SessionNotAvailableException $ex) {
102
+            $this->logger->debug('could not get login credentials because session is unavailable', ['app' => 'core']);
103
+        } catch (InvalidTokenException $ex) {
104
+            $this->logger->debug('could not get login credentials because the token is invalid', ['app' => 'core']);
105
+            $trySession = true;
106
+        } catch (PasswordlessTokenException $ex) {
107
+            $this->logger->debug('could not get login credentials because the token has no password', ['app' => 'core']);
108
+            $trySession = true;
109
+        }
110
+
111
+        if ($trySession && $this->session->exists('login_credentials')) {
112
+            /** @var array $creds */
113
+            $creds = json_decode($this->session->get('login_credentials'), true);
114
+            return new Credentials(
115
+                $creds['uid'],
116
+                $creds['loginName'] ?? $this->session->get('loginname') ?? $creds['uid'], // Pre 20 didn't have a loginName property, hence fall back to the session value and then to the UID
117
+                $creds['password']
118
+            );
119
+        }
120
+
121
+        // If we reach this line, an exception was thrown.
122
+        throw new CredentialsUnavailableException();
123
+    }
124 124
 }
Please login to merge, or discard this patch.
lib/private/Authentication/WebAuthn/Manager.php 1 patch
Indentation   +214 added lines, -214 removed lines patch added patch discarded remove patch
@@ -57,219 +57,219 @@
 block discarded – undo
57 57
 
58 58
 class Manager {
59 59
 
60
-	/** @var CredentialRepository */
61
-	private $repository;
62
-
63
-	/** @var PublicKeyCredentialMapper */
64
-	private $credentialMapper;
65
-
66
-	/** @var LoggerInterface */
67
-	private $logger;
68
-
69
-	/** @var IConfig */
70
-	private $config;
71
-
72
-	public function __construct(
73
-		CredentialRepository $repository,
74
-		PublicKeyCredentialMapper $credentialMapper,
75
-		LoggerInterface $logger,
76
-		IConfig $config
77
-	) {
78
-		$this->repository = $repository;
79
-		$this->credentialMapper = $credentialMapper;
80
-		$this->logger = $logger;
81
-		$this->config = $config;
82
-	}
83
-
84
-	public function startRegistration(IUser $user, string $serverHost): PublicKeyCredentialCreationOptions {
85
-		$rpEntity = new PublicKeyCredentialRpEntity(
86
-			'Nextcloud', //Name
87
-			$this->stripPort($serverHost),        //ID
88
-			null                            //Icon
89
-		);
90
-
91
-		$userEntity = new PublicKeyCredentialUserEntity(
92
-			$user->getUID(),                              //Name
93
-			$user->getUID(),                              //ID
94
-			$user->getDisplayName()                      //Display name
60
+    /** @var CredentialRepository */
61
+    private $repository;
62
+
63
+    /** @var PublicKeyCredentialMapper */
64
+    private $credentialMapper;
65
+
66
+    /** @var LoggerInterface */
67
+    private $logger;
68
+
69
+    /** @var IConfig */
70
+    private $config;
71
+
72
+    public function __construct(
73
+        CredentialRepository $repository,
74
+        PublicKeyCredentialMapper $credentialMapper,
75
+        LoggerInterface $logger,
76
+        IConfig $config
77
+    ) {
78
+        $this->repository = $repository;
79
+        $this->credentialMapper = $credentialMapper;
80
+        $this->logger = $logger;
81
+        $this->config = $config;
82
+    }
83
+
84
+    public function startRegistration(IUser $user, string $serverHost): PublicKeyCredentialCreationOptions {
85
+        $rpEntity = new PublicKeyCredentialRpEntity(
86
+            'Nextcloud', //Name
87
+            $this->stripPort($serverHost),        //ID
88
+            null                            //Icon
89
+        );
90
+
91
+        $userEntity = new PublicKeyCredentialUserEntity(
92
+            $user->getUID(),                              //Name
93
+            $user->getUID(),                              //ID
94
+            $user->getDisplayName()                      //Display name
95 95
 //            'https://foo.example.co/avatar/123e4567-e89b-12d3-a456-426655440000' //Icon
96
-		);
97
-
98
-		$challenge = random_bytes(32);
99
-
100
-		$publicKeyCredentialParametersList = [
101
-			new PublicKeyCredentialParameters('public-key', Algorithms::COSE_ALGORITHM_ES256),
102
-			new PublicKeyCredentialParameters('public-key', Algorithms::COSE_ALGORITHM_RS256),
103
-		];
104
-
105
-		$timeout = 60000;
106
-
107
-		$excludedPublicKeyDescriptors = [
108
-		];
109
-
110
-		$authenticatorSelectionCriteria = new AuthenticatorSelectionCriteria(
111
-			null,
112
-			false,
113
-			AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_DISCOURAGED
114
-		);
115
-
116
-		return new PublicKeyCredentialCreationOptions(
117
-			$rpEntity,
118
-			$userEntity,
119
-			$challenge,
120
-			$publicKeyCredentialParametersList,
121
-			$timeout,
122
-			$excludedPublicKeyDescriptors,
123
-			$authenticatorSelectionCriteria,
124
-			PublicKeyCredentialCreationOptions::ATTESTATION_CONVEYANCE_PREFERENCE_NONE,
125
-			null
126
-		);
127
-	}
128
-
129
-	public function finishRegister(PublicKeyCredentialCreationOptions $publicKeyCredentialCreationOptions, string $name, string $data): PublicKeyCredentialEntity {
130
-		$tokenBindingHandler = new TokenBindingNotSupportedHandler();
131
-
132
-		$attestationStatementSupportManager = new AttestationStatementSupportManager();
133
-		$attestationStatementSupportManager->add(new NoneAttestationStatementSupport());
134
-
135
-		$attestationObjectLoader = new AttestationObjectLoader($attestationStatementSupportManager);
136
-		$publicKeyCredentialLoader = new PublicKeyCredentialLoader($attestationObjectLoader);
137
-
138
-		// Extension Output Checker Handler
139
-		$extensionOutputCheckerHandler = new ExtensionOutputCheckerHandler();
140
-
141
-		// Authenticator Attestation Response Validator
142
-		$authenticatorAttestationResponseValidator = new AuthenticatorAttestationResponseValidator(
143
-			$attestationStatementSupportManager,
144
-			$this->repository,
145
-			$tokenBindingHandler,
146
-			$extensionOutputCheckerHandler
147
-		);
148
-
149
-		try {
150
-			// Load the data
151
-			$publicKeyCredential = $publicKeyCredentialLoader->load($data);
152
-			$response = $publicKeyCredential->getResponse();
153
-
154
-			// Check if the response is an Authenticator Attestation Response
155
-			if (!$response instanceof AuthenticatorAttestationResponse) {
156
-				throw new \RuntimeException('Not an authenticator attestation response');
157
-			}
158
-
159
-			// Check the response against the request
160
-			$request = ServerRequest::fromGlobals();
161
-
162
-			$publicKeyCredentialSource = $authenticatorAttestationResponseValidator->check(
163
-				$response,
164
-				$publicKeyCredentialCreationOptions,
165
-				$request);
166
-		} catch (\Throwable $exception) {
167
-			throw $exception;
168
-		}
169
-
170
-		// Persist the data
171
-		return $this->repository->saveAndReturnCredentialSource($publicKeyCredentialSource, $name);
172
-	}
173
-
174
-	private function stripPort(string $serverHost): string {
175
-		return preg_replace('/(:\d+$)/', '', $serverHost);
176
-	}
177
-
178
-	public function startAuthentication(string $uid, string $serverHost): PublicKeyCredentialRequestOptions {
179
-		// List of registered PublicKeyCredentialDescriptor classes associated to the user
180
-		$registeredPublicKeyCredentialDescriptors = array_map(function (PublicKeyCredentialEntity $entity) {
181
-			$credential = $entity->toPublicKeyCredentialSource();
182
-			return new PublicKeyCredentialDescriptor(
183
-				$credential->getType(),
184
-				$credential->getPublicKeyCredentialId()
185
-			);
186
-		}, $this->credentialMapper->findAllForUid($uid));
187
-
188
-		// Public Key Credential Request Options
189
-		return new PublicKeyCredentialRequestOptions(
190
-			random_bytes(32),                                                    // Challenge
191
-			60000,                                                              // Timeout
192
-			$this->stripPort($serverHost),                                                                  // Relying Party ID
193
-			$registeredPublicKeyCredentialDescriptors,                                  // Registered PublicKeyCredentialDescriptor classes
194
-			AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_DISCOURAGED
195
-		);
196
-	}
197
-
198
-	public function finishAuthentication(PublicKeyCredentialRequestOptions $publicKeyCredentialRequestOptions, string $data, string $uid) {
199
-		$attestationStatementSupportManager = new AttestationStatementSupportManager();
200
-		$attestationStatementSupportManager->add(new NoneAttestationStatementSupport());
201
-
202
-		$attestationObjectLoader = new AttestationObjectLoader($attestationStatementSupportManager);
203
-		$publicKeyCredentialLoader = new PublicKeyCredentialLoader($attestationObjectLoader);
204
-
205
-		$tokenBindingHandler = new TokenBindingNotSupportedHandler();
206
-		$extensionOutputCheckerHandler = new ExtensionOutputCheckerHandler();
207
-		$algorithmManager = new \Cose\Algorithm\Manager();
208
-		$algorithmManager->add(new ES256());
209
-		$algorithmManager->add(new RS256());
210
-
211
-		$authenticatorAssertionResponseValidator = new AuthenticatorAssertionResponseValidator(
212
-			$this->repository,
213
-			$tokenBindingHandler,
214
-			$extensionOutputCheckerHandler,
215
-			$algorithmManager
216
-		);
217
-
218
-		try {
219
-			$this->logger->debug('Loading publickey credentials from: ' . $data);
220
-
221
-			// Load the data
222
-			$publicKeyCredential = $publicKeyCredentialLoader->load($data);
223
-			$response = $publicKeyCredential->getResponse();
224
-
225
-			// Check if the response is an Authenticator Attestation Response
226
-			if (!$response instanceof AuthenticatorAssertionResponse) {
227
-				throw new \RuntimeException('Not an authenticator attestation response');
228
-			}
229
-
230
-			// Check the response against the request
231
-			$request = ServerRequest::fromGlobals();
232
-
233
-			$publicKeyCredentialSource = $authenticatorAssertionResponseValidator->check(
234
-				$publicKeyCredential->getRawId(),
235
-				$response,
236
-				$publicKeyCredentialRequestOptions,
237
-				$request,
238
-				$uid
239
-			);
240
-		} catch (\Throwable $e) {
241
-			throw $e;
242
-		}
243
-
244
-
245
-
246
-		return true;
247
-	}
248
-
249
-	public function deleteRegistration(IUser $user, int $id): void {
250
-		try {
251
-			$entry = $this->credentialMapper->findById($user->getUID(), $id);
252
-		} catch (DoesNotExistException $e) {
253
-			$this->logger->warning("WebAuthn device $id does not exist, can't delete it");
254
-			return;
255
-		}
256
-
257
-		$this->credentialMapper->delete($entry);
258
-	}
259
-
260
-	public function isWebAuthnAvailable(): bool {
261
-		if (!extension_loaded('bcmath')) {
262
-			return false;
263
-		}
264
-
265
-		if (!extension_loaded('gmp')) {
266
-			return false;
267
-		}
268
-
269
-		if (!$this->config->getSystemValueBool('auth.webauthn.enabled', true)) {
270
-			return false;
271
-		}
272
-
273
-		return true;
274
-	}
96
+        );
97
+
98
+        $challenge = random_bytes(32);
99
+
100
+        $publicKeyCredentialParametersList = [
101
+            new PublicKeyCredentialParameters('public-key', Algorithms::COSE_ALGORITHM_ES256),
102
+            new PublicKeyCredentialParameters('public-key', Algorithms::COSE_ALGORITHM_RS256),
103
+        ];
104
+
105
+        $timeout = 60000;
106
+
107
+        $excludedPublicKeyDescriptors = [
108
+        ];
109
+
110
+        $authenticatorSelectionCriteria = new AuthenticatorSelectionCriteria(
111
+            null,
112
+            false,
113
+            AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_DISCOURAGED
114
+        );
115
+
116
+        return new PublicKeyCredentialCreationOptions(
117
+            $rpEntity,
118
+            $userEntity,
119
+            $challenge,
120
+            $publicKeyCredentialParametersList,
121
+            $timeout,
122
+            $excludedPublicKeyDescriptors,
123
+            $authenticatorSelectionCriteria,
124
+            PublicKeyCredentialCreationOptions::ATTESTATION_CONVEYANCE_PREFERENCE_NONE,
125
+            null
126
+        );
127
+    }
128
+
129
+    public function finishRegister(PublicKeyCredentialCreationOptions $publicKeyCredentialCreationOptions, string $name, string $data): PublicKeyCredentialEntity {
130
+        $tokenBindingHandler = new TokenBindingNotSupportedHandler();
131
+
132
+        $attestationStatementSupportManager = new AttestationStatementSupportManager();
133
+        $attestationStatementSupportManager->add(new NoneAttestationStatementSupport());
134
+
135
+        $attestationObjectLoader = new AttestationObjectLoader($attestationStatementSupportManager);
136
+        $publicKeyCredentialLoader = new PublicKeyCredentialLoader($attestationObjectLoader);
137
+
138
+        // Extension Output Checker Handler
139
+        $extensionOutputCheckerHandler = new ExtensionOutputCheckerHandler();
140
+
141
+        // Authenticator Attestation Response Validator
142
+        $authenticatorAttestationResponseValidator = new AuthenticatorAttestationResponseValidator(
143
+            $attestationStatementSupportManager,
144
+            $this->repository,
145
+            $tokenBindingHandler,
146
+            $extensionOutputCheckerHandler
147
+        );
148
+
149
+        try {
150
+            // Load the data
151
+            $publicKeyCredential = $publicKeyCredentialLoader->load($data);
152
+            $response = $publicKeyCredential->getResponse();
153
+
154
+            // Check if the response is an Authenticator Attestation Response
155
+            if (!$response instanceof AuthenticatorAttestationResponse) {
156
+                throw new \RuntimeException('Not an authenticator attestation response');
157
+            }
158
+
159
+            // Check the response against the request
160
+            $request = ServerRequest::fromGlobals();
161
+
162
+            $publicKeyCredentialSource = $authenticatorAttestationResponseValidator->check(
163
+                $response,
164
+                $publicKeyCredentialCreationOptions,
165
+                $request);
166
+        } catch (\Throwable $exception) {
167
+            throw $exception;
168
+        }
169
+
170
+        // Persist the data
171
+        return $this->repository->saveAndReturnCredentialSource($publicKeyCredentialSource, $name);
172
+    }
173
+
174
+    private function stripPort(string $serverHost): string {
175
+        return preg_replace('/(:\d+$)/', '', $serverHost);
176
+    }
177
+
178
+    public function startAuthentication(string $uid, string $serverHost): PublicKeyCredentialRequestOptions {
179
+        // List of registered PublicKeyCredentialDescriptor classes associated to the user
180
+        $registeredPublicKeyCredentialDescriptors = array_map(function (PublicKeyCredentialEntity $entity) {
181
+            $credential = $entity->toPublicKeyCredentialSource();
182
+            return new PublicKeyCredentialDescriptor(
183
+                $credential->getType(),
184
+                $credential->getPublicKeyCredentialId()
185
+            );
186
+        }, $this->credentialMapper->findAllForUid($uid));
187
+
188
+        // Public Key Credential Request Options
189
+        return new PublicKeyCredentialRequestOptions(
190
+            random_bytes(32),                                                    // Challenge
191
+            60000,                                                              // Timeout
192
+            $this->stripPort($serverHost),                                                                  // Relying Party ID
193
+            $registeredPublicKeyCredentialDescriptors,                                  // Registered PublicKeyCredentialDescriptor classes
194
+            AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_DISCOURAGED
195
+        );
196
+    }
197
+
198
+    public function finishAuthentication(PublicKeyCredentialRequestOptions $publicKeyCredentialRequestOptions, string $data, string $uid) {
199
+        $attestationStatementSupportManager = new AttestationStatementSupportManager();
200
+        $attestationStatementSupportManager->add(new NoneAttestationStatementSupport());
201
+
202
+        $attestationObjectLoader = new AttestationObjectLoader($attestationStatementSupportManager);
203
+        $publicKeyCredentialLoader = new PublicKeyCredentialLoader($attestationObjectLoader);
204
+
205
+        $tokenBindingHandler = new TokenBindingNotSupportedHandler();
206
+        $extensionOutputCheckerHandler = new ExtensionOutputCheckerHandler();
207
+        $algorithmManager = new \Cose\Algorithm\Manager();
208
+        $algorithmManager->add(new ES256());
209
+        $algorithmManager->add(new RS256());
210
+
211
+        $authenticatorAssertionResponseValidator = new AuthenticatorAssertionResponseValidator(
212
+            $this->repository,
213
+            $tokenBindingHandler,
214
+            $extensionOutputCheckerHandler,
215
+            $algorithmManager
216
+        );
217
+
218
+        try {
219
+            $this->logger->debug('Loading publickey credentials from: ' . $data);
220
+
221
+            // Load the data
222
+            $publicKeyCredential = $publicKeyCredentialLoader->load($data);
223
+            $response = $publicKeyCredential->getResponse();
224
+
225
+            // Check if the response is an Authenticator Attestation Response
226
+            if (!$response instanceof AuthenticatorAssertionResponse) {
227
+                throw new \RuntimeException('Not an authenticator attestation response');
228
+            }
229
+
230
+            // Check the response against the request
231
+            $request = ServerRequest::fromGlobals();
232
+
233
+            $publicKeyCredentialSource = $authenticatorAssertionResponseValidator->check(
234
+                $publicKeyCredential->getRawId(),
235
+                $response,
236
+                $publicKeyCredentialRequestOptions,
237
+                $request,
238
+                $uid
239
+            );
240
+        } catch (\Throwable $e) {
241
+            throw $e;
242
+        }
243
+
244
+
245
+
246
+        return true;
247
+    }
248
+
249
+    public function deleteRegistration(IUser $user, int $id): void {
250
+        try {
251
+            $entry = $this->credentialMapper->findById($user->getUID(), $id);
252
+        } catch (DoesNotExistException $e) {
253
+            $this->logger->warning("WebAuthn device $id does not exist, can't delete it");
254
+            return;
255
+        }
256
+
257
+        $this->credentialMapper->delete($entry);
258
+    }
259
+
260
+    public function isWebAuthnAvailable(): bool {
261
+        if (!extension_loaded('bcmath')) {
262
+            return false;
263
+        }
264
+
265
+        if (!extension_loaded('gmp')) {
266
+            return false;
267
+        }
268
+
269
+        if (!$this->config->getSystemValueBool('auth.webauthn.enabled', true)) {
270
+            return false;
271
+        }
272
+
273
+        return true;
274
+    }
275 275
 }
Please login to merge, or discard this patch.
lib/private/Authentication/Login/UserDisabledCheckCommand.php 1 patch
Indentation   +19 added lines, -19 removed lines patch added patch discarded remove patch
@@ -31,29 +31,29 @@
 block discarded – undo
31 31
 
32 32
 class UserDisabledCheckCommand extends ALoginCommand {
33 33
 
34
-	/** @var IUserManager */
35
-	private $userManager;
34
+    /** @var IUserManager */
35
+    private $userManager;
36 36
 
37
-	/** @var LoggerInterface */
38
-	private $logger;
37
+    /** @var LoggerInterface */
38
+    private $logger;
39 39
 
40
-	public function __construct(IUserManager $userManager,
41
-								LoggerInterface $logger) {
42
-		$this->userManager = $userManager;
43
-		$this->logger = $logger;
44
-	}
40
+    public function __construct(IUserManager $userManager,
41
+                                LoggerInterface $logger) {
42
+        $this->userManager = $userManager;
43
+        $this->logger = $logger;
44
+    }
45 45
 
46
-	public function process(LoginData $loginData): LoginResult {
47
-		$user = $this->userManager->get($loginData->getUsername());
48
-		if ($user !== null && $user->isEnabled() === false) {
49
-			$username = $loginData->getUsername();
50
-			$ip = $loginData->getRequest()->getRemoteAddress();
46
+    public function process(LoginData $loginData): LoginResult {
47
+        $user = $this->userManager->get($loginData->getUsername());
48
+        if ($user !== null && $user->isEnabled() === false) {
49
+            $username = $loginData->getUsername();
50
+            $ip = $loginData->getRequest()->getRemoteAddress();
51 51
 
52
-			$this->logger->warning("Login failed: $username disabled (Remote IP: $ip)");
52
+            $this->logger->warning("Login failed: $username disabled (Remote IP: $ip)");
53 53
 
54
-			return LoginResult::failure($loginData, LoginController::LOGIN_MSG_USERDISABLED);
55
-		}
54
+            return LoginResult::failure($loginData, LoginController::LOGIN_MSG_USERDISABLED);
55
+        }
56 56
 
57
-		return $this->processNextOrFinishSuccessfully($loginData);
58
-	}
57
+        return $this->processNextOrFinishSuccessfully($loginData);
58
+    }
59 59
 }
Please login to merge, or discard this patch.
lib/private/Authentication/Login/LoggedInCheckCommand.php 1 patch
Indentation   +19 added lines, -19 removed lines patch added patch discarded remove patch
@@ -33,29 +33,29 @@
 block discarded – undo
33 33
 
34 34
 class LoggedInCheckCommand extends ALoginCommand {
35 35
 
36
-	/** @var LoggerInterface */
37
-	private $logger;
38
-	/** @var IEventDispatcher */
39
-	private $dispatcher;
36
+    /** @var LoggerInterface */
37
+    private $logger;
38
+    /** @var IEventDispatcher */
39
+    private $dispatcher;
40 40
 
41
-	public function __construct(LoggerInterface $logger,
42
-								IEventDispatcher $dispatcher) {
43
-		$this->logger = $logger;
44
-		$this->dispatcher = $dispatcher;
45
-	}
41
+    public function __construct(LoggerInterface $logger,
42
+                                IEventDispatcher $dispatcher) {
43
+        $this->logger = $logger;
44
+        $this->dispatcher = $dispatcher;
45
+    }
46 46
 
47
-	public function process(LoginData $loginData): LoginResult {
48
-		if ($loginData->getUser() === false) {
49
-			$loginName = $loginData->getUsername();
50
-			$ip = $loginData->getRequest()->getRemoteAddress();
47
+    public function process(LoginData $loginData): LoginResult {
48
+        if ($loginData->getUser() === false) {
49
+            $loginName = $loginData->getUsername();
50
+            $ip = $loginData->getRequest()->getRemoteAddress();
51 51
 
52
-			$this->logger->warning("Login failed: $loginName (Remote IP: $ip)");
52
+            $this->logger->warning("Login failed: $loginName (Remote IP: $ip)");
53 53
 
54
-			$this->dispatcher->dispatchTyped(new LoginFailed($loginName));
54
+            $this->dispatcher->dispatchTyped(new LoginFailed($loginName));
55 55
 
56
-			return LoginResult::failure($loginData, LoginController::LOGIN_MSG_INVALIDPASSWORD);
57
-		}
56
+            return LoginResult::failure($loginData, LoginController::LOGIN_MSG_INVALIDPASSWORD);
57
+        }
58 58
 
59
-		return $this->processNextOrFinishSuccessfully($loginData);
60
-	}
59
+        return $this->processNextOrFinishSuccessfully($loginData);
60
+    }
61 61
 }
Please login to merge, or discard this patch.
lib/private/Authentication/Listeners/RemoteWipeEmailListener.php 1 patch
Indentation   +126 added lines, -126 removed lines patch added patch discarded remove patch
@@ -42,130 +42,130 @@
 block discarded – undo
42 42
 
43 43
 class RemoteWipeEmailListener implements IEventListener {
44 44
 
45
-	/** @var IMailer */
46
-	private $mailer;
47
-
48
-	/** @var IUserManager */
49
-	private $userManager;
50
-
51
-	/** @var IL10N */
52
-	private $l10n;
53
-
54
-	/** @var LoggerInterface */
55
-	private $logger;
56
-
57
-	public function __construct(IMailer $mailer,
58
-								IUserManager $userManager,
59
-								IL10nFactory $l10nFactory,
60
-								LoggerInterface $logger) {
61
-		$this->mailer = $mailer;
62
-		$this->userManager = $userManager;
63
-		$this->l10n = $l10nFactory->get('core');
64
-		$this->logger = $logger;
65
-	}
66
-
67
-	/**
68
-	 * @param Event $event
69
-	 */
70
-	public function handle(Event $event): void {
71
-		if ($event instanceof RemoteWipeStarted) {
72
-			$uid = $event->getToken()->getUID();
73
-			$user = $this->userManager->get($uid);
74
-			if ($user === null) {
75
-				$this->logger->warning("not sending a wipe started email because user <$uid> does not exist (anymore)");
76
-				return;
77
-			}
78
-			if ($user->getEMailAddress() === null) {
79
-				$this->logger->info("not sending a wipe started email because user <$uid> has no email set");
80
-				return;
81
-			}
82
-
83
-			try {
84
-				$this->mailer->send(
85
-					$this->getWipingStartedMessage($event, $user)
86
-				);
87
-			} catch (Exception $e) {
88
-				$this->logger->error("Could not send remote wipe started email to <$uid>", [
89
-					'exception' => $e,
90
-				]);
91
-			}
92
-		} elseif ($event instanceof RemoteWipeFinished) {
93
-			$uid = $event->getToken()->getUID();
94
-			$user = $this->userManager->get($uid);
95
-			if ($user === null) {
96
-				$this->logger->warning("not sending a wipe finished email because user <$uid> does not exist (anymore)");
97
-				return;
98
-			}
99
-			if ($user->getEMailAddress() === null) {
100
-				$this->logger->info("not sending a wipe finished email because user <$uid> has no email set");
101
-				return;
102
-			}
103
-
104
-			try {
105
-				$this->mailer->send(
106
-					$this->getWipingFinishedMessage($event, $user)
107
-				);
108
-			} catch (Exception $e) {
109
-				$this->logger->error("Could not send remote wipe finished email to <$uid>", [
110
-					'exception' => $e,
111
-				]);
112
-			}
113
-		}
114
-	}
115
-
116
-	private function getWipingStartedMessage(RemoteWipeStarted $event, IUser $user): IMessage {
117
-		$message = $this->mailer->createMessage();
118
-		$emailTemplate = $this->mailer->createEMailTemplate('auth.RemoteWipeStarted');
119
-		$plainHeading = $this->l10n->t('Wiping of device %s has started', [$event->getToken()->getName()]);
120
-		$htmlHeading = $this->l10n->t('Wiping of device »%s« has started', [$event->getToken()->getName()]);
121
-		$emailTemplate->setSubject(
122
-			$this->l10n->t(
123
-				'»%s« started remote wipe',
124
-				[
125
-					substr($event->getToken()->getName(), 0, 15)
126
-				]
127
-			)
128
-		);
129
-		$emailTemplate->addHeader();
130
-		$emailTemplate->addHeading(
131
-			$htmlHeading,
132
-			$plainHeading
133
-		);
134
-		$emailTemplate->addBodyText(
135
-			$this->l10n->t('Device or application »%s« has started the remote wipe process. You will receive another email once the process has finished', [$event->getToken()->getName()])
136
-		);
137
-		$emailTemplate->addFooter();
138
-		$message->setTo([$user->getEMailAddress()]);
139
-		$message->useTemplate($emailTemplate);
140
-
141
-		return $message;
142
-	}
143
-
144
-	private function getWipingFinishedMessage(RemoteWipeFinished $event, IUser $user): IMessage {
145
-		$message = $this->mailer->createMessage();
146
-		$emailTemplate = $this->mailer->createEMailTemplate('auth.RemoteWipeFinished');
147
-		$plainHeading = $this->l10n->t('Wiping of device %s has finished', [$event->getToken()->getName()]);
148
-		$htmlHeading = $this->l10n->t('Wiping of device »%s« has finished', [$event->getToken()->getName()]);
149
-		$emailTemplate->setSubject(
150
-			$this->l10n->t(
151
-				'»%s« finished remote wipe',
152
-				[
153
-					substr($event->getToken()->getName(), 0, 15)
154
-				]
155
-			)
156
-		);
157
-		$emailTemplate->addHeader();
158
-		$emailTemplate->addHeading(
159
-			$htmlHeading,
160
-			$plainHeading
161
-		);
162
-		$emailTemplate->addBodyText(
163
-			$this->l10n->t('Device or application »%s« has finished the remote wipe process.', [$event->getToken()->getName()])
164
-		);
165
-		$emailTemplate->addFooter();
166
-		$message->setTo([$user->getEMailAddress()]);
167
-		$message->useTemplate($emailTemplate);
168
-
169
-		return $message;
170
-	}
45
+    /** @var IMailer */
46
+    private $mailer;
47
+
48
+    /** @var IUserManager */
49
+    private $userManager;
50
+
51
+    /** @var IL10N */
52
+    private $l10n;
53
+
54
+    /** @var LoggerInterface */
55
+    private $logger;
56
+
57
+    public function __construct(IMailer $mailer,
58
+                                IUserManager $userManager,
59
+                                IL10nFactory $l10nFactory,
60
+                                LoggerInterface $logger) {
61
+        $this->mailer = $mailer;
62
+        $this->userManager = $userManager;
63
+        $this->l10n = $l10nFactory->get('core');
64
+        $this->logger = $logger;
65
+    }
66
+
67
+    /**
68
+     * @param Event $event
69
+     */
70
+    public function handle(Event $event): void {
71
+        if ($event instanceof RemoteWipeStarted) {
72
+            $uid = $event->getToken()->getUID();
73
+            $user = $this->userManager->get($uid);
74
+            if ($user === null) {
75
+                $this->logger->warning("not sending a wipe started email because user <$uid> does not exist (anymore)");
76
+                return;
77
+            }
78
+            if ($user->getEMailAddress() === null) {
79
+                $this->logger->info("not sending a wipe started email because user <$uid> has no email set");
80
+                return;
81
+            }
82
+
83
+            try {
84
+                $this->mailer->send(
85
+                    $this->getWipingStartedMessage($event, $user)
86
+                );
87
+            } catch (Exception $e) {
88
+                $this->logger->error("Could not send remote wipe started email to <$uid>", [
89
+                    'exception' => $e,
90
+                ]);
91
+            }
92
+        } elseif ($event instanceof RemoteWipeFinished) {
93
+            $uid = $event->getToken()->getUID();
94
+            $user = $this->userManager->get($uid);
95
+            if ($user === null) {
96
+                $this->logger->warning("not sending a wipe finished email because user <$uid> does not exist (anymore)");
97
+                return;
98
+            }
99
+            if ($user->getEMailAddress() === null) {
100
+                $this->logger->info("not sending a wipe finished email because user <$uid> has no email set");
101
+                return;
102
+            }
103
+
104
+            try {
105
+                $this->mailer->send(
106
+                    $this->getWipingFinishedMessage($event, $user)
107
+                );
108
+            } catch (Exception $e) {
109
+                $this->logger->error("Could not send remote wipe finished email to <$uid>", [
110
+                    'exception' => $e,
111
+                ]);
112
+            }
113
+        }
114
+    }
115
+
116
+    private function getWipingStartedMessage(RemoteWipeStarted $event, IUser $user): IMessage {
117
+        $message = $this->mailer->createMessage();
118
+        $emailTemplate = $this->mailer->createEMailTemplate('auth.RemoteWipeStarted');
119
+        $plainHeading = $this->l10n->t('Wiping of device %s has started', [$event->getToken()->getName()]);
120
+        $htmlHeading = $this->l10n->t('Wiping of device »%s« has started', [$event->getToken()->getName()]);
121
+        $emailTemplate->setSubject(
122
+            $this->l10n->t(
123
+                '»%s« started remote wipe',
124
+                [
125
+                    substr($event->getToken()->getName(), 0, 15)
126
+                ]
127
+            )
128
+        );
129
+        $emailTemplate->addHeader();
130
+        $emailTemplate->addHeading(
131
+            $htmlHeading,
132
+            $plainHeading
133
+        );
134
+        $emailTemplate->addBodyText(
135
+            $this->l10n->t('Device or application »%s« has started the remote wipe process. You will receive another email once the process has finished', [$event->getToken()->getName()])
136
+        );
137
+        $emailTemplate->addFooter();
138
+        $message->setTo([$user->getEMailAddress()]);
139
+        $message->useTemplate($emailTemplate);
140
+
141
+        return $message;
142
+    }
143
+
144
+    private function getWipingFinishedMessage(RemoteWipeFinished $event, IUser $user): IMessage {
145
+        $message = $this->mailer->createMessage();
146
+        $emailTemplate = $this->mailer->createEMailTemplate('auth.RemoteWipeFinished');
147
+        $plainHeading = $this->l10n->t('Wiping of device %s has finished', [$event->getToken()->getName()]);
148
+        $htmlHeading = $this->l10n->t('Wiping of device »%s« has finished', [$event->getToken()->getName()]);
149
+        $emailTemplate->setSubject(
150
+            $this->l10n->t(
151
+                '»%s« finished remote wipe',
152
+                [
153
+                    substr($event->getToken()->getName(), 0, 15)
154
+                ]
155
+            )
156
+        );
157
+        $emailTemplate->addHeader();
158
+        $emailTemplate->addHeading(
159
+            $htmlHeading,
160
+            $plainHeading
161
+        );
162
+        $emailTemplate->addBodyText(
163
+            $this->l10n->t('Device or application »%s« has finished the remote wipe process.', [$event->getToken()->getName()])
164
+        );
165
+        $emailTemplate->addFooter();
166
+        $message->setTo([$user->getEMailAddress()]);
167
+        $message->useTemplate($emailTemplate);
168
+
169
+        return $message;
170
+    }
171 171
 }
Please login to merge, or discard this patch.
lib/private/Authentication/Listeners/UserDeletedTokenCleanupListener.php 2 patches
Indentation   +30 added lines, -30 removed lines patch added patch discarded remove patch
@@ -35,38 +35,38 @@
 block discarded – undo
35 35
 
36 36
 class UserDeletedTokenCleanupListener implements IEventListener {
37 37
 
38
-	/** @var Manager */
39
-	private $manager;
38
+    /** @var Manager */
39
+    private $manager;
40 40
 
41
-	/** @var LoggerInterface */
42
-	private $logger;
41
+    /** @var LoggerInterface */
42
+    private $logger;
43 43
 
44
-	public function __construct(Manager $manager,
45
-								LoggerInterface $logger) {
46
-		$this->manager = $manager;
47
-		$this->logger = $logger;
48
-	}
44
+    public function __construct(Manager $manager,
45
+                                LoggerInterface $logger) {
46
+        $this->manager = $manager;
47
+        $this->logger = $logger;
48
+    }
49 49
 
50
-	public function handle(Event $event): void {
51
-		if (!($event instanceof UserDeletedEvent)) {
52
-			// Unrelated
53
-			return;
54
-		}
50
+    public function handle(Event $event): void {
51
+        if (!($event instanceof UserDeletedEvent)) {
52
+            // Unrelated
53
+            return;
54
+        }
55 55
 
56
-		/**
57
-		 * Catch any exception during this process as any failure here shouldn't block the
58
-		 * user deletion.
59
-		 */
60
-		try {
61
-			$uid = $event->getUser()->getUID();
62
-			$tokens = $this->manager->getTokenByUser($uid);
63
-			foreach ($tokens as $token) {
64
-				$this->manager->invalidateTokenById($uid, $token->getId());
65
-			}
66
-		} catch (Throwable $e) {
67
-			$this->logger->error('Could not clean up auth tokens after user deletion: ' . $e->getMessage(), [
68
-				'exception' => $e,
69
-			]);
70
-		}
71
-	}
56
+        /**
57
+         * Catch any exception during this process as any failure here shouldn't block the
58
+         * user deletion.
59
+         */
60
+        try {
61
+            $uid = $event->getUser()->getUID();
62
+            $tokens = $this->manager->getTokenByUser($uid);
63
+            foreach ($tokens as $token) {
64
+                $this->manager->invalidateTokenById($uid, $token->getId());
65
+            }
66
+        } catch (Throwable $e) {
67
+            $this->logger->error('Could not clean up auth tokens after user deletion: ' . $e->getMessage(), [
68
+                'exception' => $e,
69
+            ]);
70
+        }
71
+    }
72 72
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -64,7 +64,7 @@
 block discarded – undo
64 64
 				$this->manager->invalidateTokenById($uid, $token->getId());
65 65
 			}
66 66
 		} catch (Throwable $e) {
67
-			$this->logger->error('Could not clean up auth tokens after user deletion: ' . $e->getMessage(), [
67
+			$this->logger->error('Could not clean up auth tokens after user deletion: '.$e->getMessage(), [
68 68
 				'exception' => $e,
69 69
 			]);
70 70
 		}
Please login to merge, or discard this patch.
lib/private/Authentication/Listeners/RemoteWipeActivityListener.php 1 patch
Indentation   +34 added lines, -34 removed lines patch added patch discarded remove patch
@@ -37,42 +37,42 @@
 block discarded – undo
37 37
 
38 38
 class RemoteWipeActivityListener implements IEventListener {
39 39
 
40
-	/** @var IActvityManager */
41
-	private $activityManager;
40
+    /** @var IActvityManager */
41
+    private $activityManager;
42 42
 
43
-	/** @var LoggerInterface */
44
-	private $logger;
43
+    /** @var LoggerInterface */
44
+    private $logger;
45 45
 
46
-	public function __construct(IActvityManager $activityManager,
47
-								LoggerInterface $logger) {
48
-		$this->activityManager = $activityManager;
49
-		$this->logger = $logger;
50
-	}
46
+    public function __construct(IActvityManager $activityManager,
47
+                                LoggerInterface $logger) {
48
+        $this->activityManager = $activityManager;
49
+        $this->logger = $logger;
50
+    }
51 51
 
52
-	public function handle(Event $event): void {
53
-		if ($event instanceof RemoteWipeStarted) {
54
-			$this->publishActivity('remote_wipe_start', $event->getToken());
55
-		} elseif ($event instanceof RemoteWipeFinished) {
56
-			$this->publishActivity('remote_wipe_finish', $event->getToken());
57
-		}
58
-	}
52
+    public function handle(Event $event): void {
53
+        if ($event instanceof RemoteWipeStarted) {
54
+            $this->publishActivity('remote_wipe_start', $event->getToken());
55
+        } elseif ($event instanceof RemoteWipeFinished) {
56
+            $this->publishActivity('remote_wipe_finish', $event->getToken());
57
+        }
58
+    }
59 59
 
60
-	private function publishActivity(string $event, IToken $token): void {
61
-		$activity = $this->activityManager->generateEvent();
62
-		$activity->setApp('core')
63
-			->setType('security')
64
-			->setAuthor($token->getUID())
65
-			->setAffectedUser($token->getUID())
66
-			->setSubject($event, [
67
-				'name' => $token->getName(),
68
-			]);
69
-		try {
70
-			$this->activityManager->publish($activity);
71
-		} catch (BadMethodCallException $e) {
72
-			$this->logger->warning('could not publish activity', [
73
-				'app' => 'core',
74
-				'exception' => $e,
75
-			]);
76
-		}
77
-	}
60
+    private function publishActivity(string $event, IToken $token): void {
61
+        $activity = $this->activityManager->generateEvent();
62
+        $activity->setApp('core')
63
+            ->setType('security')
64
+            ->setAuthor($token->getUID())
65
+            ->setAffectedUser($token->getUID())
66
+            ->setSubject($event, [
67
+                'name' => $token->getName(),
68
+            ]);
69
+        try {
70
+            $this->activityManager->publish($activity);
71
+        } catch (BadMethodCallException $e) {
72
+            $this->logger->warning('could not publish activity', [
73
+                'app' => 'core',
74
+                'exception' => $e,
75
+            ]);
76
+        }
77
+    }
78 78
 }
Please login to merge, or discard this patch.
lib/private/Authentication/TwoFactorAuth/Manager.php 1 patch
Indentation   +336 added lines, -336 removed lines patch added patch discarded remove patch
@@ -46,343 +46,343 @@
 block discarded – undo
46 46
 use function array_filter;
47 47
 
48 48
 class Manager {
49
-	public const SESSION_UID_KEY = 'two_factor_auth_uid';
50
-	public const SESSION_UID_DONE = 'two_factor_auth_passed';
51
-	public const REMEMBER_LOGIN = 'two_factor_remember_login';
52
-	public const BACKUP_CODES_PROVIDER_ID = 'backup_codes';
53
-
54
-	/** @var ProviderLoader */
55
-	private $providerLoader;
56
-
57
-	/** @var IRegistry */
58
-	private $providerRegistry;
59
-
60
-	/** @var MandatoryTwoFactor */
61
-	private $mandatoryTwoFactor;
62
-
63
-	/** @var ISession */
64
-	private $session;
65
-
66
-	/** @var IConfig */
67
-	private $config;
68
-
69
-	/** @var IManager */
70
-	private $activityManager;
71
-
72
-	/** @var LoggerInterface */
73
-	private $logger;
74
-
75
-	/** @var TokenProvider */
76
-	private $tokenProvider;
77
-
78
-	/** @var ITimeFactory */
79
-	private $timeFactory;
80
-
81
-	/** @var EventDispatcherInterface */
82
-	private $dispatcher;
83
-
84
-	public function __construct(ProviderLoader $providerLoader,
85
-								IRegistry $providerRegistry,
86
-								MandatoryTwoFactor $mandatoryTwoFactor,
87
-								ISession $session,
88
-								IConfig $config,
89
-								IManager $activityManager,
90
-								LoggerInterface $logger,
91
-								TokenProvider $tokenProvider,
92
-								ITimeFactory $timeFactory,
93
-								EventDispatcherInterface $eventDispatcher) {
94
-		$this->providerLoader = $providerLoader;
95
-		$this->providerRegistry = $providerRegistry;
96
-		$this->mandatoryTwoFactor = $mandatoryTwoFactor;
97
-		$this->session = $session;
98
-		$this->config = $config;
99
-		$this->activityManager = $activityManager;
100
-		$this->logger = $logger;
101
-		$this->tokenProvider = $tokenProvider;
102
-		$this->timeFactory = $timeFactory;
103
-		$this->dispatcher = $eventDispatcher;
104
-	}
105
-
106
-	/**
107
-	 * Determine whether the user must provide a second factor challenge
108
-	 *
109
-	 * @param IUser $user
110
-	 * @return boolean
111
-	 */
112
-	public function isTwoFactorAuthenticated(IUser $user): bool {
113
-		if ($this->mandatoryTwoFactor->isEnforcedFor($user)) {
114
-			return true;
115
-		}
116
-
117
-		$providerStates = $this->providerRegistry->getProviderStates($user);
118
-		$providers = $this->providerLoader->getProviders($user);
119
-		$fixedStates = $this->fixMissingProviderStates($providerStates, $providers, $user);
120
-		$enabled = array_filter($fixedStates);
121
-		$providerIds = array_keys($enabled);
122
-		$providerIdsWithoutBackupCodes = array_diff($providerIds, [self::BACKUP_CODES_PROVIDER_ID]);
123
-
124
-		return !empty($providerIdsWithoutBackupCodes);
125
-	}
126
-
127
-	/**
128
-	 * Get a 2FA provider by its ID
129
-	 *
130
-	 * @param IUser $user
131
-	 * @param string $challengeProviderId
132
-	 * @return IProvider|null
133
-	 */
134
-	public function getProvider(IUser $user, string $challengeProviderId) {
135
-		$providers = $this->getProviderSet($user)->getProviders();
136
-		return $providers[$challengeProviderId] ?? null;
137
-	}
138
-
139
-	/**
140
-	 * @param IUser $user
141
-	 * @return IActivatableAtLogin[]
142
-	 * @throws Exception
143
-	 */
144
-	public function getLoginSetupProviders(IUser $user): array {
145
-		$providers = $this->providerLoader->getProviders($user);
146
-		return array_filter($providers, function (IProvider $provider) {
147
-			return ($provider instanceof IActivatableAtLogin);
148
-		});
149
-	}
150
-
151
-	/**
152
-	 * Check if the persistant mapping of enabled/disabled state of each available
153
-	 * provider is missing an entry and add it to the registry in that case.
154
-	 *
155
-	 * @todo remove in Nextcloud 17 as by then all providers should have been updated
156
-	 *
157
-	 * @param string[] $providerStates
158
-	 * @param IProvider[] $providers
159
-	 * @param IUser $user
160
-	 * @return string[] the updated $providerStates variable
161
-	 */
162
-	private function fixMissingProviderStates(array $providerStates,
163
-		array $providers, IUser $user): array {
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', 'exception' => $e]);
303
-		}
304
-	}
305
-
306
-	/**
307
-	 * Check if the currently logged in user needs to pass 2FA
308
-	 *
309
-	 * @param IUser $user the currently logged in user
310
-	 * @return boolean
311
-	 */
312
-	public function needsSecondFactor(IUser $user = null): bool {
313
-		if ($user === null) {
314
-			return false;
315
-		}
316
-
317
-		// If we are authenticated using an app password skip all this
318
-		if ($this->session->exists('app_password')) {
319
-			return false;
320
-		}
321
-
322
-		// First check if the session tells us we should do 2FA (99% case)
323
-		if (!$this->session->exists(self::SESSION_UID_KEY)) {
324
-
325
-			// Check if the session tells us it is 2FA authenticated already
326
-			if ($this->session->exists(self::SESSION_UID_DONE) &&
327
-				$this->session->get(self::SESSION_UID_DONE) === $user->getUID()) {
328
-				return false;
329
-			}
330
-
331
-			/*
49
+    public const SESSION_UID_KEY = 'two_factor_auth_uid';
50
+    public const SESSION_UID_DONE = 'two_factor_auth_passed';
51
+    public const REMEMBER_LOGIN = 'two_factor_remember_login';
52
+    public const BACKUP_CODES_PROVIDER_ID = 'backup_codes';
53
+
54
+    /** @var ProviderLoader */
55
+    private $providerLoader;
56
+
57
+    /** @var IRegistry */
58
+    private $providerRegistry;
59
+
60
+    /** @var MandatoryTwoFactor */
61
+    private $mandatoryTwoFactor;
62
+
63
+    /** @var ISession */
64
+    private $session;
65
+
66
+    /** @var IConfig */
67
+    private $config;
68
+
69
+    /** @var IManager */
70
+    private $activityManager;
71
+
72
+    /** @var LoggerInterface */
73
+    private $logger;
74
+
75
+    /** @var TokenProvider */
76
+    private $tokenProvider;
77
+
78
+    /** @var ITimeFactory */
79
+    private $timeFactory;
80
+
81
+    /** @var EventDispatcherInterface */
82
+    private $dispatcher;
83
+
84
+    public function __construct(ProviderLoader $providerLoader,
85
+                                IRegistry $providerRegistry,
86
+                                MandatoryTwoFactor $mandatoryTwoFactor,
87
+                                ISession $session,
88
+                                IConfig $config,
89
+                                IManager $activityManager,
90
+                                LoggerInterface $logger,
91
+                                TokenProvider $tokenProvider,
92
+                                ITimeFactory $timeFactory,
93
+                                EventDispatcherInterface $eventDispatcher) {
94
+        $this->providerLoader = $providerLoader;
95
+        $this->providerRegistry = $providerRegistry;
96
+        $this->mandatoryTwoFactor = $mandatoryTwoFactor;
97
+        $this->session = $session;
98
+        $this->config = $config;
99
+        $this->activityManager = $activityManager;
100
+        $this->logger = $logger;
101
+        $this->tokenProvider = $tokenProvider;
102
+        $this->timeFactory = $timeFactory;
103
+        $this->dispatcher = $eventDispatcher;
104
+    }
105
+
106
+    /**
107
+     * Determine whether the user must provide a second factor challenge
108
+     *
109
+     * @param IUser $user
110
+     * @return boolean
111
+     */
112
+    public function isTwoFactorAuthenticated(IUser $user): bool {
113
+        if ($this->mandatoryTwoFactor->isEnforcedFor($user)) {
114
+            return true;
115
+        }
116
+
117
+        $providerStates = $this->providerRegistry->getProviderStates($user);
118
+        $providers = $this->providerLoader->getProviders($user);
119
+        $fixedStates = $this->fixMissingProviderStates($providerStates, $providers, $user);
120
+        $enabled = array_filter($fixedStates);
121
+        $providerIds = array_keys($enabled);
122
+        $providerIdsWithoutBackupCodes = array_diff($providerIds, [self::BACKUP_CODES_PROVIDER_ID]);
123
+
124
+        return !empty($providerIdsWithoutBackupCodes);
125
+    }
126
+
127
+    /**
128
+     * Get a 2FA provider by its ID
129
+     *
130
+     * @param IUser $user
131
+     * @param string $challengeProviderId
132
+     * @return IProvider|null
133
+     */
134
+    public function getProvider(IUser $user, string $challengeProviderId) {
135
+        $providers = $this->getProviderSet($user)->getProviders();
136
+        return $providers[$challengeProviderId] ?? null;
137
+    }
138
+
139
+    /**
140
+     * @param IUser $user
141
+     * @return IActivatableAtLogin[]
142
+     * @throws Exception
143
+     */
144
+    public function getLoginSetupProviders(IUser $user): array {
145
+        $providers = $this->providerLoader->getProviders($user);
146
+        return array_filter($providers, function (IProvider $provider) {
147
+            return ($provider instanceof IActivatableAtLogin);
148
+        });
149
+    }
150
+
151
+    /**
152
+     * Check if the persistant mapping of enabled/disabled state of each available
153
+     * provider is missing an entry and add it to the registry in that case.
154
+     *
155
+     * @todo remove in Nextcloud 17 as by then all providers should have been updated
156
+     *
157
+     * @param string[] $providerStates
158
+     * @param IProvider[] $providers
159
+     * @param IUser $user
160
+     * @return string[] the updated $providerStates variable
161
+     */
162
+    private function fixMissingProviderStates(array $providerStates,
163
+        array $providers, IUser $user): array {
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', 'exception' => $e]);
303
+        }
304
+    }
305
+
306
+    /**
307
+     * Check if the currently logged in user needs to pass 2FA
308
+     *
309
+     * @param IUser $user the currently logged in user
310
+     * @return boolean
311
+     */
312
+    public function needsSecondFactor(IUser $user = null): bool {
313
+        if ($user === null) {
314
+            return false;
315
+        }
316
+
317
+        // If we are authenticated using an app password skip all this
318
+        if ($this->session->exists('app_password')) {
319
+            return false;
320
+        }
321
+
322
+        // First check if the session tells us we should do 2FA (99% case)
323
+        if (!$this->session->exists(self::SESSION_UID_KEY)) {
324
+
325
+            // Check if the session tells us it is 2FA authenticated already
326
+            if ($this->session->exists(self::SESSION_UID_DONE) &&
327
+                $this->session->get(self::SESSION_UID_DONE) === $user->getUID()) {
328
+                return false;
329
+            }
330
+
331
+            /*
332 332
 			 * If the session is expired check if we are not logged in by a token
333 333
 			 * that still needs 2FA auth
334 334
 			 */
335
-			try {
336
-				$sessionId = $this->session->getId();
337
-				$token = $this->tokenProvider->getToken($sessionId);
338
-				$tokenId = $token->getId();
339
-				$tokensNeeding2FA = $this->config->getUserKeys($user->getUID(), 'login_token_2fa');
340
-
341
-				if (!\in_array($tokenId, $tokensNeeding2FA, true)) {
342
-					$this->session->set(self::SESSION_UID_DONE, $user->getUID());
343
-					return false;
344
-				}
345
-			} catch (InvalidTokenException $e) {
346
-			}
347
-		}
348
-
349
-		if (!$this->isTwoFactorAuthenticated($user)) {
350
-			// There is no second factor any more -> let the user pass
351
-			//   This prevents infinite redirect loops when a user is about
352
-			//   to solve the 2FA challenge, and the provider app is
353
-			//   disabled the same time
354
-			$this->session->remove(self::SESSION_UID_KEY);
355
-
356
-			$keys = $this->config->getUserKeys($user->getUID(), 'login_token_2fa');
357
-			foreach ($keys as $key) {
358
-				$this->config->deleteUserValue($user->getUID(), 'login_token_2fa', $key);
359
-			}
360
-			return false;
361
-		}
362
-
363
-		return true;
364
-	}
365
-
366
-	/**
367
-	 * Prepare the 2FA login
368
-	 *
369
-	 * @param IUser $user
370
-	 * @param boolean $rememberMe
371
-	 */
372
-	public function prepareTwoFactorLogin(IUser $user, bool $rememberMe) {
373
-		$this->session->set(self::SESSION_UID_KEY, $user->getUID());
374
-		$this->session->set(self::REMEMBER_LOGIN, $rememberMe);
375
-
376
-		$id = $this->session->getId();
377
-		$token = $this->tokenProvider->getToken($id);
378
-		$this->config->setUserValue($user->getUID(), 'login_token_2fa', $token->getId(), $this->timeFactory->getTime());
379
-	}
380
-
381
-	public function clearTwoFactorPending(string $userId) {
382
-		$tokensNeeding2FA = $this->config->getUserKeys($userId, 'login_token_2fa');
383
-
384
-		foreach ($tokensNeeding2FA as $tokenId) {
385
-			$this->tokenProvider->invalidateTokenById($userId, $tokenId);
386
-		}
387
-	}
335
+            try {
336
+                $sessionId = $this->session->getId();
337
+                $token = $this->tokenProvider->getToken($sessionId);
338
+                $tokenId = $token->getId();
339
+                $tokensNeeding2FA = $this->config->getUserKeys($user->getUID(), 'login_token_2fa');
340
+
341
+                if (!\in_array($tokenId, $tokensNeeding2FA, true)) {
342
+                    $this->session->set(self::SESSION_UID_DONE, $user->getUID());
343
+                    return false;
344
+                }
345
+            } catch (InvalidTokenException $e) {
346
+            }
347
+        }
348
+
349
+        if (!$this->isTwoFactorAuthenticated($user)) {
350
+            // There is no second factor any more -> let the user pass
351
+            //   This prevents infinite redirect loops when a user is about
352
+            //   to solve the 2FA challenge, and the provider app is
353
+            //   disabled the same time
354
+            $this->session->remove(self::SESSION_UID_KEY);
355
+
356
+            $keys = $this->config->getUserKeys($user->getUID(), 'login_token_2fa');
357
+            foreach ($keys as $key) {
358
+                $this->config->deleteUserValue($user->getUID(), 'login_token_2fa', $key);
359
+            }
360
+            return false;
361
+        }
362
+
363
+        return true;
364
+    }
365
+
366
+    /**
367
+     * Prepare the 2FA login
368
+     *
369
+     * @param IUser $user
370
+     * @param boolean $rememberMe
371
+     */
372
+    public function prepareTwoFactorLogin(IUser $user, bool $rememberMe) {
373
+        $this->session->set(self::SESSION_UID_KEY, $user->getUID());
374
+        $this->session->set(self::REMEMBER_LOGIN, $rememberMe);
375
+
376
+        $id = $this->session->getId();
377
+        $token = $this->tokenProvider->getToken($id);
378
+        $this->config->setUserValue($user->getUID(), 'login_token_2fa', $token->getId(), $this->timeFactory->getTime());
379
+    }
380
+
381
+    public function clearTwoFactorPending(string $userId) {
382
+        $tokensNeeding2FA = $this->config->getUserKeys($userId, 'login_token_2fa');
383
+
384
+        foreach ($tokensNeeding2FA as $tokenId) {
385
+            $this->tokenProvider->invalidateTokenById($userId, $tokenId);
386
+        }
387
+    }
388 388
 }
Please login to merge, or discard this patch.
lib/private/Authentication/Token/DefaultTokenProvider.php 1 patch
Indentation   +306 added lines, -306 removed lines patch added patch discarded remove patch
@@ -44,310 +44,310 @@
 block discarded – undo
44 44
 
45 45
 class DefaultTokenProvider implements IProvider {
46 46
 
47
-	/** @var DefaultTokenMapper */
48
-	private $mapper;
49
-
50
-	/** @var ICrypto */
51
-	private $crypto;
52
-
53
-	/** @var IConfig */
54
-	private $config;
55
-
56
-	/** @var LoggerInterface */
57
-	private $logger;
58
-
59
-	/** @var ITimeFactory */
60
-	private $time;
61
-
62
-	public function __construct(DefaultTokenMapper $mapper,
63
-								ICrypto $crypto,
64
-								IConfig $config,
65
-								LoggerInterface $logger,
66
-								ITimeFactory $time) {
67
-		$this->mapper = $mapper;
68
-		$this->crypto = $crypto;
69
-		$this->config = $config;
70
-		$this->logger = $logger;
71
-		$this->time = $time;
72
-	}
73
-
74
-	/**
75
-	 * Create and persist a new token
76
-	 *
77
-	 * @param string $token
78
-	 * @param string $uid
79
-	 * @param string $loginName
80
-	 * @param string|null $password
81
-	 * @param string $name
82
-	 * @param int $type token type
83
-	 * @param int $remember whether the session token should be used for remember-me
84
-	 * @return IToken
85
-	 */
86
-	public function generateToken(string $token,
87
-								  string $uid,
88
-								  string $loginName,
89
-								  $password,
90
-								  string $name,
91
-								  int $type = IToken::TEMPORARY_TOKEN,
92
-								  int $remember = IToken::DO_NOT_REMEMBER): IToken {
93
-		$dbToken = new DefaultToken();
94
-		$dbToken->setUid($uid);
95
-		$dbToken->setLoginName($loginName);
96
-		if (!is_null($password)) {
97
-			$dbToken->setPassword($this->encryptPassword($password, $token));
98
-		}
99
-		$dbToken->setName($name);
100
-		$dbToken->setToken($this->hashToken($token));
101
-		$dbToken->setType($type);
102
-		$dbToken->setRemember($remember);
103
-		$dbToken->setLastActivity($this->time->getTime());
104
-		$dbToken->setLastCheck($this->time->getTime());
105
-		$dbToken->setVersion(DefaultToken::VERSION);
106
-
107
-		$this->mapper->insert($dbToken);
108
-
109
-		return $dbToken;
110
-	}
111
-
112
-	/**
113
-	 * Save the updated token
114
-	 *
115
-	 * @param IToken $token
116
-	 * @throws InvalidTokenException
117
-	 */
118
-	public function updateToken(IToken $token) {
119
-		if (!($token instanceof DefaultToken)) {
120
-			throw new InvalidTokenException("Invalid token type");
121
-		}
122
-		$this->mapper->update($token);
123
-	}
124
-
125
-	/**
126
-	 * Update token activity timestamp
127
-	 *
128
-	 * @throws InvalidTokenException
129
-	 * @param IToken $token
130
-	 */
131
-	public function updateTokenActivity(IToken $token) {
132
-		if (!($token instanceof DefaultToken)) {
133
-			throw new InvalidTokenException("Invalid token type");
134
-		}
135
-		/** @var DefaultToken $token */
136
-		$now = $this->time->getTime();
137
-		if ($token->getLastActivity() < ($now - 60)) {
138
-			// Update token only once per minute
139
-			$token->setLastActivity($now);
140
-			$this->mapper->update($token);
141
-		}
142
-	}
143
-
144
-	public function getTokenByUser(string $uid): array {
145
-		return $this->mapper->getTokenByUser($uid);
146
-	}
147
-
148
-	/**
149
-	 * Get a token by token
150
-	 *
151
-	 * @param string $tokenId
152
-	 * @throws InvalidTokenException
153
-	 * @throws ExpiredTokenException
154
-	 * @return IToken
155
-	 */
156
-	public function getToken(string $tokenId): IToken {
157
-		try {
158
-			$token = $this->mapper->getToken($this->hashToken($tokenId));
159
-		} catch (DoesNotExistException $ex) {
160
-			throw new InvalidTokenException("Token does not exist", 0, $ex);
161
-		}
162
-
163
-		if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) {
164
-			throw new ExpiredTokenException($token);
165
-		}
166
-
167
-		return $token;
168
-	}
169
-
170
-	/**
171
-	 * Get a token by token id
172
-	 *
173
-	 * @param int $tokenId
174
-	 * @throws InvalidTokenException
175
-	 * @throws ExpiredTokenException
176
-	 * @return IToken
177
-	 */
178
-	public function getTokenById(int $tokenId): IToken {
179
-		try {
180
-			$token = $this->mapper->getTokenById($tokenId);
181
-		} catch (DoesNotExistException $ex) {
182
-			throw new InvalidTokenException("Token with ID $tokenId does not exist", 0, $ex);
183
-		}
184
-
185
-		if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) {
186
-			throw new ExpiredTokenException($token);
187
-		}
188
-
189
-		return $token;
190
-	}
191
-
192
-	/**
193
-	 * @param string $oldSessionId
194
-	 * @param string $sessionId
195
-	 * @throws InvalidTokenException
196
-	 * @return IToken
197
-	 */
198
-	public function renewSessionToken(string $oldSessionId, string $sessionId): IToken {
199
-		$token = $this->getToken($oldSessionId);
200
-
201
-		$newToken = new DefaultToken();
202
-		$newToken->setUid($token->getUID());
203
-		$newToken->setLoginName($token->getLoginName());
204
-		if (!is_null($token->getPassword())) {
205
-			$password = $this->decryptPassword($token->getPassword(), $oldSessionId);
206
-			$newToken->setPassword($this->encryptPassword($password, $sessionId));
207
-		}
208
-		$newToken->setName($token->getName());
209
-		$newToken->setToken($this->hashToken($sessionId));
210
-		$newToken->setType(IToken::TEMPORARY_TOKEN);
211
-		$newToken->setRemember($token->getRemember());
212
-		$newToken->setLastActivity($this->time->getTime());
213
-		$this->mapper->insert($newToken);
214
-		$this->mapper->delete($token);
215
-
216
-		return $newToken;
217
-	}
218
-
219
-	/**
220
-	 * @param IToken $savedToken
221
-	 * @param string $tokenId session token
222
-	 * @throws InvalidTokenException
223
-	 * @throws PasswordlessTokenException
224
-	 * @return string
225
-	 */
226
-	public function getPassword(IToken $savedToken, string $tokenId): string {
227
-		$password = $savedToken->getPassword();
228
-		if (is_null($password)) {
229
-			throw new PasswordlessTokenException();
230
-		}
231
-		return $this->decryptPassword($password, $tokenId);
232
-	}
233
-
234
-	/**
235
-	 * Encrypt and set the password of the given token
236
-	 *
237
-	 * @param IToken $token
238
-	 * @param string $tokenId
239
-	 * @param string $password
240
-	 * @throws InvalidTokenException
241
-	 */
242
-	public function setPassword(IToken $token, string $tokenId, string $password) {
243
-		if (!($token instanceof DefaultToken)) {
244
-			throw new InvalidTokenException("Invalid token type");
245
-		}
246
-		/** @var DefaultToken $token */
247
-		$token->setPassword($this->encryptPassword($password, $tokenId));
248
-		$this->mapper->update($token);
249
-	}
250
-
251
-	/**
252
-	 * Invalidate (delete) the given session token
253
-	 *
254
-	 * @param string $token
255
-	 */
256
-	public function invalidateToken(string $token) {
257
-		$this->mapper->invalidate($this->hashToken($token));
258
-	}
259
-
260
-	public function invalidateTokenById(string $uid, int $id) {
261
-		$this->mapper->deleteById($uid, $id);
262
-	}
263
-
264
-	/**
265
-	 * Invalidate (delete) old session tokens
266
-	 */
267
-	public function invalidateOldTokens() {
268
-		$olderThan = $this->time->getTime() - (int) $this->config->getSystemValue('session_lifetime', 60 * 60 * 24);
269
-		$this->logger->debug('Invalidating session tokens older than ' . date('c', $olderThan), ['app' => 'cron']);
270
-		$this->mapper->invalidateOld($olderThan, IToken::DO_NOT_REMEMBER);
271
-		$rememberThreshold = $this->time->getTime() - (int) $this->config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15);
272
-		$this->logger->debug('Invalidating remembered session tokens older than ' . date('c', $rememberThreshold), ['app' => 'cron']);
273
-		$this->mapper->invalidateOld($rememberThreshold, IToken::REMEMBER);
274
-	}
275
-
276
-	/**
277
-	 * Rotate the token. Usefull for for example oauth tokens
278
-	 *
279
-	 * @param IToken $token
280
-	 * @param string $oldTokenId
281
-	 * @param string $newTokenId
282
-	 * @return IToken
283
-	 */
284
-	public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken {
285
-		try {
286
-			$password = $this->getPassword($token, $oldTokenId);
287
-			$token->setPassword($this->encryptPassword($password, $newTokenId));
288
-		} catch (PasswordlessTokenException $e) {
289
-		}
290
-
291
-		$token->setToken($this->hashToken($newTokenId));
292
-		$this->updateToken($token);
293
-
294
-		return $token;
295
-	}
296
-
297
-	/**
298
-	 * @param string $token
299
-	 * @return string
300
-	 */
301
-	private function hashToken(string $token): string {
302
-		$secret = $this->config->getSystemValue('secret');
303
-		return hash('sha512', $token . $secret);
304
-	}
305
-
306
-	/**
307
-	 * Encrypt the given password
308
-	 *
309
-	 * The token is used as key
310
-	 *
311
-	 * @param string $password
312
-	 * @param string $token
313
-	 * @return string encrypted password
314
-	 */
315
-	private function encryptPassword(string $password, string $token): string {
316
-		$secret = $this->config->getSystemValue('secret');
317
-		return $this->crypto->encrypt($password, $token . $secret);
318
-	}
319
-
320
-	/**
321
-	 * Decrypt the given password
322
-	 *
323
-	 * The token is used as key
324
-	 *
325
-	 * @param string $password
326
-	 * @param string $token
327
-	 * @throws InvalidTokenException
328
-	 * @return string the decrypted key
329
-	 */
330
-	private function decryptPassword(string $password, string $token): string {
331
-		$secret = $this->config->getSystemValue('secret');
332
-		try {
333
-			return $this->crypto->decrypt($password, $token . $secret);
334
-		} catch (Exception $ex) {
335
-			// Delete the invalid token
336
-			$this->invalidateToken($token);
337
-			throw new InvalidTokenException("Can not decrypt token password: " . $ex->getMessage(), 0, $ex);
338
-		}
339
-	}
340
-
341
-	public function markPasswordInvalid(IToken $token, string $tokenId) {
342
-		if (!($token instanceof DefaultToken)) {
343
-			throw new InvalidTokenException("Invalid token type");
344
-		}
345
-
346
-		//No need to mark as invalid. We just invalide default tokens
347
-		$this->invalidateToken($tokenId);
348
-	}
349
-
350
-	public function updatePasswords(string $uid, string $password) {
351
-		// Nothing to do here
352
-	}
47
+    /** @var DefaultTokenMapper */
48
+    private $mapper;
49
+
50
+    /** @var ICrypto */
51
+    private $crypto;
52
+
53
+    /** @var IConfig */
54
+    private $config;
55
+
56
+    /** @var LoggerInterface */
57
+    private $logger;
58
+
59
+    /** @var ITimeFactory */
60
+    private $time;
61
+
62
+    public function __construct(DefaultTokenMapper $mapper,
63
+                                ICrypto $crypto,
64
+                                IConfig $config,
65
+                                LoggerInterface $logger,
66
+                                ITimeFactory $time) {
67
+        $this->mapper = $mapper;
68
+        $this->crypto = $crypto;
69
+        $this->config = $config;
70
+        $this->logger = $logger;
71
+        $this->time = $time;
72
+    }
73
+
74
+    /**
75
+     * Create and persist a new token
76
+     *
77
+     * @param string $token
78
+     * @param string $uid
79
+     * @param string $loginName
80
+     * @param string|null $password
81
+     * @param string $name
82
+     * @param int $type token type
83
+     * @param int $remember whether the session token should be used for remember-me
84
+     * @return IToken
85
+     */
86
+    public function generateToken(string $token,
87
+                                    string $uid,
88
+                                    string $loginName,
89
+                                    $password,
90
+                                    string $name,
91
+                                    int $type = IToken::TEMPORARY_TOKEN,
92
+                                    int $remember = IToken::DO_NOT_REMEMBER): IToken {
93
+        $dbToken = new DefaultToken();
94
+        $dbToken->setUid($uid);
95
+        $dbToken->setLoginName($loginName);
96
+        if (!is_null($password)) {
97
+            $dbToken->setPassword($this->encryptPassword($password, $token));
98
+        }
99
+        $dbToken->setName($name);
100
+        $dbToken->setToken($this->hashToken($token));
101
+        $dbToken->setType($type);
102
+        $dbToken->setRemember($remember);
103
+        $dbToken->setLastActivity($this->time->getTime());
104
+        $dbToken->setLastCheck($this->time->getTime());
105
+        $dbToken->setVersion(DefaultToken::VERSION);
106
+
107
+        $this->mapper->insert($dbToken);
108
+
109
+        return $dbToken;
110
+    }
111
+
112
+    /**
113
+     * Save the updated token
114
+     *
115
+     * @param IToken $token
116
+     * @throws InvalidTokenException
117
+     */
118
+    public function updateToken(IToken $token) {
119
+        if (!($token instanceof DefaultToken)) {
120
+            throw new InvalidTokenException("Invalid token type");
121
+        }
122
+        $this->mapper->update($token);
123
+    }
124
+
125
+    /**
126
+     * Update token activity timestamp
127
+     *
128
+     * @throws InvalidTokenException
129
+     * @param IToken $token
130
+     */
131
+    public function updateTokenActivity(IToken $token) {
132
+        if (!($token instanceof DefaultToken)) {
133
+            throw new InvalidTokenException("Invalid token type");
134
+        }
135
+        /** @var DefaultToken $token */
136
+        $now = $this->time->getTime();
137
+        if ($token->getLastActivity() < ($now - 60)) {
138
+            // Update token only once per minute
139
+            $token->setLastActivity($now);
140
+            $this->mapper->update($token);
141
+        }
142
+    }
143
+
144
+    public function getTokenByUser(string $uid): array {
145
+        return $this->mapper->getTokenByUser($uid);
146
+    }
147
+
148
+    /**
149
+     * Get a token by token
150
+     *
151
+     * @param string $tokenId
152
+     * @throws InvalidTokenException
153
+     * @throws ExpiredTokenException
154
+     * @return IToken
155
+     */
156
+    public function getToken(string $tokenId): IToken {
157
+        try {
158
+            $token = $this->mapper->getToken($this->hashToken($tokenId));
159
+        } catch (DoesNotExistException $ex) {
160
+            throw new InvalidTokenException("Token does not exist", 0, $ex);
161
+        }
162
+
163
+        if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) {
164
+            throw new ExpiredTokenException($token);
165
+        }
166
+
167
+        return $token;
168
+    }
169
+
170
+    /**
171
+     * Get a token by token id
172
+     *
173
+     * @param int $tokenId
174
+     * @throws InvalidTokenException
175
+     * @throws ExpiredTokenException
176
+     * @return IToken
177
+     */
178
+    public function getTokenById(int $tokenId): IToken {
179
+        try {
180
+            $token = $this->mapper->getTokenById($tokenId);
181
+        } catch (DoesNotExistException $ex) {
182
+            throw new InvalidTokenException("Token with ID $tokenId does not exist", 0, $ex);
183
+        }
184
+
185
+        if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) {
186
+            throw new ExpiredTokenException($token);
187
+        }
188
+
189
+        return $token;
190
+    }
191
+
192
+    /**
193
+     * @param string $oldSessionId
194
+     * @param string $sessionId
195
+     * @throws InvalidTokenException
196
+     * @return IToken
197
+     */
198
+    public function renewSessionToken(string $oldSessionId, string $sessionId): IToken {
199
+        $token = $this->getToken($oldSessionId);
200
+
201
+        $newToken = new DefaultToken();
202
+        $newToken->setUid($token->getUID());
203
+        $newToken->setLoginName($token->getLoginName());
204
+        if (!is_null($token->getPassword())) {
205
+            $password = $this->decryptPassword($token->getPassword(), $oldSessionId);
206
+            $newToken->setPassword($this->encryptPassword($password, $sessionId));
207
+        }
208
+        $newToken->setName($token->getName());
209
+        $newToken->setToken($this->hashToken($sessionId));
210
+        $newToken->setType(IToken::TEMPORARY_TOKEN);
211
+        $newToken->setRemember($token->getRemember());
212
+        $newToken->setLastActivity($this->time->getTime());
213
+        $this->mapper->insert($newToken);
214
+        $this->mapper->delete($token);
215
+
216
+        return $newToken;
217
+    }
218
+
219
+    /**
220
+     * @param IToken $savedToken
221
+     * @param string $tokenId session token
222
+     * @throws InvalidTokenException
223
+     * @throws PasswordlessTokenException
224
+     * @return string
225
+     */
226
+    public function getPassword(IToken $savedToken, string $tokenId): string {
227
+        $password = $savedToken->getPassword();
228
+        if (is_null($password)) {
229
+            throw new PasswordlessTokenException();
230
+        }
231
+        return $this->decryptPassword($password, $tokenId);
232
+    }
233
+
234
+    /**
235
+     * Encrypt and set the password of the given token
236
+     *
237
+     * @param IToken $token
238
+     * @param string $tokenId
239
+     * @param string $password
240
+     * @throws InvalidTokenException
241
+     */
242
+    public function setPassword(IToken $token, string $tokenId, string $password) {
243
+        if (!($token instanceof DefaultToken)) {
244
+            throw new InvalidTokenException("Invalid token type");
245
+        }
246
+        /** @var DefaultToken $token */
247
+        $token->setPassword($this->encryptPassword($password, $tokenId));
248
+        $this->mapper->update($token);
249
+    }
250
+
251
+    /**
252
+     * Invalidate (delete) the given session token
253
+     *
254
+     * @param string $token
255
+     */
256
+    public function invalidateToken(string $token) {
257
+        $this->mapper->invalidate($this->hashToken($token));
258
+    }
259
+
260
+    public function invalidateTokenById(string $uid, int $id) {
261
+        $this->mapper->deleteById($uid, $id);
262
+    }
263
+
264
+    /**
265
+     * Invalidate (delete) old session tokens
266
+     */
267
+    public function invalidateOldTokens() {
268
+        $olderThan = $this->time->getTime() - (int) $this->config->getSystemValue('session_lifetime', 60 * 60 * 24);
269
+        $this->logger->debug('Invalidating session tokens older than ' . date('c', $olderThan), ['app' => 'cron']);
270
+        $this->mapper->invalidateOld($olderThan, IToken::DO_NOT_REMEMBER);
271
+        $rememberThreshold = $this->time->getTime() - (int) $this->config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15);
272
+        $this->logger->debug('Invalidating remembered session tokens older than ' . date('c', $rememberThreshold), ['app' => 'cron']);
273
+        $this->mapper->invalidateOld($rememberThreshold, IToken::REMEMBER);
274
+    }
275
+
276
+    /**
277
+     * Rotate the token. Usefull for for example oauth tokens
278
+     *
279
+     * @param IToken $token
280
+     * @param string $oldTokenId
281
+     * @param string $newTokenId
282
+     * @return IToken
283
+     */
284
+    public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken {
285
+        try {
286
+            $password = $this->getPassword($token, $oldTokenId);
287
+            $token->setPassword($this->encryptPassword($password, $newTokenId));
288
+        } catch (PasswordlessTokenException $e) {
289
+        }
290
+
291
+        $token->setToken($this->hashToken($newTokenId));
292
+        $this->updateToken($token);
293
+
294
+        return $token;
295
+    }
296
+
297
+    /**
298
+     * @param string $token
299
+     * @return string
300
+     */
301
+    private function hashToken(string $token): string {
302
+        $secret = $this->config->getSystemValue('secret');
303
+        return hash('sha512', $token . $secret);
304
+    }
305
+
306
+    /**
307
+     * Encrypt the given password
308
+     *
309
+     * The token is used as key
310
+     *
311
+     * @param string $password
312
+     * @param string $token
313
+     * @return string encrypted password
314
+     */
315
+    private function encryptPassword(string $password, string $token): string {
316
+        $secret = $this->config->getSystemValue('secret');
317
+        return $this->crypto->encrypt($password, $token . $secret);
318
+    }
319
+
320
+    /**
321
+     * Decrypt the given password
322
+     *
323
+     * The token is used as key
324
+     *
325
+     * @param string $password
326
+     * @param string $token
327
+     * @throws InvalidTokenException
328
+     * @return string the decrypted key
329
+     */
330
+    private function decryptPassword(string $password, string $token): string {
331
+        $secret = $this->config->getSystemValue('secret');
332
+        try {
333
+            return $this->crypto->decrypt($password, $token . $secret);
334
+        } catch (Exception $ex) {
335
+            // Delete the invalid token
336
+            $this->invalidateToken($token);
337
+            throw new InvalidTokenException("Can not decrypt token password: " . $ex->getMessage(), 0, $ex);
338
+        }
339
+    }
340
+
341
+    public function markPasswordInvalid(IToken $token, string $tokenId) {
342
+        if (!($token instanceof DefaultToken)) {
343
+            throw new InvalidTokenException("Invalid token type");
344
+        }
345
+
346
+        //No need to mark as invalid. We just invalide default tokens
347
+        $this->invalidateToken($tokenId);
348
+    }
349
+
350
+    public function updatePasswords(string $uid, string $password) {
351
+        // Nothing to do here
352
+    }
353 353
 }
Please login to merge, or discard this patch.