@@ -34,73 +34,73 @@ |
||
34 | 34 | use OCP\IURLGenerator; |
35 | 35 | |
36 | 36 | class LoginRedirectorController extends Controller { |
37 | - /** @var IURLGenerator */ |
|
38 | - private $urlGenerator; |
|
39 | - /** @var ClientMapper */ |
|
40 | - private $clientMapper; |
|
41 | - /** @var ISession */ |
|
42 | - private $session; |
|
43 | - /** @var IL10N */ |
|
44 | - private $l; |
|
37 | + /** @var IURLGenerator */ |
|
38 | + private $urlGenerator; |
|
39 | + /** @var ClientMapper */ |
|
40 | + private $clientMapper; |
|
41 | + /** @var ISession */ |
|
42 | + private $session; |
|
43 | + /** @var IL10N */ |
|
44 | + private $l; |
|
45 | 45 | |
46 | - /** |
|
47 | - * @param string $appName |
|
48 | - * @param IRequest $request |
|
49 | - * @param IURLGenerator $urlGenerator |
|
50 | - * @param ClientMapper $clientMapper |
|
51 | - * @param ISession $session |
|
52 | - * @param IL10N $l |
|
53 | - */ |
|
54 | - public function __construct(string $appName, |
|
55 | - IRequest $request, |
|
56 | - IURLGenerator $urlGenerator, |
|
57 | - ClientMapper $clientMapper, |
|
58 | - ISession $session, |
|
59 | - IL10N $l) { |
|
60 | - parent::__construct($appName, $request); |
|
61 | - $this->urlGenerator = $urlGenerator; |
|
62 | - $this->clientMapper = $clientMapper; |
|
63 | - $this->session = $session; |
|
64 | - $this->l = $l; |
|
65 | - } |
|
46 | + /** |
|
47 | + * @param string $appName |
|
48 | + * @param IRequest $request |
|
49 | + * @param IURLGenerator $urlGenerator |
|
50 | + * @param ClientMapper $clientMapper |
|
51 | + * @param ISession $session |
|
52 | + * @param IL10N $l |
|
53 | + */ |
|
54 | + public function __construct(string $appName, |
|
55 | + IRequest $request, |
|
56 | + IURLGenerator $urlGenerator, |
|
57 | + ClientMapper $clientMapper, |
|
58 | + ISession $session, |
|
59 | + IL10N $l) { |
|
60 | + parent::__construct($appName, $request); |
|
61 | + $this->urlGenerator = $urlGenerator; |
|
62 | + $this->clientMapper = $clientMapper; |
|
63 | + $this->session = $session; |
|
64 | + $this->l = $l; |
|
65 | + } |
|
66 | 66 | |
67 | - /** |
|
68 | - * @PublicPage |
|
69 | - * @NoCSRFRequired |
|
70 | - * @UseSession |
|
71 | - * |
|
72 | - * @param string $client_id |
|
73 | - * @param string $state |
|
74 | - * @param string $response_type |
|
75 | - * @return Response |
|
76 | - */ |
|
77 | - public function authorize($client_id, |
|
78 | - $state, |
|
79 | - $response_type): Response { |
|
80 | - try { |
|
81 | - $client = $this->clientMapper->getByIdentifier($client_id); |
|
82 | - } catch (ClientNotFoundException $e) { |
|
83 | - $response = new TemplateResponse('core', '404', 'guest'); |
|
84 | - $response->setParams([ |
|
85 | - 'content' => $this->l->t('Your client is not authorized to connect. Please inform the administrator of your client.'), |
|
86 | - ]); |
|
87 | - return $response; |
|
88 | - } |
|
67 | + /** |
|
68 | + * @PublicPage |
|
69 | + * @NoCSRFRequired |
|
70 | + * @UseSession |
|
71 | + * |
|
72 | + * @param string $client_id |
|
73 | + * @param string $state |
|
74 | + * @param string $response_type |
|
75 | + * @return Response |
|
76 | + */ |
|
77 | + public function authorize($client_id, |
|
78 | + $state, |
|
79 | + $response_type): Response { |
|
80 | + try { |
|
81 | + $client = $this->clientMapper->getByIdentifier($client_id); |
|
82 | + } catch (ClientNotFoundException $e) { |
|
83 | + $response = new TemplateResponse('core', '404', 'guest'); |
|
84 | + $response->setParams([ |
|
85 | + 'content' => $this->l->t('Your client is not authorized to connect. Please inform the administrator of your client.'), |
|
86 | + ]); |
|
87 | + return $response; |
|
88 | + } |
|
89 | 89 | |
90 | - if ($response_type !== 'code') { |
|
91 | - //Fail |
|
92 | - $url = $client->getRedirectUri() . '?error=unsupported_response_type&state=' . $state; |
|
93 | - return new RedirectResponse($url); |
|
94 | - } |
|
90 | + if ($response_type !== 'code') { |
|
91 | + //Fail |
|
92 | + $url = $client->getRedirectUri() . '?error=unsupported_response_type&state=' . $state; |
|
93 | + return new RedirectResponse($url); |
|
94 | + } |
|
95 | 95 | |
96 | - $this->session->set('oauth.state', $state); |
|
96 | + $this->session->set('oauth.state', $state); |
|
97 | 97 | |
98 | - $targetUrl = $this->urlGenerator->linkToRouteAbsolute( |
|
99 | - 'core.ClientFlowLogin.showAuthPickerPage', |
|
100 | - [ |
|
101 | - 'clientIdentifier' => $client->getClientIdentifier(), |
|
102 | - ] |
|
103 | - ); |
|
104 | - return new RedirectResponse($targetUrl); |
|
105 | - } |
|
98 | + $targetUrl = $this->urlGenerator->linkToRouteAbsolute( |
|
99 | + 'core.ClientFlowLogin.showAuthPickerPage', |
|
100 | + [ |
|
101 | + 'clientIdentifier' => $client->getClientIdentifier(), |
|
102 | + ] |
|
103 | + ); |
|
104 | + return new RedirectResponse($targetUrl); |
|
105 | + } |
|
106 | 106 | } |
@@ -39,137 +39,137 @@ |
||
39 | 39 | use OCP\Security\ISecureRandom; |
40 | 40 | |
41 | 41 | class OauthApiController extends Controller { |
42 | - /** @var AccessTokenMapper */ |
|
43 | - private $accessTokenMapper; |
|
44 | - /** @var ClientMapper */ |
|
45 | - private $clientMapper; |
|
46 | - /** @var ICrypto */ |
|
47 | - private $crypto; |
|
48 | - /** @var TokenProvider */ |
|
49 | - private $tokenProvider; |
|
50 | - /** @var ISecureRandom */ |
|
51 | - private $secureRandom; |
|
52 | - /** @var ITimeFactory */ |
|
53 | - private $time; |
|
54 | - /** @var Throttler */ |
|
55 | - private $throttler; |
|
56 | - |
|
57 | - public function __construct(string $appName, |
|
58 | - IRequest $request, |
|
59 | - ICrypto $crypto, |
|
60 | - AccessTokenMapper $accessTokenMapper, |
|
61 | - ClientMapper $clientMapper, |
|
62 | - TokenProvider $tokenProvider, |
|
63 | - ISecureRandom $secureRandom, |
|
64 | - ITimeFactory $time, |
|
65 | - Throttler $throttler) { |
|
66 | - parent::__construct($appName, $request); |
|
67 | - $this->crypto = $crypto; |
|
68 | - $this->accessTokenMapper = $accessTokenMapper; |
|
69 | - $this->clientMapper = $clientMapper; |
|
70 | - $this->tokenProvider = $tokenProvider; |
|
71 | - $this->secureRandom = $secureRandom; |
|
72 | - $this->time = $time; |
|
73 | - $this->throttler = $throttler; |
|
74 | - } |
|
75 | - |
|
76 | - /** |
|
77 | - * @PublicPage |
|
78 | - * @NoCSRFRequired |
|
79 | - * |
|
80 | - * @param string $grant_type |
|
81 | - * @param string $code |
|
82 | - * @param string $refresh_token |
|
83 | - * @param string $client_id |
|
84 | - * @param string $client_secret |
|
85 | - * @return JSONResponse |
|
86 | - */ |
|
87 | - public function getToken($grant_type, $code, $refresh_token, $client_id, $client_secret): JSONResponse { |
|
88 | - |
|
89 | - // We only handle two types |
|
90 | - if ($grant_type !== 'authorization_code' && $grant_type !== 'refresh_token') { |
|
91 | - return new JSONResponse([ |
|
92 | - 'error' => 'invalid_grant', |
|
93 | - ], Http::STATUS_BAD_REQUEST); |
|
94 | - } |
|
95 | - |
|
96 | - // We handle the initial and refresh tokens the same way |
|
97 | - if ($grant_type === 'refresh_token' ) { |
|
98 | - $code = $refresh_token; |
|
99 | - } |
|
100 | - |
|
101 | - try { |
|
102 | - $accessToken = $this->accessTokenMapper->getByCode($code); |
|
103 | - } catch (AccessTokenNotFoundException $e) { |
|
104 | - return new JSONResponse([ |
|
105 | - 'error' => 'invalid_request', |
|
106 | - ], Http::STATUS_BAD_REQUEST); |
|
107 | - } |
|
108 | - |
|
109 | - try { |
|
110 | - $client = $this->clientMapper->getByUid($accessToken->getClientId()); |
|
111 | - } catch (ClientNotFoundException $e) { |
|
112 | - return new JSONResponse([ |
|
113 | - 'error' => 'invalid_request', |
|
114 | - ], Http::STATUS_BAD_REQUEST); |
|
115 | - } |
|
116 | - |
|
117 | - if (isset($this->request->server['PHP_AUTH_USER'])) { |
|
118 | - $client_id = $this->request->server['PHP_AUTH_USER']; |
|
119 | - $client_secret = $this->request->server['PHP_AUTH_PW']; |
|
120 | - } |
|
121 | - |
|
122 | - // The client id and secret must match. Else we don't provide an access token! |
|
123 | - if ($client->getClientIdentifier() !== $client_id || $client->getSecret() !== $client_secret) { |
|
124 | - return new JSONResponse([ |
|
125 | - 'error' => 'invalid_client', |
|
126 | - ], Http::STATUS_BAD_REQUEST); |
|
127 | - } |
|
128 | - |
|
129 | - $decryptedToken = $this->crypto->decrypt($accessToken->getEncryptedToken(), $code); |
|
130 | - |
|
131 | - // Obtain the appToken assoicated |
|
132 | - try { |
|
133 | - $appToken = $this->tokenProvider->getTokenById($accessToken->getTokenId()); |
|
134 | - } catch (ExpiredTokenException $e) { |
|
135 | - $appToken = $e->getToken(); |
|
136 | - } catch (InvalidTokenException $e) { |
|
137 | - //We can't do anything... |
|
138 | - $this->accessTokenMapper->delete($accessToken); |
|
139 | - return new JSONResponse([ |
|
140 | - 'error' => 'invalid_request', |
|
141 | - ], Http::STATUS_BAD_REQUEST); |
|
142 | - } |
|
143 | - |
|
144 | - // Rotate the apptoken (so the old one becomes invalid basically) |
|
145 | - $newToken = $this->secureRandom->generate(72, ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_DIGITS); |
|
146 | - |
|
147 | - $appToken = $this->tokenProvider->rotate( |
|
148 | - $appToken, |
|
149 | - $decryptedToken, |
|
150 | - $newToken |
|
151 | - ); |
|
152 | - |
|
153 | - // Expiration is in 1 hour again |
|
154 | - $appToken->setExpires($this->time->getTime() + 3600); |
|
155 | - $this->tokenProvider->updateToken($appToken); |
|
156 | - |
|
157 | - // Generate a new refresh token and encrypt the new apptoken in the DB |
|
158 | - $newCode = $this->secureRandom->generate(128, ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_DIGITS); |
|
159 | - $accessToken->setHashedCode(hash('sha512', $newCode)); |
|
160 | - $accessToken->setEncryptedToken($this->crypto->encrypt($newToken, $newCode)); |
|
161 | - $this->accessTokenMapper->update($accessToken); |
|
162 | - |
|
163 | - $this->throttler->resetDelay($this->request->getRemoteAddress(), 'login', ['user' => $appToken->getUID()]); |
|
164 | - |
|
165 | - return new JSONResponse( |
|
166 | - [ |
|
167 | - 'access_token' => $newToken, |
|
168 | - 'token_type' => 'Bearer', |
|
169 | - 'expires_in' => 3600, |
|
170 | - 'refresh_token' => $newCode, |
|
171 | - 'user_id' => $appToken->getUID(), |
|
172 | - ] |
|
173 | - ); |
|
174 | - } |
|
42 | + /** @var AccessTokenMapper */ |
|
43 | + private $accessTokenMapper; |
|
44 | + /** @var ClientMapper */ |
|
45 | + private $clientMapper; |
|
46 | + /** @var ICrypto */ |
|
47 | + private $crypto; |
|
48 | + /** @var TokenProvider */ |
|
49 | + private $tokenProvider; |
|
50 | + /** @var ISecureRandom */ |
|
51 | + private $secureRandom; |
|
52 | + /** @var ITimeFactory */ |
|
53 | + private $time; |
|
54 | + /** @var Throttler */ |
|
55 | + private $throttler; |
|
56 | + |
|
57 | + public function __construct(string $appName, |
|
58 | + IRequest $request, |
|
59 | + ICrypto $crypto, |
|
60 | + AccessTokenMapper $accessTokenMapper, |
|
61 | + ClientMapper $clientMapper, |
|
62 | + TokenProvider $tokenProvider, |
|
63 | + ISecureRandom $secureRandom, |
|
64 | + ITimeFactory $time, |
|
65 | + Throttler $throttler) { |
|
66 | + parent::__construct($appName, $request); |
|
67 | + $this->crypto = $crypto; |
|
68 | + $this->accessTokenMapper = $accessTokenMapper; |
|
69 | + $this->clientMapper = $clientMapper; |
|
70 | + $this->tokenProvider = $tokenProvider; |
|
71 | + $this->secureRandom = $secureRandom; |
|
72 | + $this->time = $time; |
|
73 | + $this->throttler = $throttler; |
|
74 | + } |
|
75 | + |
|
76 | + /** |
|
77 | + * @PublicPage |
|
78 | + * @NoCSRFRequired |
|
79 | + * |
|
80 | + * @param string $grant_type |
|
81 | + * @param string $code |
|
82 | + * @param string $refresh_token |
|
83 | + * @param string $client_id |
|
84 | + * @param string $client_secret |
|
85 | + * @return JSONResponse |
|
86 | + */ |
|
87 | + public function getToken($grant_type, $code, $refresh_token, $client_id, $client_secret): JSONResponse { |
|
88 | + |
|
89 | + // We only handle two types |
|
90 | + if ($grant_type !== 'authorization_code' && $grant_type !== 'refresh_token') { |
|
91 | + return new JSONResponse([ |
|
92 | + 'error' => 'invalid_grant', |
|
93 | + ], Http::STATUS_BAD_REQUEST); |
|
94 | + } |
|
95 | + |
|
96 | + // We handle the initial and refresh tokens the same way |
|
97 | + if ($grant_type === 'refresh_token' ) { |
|
98 | + $code = $refresh_token; |
|
99 | + } |
|
100 | + |
|
101 | + try { |
|
102 | + $accessToken = $this->accessTokenMapper->getByCode($code); |
|
103 | + } catch (AccessTokenNotFoundException $e) { |
|
104 | + return new JSONResponse([ |
|
105 | + 'error' => 'invalid_request', |
|
106 | + ], Http::STATUS_BAD_REQUEST); |
|
107 | + } |
|
108 | + |
|
109 | + try { |
|
110 | + $client = $this->clientMapper->getByUid($accessToken->getClientId()); |
|
111 | + } catch (ClientNotFoundException $e) { |
|
112 | + return new JSONResponse([ |
|
113 | + 'error' => 'invalid_request', |
|
114 | + ], Http::STATUS_BAD_REQUEST); |
|
115 | + } |
|
116 | + |
|
117 | + if (isset($this->request->server['PHP_AUTH_USER'])) { |
|
118 | + $client_id = $this->request->server['PHP_AUTH_USER']; |
|
119 | + $client_secret = $this->request->server['PHP_AUTH_PW']; |
|
120 | + } |
|
121 | + |
|
122 | + // The client id and secret must match. Else we don't provide an access token! |
|
123 | + if ($client->getClientIdentifier() !== $client_id || $client->getSecret() !== $client_secret) { |
|
124 | + return new JSONResponse([ |
|
125 | + 'error' => 'invalid_client', |
|
126 | + ], Http::STATUS_BAD_REQUEST); |
|
127 | + } |
|
128 | + |
|
129 | + $decryptedToken = $this->crypto->decrypt($accessToken->getEncryptedToken(), $code); |
|
130 | + |
|
131 | + // Obtain the appToken assoicated |
|
132 | + try { |
|
133 | + $appToken = $this->tokenProvider->getTokenById($accessToken->getTokenId()); |
|
134 | + } catch (ExpiredTokenException $e) { |
|
135 | + $appToken = $e->getToken(); |
|
136 | + } catch (InvalidTokenException $e) { |
|
137 | + //We can't do anything... |
|
138 | + $this->accessTokenMapper->delete($accessToken); |
|
139 | + return new JSONResponse([ |
|
140 | + 'error' => 'invalid_request', |
|
141 | + ], Http::STATUS_BAD_REQUEST); |
|
142 | + } |
|
143 | + |
|
144 | + // Rotate the apptoken (so the old one becomes invalid basically) |
|
145 | + $newToken = $this->secureRandom->generate(72, ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_DIGITS); |
|
146 | + |
|
147 | + $appToken = $this->tokenProvider->rotate( |
|
148 | + $appToken, |
|
149 | + $decryptedToken, |
|
150 | + $newToken |
|
151 | + ); |
|
152 | + |
|
153 | + // Expiration is in 1 hour again |
|
154 | + $appToken->setExpires($this->time->getTime() + 3600); |
|
155 | + $this->tokenProvider->updateToken($appToken); |
|
156 | + |
|
157 | + // Generate a new refresh token and encrypt the new apptoken in the DB |
|
158 | + $newCode = $this->secureRandom->generate(128, ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_DIGITS); |
|
159 | + $accessToken->setHashedCode(hash('sha512', $newCode)); |
|
160 | + $accessToken->setEncryptedToken($this->crypto->encrypt($newToken, $newCode)); |
|
161 | + $this->accessTokenMapper->update($accessToken); |
|
162 | + |
|
163 | + $this->throttler->resetDelay($this->request->getRemoteAddress(), 'login', ['user' => $appToken->getUID()]); |
|
164 | + |
|
165 | + return new JSONResponse( |
|
166 | + [ |
|
167 | + 'access_token' => $newToken, |
|
168 | + 'token_type' => 'Bearer', |
|
169 | + 'expires_in' => 3600, |
|
170 | + 'refresh_token' => $newCode, |
|
171 | + 'user_id' => $appToken->getUID(), |
|
172 | + ] |
|
173 | + ); |
|
174 | + } |
|
175 | 175 | } |
@@ -34,45 +34,45 @@ |
||
34 | 34 | |
35 | 35 | class SetTokenExpiration implements IRepairStep { |
36 | 36 | |
37 | - /** @var IDBConnection */ |
|
38 | - private $connection; |
|
37 | + /** @var IDBConnection */ |
|
38 | + private $connection; |
|
39 | 39 | |
40 | - /** @var ITimeFactory */ |
|
41 | - private $time; |
|
40 | + /** @var ITimeFactory */ |
|
41 | + private $time; |
|
42 | 42 | |
43 | - /** @var TokenProvider */ |
|
44 | - private $tokenProvider; |
|
43 | + /** @var TokenProvider */ |
|
44 | + private $tokenProvider; |
|
45 | 45 | |
46 | - public function __construct(IDBConnection $connection, |
|
47 | - ITimeFactory $timeFactory, |
|
48 | - TokenProvider $tokenProvider) { |
|
49 | - $this->connection = $connection; |
|
50 | - $this->time = $timeFactory; |
|
51 | - $this->tokenProvider = $tokenProvider; |
|
52 | - } |
|
46 | + public function __construct(IDBConnection $connection, |
|
47 | + ITimeFactory $timeFactory, |
|
48 | + TokenProvider $tokenProvider) { |
|
49 | + $this->connection = $connection; |
|
50 | + $this->time = $timeFactory; |
|
51 | + $this->tokenProvider = $tokenProvider; |
|
52 | + } |
|
53 | 53 | |
54 | - public function getName(): string { |
|
55 | - return 'Update OAuth token expiration times'; |
|
56 | - } |
|
54 | + public function getName(): string { |
|
55 | + return 'Update OAuth token expiration times'; |
|
56 | + } |
|
57 | 57 | |
58 | - public function run(IOutput $output) { |
|
59 | - $qb = $this->connection->getQueryBuilder(); |
|
60 | - $qb->select('*') |
|
61 | - ->from('oauth2_access_tokens'); |
|
58 | + public function run(IOutput $output) { |
|
59 | + $qb = $this->connection->getQueryBuilder(); |
|
60 | + $qb->select('*') |
|
61 | + ->from('oauth2_access_tokens'); |
|
62 | 62 | |
63 | - $cursor = $qb->execute(); |
|
63 | + $cursor = $qb->execute(); |
|
64 | 64 | |
65 | - while($row = $cursor->fetch()) { |
|
66 | - $token = AccessToken::fromRow($row); |
|
67 | - try { |
|
68 | - $appToken = $this->tokenProvider->getTokenById($token->getTokenId()); |
|
69 | - $appToken->setExpires($this->time->getTime() + 3600); |
|
70 | - $this->tokenProvider->updateToken($appToken); |
|
71 | - } catch (InvalidTokenException $e) { |
|
72 | - //Skip this token |
|
73 | - } |
|
74 | - } |
|
75 | - $cursor->closeCursor(); |
|
76 | - } |
|
65 | + while($row = $cursor->fetch()) { |
|
66 | + $token = AccessToken::fromRow($row); |
|
67 | + try { |
|
68 | + $appToken = $this->tokenProvider->getTokenById($token->getTokenId()); |
|
69 | + $appToken->setExpires($this->time->getTime() + 3600); |
|
70 | + $this->tokenProvider->updateToken($appToken); |
|
71 | + } catch (InvalidTokenException $e) { |
|
72 | + //Skip this token |
|
73 | + } |
|
74 | + } |
|
75 | + $cursor->closeCursor(); |
|
76 | + } |
|
77 | 77 | |
78 | 78 | } |