1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* DefaultController.php |
4
|
|
|
* |
5
|
|
|
* PHP version 5.6+ |
6
|
|
|
* |
7
|
|
|
* @author pgaultier |
8
|
|
|
* @copyright 2010-2017 Philippe Gaultier |
9
|
|
|
* @license http://www.sweelix.net/license license |
10
|
|
|
* @version 1.2.0 |
11
|
|
|
* @link http://www.sweelix.net |
12
|
|
|
* @package sweelix\oauth2\server\controllers |
13
|
|
|
*/ |
14
|
|
|
|
15
|
|
|
namespace sweelix\oauth2\server\controllers; |
16
|
|
|
|
17
|
|
|
use OAuth2\Request as OAuth2Request; |
18
|
|
|
use OAuth2\Response as OAuth2Response; |
19
|
|
|
use sweelix\oauth2\server\models\Client; |
20
|
|
|
use sweelix\oauth2\server\models\Scope; |
21
|
|
|
use sweelix\oauth2\server\Module; |
22
|
|
|
use yii\filters\AccessControl; |
23
|
|
|
use yii\web\Controller; |
24
|
|
|
use yii\web\Response; |
25
|
|
|
use Yii; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Oauth2 main controller |
29
|
|
|
* |
30
|
|
|
* @author pgaultier |
31
|
|
|
* @copyright 2010-2017 Philippe Gaultier |
32
|
|
|
* @license http://www.sweelix.net/license license |
33
|
|
|
* @version 1.2.0 |
34
|
|
|
* @link http://www.sweelix.net |
35
|
|
|
* @package sweelix\oauth2\server\controllers |
36
|
|
|
* @since 1.0.0 |
37
|
|
|
*/ |
38
|
|
|
class AuthorizeController extends Controller |
39
|
|
|
{ |
40
|
|
|
/** |
41
|
|
|
* @var string |
42
|
|
|
*/ |
43
|
|
|
private $userClass; |
44
|
10 |
|
|
45
|
|
|
/** |
46
|
10 |
|
* @inheritdoc |
47
|
|
|
*/ |
48
|
10 |
|
public function init() |
49
|
|
|
{ |
50
|
|
|
$module = Module::getInstance(); |
51
|
|
|
|
52
|
10 |
|
if ($module->overrideLayout !== null) { |
|
|
|
|
53
|
|
|
$this->layout = $module->overrideLayout; |
|
|
|
|
54
|
|
|
} |
55
|
|
|
|
56
|
10 |
|
if ($module->overrideViewPath !== null) { |
|
|
|
|
57
|
10 |
|
$this->setViewPath($module->overrideViewPath); |
|
|
|
|
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
parent::init(); |
61
|
|
|
} |
62
|
10 |
|
|
63
|
|
|
/** |
64
|
10 |
|
* @inheritdoc |
65
|
10 |
|
*/ |
66
|
10 |
|
public function behaviors() |
67
|
10 |
|
{ |
68
|
|
|
$behaviors = parent::behaviors(); |
69
|
|
|
$behaviors['access'] = [ |
70
|
10 |
|
'class' => AccessControl::class, |
71
|
10 |
|
'only' => ['authorize'], |
72
|
10 |
|
'rules' => [ |
73
|
10 |
|
[ |
74
|
10 |
|
'allow' => true, |
75
|
|
|
'actions' => ['authorize'], |
76
|
10 |
|
'roles' => ['@'], |
77
|
|
|
], |
78
|
|
|
], |
79
|
|
|
]; |
80
|
|
|
return $behaviors; |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
9 |
|
* Send back an oauth token |
85
|
|
|
* |
86
|
9 |
|
* @return Response |
87
|
9 |
|
* @throws \yii\base\InvalidConfigException |
88
|
|
|
* @since 1.0.0 |
89
|
9 |
|
*/ |
90
|
9 |
|
public function actionIndex() |
91
|
9 |
|
{ |
92
|
9 |
|
Yii::$app->response->headers->add('Content-Security-Policy', 'frame-ancestors \'none\';'); |
93
|
|
|
|
94
|
|
|
$status = false; |
95
|
9 |
|
|
96
|
5 |
|
/** @var \OAuth2\Server $oauthServer */ |
97
|
4 |
|
$oauthServer = Yii::createObject('OAuth2\Server'); |
98
|
|
|
|
99
|
4 |
|
/** @var \OAuth2\Request $oauthRequest */ |
100
|
4 |
|
$oauthRequest = OAuth2Request::createFromGlobals(); |
101
|
4 |
|
|
102
|
4 |
|
$oauthResponse = new OAuth2Response(); |
103
|
2 |
|
|
104
|
|
|
/** @var string $grantType */ |
105
|
2 |
|
$grantType = Yii::$app->request->getQueryParam('response_type'); |
106
|
4 |
|
$promptValues = key_exists('prompt', $oauthRequest->query) ? explode(" ", $oauthRequest->query['prompt']) : []; |
107
|
1 |
|
|
108
|
1 |
|
switch ($grantType) { |
109
|
|
|
case 'code': |
110
|
5 |
|
// Authorization Code |
111
|
|
|
if (Module::getInstance()->allowAuthorizationCode === true) { |
112
|
4 |
|
/** @var \Oauth2\GrantType\AuthorizationCode $oauthGrantType */ |
113
|
4 |
|
$oauthGrantType = Yii::createObject('OAuth2\GrantType\AuthorizationCode'); |
114
|
4 |
|
$oauthServer->addGrantType($oauthGrantType); |
115
|
4 |
|
$status = $oauthServer->validateAuthorizeRequest($oauthRequest, $oauthResponse); |
116
|
2 |
|
} else { |
117
|
|
|
$status = false; |
118
|
2 |
|
$oauthResponse->setError(400, 'invalid_grant', 'Authorization code grant is not supported'); |
119
|
4 |
|
} |
120
|
|
|
break; |
121
|
|
|
case 'token': |
122
|
9 |
|
// Implicit |
123
|
4 |
|
$status = $oauthServer->validateAuthorizeRequest($oauthRequest, $oauthResponse); |
124
|
4 |
|
break; |
125
|
4 |
|
} |
126
|
4 |
|
|
127
|
4 |
|
if ($status === false) { |
128
|
4 |
|
// handle request validation error |
129
|
4 |
|
$this->handleErrorResponse($oauthResponse); |
130
|
1 |
|
} else { |
131
|
|
|
Yii::$app->session->set('oauthServer', $oauthServer); |
132
|
4 |
|
if (isset($oauthRequest) === true) { |
133
|
|
|
Yii::$app->session->set('oauthRequest', $oauthRequest); |
134
|
5 |
|
} |
135
|
|
|
|
136
|
9 |
|
if (Yii::$app->user->isGuest === true || in_array('login', $promptValues, true)) { |
137
|
|
|
/** @var \Oauth2\Controller\AuthorizeController $authController */ |
138
|
|
|
$authController = $oauthServer->getAuthorizeController(); |
139
|
|
|
|
140
|
|
|
//TODO: check if the user should get logged out |
141
|
|
|
if (in_array('none', $promptValues, true)) { |
142
|
|
|
$response = new OAuth2Response(); |
143
|
|
|
$response->setRedirect(302, $authController->getRedirectUri(), $authController->getState(), |
144
|
5 |
|
'login_required', |
145
|
|
|
'Authentication Request cannot be completed without user authentication.', |
146
|
5 |
|
NULL |
147
|
|
|
); |
148
|
5 |
|
$this->handleErrorResponse($response); |
149
|
|
|
} else { |
150
|
5 |
|
return $this->redirect(['login']); |
151
|
1 |
|
} |
152
|
1 |
|
} else { |
153
|
1 |
|
return $this->redirect(['authorize']); |
154
|
1 |
|
} |
155
|
1 |
|
} |
156
|
|
|
} |
157
|
|
|
|
158
|
4 |
|
/** |
159
|
4 |
|
* Display login page |
160
|
|
|
* |
161
|
4 |
|
* @return Response | string |
162
|
|
|
* @throws \yii\base\InvalidConfigException |
163
|
4 |
|
* @since 1.0.0 |
164
|
4 |
|
*/ |
165
|
4 |
|
public function actionLogin() |
166
|
4 |
|
{ |
167
|
|
|
Yii::$app->response->headers->add('Content-Security-Policy', 'frame-ancestors \'none\';'); |
168
|
4 |
|
|
169
|
4 |
|
/* @var \Oauth2\Server $oauthServer */ |
170
|
4 |
|
$oauthServer = Yii::$app->session->get('oauthServer'); |
171
|
4 |
|
|
172
|
2 |
|
if ($oauthServer === null) { |
173
|
|
|
$response = new OAuth2Response(); |
174
|
4 |
|
$response->setError(400, 'invalid_request', 'The request was not performed as expected.'); |
175
|
4 |
|
$this->handleErrorResponse($response); |
176
|
4 |
|
} |
177
|
|
|
|
178
|
4 |
|
$response = null; |
179
|
4 |
|
$oauthRequest = Yii::$app->session->get('oauthRequest'); |
180
|
4 |
|
$promptValues = $oauthRequest && key_exists('prompt', $oauthRequest->query) ? explode(" ", $oauthRequest->query['prompt']) : []; |
181
|
4 |
|
/* @var \sweelix\oauth2\server\forms\User $userForm */ |
182
|
4 |
|
$userForm = Yii::createObject('sweelix\oauth2\server\forms\User'); |
183
|
4 |
|
|
184
|
|
|
if (Yii::$app->request->isPost === true) { |
185
|
|
|
//TODO: handle case when user decline the grants |
186
|
|
|
$userForm->load(Yii::$app->request->bodyParams); |
187
|
|
|
if ($userForm->validate() === true) { |
188
|
|
|
$userClass = $this->getUserClass(); |
189
|
|
|
/* @var \sweelix\oauth2\server\interfaces\UserModelInterface $realUser */ |
190
|
|
|
$realUser = $userClass::findByUsernameAndPassword($userForm->username, $userForm->password); |
191
|
4 |
|
if ($realUser !== null) { |
192
|
|
|
Yii::$app->user->login($realUser, Module::getInstance()->loginDuration); |
193
|
4 |
|
$response = $this->redirect(['authorize']); |
194
|
4 |
|
} else { |
195
|
|
|
$userForm->addError('username'); |
196
|
4 |
|
} |
197
|
|
|
} |
198
|
|
|
} |
199
|
|
|
if ($response === null) { |
200
|
|
|
if(in_array('none', $promptValues, true)) { |
201
|
|
|
/** @var \Oauth2\Controller\AuthorizeController $authController */ |
202
|
1 |
|
$authController = $oauthServer->getAuthorizeController(); |
203
|
4 |
|
$oauthResponse = $oauthServer->getResponse(); |
204
|
4 |
|
$oauthResponse->setRedirect(302, $authController->getRedirectUri(), $authController->getState(), |
205
|
|
|
'login_required', |
206
|
4 |
|
'Authentication Request cannot be completed without user authentication.', |
207
|
|
|
null |
208
|
1 |
|
); |
209
|
1 |
|
$this->handleErrorResponse($oauthResponse); |
|
|
|
|
210
|
1 |
|
} |
211
|
|
|
// force empty password |
212
|
1 |
|
$userForm->password = ''; |
213
|
1 |
|
$response = $this->render('login', [ |
|
|
|
|
214
|
1 |
|
'user' => $userForm, |
215
|
1 |
|
]); |
216
|
|
|
} |
217
|
|
|
return $response; |
218
|
|
|
} |
219
|
1 |
|
|
220
|
|
|
/** |
221
|
|
|
* Display authorize page |
222
|
|
|
* |
223
|
4 |
|
* @return Response | string |
224
|
4 |
|
* @throws \yii\base\UnknownClassException |
225
|
4 |
|
* @since 1.0.0 |
226
|
|
|
*/ |
227
|
|
|
public function actionAuthorize() |
228
|
|
|
{ |
229
|
|
|
Yii::$app->response->headers->add('Content-Security-Policy', 'frame-ancestors \'none\';'); |
230
|
|
|
|
231
|
|
|
/* @var \Oauth2\Server $oauthServer */ |
232
|
|
|
$oauthServer = Yii::$app->session->get('oauthServer'); |
233
|
|
|
|
234
|
|
|
if ($oauthServer === NULL) { |
235
|
|
|
$response = new OAuth2Response(); |
236
|
|
|
$response->setError(400, 'invalid_request', 'The request was not performed as expected.'); |
237
|
|
|
$this->handleErrorResponse($response); |
238
|
|
|
} |
239
|
|
|
|
240
|
4 |
|
/** @var \OAuth2\Controller\AuthorizeController $authController */ |
241
|
4 |
|
$authController = $oauthServer->getAuthorizeController(); |
242
|
4 |
|
$client = Client::findOne($authController->getClientId()); |
243
|
4 |
|
$oauthRequest = Yii::$app->session->get('oauthRequest'); |
244
|
|
|
$promptValues = $oauthRequest && key_exists('prompt', $oauthRequest->query) ? explode(" ", $oauthRequest->query['prompt']) : []; |
245
|
|
|
|
246
|
4 |
|
// user has already granted permission |
247
|
2 |
|
if ($client->hasUser(Yii::$app->user->id) === true && !in_array('consent', $promptValues, true)) { |
248
|
2 |
|
//TODO: check if all consents should be removed |
249
|
|
|
/** @var \OAuth2\Response $oauthResponse */ |
250
|
2 |
|
$oauthResponse = new OAuth2Response(); |
251
|
2 |
|
$oauthResponse = $oauthServer->handleAuthorizeRequest($oauthRequest, $oauthResponse, true, Yii::$app->user->id); |
252
|
2 |
|
|
253
|
|
|
if ($oauthResponse->getParameter('error') === NULL) { |
254
|
|
|
Yii::$app->session->remove('oauthServer'); |
255
|
|
|
Yii::$app->session->remove('oauthRequest'); |
256
|
4 |
|
|
257
|
4 |
|
return $this->redirect($oauthResponse->getHttpHeader('Location')); |
258
|
4 |
|
} else { |
259
|
4 |
|
$this->handleErrorResponse($oauthResponse); |
|
|
|
|
260
|
4 |
|
} |
261
|
|
|
} else { |
262
|
|
|
// perform regular authorization |
263
|
|
|
if (in_array('none', $promptValues, true)) { |
264
|
4 |
|
$oauthResponse = $oauthServer->getResponse(); |
265
|
|
|
$oauthResponse->setRedirect(302, $authController->getRedirectUri(), $authController->getState(), |
266
|
|
|
'consent_required', |
267
|
|
|
'Authentication Request cannot be completed without End-User consent.', |
268
|
|
|
NULL |
269
|
4 |
|
); |
270
|
4 |
|
$this->handleErrorResponse($oauthResponse); |
|
|
|
|
271
|
4 |
|
} |
272
|
4 |
|
|
273
|
|
|
$additionalScopes = $authController->getScope(); |
274
|
|
|
$requestedScopes = []; |
275
|
|
|
if (empty($additionalScopes) === false) { |
276
|
|
|
$additionalScopes = explode(' ', $additionalScopes); |
277
|
|
|
foreach ($additionalScopes as $scope) { |
278
|
|
|
$dbScope = Scope::findOne($scope); |
279
|
|
|
if ($dbScope !== NULL) { |
280
|
6 |
|
$requestedScopes[] = $dbScope; |
281
|
|
|
} else { |
282
|
6 |
|
$response = new OAuth2Response(); |
283
|
6 |
|
$response->setError(400, 'invalid_scope', 'Scope ' . $scope . ' does not exist.'); |
284
|
6 |
|
$this->handleErrorResponse($response); |
285
|
6 |
|
} |
286
|
6 |
|
} |
287
|
6 |
|
} |
288
|
|
|
if (Yii::$app->request->isPost === true) { |
289
|
|
|
$accept = Yii::$app->request->getBodyParam('accept'); |
290
|
|
|
/** @var \Oauth2\Request $oauthRequest */ |
291
|
|
|
$oauthRequest = Yii::$app->session->get('oauthRequest'); |
292
|
|
|
/** @var \Oauth2\Response $oauthResponse */ |
293
|
|
|
$oauthResponse = new OAuth2Response(); |
294
|
|
|
|
295
|
|
|
if ($accept !== NULL) { |
296
|
|
|
// authorize |
297
|
|
|
$oauthResponse = $oauthServer->handleAuthorizeRequest($oauthRequest, $oauthResponse, true, Yii::$app->user->id); |
298
|
|
|
// check if user has not already granted the client |
299
|
4 |
|
if ($client->hasUser(Yii::$app->user->id) === false) { |
300
|
|
|
$client->addUser(Yii::$app->user->id); |
301
|
4 |
|
} |
302
|
4 |
|
} else { |
303
|
4 |
|
// decline |
304
|
4 |
|
$oauthResponse = $oauthServer->handleAuthorizeRequest($oauthRequest, $oauthResponse, false, Yii::$app->user->id); |
305
|
4 |
|
$client->removeUser(Yii::$app->user->id); |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
Yii::$app->session->remove('oauthServer'); |
309
|
|
|
Yii::$app->session->remove('oauthRequest'); |
310
|
|
|
$error = $oauthResponse->getParameters(); |
|
|
|
|
311
|
|
|
$redirect = $oauthResponse->getHttpHeader('Location'); |
312
|
|
|
if ((empty($error) === false) && ($redirect === NULL)) { |
313
|
|
|
Yii::$app->session->setFlash('error', $error, false); |
314
|
|
|
return $this->redirect(['error']); |
315
|
|
|
} else { |
316
|
|
|
return $this->redirect($redirect); |
317
|
|
|
} |
318
|
|
|
} |
319
|
|
|
} |
320
|
|
|
return $this->render('authorize', [ |
|
|
|
|
321
|
|
|
'client' => $client, |
322
|
|
|
'requestedScopes' => $requestedScopes, |
|
|
|
|
323
|
|
|
]); |
324
|
|
|
} |
325
|
|
|
|
326
|
|
|
/** |
327
|
|
|
* Display an error page |
328
|
|
|
* |
329
|
|
|
* @return Response | string |
330
|
|
|
* @since 1.0.0 |
331
|
|
|
*/ |
332
|
|
|
public function actionError() |
333
|
|
|
{ |
334
|
|
|
Yii::$app->response->headers->add('Content-Security-Policy', 'frame-ancestors \'none\';'); |
335
|
|
|
|
336
|
|
|
$errorData = Yii::$app->session->getFlash('error'); |
337
|
|
|
$code = isset($errorData['code']) && is_numeric($errorData['code']) ? $errorData['code'] : 400; |
338
|
|
|
Yii::$app->response->setStatusCode($code); |
339
|
|
|
|
340
|
|
|
return $this->render('error', [ |
341
|
|
|
'type' => (isset($errorData['error']) ? $errorData['error'] : null), |
342
|
|
|
'description' => (isset($errorData['error_description']) ? $errorData['error_description'] : null), |
343
|
|
|
]); |
344
|
|
|
} |
345
|
|
|
|
346
|
|
|
/** |
347
|
|
|
* Handles OAuth errors by either displaying an error page or redirecting to the callback |
348
|
|
|
* |
349
|
|
|
* @param \Oauth2\Response $response |
350
|
|
|
* @return Response | null |
351
|
|
|
*/ |
352
|
|
|
public function handleErrorResponse($response) |
353
|
|
|
{ |
354
|
|
|
if ($response->getParameter('error') !== null) { |
355
|
|
|
Yii::$app->session->remove('oauthServer'); |
356
|
|
|
Yii::$app->session->remove('oauthRequest'); |
357
|
|
|
|
358
|
|
|
if ($response->isRedirection()) { |
359
|
|
|
return $this->redirect($response->getHttpHeader('Location')); |
360
|
|
|
} else { |
361
|
|
|
$code = $response->getStatusCode(); |
362
|
|
|
$error = $response->getParameter('error', 'Unkown error'); |
363
|
|
|
$description = $response->getParameter('error_description', 'Please check your request.'); |
364
|
|
|
Yii::$app->session->setFlash('error', [ |
365
|
|
|
'code' => $code, |
366
|
|
|
'error' => $error, |
367
|
|
|
'error_description' => $description |
368
|
|
|
], false |
369
|
|
|
); |
370
|
|
|
return $this->redirect(['error']); |
371
|
|
|
} |
372
|
|
|
} |
373
|
|
|
|
374
|
|
|
return null; |
375
|
|
|
} |
376
|
|
|
|
377
|
|
|
/** |
378
|
|
|
* @return string classname for selected interface |
379
|
|
|
* @throws \yii\base\InvalidConfigException |
380
|
|
|
* @since 1.0.0 |
381
|
|
|
*/ |
382
|
|
|
public function getUserClass() |
383
|
|
|
{ |
384
|
|
|
if ($this->userClass === null) { |
385
|
|
|
$scope = Yii::createObject('sweelix\oauth2\server\interfaces\UserModelInterface'); |
386
|
|
|
$this->userClass = get_class($scope); |
387
|
|
|
} |
388
|
|
|
return $this->userClass; |
389
|
|
|
} |
390
|
|
|
} |
391
|
|
|
|
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.
If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.