Passed
Push — master ( bed972...c9ea23 )
by Joas
15:02 queued 16s
created
core/Controller/AppPasswordController.php 1 patch
Indentation   +123 added lines, -123 removed lines patch added patch discarded remove patch
@@ -43,127 +43,127 @@
 block discarded – undo
43 43
 
44 44
 class AppPasswordController extends \OCP\AppFramework\OCSController {
45 45
 
46
-	/** @var ISession */
47
-	private $session;
48
-
49
-	/** @var ISecureRandom */
50
-	private $random;
51
-
52
-	/** @var IProvider */
53
-	private $tokenProvider;
54
-
55
-	/** @var IStore */
56
-	private $credentialStore;
57
-
58
-	/** @var IEventDispatcher */
59
-	private $eventDispatcher;
60
-
61
-	public function __construct(string $appName,
62
-								IRequest $request,
63
-								ISession $session,
64
-								ISecureRandom $random,
65
-								IProvider $tokenProvider,
66
-								IStore $credentialStore,
67
-								IEventDispatcher $eventDispatcher) {
68
-		parent::__construct($appName, $request);
69
-
70
-		$this->session = $session;
71
-		$this->random = $random;
72
-		$this->tokenProvider = $tokenProvider;
73
-		$this->credentialStore = $credentialStore;
74
-		$this->eventDispatcher = $eventDispatcher;
75
-	}
76
-
77
-	/**
78
-	 * @NoAdminRequired
79
-	 *
80
-	 * @return DataResponse
81
-	 * @throws OCSForbiddenException
82
-	 */
83
-	public function getAppPassword(): DataResponse {
84
-		// We do not allow the creation of new tokens if this is an app password
85
-		if ($this->session->exists('app_password')) {
86
-			throw new OCSForbiddenException('You cannot request an new apppassword with an apppassword');
87
-		}
88
-
89
-		try {
90
-			$credentials = $this->credentialStore->getLoginCredentials();
91
-		} catch (CredentialsUnavailableException $e) {
92
-			throw new OCSForbiddenException();
93
-		}
94
-
95
-		try {
96
-			$password = $credentials->getPassword();
97
-		} catch (PasswordUnavailableException $e) {
98
-			$password = null;
99
-		}
100
-
101
-		$userAgent = $this->request->getHeader('USER_AGENT');
102
-
103
-		$token = $this->random->generate(72, ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_DIGITS);
104
-
105
-		$generatedToken = $this->tokenProvider->generateToken(
106
-			$token,
107
-			$credentials->getUID(),
108
-			$credentials->getLoginName(),
109
-			$password,
110
-			$userAgent,
111
-			IToken::PERMANENT_TOKEN,
112
-			IToken::DO_NOT_REMEMBER
113
-		);
114
-
115
-		$this->eventDispatcher->dispatchTyped(
116
-			new AppPasswordCreatedEvent($generatedToken)
117
-		);
118
-
119
-		return new DataResponse([
120
-			'apppassword' => $token
121
-		]);
122
-	}
123
-
124
-	/**
125
-	 * @NoAdminRequired
126
-	 *
127
-	 * @return DataResponse
128
-	 */
129
-	public function deleteAppPassword() {
130
-		if (!$this->session->exists('app_password')) {
131
-			throw new OCSForbiddenException('no app password in use');
132
-		}
133
-
134
-		$appPassword = $this->session->get('app_password');
135
-
136
-		try {
137
-			$token = $this->tokenProvider->getToken($appPassword);
138
-		} catch (InvalidTokenException $e) {
139
-			throw new OCSForbiddenException('could not remove apptoken');
140
-		}
141
-
142
-		$this->tokenProvider->invalidateTokenById($token->getUID(), $token->getId());
143
-		return new DataResponse();
144
-	}
145
-
146
-	/**
147
-	 * @NoAdminRequired
148
-	 */
149
-	public function rotateAppPassword(): DataResponse {
150
-		if (!$this->session->exists('app_password')) {
151
-			throw new OCSForbiddenException('no app password in use');
152
-		}
153
-
154
-		$appPassword = $this->session->get('app_password');
155
-
156
-		try {
157
-			$token = $this->tokenProvider->getToken($appPassword);
158
-		} catch (InvalidTokenException $e) {
159
-			throw new OCSForbiddenException('could not rotate apptoken');
160
-		}
161
-
162
-		$newToken = $this->random->generate(72, ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_DIGITS);
163
-		$this->tokenProvider->rotate($token, $appPassword, $newToken);
164
-
165
-		return new DataResponse([
166
-			'apppassword' => $newToken,
167
-		]);
168
-	}
46
+    /** @var ISession */
47
+    private $session;
48
+
49
+    /** @var ISecureRandom */
50
+    private $random;
51
+
52
+    /** @var IProvider */
53
+    private $tokenProvider;
54
+
55
+    /** @var IStore */
56
+    private $credentialStore;
57
+
58
+    /** @var IEventDispatcher */
59
+    private $eventDispatcher;
60
+
61
+    public function __construct(string $appName,
62
+                                IRequest $request,
63
+                                ISession $session,
64
+                                ISecureRandom $random,
65
+                                IProvider $tokenProvider,
66
+                                IStore $credentialStore,
67
+                                IEventDispatcher $eventDispatcher) {
68
+        parent::__construct($appName, $request);
69
+
70
+        $this->session = $session;
71
+        $this->random = $random;
72
+        $this->tokenProvider = $tokenProvider;
73
+        $this->credentialStore = $credentialStore;
74
+        $this->eventDispatcher = $eventDispatcher;
75
+    }
76
+
77
+    /**
78
+     * @NoAdminRequired
79
+     *
80
+     * @return DataResponse
81
+     * @throws OCSForbiddenException
82
+     */
83
+    public function getAppPassword(): DataResponse {
84
+        // We do not allow the creation of new tokens if this is an app password
85
+        if ($this->session->exists('app_password')) {
86
+            throw new OCSForbiddenException('You cannot request an new apppassword with an apppassword');
87
+        }
88
+
89
+        try {
90
+            $credentials = $this->credentialStore->getLoginCredentials();
91
+        } catch (CredentialsUnavailableException $e) {
92
+            throw new OCSForbiddenException();
93
+        }
94
+
95
+        try {
96
+            $password = $credentials->getPassword();
97
+        } catch (PasswordUnavailableException $e) {
98
+            $password = null;
99
+        }
100
+
101
+        $userAgent = $this->request->getHeader('USER_AGENT');
102
+
103
+        $token = $this->random->generate(72, ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_DIGITS);
104
+
105
+        $generatedToken = $this->tokenProvider->generateToken(
106
+            $token,
107
+            $credentials->getUID(),
108
+            $credentials->getLoginName(),
109
+            $password,
110
+            $userAgent,
111
+            IToken::PERMANENT_TOKEN,
112
+            IToken::DO_NOT_REMEMBER
113
+        );
114
+
115
+        $this->eventDispatcher->dispatchTyped(
116
+            new AppPasswordCreatedEvent($generatedToken)
117
+        );
118
+
119
+        return new DataResponse([
120
+            'apppassword' => $token
121
+        ]);
122
+    }
123
+
124
+    /**
125
+     * @NoAdminRequired
126
+     *
127
+     * @return DataResponse
128
+     */
129
+    public function deleteAppPassword() {
130
+        if (!$this->session->exists('app_password')) {
131
+            throw new OCSForbiddenException('no app password in use');
132
+        }
133
+
134
+        $appPassword = $this->session->get('app_password');
135
+
136
+        try {
137
+            $token = $this->tokenProvider->getToken($appPassword);
138
+        } catch (InvalidTokenException $e) {
139
+            throw new OCSForbiddenException('could not remove apptoken');
140
+        }
141
+
142
+        $this->tokenProvider->invalidateTokenById($token->getUID(), $token->getId());
143
+        return new DataResponse();
144
+    }
145
+
146
+    /**
147
+     * @NoAdminRequired
148
+     */
149
+    public function rotateAppPassword(): DataResponse {
150
+        if (!$this->session->exists('app_password')) {
151
+            throw new OCSForbiddenException('no app password in use');
152
+        }
153
+
154
+        $appPassword = $this->session->get('app_password');
155
+
156
+        try {
157
+            $token = $this->tokenProvider->getToken($appPassword);
158
+        } catch (InvalidTokenException $e) {
159
+            throw new OCSForbiddenException('could not rotate apptoken');
160
+        }
161
+
162
+        $newToken = $this->random->generate(72, ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_DIGITS);
163
+        $this->tokenProvider->rotate($token, $appPassword, $newToken);
164
+
165
+        return new DataResponse([
166
+            'apppassword' => $newToken,
167
+        ]);
168
+    }
169 169
 }
