mirror of
https://github.com/elyby/accounts.git
synced 2024-11-30 02:32:26 +05:30
Протестирована логика подписи access_token и refresh_token, добавлены базовые скоупы, подчищен проект
This commit is contained in:
parent
f5f93ddef1
commit
1c6ba30abf
@ -1,29 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace api\assets;
|
||||
|
||||
use yii\web\AssetBundle;
|
||||
|
||||
/**
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class AppAsset extends AssetBundle
|
||||
{
|
||||
public $basePath = '@webroot';
|
||||
public $baseUrl = '@web';
|
||||
public $css = [
|
||||
'css/site.css',
|
||||
];
|
||||
public $js = [
|
||||
];
|
||||
public $depends = [
|
||||
'yii\web\YiiAsset',
|
||||
'yii\bootstrap\BootstrapAsset',
|
||||
];
|
||||
}
|
@ -4,7 +4,9 @@ namespace api\controllers;
|
||||
use common\components\oauth\Exception\AcceptRequiredException;
|
||||
use common\components\oauth\Exception\AccessDeniedException;
|
||||
use common\models\OauthClient;
|
||||
use common\models\OauthScope;
|
||||
use League\OAuth2\Server\Exception\OAuthException;
|
||||
use League\OAuth2\Server\Grant\RefreshTokenGrant;
|
||||
use Yii;
|
||||
use yii\filters\AccessControl;
|
||||
use yii\helpers\ArrayHelper;
|
||||
@ -17,7 +19,7 @@ class OauthController extends Controller {
|
||||
'class' => AccessControl::class,
|
||||
'rules' => [
|
||||
[
|
||||
'actions' => ['validate'],
|
||||
'actions' => ['validate', 'issue-token'],
|
||||
'allow' => true,
|
||||
],
|
||||
[
|
||||
@ -32,8 +34,9 @@ class OauthController extends Controller {
|
||||
|
||||
public function verbs() {
|
||||
return [
|
||||
'validate' => ['GET'],
|
||||
'complete' => ['POST'],
|
||||
'validate' => ['GET'],
|
||||
'complete' => ['POST'],
|
||||
'issue-token' => ['POST'],
|
||||
];
|
||||
}
|
||||
|
||||
@ -141,20 +144,28 @@ class OauthController extends Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Метод выполняется сервером приложения, которому был выдан auth_token.
|
||||
* Метод выполняется сервером приложения, которому был выдан auth_token иди refresh_token.
|
||||
*
|
||||
* Входными данными является стандартный список GET параметров по стандарту oAuth:
|
||||
* $_GET = [
|
||||
* client_id,
|
||||
* client_secret,
|
||||
* redirect_uri,
|
||||
* code|refresh_token,
|
||||
* code,
|
||||
* grant_type,
|
||||
* ]
|
||||
* для запроса grant_type = authentication_code.
|
||||
* $_GET = [
|
||||
* client_id,
|
||||
* client_secret,
|
||||
* refresh_token,
|
||||
* grant_type,
|
||||
* ]
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function actionIssueToken() {
|
||||
$this->attachRefreshTokenGrantIfNeedle();
|
||||
try {
|
||||
$response = $this->getServer()->issueAccessToken();
|
||||
} catch (OAuthException $e) {
|
||||
@ -168,6 +179,28 @@ class OauthController extends Controller {
|
||||
return $response;
|
||||
}
|
||||
|
||||
private function attachRefreshTokenGrantIfNeedle() {
|
||||
$grantType = Yii::$app->request->post('grant_type');
|
||||
if ($grantType === 'authorization_code' && Yii::$app->request->post('code')) {
|
||||
$authCode = Yii::$app->request->post('code');
|
||||
$codeModel = $this->getServer()->getAuthCodeStorage()->get($authCode);
|
||||
if ($codeModel === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scopes = $codeModel->getScopes();
|
||||
if (array_search(OauthScope::OFFLINE_ACCESS, array_keys($scopes)) === false) {
|
||||
return;
|
||||
}
|
||||
} elseif ($grantType === 'refresh_token') {
|
||||
// Это валидный кейс
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->getServer()->addGrantType(new RefreshTokenGrant());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $queryParams
|
||||
* @param OauthClient $clientModel
|
||||
|
@ -1,77 +0,0 @@
|
||||
<?php
|
||||
|
||||
/* @var $this \yii\web\View */
|
||||
/* @var $content string */
|
||||
|
||||
use yii\helpers\Html;
|
||||
use yii\bootstrap\Nav;
|
||||
use yii\bootstrap\NavBar;
|
||||
use yii\widgets\Breadcrumbs;
|
||||
use api\assets\AppAsset;
|
||||
|
||||
AppAsset::register($this);
|
||||
?>
|
||||
<?php $this->beginPage() ?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="<?= Yii::$app->language ?>">
|
||||
<head>
|
||||
<meta charset="<?= Yii::$app->charset ?>">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<?= Html::csrfMetaTags() ?>
|
||||
<title><?= Html::encode($this->title) ?></title>
|
||||
<?php $this->head() ?>
|
||||
</head>
|
||||
<body>
|
||||
<?php $this->beginBody() ?>
|
||||
|
||||
<div class="wrap">
|
||||
<?php
|
||||
NavBar::begin([
|
||||
'brandLabel' => 'My Company',
|
||||
'brandUrl' => Yii::$app->homeUrl,
|
||||
'options' => [
|
||||
'class' => 'navbar-inverse navbar-fixed-top',
|
||||
],
|
||||
]);
|
||||
$menuItems = [
|
||||
['label' => 'Home', 'url' => ['site/index']],
|
||||
['label' => 'About', 'url' => ['site/about']],
|
||||
['label' => 'Contact', 'url' => ['site/contact']],
|
||||
];
|
||||
if (Yii::$app->user->isGuest) {
|
||||
$menuItems[] = ['label' => 'Signup', 'url' => ['site/signup']];
|
||||
$menuItems[] = ['label' => 'Login', 'url' => ['site/login']];
|
||||
} else {
|
||||
$menuItems[] = [
|
||||
'label' => 'Logout (' . Yii::$app->user->identity->username . ')',
|
||||
'url' => ['site/logout'],
|
||||
'linkOptions' => ['data-method' => 'post']
|
||||
];
|
||||
}
|
||||
echo Nav::widget([
|
||||
'options' => ['class' => 'navbar-nav navbar-right'],
|
||||
'items' => $menuItems,
|
||||
]);
|
||||
NavBar::end();
|
||||
?>
|
||||
|
||||
<div class="container">
|
||||
<?= Breadcrumbs::widget([
|
||||
'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],
|
||||
]) ?>
|
||||
<?= $content ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<p class="pull-left">© My Company <?= date('Y') ?></p>
|
||||
|
||||
<p class="pull-right"><?= Yii::powered() ?></p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<?php $this->endBody() ?>
|
||||
</body>
|
||||
</html>
|
||||
<?php $this->endPage() ?>
|
@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
/* @var $this yii\web\View */
|
||||
|
||||
use yii\helpers\Html;
|
||||
|
||||
$this->title = 'About';
|
||||
$this->params['breadcrumbs'][] = $this->title;
|
||||
?>
|
||||
<div class="site-about">
|
||||
<h1><?= Html::encode($this->title) ?></h1>
|
||||
|
||||
<p>This is the About page. You may modify the following file to customize its content:</p>
|
||||
|
||||
<code><?= __FILE__ ?></code>
|
||||
</div>
|
@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
/* @var $this yii\web\View */
|
||||
/* @var $form yii\bootstrap\ActiveForm */
|
||||
/* @var $model \api\models\ContactForm */
|
||||
|
||||
use yii\helpers\Html;
|
||||
use yii\bootstrap\ActiveForm;
|
||||
use yii\captcha\Captcha;
|
||||
|
||||
$this->title = 'Contact';
|
||||
$this->params['breadcrumbs'][] = $this->title;
|
||||
?>
|
||||
<div class="site-contact">
|
||||
<h1><?= Html::encode($this->title) ?></h1>
|
||||
|
||||
<p>
|
||||
If you have business inquiries or other questions, please fill out the following form to contact us. Thank you.
|
||||
</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-5">
|
||||
<?php $form = ActiveForm::begin(['id' => 'contact-form']); ?>
|
||||
|
||||
<?= $form->field($model, 'name') ?>
|
||||
|
||||
<?= $form->field($model, 'email') ?>
|
||||
|
||||
<?= $form->field($model, 'subject') ?>
|
||||
|
||||
<?= $form->field($model, 'body')->textArea(['rows' => 6]) ?>
|
||||
|
||||
<?= $form->field($model, 'verifyCode')->widget(Captcha::className(), [
|
||||
'template' => '<div class="row"><div class="col-lg-3">{image}</div><div class="col-lg-6">{input}</div></div>',
|
||||
]) ?>
|
||||
|
||||
<div class="form-group">
|
||||
<?= Html::submitButton('Submit', ['class' => 'btn btn-primary', 'name' => 'contact-button']) ?>
|
||||
</div>
|
||||
|
||||
<?php ActiveForm::end(); ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
@ -1,27 +0,0 @@
|
||||
<?php
|
||||
|
||||
/* @var $this yii\web\View */
|
||||
/* @var $name string */
|
||||
/* @var $message string */
|
||||
/* @var $exception Exception */
|
||||
|
||||
use yii\helpers\Html;
|
||||
|
||||
$this->title = $name;
|
||||
?>
|
||||
<div class="site-error">
|
||||
|
||||
<h1><?= Html::encode($this->title) ?></h1>
|
||||
|
||||
<div class="alert alert-danger">
|
||||
<?= nl2br(Html::encode($message)) ?>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
The above error occurred while the Web server was processing your request.
|
||||
</p>
|
||||
<p>
|
||||
Please contact us if you think this is a server error. Thank you.
|
||||
</p>
|
||||
|
||||
</div>
|
@ -1,53 +0,0 @@
|
||||
<?php
|
||||
|
||||
/* @var $this yii\web\View */
|
||||
|
||||
$this->title = 'My Yii Application';
|
||||
?>
|
||||
<div class="site-index">
|
||||
|
||||
<div class="jumbotron">
|
||||
<h1>Congratulations!</h1>
|
||||
|
||||
<p class="lead">You have successfully created your Yii-powered application.</p>
|
||||
|
||||
<p><a class="btn btn-lg btn-success" href="http://www.yiiframework.com">Get started with Yii</a></p>
|
||||
</div>
|
||||
|
||||
<div class="body-content">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-4">
|
||||
<h2>Heading</h2>
|
||||
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
|
||||
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
|
||||
ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
|
||||
fugiat nulla pariatur.</p>
|
||||
|
||||
<p><a class="btn btn-default" href="http://www.yiiframework.com/doc/">Yii Documentation »</a></p>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<h2>Heading</h2>
|
||||
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
|
||||
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
|
||||
ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
|
||||
fugiat nulla pariatur.</p>
|
||||
|
||||
<p><a class="btn btn-default" href="http://www.yiiframework.com/forum/">Yii Forum »</a></p>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<h2>Heading</h2>
|
||||
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
|
||||
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
|
||||
ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
|
||||
fugiat nulla pariatur.</p>
|
||||
|
||||
<p><a class="btn btn-default" href="http://www.yiiframework.com/extensions/">Yii Extensions »</a></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
/* @var $this yii\web\View */
|
||||
/* @var $form yii\bootstrap\ActiveForm */
|
||||
/* @var $model \api\models\PasswordResetRequestForm */
|
||||
|
||||
use yii\helpers\Html;
|
||||
use yii\bootstrap\ActiveForm;
|
||||
|
||||
$this->title = 'Request password reset';
|
||||
$this->params['breadcrumbs'][] = $this->title;
|
||||
?>
|
||||
<div class="site-request-password-reset">
|
||||
<h1><?= Html::encode($this->title) ?></h1>
|
||||
|
||||
<p>Please fill out your email. A link to reset password will be sent there.</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-5">
|
||||
<?php $form = ActiveForm::begin(['id' => 'request-password-reset-form']); ?>
|
||||
|
||||
<?= $form->field($model, 'email') ?>
|
||||
|
||||
<div class="form-group">
|
||||
<?= Html::submitButton('Send', ['class' => 'btn btn-primary']) ?>
|
||||
</div>
|
||||
|
||||
<?php ActiveForm::end(); ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
/* @var $this yii\web\View */
|
||||
/* @var $form yii\bootstrap\ActiveForm */
|
||||
/* @var $model \api\models\ResetPasswordForm */
|
||||
|
||||
use yii\helpers\Html;
|
||||
use yii\bootstrap\ActiveForm;
|
||||
|
||||
$this->title = 'Reset password';
|
||||
$this->params['breadcrumbs'][] = $this->title;
|
||||
?>
|
||||
<div class="site-reset-password">
|
||||
<h1><?= Html::encode($this->title) ?></h1>
|
||||
|
||||
<p>Please choose your new password:</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-5">
|
||||
<?php $form = ActiveForm::begin(['id' => 'reset-password-form']); ?>
|
||||
|
||||
<?= $form->field($model, 'password')->passwordInput() ?>
|
||||
|
||||
<div class="form-group">
|
||||
<?= Html::submitButton('Save', ['class' => 'btn btn-primary']) ?>
|
||||
</div>
|
||||
|
||||
<?php ActiveForm::end(); ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,35 +0,0 @@
|
||||
<?php
|
||||
|
||||
/* @var $this yii\web\View */
|
||||
/* @var $form yii\bootstrap\ActiveForm */
|
||||
/* @var $model \api\models\SignupForm */
|
||||
|
||||
use yii\helpers\Html;
|
||||
use yii\bootstrap\ActiveForm;
|
||||
|
||||
$this->title = 'Signup';
|
||||
$this->params['breadcrumbs'][] = $this->title;
|
||||
?>
|
||||
<div class="site-signup">
|
||||
<h1><?= Html::encode($this->title) ?></h1>
|
||||
|
||||
<p>Please fill out the following fields to signup:</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-5">
|
||||
<?php $form = ActiveForm::begin(['id' => 'form-signup']); ?>
|
||||
|
||||
<?= $form->field($model, 'username') ?>
|
||||
|
||||
<?= $form->field($model, 'email') ?>
|
||||
|
||||
<?= $form->field($model, 'password')->passwordInput() ?>
|
||||
|
||||
<div class="form-group">
|
||||
<?= Html::submitButton('Signup', ['class' => 'btn btn-primary', 'name' => 'signup-button']) ?>
|
||||
</div>
|
||||
|
||||
<?php ActiveForm::end(); ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -2,6 +2,7 @@
|
||||
namespace common\components\oauth;
|
||||
|
||||
use common\components\oauth\Storage\Redis\AuthCodeStorage;
|
||||
use common\components\oauth\Storage\Redis\RefreshTokenStorage;
|
||||
use common\components\oauth\Storage\Yii2\AccessTokenStorage;
|
||||
use common\components\oauth\Storage\Yii2\ClientStorage;
|
||||
use common\components\oauth\Storage\Yii2\ScopeStorage;
|
||||
@ -43,6 +44,7 @@ class Component extends \yii\base\Component {
|
||||
->setScopeStorage(new ScopeStorage())
|
||||
->setSessionStorage(new SessionStorage())
|
||||
->setAuthCodeStorage(new AuthCodeStorage())
|
||||
->setRefreshTokenStorage(new RefreshTokenStorage())
|
||||
->setScopeDelimiter(',');
|
||||
|
||||
$this->_authServer = $authServer;
|
||||
|
@ -2,7 +2,7 @@
|
||||
namespace common\components\oauth\Entity;
|
||||
|
||||
use League\OAuth2\Server\Entity\EntityTrait;
|
||||
use League\OAuth2\Server\Entity\SessionEntity;
|
||||
use League\OAuth2\Server\Entity\SessionEntity as OriginalSessionEntity;
|
||||
|
||||
class AccessTokenEntity extends \League\OAuth2\Server\Entity\AccessTokenEntity {
|
||||
use EntityTrait;
|
||||
@ -17,7 +17,7 @@ class AccessTokenEntity extends \League\OAuth2\Server\Entity\AccessTokenEntity {
|
||||
* @inheritdoc
|
||||
* @return static
|
||||
*/
|
||||
public function setSession(SessionEntity $session) {
|
||||
public function setSession(OriginalSessionEntity $session) {
|
||||
parent::setSession($session);
|
||||
$this->sessionId = $session->getId();
|
||||
|
||||
|
@ -32,7 +32,7 @@ class AuthCodeStorage extends AbstractStorage implements AuthCodeInterface {
|
||||
'id' => $result['id'],
|
||||
'redirectUri' => $result['client_redirect_uri'],
|
||||
'expireTime' => $result['expire_time'],
|
||||
'sessionId' => $result['sessionId'],
|
||||
'sessionId' => $result['session_id'],
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
namespace Fahmiardi\OAuth2\Server\Storage\Redis;
|
||||
namespace common\components\oauth\Storage\Redis;
|
||||
|
||||
use common\components\redis\Key;
|
||||
use League\OAuth2\Server\Entity\RefreshTokenEntity;
|
||||
|
@ -18,7 +18,7 @@ class AccessTokenStorage extends AbstractStorage implements AccessTokenInterface
|
||||
* @return OauthAccessToken|null
|
||||
*/
|
||||
private function getTokenModel($token) {
|
||||
if (isset($this->cache[$token])) {
|
||||
if (!isset($this->cache[$token])) {
|
||||
$this->cache[$token] = OauthAccessToken::findOne($token);
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ class Key {
|
||||
}
|
||||
|
||||
public function getValue() {
|
||||
return $this->getRedis()->get(json_decode($this->key));
|
||||
return json_decode($this->getRedis()->get($this->key), true);
|
||||
}
|
||||
|
||||
public function setValue($value) {
|
||||
|
@ -10,6 +10,9 @@ use yii\db\ActiveRecord;
|
||||
*/
|
||||
class OauthScope extends ActiveRecord {
|
||||
|
||||
const OFFLINE_ACCESS = 'offline_access';
|
||||
const MINECRAFT_SERVER_SESSION = 'minecraft_server_session';
|
||||
|
||||
public static function tableName() {
|
||||
return '{{%oauth_scopes}}';
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ class OauthSession extends ActiveRecord {
|
||||
return '{{%oauth_sessions}}';
|
||||
}
|
||||
|
||||
public function getOauthAccessTokens() {
|
||||
public function getAccessTokens() {
|
||||
return $this->hasMany(OauthAccessToken::class, ['session_id' => 'id']);
|
||||
}
|
||||
|
||||
|
18
console/migrations/m160222_204006_add_init_scopes.php
Normal file
18
console/migrations/m160222_204006_add_init_scopes.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
use console\db\Migration;
|
||||
|
||||
class m160222_204006_add_init_scopes extends Migration {
|
||||
|
||||
public function safeUp() {
|
||||
$this->batchInsert('{{%oauth_scopes}}', ['id'], [
|
||||
['offline_access'],
|
||||
['minecraft_server_session'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function safeDown() {
|
||||
$this->delete('{{%oauth_scopes}}');
|
||||
}
|
||||
|
||||
}
|
@ -18,4 +18,9 @@ class OauthRoute extends BasePage {
|
||||
$this->actor->sendPOST($this->getUrl($queryParams), $postParams);
|
||||
}
|
||||
|
||||
public function issueToken($postParams = []) {
|
||||
$this->route = ['oauth/issue-token'];
|
||||
$this->actor->sendPOST($this->getUrl(), $postParams);
|
||||
}
|
||||
|
||||
}
|
||||
|
99
tests/codeception/api/functional/OauthAccessTokenCest.php
Normal file
99
tests/codeception/api/functional/OauthAccessTokenCest.php
Normal file
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
namespace tests\codeception\api;
|
||||
|
||||
use Codeception\Scenario;
|
||||
use tests\codeception\api\_pages\OauthRoute;
|
||||
use tests\codeception\api\functional\_steps\OauthSteps;
|
||||
use Yii;
|
||||
|
||||
class OauthAccessTokenCest {
|
||||
|
||||
/**
|
||||
* @var OauthRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new OauthRoute($I);
|
||||
}
|
||||
|
||||
public function testIssueTokenWithWrongArgs(FunctionalTester $I) {
|
||||
$I->wantTo('check behavior on on request without any credentials');
|
||||
$this->route->issueToken();
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'invalid_request',
|
||||
]);
|
||||
|
||||
$I->wantTo('check behavior on passing invalid auth code');
|
||||
$this->route->issueToken($this->buildParams(
|
||||
'wrong-auth-code',
|
||||
'ely',
|
||||
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
|
||||
'http://ely.by'
|
||||
));
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'invalid_request',
|
||||
]);
|
||||
}
|
||||
|
||||
public function testIssueToken(FunctionalTester $I, Scenario $scenario) {
|
||||
$I = new OauthSteps($scenario);
|
||||
$authCode = $I->getAuthCode();
|
||||
$this->route->issueToken($this->buildParams(
|
||||
$authCode,
|
||||
'ely',
|
||||
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
|
||||
'http://ely.by'
|
||||
));
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'token_type' => 'Bearer',
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.access_token');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.expires_in');
|
||||
}
|
||||
|
||||
public function testIssueTokenWithRefreshToken(FunctionalTester $I, Scenario $scenario) {
|
||||
$I = new OauthSteps($scenario);
|
||||
$authCode = $I->getAuthCode(false);
|
||||
$this->route->issueToken($this->buildParams(
|
||||
$authCode,
|
||||
'ely',
|
||||
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
|
||||
'http://ely.by'
|
||||
));
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'token_type' => 'Bearer',
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.access_token');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.refresh_token');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.expires_in');
|
||||
}
|
||||
|
||||
private function buildParams($code = null, $clientId = null, $clientSecret = null, $redirectUri = null) {
|
||||
$params = ['grant_type' => 'authorization_code'];
|
||||
if ($code !== null) {
|
||||
$params['code'] = $code;
|
||||
}
|
||||
|
||||
if ($clientId !== null) {
|
||||
$params['client_id'] = $clientId;
|
||||
}
|
||||
|
||||
if ($clientSecret !== null) {
|
||||
$params['client_secret'] = $clientSecret;
|
||||
}
|
||||
|
||||
if ($redirectUri !== null) {
|
||||
$params['redirect_uri'] = $redirectUri;
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
}
|
@ -3,8 +3,9 @@ namespace tests\codeception\api;
|
||||
|
||||
use tests\codeception\api\_pages\OauthRoute;
|
||||
use tests\codeception\api\functional\_steps\AccountSteps;
|
||||
use Yii;
|
||||
|
||||
class OauthCest {
|
||||
class OauthAuthCodeCest {
|
||||
|
||||
/**
|
||||
* @var OauthRoute
|
99
tests/codeception/api/functional/OauthRefreshTokenCest.php
Normal file
99
tests/codeception/api/functional/OauthRefreshTokenCest.php
Normal file
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
namespace tests\codeception\api;
|
||||
|
||||
use Codeception\Scenario;
|
||||
use common\models\OauthScope;
|
||||
use tests\codeception\api\_pages\OauthRoute;
|
||||
use tests\codeception\api\functional\_steps\OauthSteps;
|
||||
use Yii;
|
||||
|
||||
class OauthRefreshTokenCest {
|
||||
|
||||
/**
|
||||
* @var OauthRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new OauthRoute($I);
|
||||
}
|
||||
|
||||
public function testRefreshToken(FunctionalTester $I, Scenario $scenario) {
|
||||
$I = new OauthSteps($scenario);
|
||||
$refreshToken = $I->getRefreshToken();
|
||||
$this->route->issueToken($this->buildParams(
|
||||
$refreshToken,
|
||||
'ely',
|
||||
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM'
|
||||
));
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'token_type' => 'Bearer',
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.access_token');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.refresh_token');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.expires_in');
|
||||
}
|
||||
|
||||
public function testRefreshTokenWithSameScopes(FunctionalTester $I, Scenario $scenario) {
|
||||
$I = new OauthSteps($scenario);
|
||||
$refreshToken = $I->getRefreshToken();
|
||||
$this->route->issueToken($this->buildParams(
|
||||
$refreshToken,
|
||||
'ely',
|
||||
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
|
||||
[OauthScope::MINECRAFT_SERVER_SESSION, OauthScope::OFFLINE_ACCESS]
|
||||
));
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'token_type' => 'Bearer',
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.access_token');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.refresh_token');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.expires_in');
|
||||
}
|
||||
|
||||
public function testRefreshTokenWithNewScopes(FunctionalTester $I, Scenario $scenario) {
|
||||
$I = new OauthSteps($scenario);
|
||||
$refreshToken = $I->getRefreshToken();
|
||||
$this->route->issueToken($this->buildParams(
|
||||
$refreshToken,
|
||||
'ely',
|
||||
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
|
||||
[OauthScope::MINECRAFT_SERVER_SESSION, OauthScope::OFFLINE_ACCESS, 'change_skin']
|
||||
));
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'invalid_scope',
|
||||
]);
|
||||
}
|
||||
|
||||
private function buildParams($refreshToken = null, $clientId = null, $clientSecret = null, $scopes = []) {
|
||||
$params = ['grant_type' => 'refresh_token'];
|
||||
if ($refreshToken !== null) {
|
||||
$params['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
if ($clientId !== null) {
|
||||
$params['client_id'] = $clientId;
|
||||
}
|
||||
|
||||
if ($clientSecret !== null) {
|
||||
$params['client_secret'] = $clientSecret;
|
||||
}
|
||||
|
||||
if (!empty($scopes)) {
|
||||
if (is_array($scopes)) {
|
||||
$scopes = implode(',', $scopes);
|
||||
}
|
||||
|
||||
$params['scope'] = $scopes;
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
}
|
42
tests/codeception/api/functional/_steps/OauthSteps.php
Normal file
42
tests/codeception/api/functional/_steps/OauthSteps.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
namespace tests\codeception\api\functional\_steps;
|
||||
|
||||
use tests\codeception\api\_pages\OauthRoute;
|
||||
|
||||
class OauthSteps extends AccountSteps {
|
||||
|
||||
public function getAuthCode($online = true) {
|
||||
// TODO: по идее можно напрямую сделать зпись в базу, что ускорит процесс тестирования
|
||||
$this->loggedInAsActiveAccount();
|
||||
$route = new OauthRoute($this);
|
||||
$route->complete([
|
||||
'client_id' => 'ely',
|
||||
'redirect_uri' => 'http://ely.by',
|
||||
'response_type' => 'code',
|
||||
'scope' => 'minecraft_server_session' . ($online ? '' : ',offline_access'),
|
||||
], ['accept' => true]);
|
||||
$this->canSeeResponseJsonMatchesJsonPath('$.redirectUri');
|
||||
$response = json_decode($this->grabResponse(), true);
|
||||
preg_match('/code=(\w+)/', $response['redirectUri'], $matches);
|
||||
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
public function getRefreshToken() {
|
||||
// TODO: по идее можно напрямую сделать зпись в базу, что ускорит процесс тестирования
|
||||
$authCode = $this->getAuthCode(false);
|
||||
$route = new OauthRoute($this);
|
||||
$route->issueToken([
|
||||
'code' => $authCode,
|
||||
'client_id' => 'ely',
|
||||
'client_secret' => 'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
|
||||
'redirect_uri' => 'http://ely.by',
|
||||
'grant_type' => 'authorization_code',
|
||||
]);
|
||||
|
||||
$response = json_decode($this->grabResponse(), true);
|
||||
|
||||
return $response['refresh_token'];
|
||||
}
|
||||
|
||||
}
|
@ -6,4 +6,7 @@ return [
|
||||
'change_skin' => [
|
||||
'id' => 'change_skin',
|
||||
],
|
||||
'offline_access' => [
|
||||
'id' => 'offline_access',
|
||||
],
|
||||
];
|
||||
|
Loading…
Reference in New Issue
Block a user