Please login to merge, or discard this patch.
core/Controller/ClientFlowLoginController.php 1 patch
Indentation   +367 added lines, -367 removed lines patch added patch discarded remove patch
@@ -55,371 +55,371 @@
 block discarded – undo
55 55
 use OCP\Session\Exceptions\SessionNotAvailableException;
56 56
 
57 57
 class ClientFlowLoginController extends Controller {
58
-	/** @var IUserSession */
59
-	private $userSession;
60
-	/** @var IL10N */
61
-	private $l10n;
62
-	/** @var Defaults */
63
-	private $defaults;
64
-	/** @var ISession */
65
-	private $session;
66
-	/** @var IProvider */
67
-	private $tokenProvider;
68
-	/** @var ISecureRandom */
69
-	private $random;
70
-	/** @var IURLGenerator */
71
-	private $urlGenerator;
72
-	/** @var ClientMapper */
73
-	private $clientMapper;
74
-	/** @var AccessTokenMapper */
75
-	private $accessTokenMapper;
76
-	/** @var ICrypto */
77
-	private $crypto;
78
-	/** @var IEventDispatcher */
79
-	private $eventDispatcher;
80
-
81
-	public const STATE_NAME = 'client.flow.state.token';
82
-
83
-	/**
84
-	 * @param string $appName
85
-	 * @param IRequest $request
86
-	 * @param IUserSession $userSession
87
-	 * @param IL10N $l10n
88
-	 * @param Defaults $defaults
89
-	 * @param ISession $session
90
-	 * @param IProvider $tokenProvider
91
-	 * @param ISecureRandom $random
92
-	 * @param IURLGenerator $urlGenerator
93
-	 * @param ClientMapper $clientMapper
94
-	 * @param AccessTokenMapper $accessTokenMapper
95
-	 * @param ICrypto $crypto
96
-	 * @param IEventDispatcher $eventDispatcher
97
-	 */
98
-	public function __construct($appName,
99
-								IRequest $request,
100
-								IUserSession $userSession,
101
-								IL10N $l10n,
102
-								Defaults $defaults,
103
-								ISession $session,
104
-								IProvider $tokenProvider,
105
-								ISecureRandom $random,
106
-								IURLGenerator $urlGenerator,
107
-								ClientMapper $clientMapper,
108
-								AccessTokenMapper $accessTokenMapper,
109
-								ICrypto $crypto,
110
-								IEventDispatcher $eventDispatcher) {
111
-		parent::__construct($appName, $request);
112
-		$this->userSession = $userSession;
113
-		$this->l10n = $l10n;
114
-		$this->defaults = $defaults;
115
-		$this->session = $session;
116
-		$this->tokenProvider = $tokenProvider;
117
-		$this->random = $random;
118
-		$this->urlGenerator = $urlGenerator;
119
-		$this->clientMapper = $clientMapper;
120
-		$this->accessTokenMapper = $accessTokenMapper;
121
-		$this->crypto = $crypto;
122
-		$this->eventDispatcher = $eventDispatcher;
123
-	}
124
-
125
-	/**
126
-	 * @return string
127
-	 */
128
-	private function getClientName() {
129
-		$userAgent = $this->request->getHeader('USER_AGENT');
130
-		return $userAgent !== '' ? $userAgent : 'unknown';
131
-	}
132
-
133
-	/**
134
-	 * @param string $stateToken
135
-	 * @return bool
136
-	 */
137
-	private function isValidToken($stateToken) {
138
-		$currentToken = $this->session->get(self::STATE_NAME);
139
-		if (!is_string($stateToken) || !is_string($currentToken)) {
140
-			return false;
141
-		}
142
-		return hash_equals($currentToken, $stateToken);
143
-	}
144
-
145
-	/**
146
-	 * @return StandaloneTemplateResponse
147
-	 */
148
-	private function stateTokenForbiddenResponse() {
149
-		$response = new StandaloneTemplateResponse(
150
-			$this->appName,
151
-			'403',
152
-			[
153
-				'message' => $this->l10n->t('State token does not match'),
154
-			],
155
-			'guest'
156
-		);
157
-		$response->setStatus(Http::STATUS_FORBIDDEN);
158
-		return $response;
159
-	}
160
-
161
-	/**
162
-	 * @PublicPage
163
-	 * @NoCSRFRequired
164
-	 * @UseSession
165
-	 *
166
-	 * @param string $clientIdentifier
167
-	 *
168
-	 * @return StandaloneTemplateResponse
169
-	 */
170
-	public function showAuthPickerPage($clientIdentifier = '', $user = '') {
171
-		$clientName = $this->getClientName();
172
-		$client = null;
173
-		if ($clientIdentifier !== '') {
174
-			$client = $this->clientMapper->getByIdentifier($clientIdentifier);
175
-			$clientName = $client->getName();
176
-		}
177
-
178
-		// No valid clientIdentifier given and no valid API Request (APIRequest header not set)
179
-		$clientRequest = $this->request->getHeader('OCS-APIREQUEST');
180
-		if ($clientRequest !== 'true' && $client === null) {
181
-			return new StandaloneTemplateResponse(
182
-				$this->appName,
183
-				'error',
184
-				[
185
-					'errors' =>
186
-					[
187
-						[
188
-							'error' => 'Access Forbidden',
189
-							'hint' => 'Invalid request',
190
-						],
191
-					],
192
-				],
193
-				'guest'
194
-			);
195
-		}
196
-
197
-		$stateToken = $this->random->generate(
198
-			64,
199
-			ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_DIGITS
200
-		);
201
-		$this->session->set(self::STATE_NAME, $stateToken);
202
-
203
-		$csp = new Http\ContentSecurityPolicy();
204
-		if ($client) {
205
-			$csp->addAllowedFormActionDomain($client->getRedirectUri());
206
-		} else {
207
-			$csp->addAllowedFormActionDomain('nc://*');
208
-		}
209
-
210
-		$response = new StandaloneTemplateResponse(
211
-			$this->appName,
212
-			'loginflow/authpicker',
213
-			[
214
-				'client' => $clientName,
215
-				'clientIdentifier' => $clientIdentifier,
216
-				'instanceName' => $this->defaults->getName(),
217
-				'urlGenerator' => $this->urlGenerator,
218
-				'stateToken' => $stateToken,
219
-				'serverHost' => $this->getServerPath(),
220
-				'oauthState' => $this->session->get('oauth.state'),
221
-				'user' => $user,
222
-			],
223
-			'guest'
224
-		);
225
-
226
-		$response->setContentSecurityPolicy($csp);
227
-		return $response;
228
-	}
229
-
230
-	/**
231
-	 * @NoAdminRequired
232
-	 * @NoCSRFRequired
233
-	 * @NoSameSiteCookieRequired
234
-	 * @UseSession
235
-	 *
236
-	 * @param string $stateToken
237
-	 * @param string $clientIdentifier
238
-	 * @return StandaloneTemplateResponse
239
-	 */
240
-	public function grantPage($stateToken = '',
241
-								 $clientIdentifier = '') {
242
-		if (!$this->isValidToken($stateToken)) {
243
-			return $this->stateTokenForbiddenResponse();
244
-		}
245
-
246
-		$clientName = $this->getClientName();
247
-		$client = null;
248
-		if ($clientIdentifier !== '') {
249
-			$client = $this->clientMapper->getByIdentifier($clientIdentifier);
250
-			$clientName = $client->getName();
251
-		}
252
-
253
-		$csp = new Http\ContentSecurityPolicy();
254
-		if ($client) {
255
-			$csp->addAllowedFormActionDomain($client->getRedirectUri());
256
-		} else {
257
-			$csp->addAllowedFormActionDomain('nc://*');
258
-		}
259
-
260
-		$response = new StandaloneTemplateResponse(
261
-			$this->appName,
262
-			'loginflow/grant',
263
-			[
264
-				'client' => $clientName,
265
-				'clientIdentifier' => $clientIdentifier,
266
-				'instanceName' => $this->defaults->getName(),
267
-				'urlGenerator' => $this->urlGenerator,
268
-				'stateToken' => $stateToken,
269
-				'serverHost' => $this->getServerPath(),
270
-				'oauthState' => $this->session->get('oauth.state'),
271
-			],
272
-			'guest'
273
-		);
274
-
275
-		$response->setContentSecurityPolicy($csp);
276
-		return $response;
277
-	}
278
-
279
-	/**
280
-	 * @NoAdminRequired
281
-	 * @UseSession
282
-	 *
283
-	 * @param string $stateToken
284
-	 * @param string $clientIdentifier
285
-	 * @return Http\RedirectResponse|Response
286
-	 */
287
-	public function generateAppPassword($stateToken,
288
-										$clientIdentifier = '') {
289
-		if (!$this->isValidToken($stateToken)) {
290
-			$this->session->remove(self::STATE_NAME);
291
-			return $this->stateTokenForbiddenResponse();
292
-		}
293
-
294
-		$this->session->remove(self::STATE_NAME);
295
-
296
-		try {
297
-			$sessionId = $this->session->getId();
298
-		} catch (SessionNotAvailableException $ex) {
299
-			$response = new Response();
300
-			$response->setStatus(Http::STATUS_FORBIDDEN);
301
-			return $response;
302
-		}
303
-
304
-		try {
305
-			$sessionToken = $this->tokenProvider->getToken($sessionId);
306
-			$loginName = $sessionToken->getLoginName();
307
-			try {
308
-				$password = $this->tokenProvider->getPassword($sessionToken, $sessionId);
309
-			} catch (PasswordlessTokenException $ex) {
310
-				$password = null;
311
-			}
312
-		} catch (InvalidTokenException $ex) {
313
-			$response = new Response();
314
-			$response->setStatus(Http::STATUS_FORBIDDEN);
315
-			return $response;
316
-		}
317
-
318
-		$clientName = $this->getClientName();
319
-		$client = false;
320
-		if ($clientIdentifier !== '') {
321
-			$client = $this->clientMapper->getByIdentifier($clientIdentifier);
322
-			$clientName = $client->getName();
323
-		}
324
-
325
-		$token = $this->random->generate(72, ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_DIGITS);
326
-		$uid = $this->userSession->getUser()->getUID();
327
-		$generatedToken = $this->tokenProvider->generateToken(
328
-			$token,
329
-			$uid,
330
-			$loginName,
331
-			$password,
332
-			$clientName,
333
-			IToken::PERMANENT_TOKEN,
334
-			IToken::DO_NOT_REMEMBER
335
-		);
336
-
337
-		if ($client) {
338
-			$code = $this->random->generate(128, ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_DIGITS);
339
-			$accessToken = new AccessToken();
340
-			$accessToken->setClientId($client->getId());
341
-			$accessToken->setEncryptedToken($this->crypto->encrypt($token, $code));
342
-			$accessToken->setHashedCode(hash('sha512', $code));
343
-			$accessToken->setTokenId($generatedToken->getId());
344
-			$this->accessTokenMapper->insert($accessToken);
345
-
346
-			$redirectUri = $client->getRedirectUri();
347
-
348
-			if (parse_url($redirectUri, PHP_URL_QUERY)) {
349
-				$redirectUri .= '&';
350
-			} else {
351
-				$redirectUri .= '?';
352
-			}
353
-
354
-			$redirectUri .= sprintf(
355
-				'state=%s&code=%s',
356
-				urlencode($this->session->get('oauth.state')),
357
-				urlencode($code)
358
-			);
359
-			$this->session->remove('oauth.state');
360
-		} else {
361
-			$redirectUri = 'nc://login/server:' . $this->getServerPath() . '&user:' . urlencode($loginName) . '&password:' . urlencode($token);
362
-
363
-			// Clear the token from the login here
364
-			$this->tokenProvider->invalidateToken($sessionId);
365
-		}
366
-
367
-		$this->eventDispatcher->dispatchTyped(
368
-			new AppPasswordCreatedEvent($generatedToken)
369
-		);
370
-
371
-		return new Http\RedirectResponse($redirectUri);
372
-	}
373
-
374
-	/**
375
-	 * @PublicPage
376
-	 */
377
-	public function apptokenRedirect(string $stateToken, string $user, string $password) {
378
-		if (!$this->isValidToken($stateToken)) {
379
-			return $this->stateTokenForbiddenResponse();
380
-		}
381
-
382
-		try {
383
-			$token = $this->tokenProvider->getToken($password);
384
-			if ($token->getLoginName() !== $user) {
385
-				throw new InvalidTokenException('login name does not match');
386
-			}
387
-		} catch (InvalidTokenException $e) {
388
-			$response = new StandaloneTemplateResponse(
389
-				$this->appName,
390
-				'403',
391
-				[
392
-					'message' => $this->l10n->t('Invalid app password'),
393
-				],
394
-				'guest'
395
-			);
396
-			$response->setStatus(Http::STATUS_FORBIDDEN);
397
-			return $response;
398
-		}
399
-
400
-		$redirectUri = 'nc://login/server:' . $this->getServerPath() . '&user:' . urlencode($user) . '&password:' . urlencode($password);
401
-		return new Http\RedirectResponse($redirectUri);
402
-	}
403
-
404
-	private function getServerPath(): string {
405
-		$serverPostfix = '';
406
-
407
-		if (strpos($this->request->getRequestUri(), '/index.php') !== false) {
408
-			$serverPostfix = substr($this->request->getRequestUri(), 0, strpos($this->request->getRequestUri(), '/index.php'));
409
-		} elseif (strpos($this->request->getRequestUri(), '/login/flow') !== false) {
410
-			$serverPostfix = substr($this->request->getRequestUri(), 0, strpos($this->request->getRequestUri(), '/login/flow'));
411
-		}
412
-
413
-		$protocol = $this->request->getServerProtocol();
414
-
415
-		if ($protocol !== "https") {
416
-			$xForwardedProto = $this->request->getHeader('X-Forwarded-Proto');
417
-			$xForwardedSSL = $this->request->getHeader('X-Forwarded-Ssl');
418
-			if ($xForwardedProto === 'https' || $xForwardedSSL === 'on') {
419
-				$protocol = 'https';
420
-			}
421
-		}
422
-
423
-		return $protocol . "://" . $this->request->getServerHost() . $serverPostfix;
424
-	}
58
+    /** @var IUserSession */
59
+    private $userSession;
60
+    /** @var IL10N */
61
+    private $l10n;
62
+    /** @var Defaults */
63
+    private $defaults;
64
+    /** @var ISession */
65
+    private $session;
66
+    /** @var IProvider */
67
+    private $tokenProvider;
68
+    /** @var ISecureRandom */
69
+    private $random;
70
+    /** @var IURLGenerator */
71
+    private $urlGenerator;
72
+    /** @var ClientMapper */
73
+    private $clientMapper;
74
+    /** @var AccessTokenMapper */
75
+    private $accessTokenMapper;
76
+    /** @var ICrypto */
77
+    private $crypto;
78
+    /** @var IEventDispatcher */
79
+    private $eventDispatcher;
80
+
81
+    public const STATE_NAME = 'client.flow.state.token';
82
+
83
+    /**
84
+     * @param string $appName
85
+     * @param IRequest $request
86
+     * @param IUserSession $userSession
87
+     * @param IL10N $l10n
88
+     * @param Defaults $defaults
89
+     * @param ISession $session
90
+     * @param IProvider $tokenProvider
91
+     * @param ISecureRandom $random
92
+     * @param IURLGenerator $urlGenerator
93
+     * @param ClientMapper $clientMapper
94
+     * @param AccessTokenMapper $accessTokenMapper
95
+     * @param ICrypto $crypto
96
+     * @param IEventDispatcher $eventDispatcher
97
+     */
98
+    public function __construct($appName,
99
+                                IRequest $request,
100
+                                IUserSession $userSession,
101
+                                IL10N $l10n,
102
+                                Defaults $defaults,
103
+                                ISession $session,
104
+                                IProvider $tokenProvider,
105
+                                ISecureRandom $random,
106
+                                IURLGenerator $urlGenerator,
107
+                                ClientMapper $clientMapper,
108
+                                AccessTokenMapper $accessTokenMapper,
109
+                                ICrypto $crypto,
110
+                                IEventDispatcher $eventDispatcher) {
111
+        parent::__construct($appName, $request);
112
+        $this->userSession = $userSession;
113
+        $this->l10n = $l10n;
114
+        $this->defaults = $defaults;
115
+        $this->session = $session;
116
+        $this->tokenProvider = $tokenProvider;
117
+        $this->random = $random;
118
+        $this->urlGenerator = $urlGenerator;
119
+        $this->clientMapper = $clientMapper;
120
+        $this->accessTokenMapper = $accessTokenMapper;
121
+        $this->crypto = $crypto;
122
+        $this->eventDispatcher = $eventDispatcher;
123
+    }
124
+
125
+    /**
126
+     * @return string
127
+     */
128
+    private function getClientName() {
129
+        $userAgent = $this->request->getHeader('USER_AGENT');
130
+        return $userAgent !== '' ? $userAgent : 'unknown';
131
+    }
132
+
133
+    /**
134
+     * @param string $stateToken
135
+     * @return bool
136
+     */
137
+    private function isValidToken($stateToken) {
138
+        $currentToken = $this->session->get(self::STATE_NAME);
139
+        if (!is_string($stateToken) || !is_string($currentToken)) {
140
+            return false;
141
+        }
142
+        return hash_equals($currentToken, $stateToken);
143
+    }
144
+
145
+    /**
146
+     * @return StandaloneTemplateResponse
147
+     */
148
+    private function stateTokenForbiddenResponse() {
149
+        $response = new StandaloneTemplateResponse(
150
+            $this->appName,
151
+            '403',
152
+            [
153
+                'message' => $this->l10n->t('State token does not match'),
154
+            ],
155
+            'guest'
156
+        );
157
+        $response->setStatus(Http::STATUS_FORBIDDEN);
158
+        return $response;
159
+    }
160
+
161
+    /**
162
+     * @PublicPage
163
+     * @NoCSRFRequired
164
+     * @UseSession
165
+     *
166
+     * @param string $clientIdentifier
167
+     *
168
+     * @return StandaloneTemplateResponse
169
+     */
170
+    public function showAuthPickerPage($clientIdentifier = '', $user = '') {
171
+        $clientName = $this->getClientName();
172
+        $client = null;
173
+        if ($clientIdentifier !== '') {
174
+            $client = $this->clientMapper->getByIdentifier($clientIdentifier);
175
+            $clientName = $client->getName();
176
+        }
177
+
178
+        // No valid clientIdentifier given and no valid API Request (APIRequest header not set)
179
+        $clientRequest = $this->request->getHeader('OCS-APIREQUEST');
180
+        if ($clientRequest !== 'true' && $client === null) {
181
+            return new StandaloneTemplateResponse(
182
+                $this->appName,
183
+                'error',
184
+                [
185
+                    'errors' =>
186
+                    [
187
+                        [
188
+                            'error' => 'Access Forbidden',
189
+                            'hint' => 'Invalid request',
190
+                        ],
191
+                    ],
192
+                ],
193
+                'guest'
194
+            );
195
+        }
196
+
197
+        $stateToken = $this->random->generate(
198
+            64,
199
+            ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_DIGITS
200
+        );
201
+        $this->session->set(self::STATE_NAME, $stateToken);
202
+
203
+        $csp = new Http\ContentSecurityPolicy();
204
+        if ($client) {
205
+            $csp->addAllowedFormActionDomain($client->getRedirectUri());
206
+        } else {
207
+            $csp->addAllowedFormActionDomain('nc://*');
208
+        }
209
+
210
+        $response = new StandaloneTemplateResponse(
211
+            $this->appName,
212
+            'loginflow/authpicker',
213
+            [
214
+                'client' => $clientName,
215
+                'clientIdentifier' => $clientIdentifier,
216
+                'instanceName' => $this->defaults->getName(),
217
+                'urlGenerator' => $this->urlGenerator,
218
+                'stateToken' => $stateToken,
219
+                'serverHost' => $this->getServerPath(),
220
+                'oauthState' => $this->session->get('oauth.state'),
221
+                'user' => $user,
222
+            ],
223
+            'guest'
224
+        );
225
+
226
+        $response->setContentSecurityPolicy($csp);
227
+        return $response;
228
+    }
229
+
230
+    /**
231
+     * @NoAdminRequired
232
+     * @NoCSRFRequired
233
+     * @NoSameSiteCookieRequired
234
+     * @UseSession
235
+     *
236
+     * @param string $stateToken
237
+     * @param string $clientIdentifier
238
+     * @return StandaloneTemplateResponse
239
+     */
240
+    public function grantPage($stateToken = '',
241
+                                    $clientIdentifier = '') {
242
+        if (!$this->isValidToken($stateToken)) {
243
+            return $this->stateTokenForbiddenResponse();
244
+        }
245
+
246
+        $clientName = $this->getClientName();
247
+        $client = null;
248
+        if ($clientIdentifier !== '') {
249
+            $client = $this->clientMapper->getByIdentifier($clientIdentifier);
250
+            $clientName = $client->getName();
251
+        }
252
+
253
+        $csp = new Http\ContentSecurityPolicy();
254
+        if ($client) {
255
+            $csp->addAllowedFormActionDomain($client->getRedirectUri());
256
+        } else {
257
+            $csp->addAllowedFormActionDomain('nc://*');
258
+        }
259
+
260
+        $response = new StandaloneTemplateResponse(
261
+            $this->appName,
262
+            'loginflow/grant',
263
+            [
264
+                'client' => $clientName,
265
+                'clientIdentifier' => $clientIdentifier,
266
+                'instanceName' => $this->defaults->getName(),
267
+                'urlGenerator' => $this->urlGenerator,
268
+                'stateToken' => $stateToken,
269
+                'serverHost' => $this->getServerPath(),
270
+                'oauthState' => $this->session->get('oauth.state'),
271
+            ],
272
+            'guest'
273
+        );
274
+
275
+        $response->setContentSecurityPolicy($csp);
276
+        return $response;
277
+    }
278
+
279
+    /**
280
+     * @NoAdminRequired
281
+     * @UseSession
282
+     *
283
+     * @param string $stateToken
284
+     * @param string $clientIdentifier
285
+     * @return Http\RedirectResponse|Response
286
+     */
287
+    public function generateAppPassword($stateToken,
288
+                                        $clientIdentifier = '') {
289
+        if (!$this->isValidToken($stateToken)) {
290
+            $this->session->remove(self::STATE_NAME);
291
+            return $this->stateTokenForbiddenResponse();
292
+        }
293
+
294
+        $this->session->remove(self::STATE_NAME);
295
+
296
+        try {
297
+            $sessionId = $this->session->getId();
298
+        } catch (SessionNotAvailableException $ex) {
299
+            $response = new Response();
300
+            $response->setStatus(Http::STATUS_FORBIDDEN);
301
+            return $response;
302
+        }
303
+
304
+        try {
305
+            $sessionToken = $this->tokenProvider->getToken($sessionId);
306
+            $loginName = $sessionToken->getLoginName();
307
+            try {
308
+                $password = $this->tokenProvider->getPassword($sessionToken, $sessionId);
309
+            } catch (PasswordlessTokenException $ex) {
310
+                $password = null;
311
+            }
312
+        } catch (InvalidTokenException $ex) {
313
+            $response = new Response();
314
+            $response->setStatus(Http::STATUS_FORBIDDEN);
315
+            return $response;
316
+        }
317
+
318
+        $clientName = $this->getClientName();
319
+        $client = false;
320
+        if ($clientIdentifier !== '') {
321
+            $client = $this->clientMapper->getByIdentifier($clientIdentifier);
322
+            $clientName = $client->getName();
323
+        }
324
+
325
+        $token = $this->random->generate(72, ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_DIGITS);
326
+        $uid = $this->userSession->getUser()->getUID();
327
+        $generatedToken = $this->tokenProvider->generateToken(
328
+            $token,
329
+            $uid,
330
+            $loginName,
331
+            $password,
332
+            $clientName,
333
+            IToken::PERMANENT_TOKEN,
334
+            IToken::DO_NOT_REMEMBER
335
+        );
336
+
337
+        if ($client) {
338
+            $code = $this->random->generate(128, ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_DIGITS);
339
+            $accessToken = new AccessToken();
340
+            $accessToken->setClientId($client->getId());
341
+            $accessToken->setEncryptedToken($this->crypto->encrypt($token, $code));
342
+            $accessToken->setHashedCode(hash('sha512', $code));
343
+            $accessToken->setTokenId($generatedToken->getId());
344
+            $this->accessTokenMapper->insert($accessToken);
345
+
346
+            $redirectUri = $client->getRedirectUri();
347
+
348
+            if (parse_url($redirectUri, PHP_URL_QUERY)) {
349
+                $redirectUri .= '&';
350
+            } else {
351
+                $redirectUri .= '?';
352
+            }
353
+
354
+            $redirectUri .= sprintf(
355
+                'state=%s&code=%s',
356
+                urlencode($this->session->get('oauth.state')),
357
+                urlencode($code)
358
+            );
359
+            $this->session->remove('oauth.state');
360
+        } else {
361
+            $redirectUri = 'nc://login/server:' . $this->getServerPath() . '&user:' . urlencode($loginName) . '&password:' . urlencode($token);
362
+
363
+            // Clear the token from the login here
364
+            $this->tokenProvider->invalidateToken($sessionId);
365
+        }
366
+
367
+        $this->eventDispatcher->dispatchTyped(
368
+            new AppPasswordCreatedEvent($generatedToken)
369
+        );
370
+
371
+        return new Http\RedirectResponse($redirectUri);
372
+    }
373
+
374
+    /**
375
+     * @PublicPage
376
+     */
377
+    public function apptokenRedirect(string $stateToken, string $user, string $password) {
378
+        if (!$this->isValidToken($stateToken)) {
379
+            return $this->stateTokenForbiddenResponse();
380
+        }
381
+
382
+        try {
383
+            $token = $this->tokenProvider->getToken($password);
384
+            if ($token->getLoginName() !== $user) {
385
+                throw new InvalidTokenException('login name does not match');
386
+            }
387
+        } catch (InvalidTokenException $e) {
388
+            $response = new StandaloneTemplateResponse(
389
+                $this->appName,
390
+                '403',
391
+                [
392
+                    'message' => $this->l10n->t('Invalid app password'),
393
+                ],
394
+                'guest'
395
+            );
396
+            $response->setStatus(Http::STATUS_FORBIDDEN);
397
+            return $response;
398
+        }
399
+
400
+        $redirectUri = 'nc://login/server:' . $this->getServerPath() . '&user:' . urlencode($user) . '&password:' . urlencode($password);
401
+        return new Http\RedirectResponse($redirectUri);
402
+    }
403
+
404
+    private function getServerPath(): string {
405
+        $serverPostfix = '';
406
+
407
+        if (strpos($this->request->getRequestUri(), '/index.php') !== false) {
408
+            $serverPostfix = substr($this->request->getRequestUri(), 0, strpos($this->request->getRequestUri(), '/index.php'));
409
+        } elseif (strpos($this->request->getRequestUri(), '/login/flow') !== false) {
410
+            $serverPostfix = substr($this->request->getRequestUri(), 0, strpos($this->request->getRequestUri(), '/login/flow'));
411
+        }
412
+
413
+        $protocol = $this->request->getServerProtocol();
414
+
415
+        if ($protocol !== "https") {
416
+            $xForwardedProto = $this->request->getHeader('X-Forwarded-Proto');
417
+            $xForwardedSSL = $this->request->getHeader('X-Forwarded-Ssl');
418
+            if ($xForwardedProto === 'https' || $xForwardedSSL === 'on') {
419
+                $protocol = 'https';
420
+            }
421
+        }
422
+
423
+        return $protocol . "://" . $this->request->getServerHost() . $serverPostfix;
424
+    }
425 425
 }
Please login to merge, or discard this patch.
lib/private/Authentication/Token/IProvider.php 1 patch
Indentation   +146 added lines, -146 removed lines patch added patch discarded remove patch
@@ -37,150 +37,150 @@
 block discarded – undo
37 37
 interface IProvider {
38 38
 
39 39
 
40
-	/**
41
-	 * Create and persist a new token
42
-	 *
43
-	 * @param string $token
44
-	 * @param string $uid
45
-	 * @param string $loginName
46
-	 * @param string|null $password
47
-	 * @param string $name Name will be trimmed to 120 chars when longer
48
-	 * @param int $type token type
49
-	 * @param int $remember whether the session token should be used for remember-me
50
-	 * @return IToken
51
-	 * @throws \RuntimeException when OpenSSL reports a problem
52
-	 */
53
-	public function generateToken(string $token,
54
-								  string $uid,
55
-								  string $loginName,
56
-								  ?string $password,
57
-								  string $name,
58
-								  int $type = IToken::TEMPORARY_TOKEN,
59
-								  int $remember = IToken::DO_NOT_REMEMBER): IToken;
60
-
61
-	/**
62
-	 * Get a token by token id
63
-	 *
64
-	 * @param string $tokenId
65
-	 * @throws InvalidTokenException
66
-	 * @throws ExpiredTokenException
67
-	 * @throws WipeTokenException
68
-	 * @return IToken
69
-	 */
70
-	public function getToken(string $tokenId): IToken;
71
-
72
-	/**
73
-	 * Get a token by token id
74
-	 *
75
-	 * @param int $tokenId
76
-	 * @throws InvalidTokenException
77
-	 * @throws ExpiredTokenException
78
-	 * @throws WipeTokenException
79
-	 * @return IToken
80
-	 */
81
-	public function getTokenById(int $tokenId): IToken;
82
-
83
-	/**
84
-	 * Duplicate an existing session token
85
-	 *
86
-	 * @param string $oldSessionId
87
-	 * @param string $sessionId
88
-	 * @throws InvalidTokenException
89
-	 * @throws \RuntimeException when OpenSSL reports a problem
90
-	 * @return IToken The new token
91
-	 */
92
-	public function renewSessionToken(string $oldSessionId, string $sessionId): IToken;
93
-
94
-	/**
95
-	 * Invalidate (delete) the given session token
96
-	 *
97
-	 * @param string $token
98
-	 */
99
-	public function invalidateToken(string $token);
100
-
101
-	/**
102
-	 * Invalidate (delete) the given token
103
-	 *
104
-	 * @param string $uid
105
-	 * @param int $id
106
-	 */
107
-	public function invalidateTokenById(string $uid, int $id);
108
-
109
-	/**
110
-	 * Invalidate (delete) old session tokens
111
-	 */
112
-	public function invalidateOldTokens();
113
-
114
-	/**
115
-	 * Save the updated token
116
-	 *
117
-	 * @param IToken $token
118
-	 */
119
-	public function updateToken(IToken $token);
120
-
121
-	/**
122
-	 * Update token activity timestamp
123
-	 *
124
-	 * @param IToken $token
125
-	 */
126
-	public function updateTokenActivity(IToken $token);
127
-
128
-	/**
129
-	 * Get all tokens of a user
130
-	 *
131
-	 * The provider may limit the number of result rows in case of an abuse
132
-	 * where a high number of (session) tokens is generated
133
-	 *
134
-	 * @param string $uid
135
-	 * @return IToken[]
136
-	 */
137
-	public function getTokenByUser(string $uid): array;
138
-
139
-	/**
140
-	 * Get the (unencrypted) password of the given token
141
-	 *
142
-	 * @param IToken $savedToken
143
-	 * @param string $tokenId
144
-	 * @throws InvalidTokenException
145
-	 * @throws PasswordlessTokenException
146
-	 * @return string
147
-	 */
148
-	public function getPassword(IToken $savedToken, string $tokenId): string;
149
-
150
-	/**
151
-	 * Encrypt and set the password of the given token
152
-	 *
153
-	 * @param IToken $token
154
-	 * @param string $tokenId
155
-	 * @param string $password
156
-	 * @throws InvalidTokenException
157
-	 */
158
-	public function setPassword(IToken $token, string $tokenId, string $password);
159
-
160
-	/**
161
-	 * Rotate the token. Usefull for for example oauth tokens
162
-	 *
163
-	 * @param IToken $token
164
-	 * @param string $oldTokenId
165
-	 * @param string $newTokenId
166
-	 * @return IToken
167
-	 * @throws \RuntimeException when OpenSSL reports a problem
168
-	 */
169
-	public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken;
170
-
171
-	/**
172
-	 * Marks a token as having an invalid password.
173
-	 *
174
-	 * @param IToken $token
175
-	 * @param string $tokenId
176
-	 */
177
-	public function markPasswordInvalid(IToken $token, string $tokenId);
178
-
179
-	/**
180
-	 * Update all the passwords of $uid if required
181
-	 *
182
-	 * @param string $uid
183
-	 * @param string $password
184
-	 */
185
-	public function updatePasswords(string $uid, string $password);
40
+    /**
41
+     * Create and persist a new token
42
+     *
43
+     * @param string $token
44
+     * @param string $uid
45
+     * @param string $loginName
46
+     * @param string|null $password
47
+     * @param string $name Name will be trimmed to 120 chars when longer
48
+     * @param int $type token type
49
+     * @param int $remember whether the session token should be used for remember-me
50
+     * @return IToken
51
+     * @throws \RuntimeException when OpenSSL reports a problem
52
+     */
53
+    public function generateToken(string $token,
54
+                                    string $uid,
55
+                                    string $loginName,
56
+                                  ?string $password,
57
+                                    string $name,
58
+                                    int $type = IToken::TEMPORARY_TOKEN,
59
+                                    int $remember = IToken::DO_NOT_REMEMBER): IToken;
60
+
61
+    /**
62
+     * Get a token by token id
63
+     *
64
+     * @param string $tokenId
65
+     * @throws InvalidTokenException
66
+     * @throws ExpiredTokenException
67
+     * @throws WipeTokenException
68
+     * @return IToken
69
+     */
70
+    public function getToken(string $tokenId): IToken;
71
+
72
+    /**
73
+     * Get a token by token id
74
+     *
75
+     * @param int $tokenId
76
+     * @throws InvalidTokenException
77
+     * @throws ExpiredTokenException
78
+     * @throws WipeTokenException
79
+     * @return IToken
80
+     */
81
+    public function getTokenById(int $tokenId): IToken;
82
+
83
+    /**
84
+     * Duplicate an existing session token
85
+     *
86
+     * @param string $oldSessionId
87
+     * @param string $sessionId
88
+     * @throws InvalidTokenException
89
+     * @throws \RuntimeException when OpenSSL reports a problem
90
+     * @return IToken The new token
91
+     */
92
+    public function renewSessionToken(string $oldSessionId, string $sessionId): IToken;
93
+
94
+    /**
95
+     * Invalidate (delete) the given session token
96
+     *
97
+     * @param string $token
98
+     */
99
+    public function invalidateToken(string $token);
100
+
101
+    /**
102
+     * Invalidate (delete) the given token
103
+     *
104
+     * @param string $uid
105
+     * @param int $id
106
+     */
107
+    public function invalidateTokenById(string $uid, int $id);
108
+
109
+    /**
110
+     * Invalidate (delete) old session tokens
111
+     */
112
+    public function invalidateOldTokens();
113
+
114
+    /**
115
+     * Save the updated token
116
+     *
117
+     * @param IToken $token
118
+     */
119
+    public function updateToken(IToken $token);
120
+
121
+    /**
122
+     * Update token activity timestamp
123
+     *
124
+     * @param IToken $token
125
+     */
126
+    public function updateTokenActivity(IToken $token);
127
+
128
+    /**
129
+     * Get all tokens of a user
130
+     *
131
+     * The provider may limit the number of result rows in case of an abuse
132
+     * where a high number of (session) tokens is generated
133
+     *
134
+     * @param string $uid
135
+     * @return IToken[]
136
+     */
137
+    public function getTokenByUser(string $uid): array;
138
+
139
+    /**
140
+     * Get the (unencrypted) password of the given token
141
+     *
142
+     * @param IToken $savedToken
143
+     * @param string $tokenId
144
+     * @throws InvalidTokenException
145
+     * @throws PasswordlessTokenException
146
+     * @return string
147
+     */
148
+    public function getPassword(IToken $savedToken, string $tokenId): string;
149
+
150
+    /**
151
+     * Encrypt and set the password of the given token
152
+     *
153
+     * @param IToken $token
154
+     * @param string $tokenId
155
+     * @param string $password
156
+     * @throws InvalidTokenException
157
+     */
158
+    public function setPassword(IToken $token, string $tokenId, string $password);
159
+
160
+    /**
161
+     * Rotate the token. Usefull for for example oauth tokens
162
+     *
163
+     * @param IToken $token
164
+     * @param string $oldTokenId
165
+     * @param string $newTokenId
166
+     * @return IToken
167
+     * @throws \RuntimeException when OpenSSL reports a problem
168
+     */
169
+    public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken;
170
+
171
+    /**
172
+     * Marks a token as having an invalid password.
173
+     *
174
+     * @param IToken $token
175
+     * @param string $tokenId
176
+     */
177
+    public function markPasswordInvalid(IToken $token, string $tokenId);
178
+
179
+    /**
180
+     * Update all the passwords of $uid if required
181
+     *
182
+     * @param string $uid
183
+     * @param string $password
184
+     */
185
+    public function updatePasswords(string $uid, string $password);
186 186
 }
Please login to merge, or discard this patch.
lib/private/Authentication/Token/Manager.php 1 patch
Indentation   +205 added lines, -205 removed lines patch added patch discarded remove patch
@@ -35,209 +35,209 @@
 block discarded – undo
35 35
 
36 36
 class Manager implements IProvider {
37 37
 
38
-	/** @var PublicKeyTokenProvider */
39
-	private $publicKeyTokenProvider;
40
-
41
-	public function __construct(PublicKeyTokenProvider $publicKeyTokenProvider) {
42
-		$this->publicKeyTokenProvider = $publicKeyTokenProvider;
43
-	}
44
-
45
-	/**
46
-	 * Create and persist a new token
47
-	 *
48
-	 * @param string $token
49
-	 * @param string $uid
50
-	 * @param string $loginName
51
-	 * @param string|null $password
52
-	 * @param string $name Name will be trimmed to 120 chars when longer
53
-	 * @param int $type token type
54
-	 * @param int $remember whether the session token should be used for remember-me
55
-	 * @return IToken
56
-	 */
57
-	public function generateToken(string $token,
58
-								  string $uid,
59
-								  string $loginName,
60
-								  $password,
61
-								  string $name,
62
-								  int $type = IToken::TEMPORARY_TOKEN,
63
-								  int $remember = IToken::DO_NOT_REMEMBER): IToken {
64
-		if (mb_strlen($name) > 128) {
65
-			$name = mb_substr($name, 0, 120) . '…';
66
-		}
67
-
68
-		try {
69
-			return $this->publicKeyTokenProvider->generateToken(
70
-				$token,
71
-				$uid,
72
-				$loginName,
73
-				$password,
74
-				$name,
75
-				$type,
76
-				$remember
77
-			);
78
-		} catch (UniqueConstraintViolationException $e) {
79
-			// It's rare, but if two requests of the same session (e.g. env-based SAML)
80
-			// try to create the session token they might end up here at the same time
81
-			// because we use the session ID as token and the db token is created anew
82
-			// with every request.
83
-			//
84
-			// If the UIDs match, then this should be fine.
85
-			$existing = $this->getToken($token);
86
-			if ($existing->getUID() !== $uid) {
87
-				throw new \Exception('Token conflict handled, but UIDs do not match. This should not happen', 0, $e);
88
-			}
89
-			return $existing;
90
-		}
91
-	}
92
-
93
-	/**
94
-	 * Save the updated token
95
-	 *
96
-	 * @param IToken $token
97
-	 * @throws InvalidTokenException
98
-	 */
99
-	public function updateToken(IToken $token) {
100
-		$provider = $this->getProvider($token);
101
-		$provider->updateToken($token);
102
-	}
103
-
104
-	/**
105
-	 * Update token activity timestamp
106
-	 *
107
-	 * @throws InvalidTokenException
108
-	 * @param IToken $token
109
-	 */
110
-	public function updateTokenActivity(IToken $token) {
111
-		$provider = $this->getProvider($token);
112
-		$provider->updateTokenActivity($token);
113
-	}
114
-
115
-	/**
116
-	 * @param string $uid
117
-	 * @return IToken[]
118
-	 */
119
-	public function getTokenByUser(string $uid): array {
120
-		return $this->publicKeyTokenProvider->getTokenByUser($uid);
121
-	}
122
-
123
-	/**
124
-	 * Get a token by token
125
-	 *
126
-	 * @param string $tokenId
127
-	 * @throws InvalidTokenException
128
-	 * @throws \RuntimeException when OpenSSL reports a problem
129
-	 * @return IToken
130
-	 */
131
-	public function getToken(string $tokenId): IToken {
132
-		try {
133
-			return $this->publicKeyTokenProvider->getToken($tokenId);
134
-		} catch (WipeTokenException $e) {
135
-			throw $e;
136
-		} catch (ExpiredTokenException $e) {
137
-			throw $e;
138
-		} catch (InvalidTokenException $e) {
139
-			throw $e;
140
-		}
141
-	}
142
-
143
-	/**
144
-	 * Get a token by token id
145
-	 *
146
-	 * @param int $tokenId
147
-	 * @throws InvalidTokenException
148
-	 * @return IToken
149
-	 */
150
-	public function getTokenById(int $tokenId): IToken {
151
-		try {
152
-			return $this->publicKeyTokenProvider->getTokenById($tokenId);
153
-		} catch (ExpiredTokenException $e) {
154
-			throw $e;
155
-		} catch (WipeTokenException $e) {
156
-			throw $e;
157
-		} catch (InvalidTokenException $e) {
158
-			throw $e;
159
-		}
160
-	}
161
-
162
-	/**
163
-	 * @param string $oldSessionId
164
-	 * @param string $sessionId
165
-	 * @throws InvalidTokenException
166
-	 * @return IToken
167
-	 */
168
-	public function renewSessionToken(string $oldSessionId, string $sessionId): IToken {
169
-		try {
170
-			return $this->publicKeyTokenProvider->renewSessionToken($oldSessionId, $sessionId);
171
-		} catch (ExpiredTokenException $e) {
172
-			throw $e;
173
-		} catch (InvalidTokenException $e) {
174
-			throw $e;
175
-		}
176
-	}
177
-
178
-	/**
179
-	 * @param IToken $savedToken
180
-	 * @param string $tokenId session token
181
-	 * @throws InvalidTokenException
182
-	 * @throws PasswordlessTokenException
183
-	 * @return string
184
-	 */
185
-	public function getPassword(IToken $savedToken, string $tokenId): string {
186
-		$provider = $this->getProvider($savedToken);
187
-		return $provider->getPassword($savedToken, $tokenId);
188
-	}
189
-
190
-	public function setPassword(IToken $token, string $tokenId, string $password) {
191
-		$provider = $this->getProvider($token);
192
-		$provider->setPassword($token, $tokenId, $password);
193
-	}
194
-
195
-	public function invalidateToken(string $token) {
196
-		$this->publicKeyTokenProvider->invalidateToken($token);
197
-	}
198
-
199
-	public function invalidateTokenById(string $uid, int $id) {
200
-		$this->publicKeyTokenProvider->invalidateTokenById($uid, $id);
201
-	}
202
-
203
-	public function invalidateOldTokens() {
204
-		$this->publicKeyTokenProvider->invalidateOldTokens();
205
-	}
206
-
207
-	/**
208
-	 * @param IToken $token
209
-	 * @param string $oldTokenId
210
-	 * @param string $newTokenId
211
-	 * @return IToken
212
-	 * @throws InvalidTokenException
213
-	 * @throws \RuntimeException when OpenSSL reports a problem
214
-	 */
215
-	public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken {
216
-		if ($token instanceof PublicKeyToken) {
217
-			return $this->publicKeyTokenProvider->rotate($token, $oldTokenId, $newTokenId);
218
-		}
219
-
220
-		throw new InvalidTokenException();
221
-	}
222
-
223
-	/**
224
-	 * @param IToken $token
225
-	 * @return IProvider
226
-	 * @throws InvalidTokenException
227
-	 */
228
-	private function getProvider(IToken $token): IProvider {
229
-		if ($token instanceof PublicKeyToken) {
230
-			return $this->publicKeyTokenProvider;
231
-		}
232
-		throw new InvalidTokenException();
233
-	}
234
-
235
-
236
-	public function markPasswordInvalid(IToken $token, string $tokenId) {
237
-		$this->getProvider($token)->markPasswordInvalid($token, $tokenId);
238
-	}
239
-
240
-	public function updatePasswords(string $uid, string $password) {
241
-		$this->publicKeyTokenProvider->updatePasswords($uid, $password);
242
-	}
38
+    /** @var PublicKeyTokenProvider */
39
+    private $publicKeyTokenProvider;
40
+
41
+    public function __construct(PublicKeyTokenProvider $publicKeyTokenProvider) {
42
+        $this->publicKeyTokenProvider = $publicKeyTokenProvider;
43
+    }
44
+
45
+    /**
46
+     * Create and persist a new token
47
+     *
48
+     * @param string $token
49
+     * @param string $uid
50
+     * @param string $loginName
51
+     * @param string|null $password
52
+     * @param string $name Name will be trimmed to 120 chars when longer
53
+     * @param int $type token type
54
+     * @param int $remember whether the session token should be used for remember-me
55
+     * @return IToken
56
+     */
57
+    public function generateToken(string $token,
58
+                                    string $uid,
59
+                                    string $loginName,
60
+                                    $password,
61
+                                    string $name,
62
+                                    int $type = IToken::TEMPORARY_TOKEN,
63
+                                    int $remember = IToken::DO_NOT_REMEMBER): IToken {
64
+        if (mb_strlen($name) > 128) {
65
+            $name = mb_substr($name, 0, 120) . '…';
66
+        }
67
+
68
+        try {
69
+            return $this->publicKeyTokenProvider->generateToken(
70
+                $token,
71
+                $uid,
72
+                $loginName,
73
+                $password,
74
+                $name,
75
+                $type,
76
+                $remember
77
+            );
78
+        } catch (UniqueConstraintViolationException $e) {
79
+            // It's rare, but if two requests of the same session (e.g. env-based SAML)
80
+            // try to create the session token they might end up here at the same time
81
+            // because we use the session ID as token and the db token is created anew
82
+            // with every request.
83
+            //
84
+            // If the UIDs match, then this should be fine.
85
+            $existing = $this->getToken($token);
86
+            if ($existing->getUID() !== $uid) {
87
+                throw new \Exception('Token conflict handled, but UIDs do not match. This should not happen', 0, $e);
88
+            }
89
+            return $existing;
90
+        }
91
+    }
92
+
93
+    /**
94
+     * Save the updated token
95
+     *
96
+     * @param IToken $token
97
+     * @throws InvalidTokenException
98
+     */
99
+    public function updateToken(IToken $token) {
100
+        $provider = $this->getProvider($token);
101
+        $provider->updateToken($token);
102
+    }
103
+
104
+    /**
105
+     * Update token activity timestamp
106
+     *
107
+     * @throws InvalidTokenException
108
+     * @param IToken $token
109
+     */
110
+    public function updateTokenActivity(IToken $token) {
111
+        $provider = $this->getProvider($token);
112
+        $provider->updateTokenActivity($token);
113
+    }
114
+
115
+    /**
116
+     * @param string $uid
117
+     * @return IToken[]
118
+     */
119
+    public function getTokenByUser(string $uid): array {
120
+        return $this->publicKeyTokenProvider->getTokenByUser($uid);
121
+    }
122
+
123
+    /**
124
+     * Get a token by token
125
+     *
126
+     * @param string $tokenId
127
+     * @throws InvalidTokenException
128
+     * @throws \RuntimeException when OpenSSL reports a problem
129
+     * @return IToken
130
+     */
131
+    public function getToken(string $tokenId): IToken {
132
+        try {
133
+            return $this->publicKeyTokenProvider->getToken($tokenId);
134
+        } catch (WipeTokenException $e) {
135
+            throw $e;
136
+        } catch (ExpiredTokenException $e) {
137
+            throw $e;
138
+        } catch (InvalidTokenException $e) {
139
+            throw $e;
140
+        }
141
+    }
142
+
143
+    /**
144
+     * Get a token by token id
145
+     *
146
+     * @param int $tokenId
147
+     * @throws InvalidTokenException
148
+     * @return IToken
149
+     */
150
+    public function getTokenById(int $tokenId): IToken {
151
+        try {
152
+            return $this->publicKeyTokenProvider->getTokenById($tokenId);
153
+        } catch (ExpiredTokenException $e) {
154
+            throw $e;
155
+        } catch (WipeTokenException $e) {
156
+            throw $e;
157
+        } catch (InvalidTokenException $e) {
158
+            throw $e;
159
+        }
160
+    }
161
+
162
+    /**
163
+     * @param string $oldSessionId
164
+     * @param string $sessionId
165
+     * @throws InvalidTokenException
166
+     * @return IToken
167
+     */
168
+    public function renewSessionToken(string $oldSessionId, string $sessionId): IToken {
169
+        try {
170
+            return $this->publicKeyTokenProvider->renewSessionToken($oldSessionId, $sessionId);
171
+        } catch (ExpiredTokenException $e) {
172
+            throw $e;
173
+        } catch (InvalidTokenException $e) {
174
+            throw $e;
175
+        }
176
+    }
177
+
178
+    /**
179
+     * @param IToken $savedToken
180
+     * @param string $tokenId session token
181
+     * @throws InvalidTokenException
182
+     * @throws PasswordlessTokenException
183
+     * @return string
184
+     */
185
+    public function getPassword(IToken $savedToken, string $tokenId): string {
186
+        $provider = $this->getProvider($savedToken);
187
+        return $provider->getPassword($savedToken, $tokenId);
188
+    }
189
+
190
+    public function setPassword(IToken $token, string $tokenId, string $password) {
191
+        $provider = $this->getProvider($token);
192
+        $provider->setPassword($token, $tokenId, $password);
193
+    }
194
+
195
+    public function invalidateToken(string $token) {
196
+        $this->publicKeyTokenProvider->invalidateToken($token);
197
+    }
198
+
199
+    public function invalidateTokenById(string $uid, int $id) {
200
+        $this->publicKeyTokenProvider->invalidateTokenById($uid, $id);
201
+    }
202
+
203
+    public function invalidateOldTokens() {
204
+        $this->publicKeyTokenProvider->invalidateOldTokens();
205
+    }
206
+
207
+    /**
208
+     * @param IToken $token
209
+     * @param string $oldTokenId
210
+     * @param string $newTokenId
211
+     * @return IToken
212
+     * @throws InvalidTokenException
213
+     * @throws \RuntimeException when OpenSSL reports a problem
214
+     */
215
+    public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken {
216
+        if ($token instanceof PublicKeyToken) {
217
+            return $this->publicKeyTokenProvider->rotate($token, $oldTokenId, $newTokenId);
218
+        }
219
+
220
+        throw new InvalidTokenException();
221
+    }
222
+
223
+    /**
224
+     * @param IToken $token
225
+     * @return IProvider
226
+     * @throws InvalidTokenException
227
+     */
228
+    private function getProvider(IToken $token): IProvider {
229
+        if ($token instanceof PublicKeyToken) {
230
+            return $this->publicKeyTokenProvider;
231
+        }
232
+        throw new InvalidTokenException();
233
+    }
234
+
235
+
236
+    public function markPasswordInvalid(IToken $token, string $tokenId) {
237
+        $this->getProvider($token)->markPasswordInvalid($token, $tokenId);
238
+    }
239
+
240
+    public function updatePasswords(string $uid, string $password) {
241
+        $this->publicKeyTokenProvider->updatePasswords($uid, $password);
242
+    }
243 243
 }
Please login to merge, or discard this patch.