Support OAuth2
This commit is contained in:
parent
6f2345efe4
commit
e2c125648f
2
.gitignore
vendored
2
.gitignore
vendored
@ -17,3 +17,5 @@ junit.xml
|
||||
storage/*.db
|
||||
storage/*.sqlite
|
||||
.vscode
|
||||
storage/oauth-public.key
|
||||
storage/oauth-private.key
|
||||
|
@ -142,6 +142,7 @@ class SetupController extends Controller
|
||||
Artisan::call('salt:random');
|
||||
}
|
||||
Artisan::call('jwt:secret', ['--no-interaction' => true]);
|
||||
Artisan::call('passport:keys', ['--no-interaction' => true]);
|
||||
|
||||
// Create tables
|
||||
Artisan::call('migrate', ['--force' => true]);
|
||||
|
@ -5,12 +5,15 @@ namespace App\Models;
|
||||
use DB;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Arr;
|
||||
use Laravel\Passport\HasApiTokens;
|
||||
use App\Events\EncryptUserPassword;
|
||||
use Tymon\JWTAuth\Contracts\JWTSubject;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
|
||||
class User extends Authenticatable implements JWTSubject
|
||||
{
|
||||
use HasApiTokens;
|
||||
|
||||
/**
|
||||
* Permissions.
|
||||
*/
|
||||
|
@ -4,6 +4,7 @@ namespace App\Providers;
|
||||
|
||||
use Route;
|
||||
use Illuminate\Routing\Router;
|
||||
use Laravel\Passport\Passport;
|
||||
use App\Events\ConfigureRoutes;
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
|
||||
@ -44,6 +45,8 @@ class RouteServiceProvider extends ServiceProvider
|
||||
|
||||
$this->mapApiRoutes();
|
||||
|
||||
Passport::routes();
|
||||
|
||||
event(new ConfigureRoutes($router));
|
||||
}
|
||||
|
||||
|
@ -91,8 +91,14 @@ if (! function_exists('bs_menu')) {
|
||||
{
|
||||
$menu = config('menu');
|
||||
|
||||
Event::dispatch($type == 'user' ? new App\Events\ConfigureUserMenu($menu)
|
||||
: new App\Events\ConfigureAdminMenu($menu));
|
||||
switch ($type) {
|
||||
case 'user':
|
||||
event(new App\Events\ConfigureUserMenu($menu));
|
||||
break;
|
||||
case 'admin':
|
||||
event(new App\Events\ConfigureAdminMenu($menu));
|
||||
break;
|
||||
}
|
||||
|
||||
if (! isset($menu[$type])) {
|
||||
throw new InvalidArgumentException;
|
||||
|
@ -23,7 +23,8 @@
|
||||
"mews/captcha": "^2.2",
|
||||
"guzzlehttp/guzzle": "^6.3",
|
||||
"doctrine/dbal": "^2.9",
|
||||
"tymon/jwt-auth": "dev-develop"
|
||||
"tymon/jwt-auth": "dev-develop",
|
||||
"laravel/passport": "^7.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"fzaninotto/faker": "~1.4",
|
||||
|
583
composer.lock
generated
583
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "ff9f059d35e12b8b7c52f529300ce9f2",
|
||||
"content-hash": "a2b9b6f33c5d48464bf9d66c7c1c2b14",
|
||||
"packages": [
|
||||
{
|
||||
"name": "composer/semver",
|
||||
@ -68,6 +68,69 @@
|
||||
],
|
||||
"time": "2016-08-30T16:08:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "defuse/php-encryption",
|
||||
"version": "v2.2.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/defuse/php-encryption.git",
|
||||
"reference": "0f407c43b953d571421e0020ba92082ed5fb7620"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/defuse/php-encryption/zipball/0f407c43b953d571421e0020ba92082ed5fb7620",
|
||||
"reference": "0f407c43b953d571421e0020ba92082ed5fb7620",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-openssl": "*",
|
||||
"paragonie/random_compat": ">= 2",
|
||||
"php": ">=5.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"nikic/php-parser": "^2.0|^3.0|^4.0",
|
||||
"phpunit/phpunit": "^4|^5"
|
||||
},
|
||||
"bin": [
|
||||
"bin/generate-defuse-key"
|
||||
],
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Defuse\\Crypto\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Taylor Hornby",
|
||||
"email": "taylor@defuse.ca",
|
||||
"homepage": "https://defuse.ca/"
|
||||
},
|
||||
{
|
||||
"name": "Scott Arciszewski",
|
||||
"email": "info@paragonie.com",
|
||||
"homepage": "https://paragonie.com"
|
||||
}
|
||||
],
|
||||
"description": "Secure PHP Encryption Library",
|
||||
"keywords": [
|
||||
"aes",
|
||||
"authenticated encryption",
|
||||
"cipher",
|
||||
"crypto",
|
||||
"cryptography",
|
||||
"encrypt",
|
||||
"encryption",
|
||||
"openssl",
|
||||
"security",
|
||||
"symmetric key cryptography"
|
||||
],
|
||||
"time": "2018-07-24T23:27:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "devitek/yaml-translation",
|
||||
"version": "4.1.0",
|
||||
@ -618,6 +681,52 @@
|
||||
],
|
||||
"time": "2018-03-08T01:11:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "firebase/php-jwt",
|
||||
"version": "v5.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/firebase/php-jwt.git",
|
||||
"reference": "9984a4d3a32ae7673d6971ea00bae9d0a1abba0e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/9984a4d3a32ae7673d6971ea00bae9d0a1abba0e",
|
||||
"reference": "9984a4d3a32ae7673d6971ea00bae9d0a1abba0e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": " 4.8.35"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Firebase\\JWT\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Neuman Vong",
|
||||
"email": "neuman+pear@twilio.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Anant Narayanan",
|
||||
"email": "anant@php.net",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
|
||||
"homepage": "https://github.com/firebase/php-jwt",
|
||||
"time": "2017-06-27T22:17:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/guzzle",
|
||||
"version": "6.3.3",
|
||||
@ -1018,6 +1127,76 @@
|
||||
],
|
||||
"time": "2019-03-26T17:19:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/passport",
|
||||
"version": "v7.2.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/passport.git",
|
||||
"reference": "c0c3fca80d8f5af90dcbf65e62bdd1abee9ac25d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/passport/zipball/c0c3fca80d8f5af90dcbf65e62bdd1abee9ac25d",
|
||||
"reference": "c0c3fca80d8f5af90dcbf65e62bdd1abee9ac25d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"firebase/php-jwt": "~3.0|~4.0|~5.0",
|
||||
"guzzlehttp/guzzle": "~6.0",
|
||||
"illuminate/auth": "~5.6.0|~5.7.0|~5.8.0|~5.9.0",
|
||||
"illuminate/console": "~5.6.0|~5.7.0|~5.8.0|~5.9.0",
|
||||
"illuminate/container": "~5.6.0|~5.7.0|~5.8.0|~5.9.0",
|
||||
"illuminate/contracts": "~5.6.0|~5.7.0|~5.8.0|~5.9.0",
|
||||
"illuminate/database": "~5.6.0|~5.7.0|~5.8.0|~5.9.0",
|
||||
"illuminate/encryption": "~5.6.0|~5.7.0|~5.8.0|~5.9.0",
|
||||
"illuminate/http": "~5.6.0|~5.7.0|~5.8.0|~5.9.0",
|
||||
"illuminate/support": "~5.6.0|~5.7.0|~5.8.0|~5.9.0",
|
||||
"league/oauth2-server": "^7.0",
|
||||
"php": ">=7.1",
|
||||
"phpseclib/phpseclib": "^2.0",
|
||||
"symfony/psr-http-message-bridge": "~1.0",
|
||||
"zendframework/zend-diactoros": "~1.0|~2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "~1.0",
|
||||
"phpunit/phpunit": "~7.4"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "7.0-dev"
|
||||
},
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Laravel\\Passport\\PassportServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Laravel\\Passport\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Taylor Otwell",
|
||||
"email": "taylor@laravel.com"
|
||||
}
|
||||
],
|
||||
"description": "Laravel Passport provides OAuth2 server support to Laravel.",
|
||||
"keywords": [
|
||||
"laravel",
|
||||
"oauth",
|
||||
"passport"
|
||||
],
|
||||
"time": "2019-03-13T14:21:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "lcobucci/jwt",
|
||||
"version": "3.2.5",
|
||||
@ -1076,6 +1255,56 @@
|
||||
],
|
||||
"time": "2018-11-11T12:22:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/event",
|
||||
"version": "2.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/event.git",
|
||||
"reference": "d2cc124cf9a3fab2bb4ff963307f60361ce4d119"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/event/zipball/d2cc124cf9a3fab2bb4ff963307f60361ce4d119",
|
||||
"reference": "d2cc124cf9a3fab2bb4ff963307f60361ce4d119",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"henrikbjorn/phpspec-code-coverage": "~1.0.1",
|
||||
"phpspec/phpspec": "^2.2"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.2-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\Event\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Frank de Jonge",
|
||||
"email": "info@frenky.net"
|
||||
}
|
||||
],
|
||||
"description": "Event package",
|
||||
"keywords": [
|
||||
"emitter",
|
||||
"event",
|
||||
"listener"
|
||||
],
|
||||
"time": "2018-11-26T11:52:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/flysystem",
|
||||
"version": "1.0.51",
|
||||
@ -1160,6 +1389,83 @@
|
||||
],
|
||||
"time": "2019-03-30T13:22:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/oauth2-server",
|
||||
"version": "7.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/oauth2-server.git",
|
||||
"reference": "c7f499849704ebe2c60b45b6d6bb231df5601d4a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/oauth2-server/zipball/c7f499849704ebe2c60b45b6d6bb231df5601d4a",
|
||||
"reference": "c7f499849704ebe2c60b45b6d6bb231df5601d4a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"defuse/php-encryption": "^2.1",
|
||||
"ext-openssl": "*",
|
||||
"lcobucci/jwt": "^3.2.2",
|
||||
"league/event": "^2.1",
|
||||
"php": ">=7.0.0",
|
||||
"psr/http-message": "^1.0.1"
|
||||
},
|
||||
"replace": {
|
||||
"league/oauth2server": "*",
|
||||
"lncd/oauth2": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^0.9.2",
|
||||
"phpstan/phpstan-phpunit": "^0.9.4",
|
||||
"phpstan/phpstan-strict-rules": "^0.9.0",
|
||||
"phpunit/phpunit": "^6.3 || ^7.0",
|
||||
"roave/security-advisories": "dev-master",
|
||||
"zendframework/zend-diactoros": "^1.3.2"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\OAuth2\\Server\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Alex Bilbie",
|
||||
"email": "hello@alexbilbie.com",
|
||||
"homepage": "http://www.alexbilbie.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Andy Millington",
|
||||
"email": "andrew@noexceptions.io",
|
||||
"homepage": "https://www.noexceptions.io",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "A lightweight and powerful OAuth 2.0 authorization and resource server library with support for all the core specification grants. This library will allow you to secure your API with OAuth and allow your applications users to approve apps that want to access their data from your API.",
|
||||
"homepage": "https://oauth2.thephpleague.com/",
|
||||
"keywords": [
|
||||
"Authentication",
|
||||
"api",
|
||||
"auth",
|
||||
"authorisation",
|
||||
"authorization",
|
||||
"oauth",
|
||||
"oauth 2",
|
||||
"oauth 2.0",
|
||||
"oauth2",
|
||||
"protect",
|
||||
"resource",
|
||||
"secure",
|
||||
"server"
|
||||
],
|
||||
"time": "2019-03-29T18:19:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mews/captcha",
|
||||
"version": "2.2.5",
|
||||
@ -1584,6 +1890,98 @@
|
||||
],
|
||||
"time": "2015-07-25T16:39:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpseclib/phpseclib",
|
||||
"version": "2.0.15",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpseclib/phpseclib.git",
|
||||
"reference": "11cf67cf78dc4acb18dc9149a57be4aee5036ce0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/11cf67cf78dc4acb18dc9149a57be4aee5036ce0",
|
||||
"reference": "11cf67cf78dc4acb18dc9149a57be4aee5036ce0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phing/phing": "~2.7",
|
||||
"phpunit/phpunit": "^4.8.35|^5.7|^6.0",
|
||||
"sami/sami": "~2.0",
|
||||
"squizlabs/php_codesniffer": "~2.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
|
||||
"ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.",
|
||||
"ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
|
||||
"ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations."
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"phpseclib/bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"phpseclib\\": "phpseclib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jim Wigginton",
|
||||
"email": "terrafrost@php.net",
|
||||
"role": "Lead Developer"
|
||||
},
|
||||
{
|
||||
"name": "Patrick Monnerat",
|
||||
"email": "pm@datasphere.ch",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Andreas Fischer",
|
||||
"email": "bantu@phpbb.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Hans-Jürgen Petrich",
|
||||
"email": "petrich@tronic-media.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "graham@alt-three.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
|
||||
"homepage": "http://phpseclib.sourceforge.net",
|
||||
"keywords": [
|
||||
"BigInteger",
|
||||
"aes",
|
||||
"asn.1",
|
||||
"asn1",
|
||||
"blowfish",
|
||||
"crypto",
|
||||
"cryptography",
|
||||
"encryption",
|
||||
"rsa",
|
||||
"security",
|
||||
"sftp",
|
||||
"signature",
|
||||
"signing",
|
||||
"ssh",
|
||||
"twofish",
|
||||
"x.509",
|
||||
"x509"
|
||||
],
|
||||
"time": "2019-03-10T16:53:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "predis/predis",
|
||||
"version": "v1.1.1",
|
||||
@ -1683,6 +2081,58 @@
|
||||
],
|
||||
"time": "2017-02-14T16:28:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-factory",
|
||||
"version": "1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-factory.git",
|
||||
"reference": "378bfe27931ecc54ff824a20d6f6bfc303bbd04c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/378bfe27931ecc54ff824a20d6f6bfc303bbd04c",
|
||||
"reference": "378bfe27931ecc54ff824a20d6f6bfc303bbd04c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.0.0",
|
||||
"psr/http-message": "^1.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Message\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interfaces for PSR-7 HTTP message factories",
|
||||
"keywords": [
|
||||
"factory",
|
||||
"http",
|
||||
"message",
|
||||
"psr",
|
||||
"psr-17",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response"
|
||||
],
|
||||
"time": "2018-07-30T21:54:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-message",
|
||||
"version": "1.0.1",
|
||||
@ -2894,6 +3344,71 @@
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2019-01-24T22:05:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/psr-http-message-bridge",
|
||||
"version": "v1.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/psr-http-message-bridge.git",
|
||||
"reference": "9ab9d71f97d5c7d35a121a7fb69f74fee95cd0ad"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/9ab9d71f97d5c7d35a121a7fb69f74fee95cd0ad",
|
||||
"reference": "9ab9d71f97d5c7d35a121a7fb69f74fee95cd0ad",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1",
|
||||
"psr/http-message": "^1.0",
|
||||
"symfony/http-foundation": "^3.4 || ^4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"nyholm/psr7": "^1.1",
|
||||
"symfony/phpunit-bridge": "^3.4.20 || ^4.0",
|
||||
"zendframework/zend-diactoros": "^1.4.1 || ^2.0"
|
||||
},
|
||||
"suggest": {
|
||||
"nyholm/psr7": "For a super lightweight PSR-7/17 implementation"
|
||||
},
|
||||
"type": "symfony-bridge",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.2-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Bridge\\PsrHttpMessage\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "http://symfony.com/contributors"
|
||||
},
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
}
|
||||
],
|
||||
"description": "PSR HTTP message bridge",
|
||||
"homepage": "http://symfony.com",
|
||||
"keywords": [
|
||||
"http",
|
||||
"http-message",
|
||||
"psr-17",
|
||||
"psr-7"
|
||||
],
|
||||
"time": "2019-03-11T18:22:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/routing",
|
||||
"version": "v4.2.3",
|
||||
@ -3352,6 +3867,72 @@
|
||||
"environment"
|
||||
],
|
||||
"time": "2019-01-30T10:43:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "zendframework/zend-diactoros",
|
||||
"version": "2.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/zendframework/zend-diactoros.git",
|
||||
"reference": "c3c330192bc9cc51b7e9ce968ff721dc32ffa986"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/c3c330192bc9cc51b7e9ce968ff721dc32ffa986",
|
||||
"reference": "c3c330192bc9cc51b7e9ce968ff721dc32ffa986",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1",
|
||||
"psr/http-factory": "^1.0",
|
||||
"psr/http-message": "^1.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/http-factory-implementation": "1.0",
|
||||
"psr/http-message-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-dom": "*",
|
||||
"ext-libxml": "*",
|
||||
"http-interop/http-factory-tests": "^0.5.0",
|
||||
"php-http/psr7-integration-tests": "dev-master",
|
||||
"phpunit/phpunit": "^7.0.2",
|
||||
"zendframework/zend-coding-standard": "~1.0.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.1.x-dev",
|
||||
"dev-develop": "2.2.x-dev",
|
||||
"dev-release-1.8": "1.8.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/functions/create_uploaded_file.php",
|
||||
"src/functions/marshal_headers_from_sapi.php",
|
||||
"src/functions/marshal_method_from_sapi.php",
|
||||
"src/functions/marshal_protocol_version_from_sapi.php",
|
||||
"src/functions/marshal_uri_from_sapi.php",
|
||||
"src/functions/normalize_server.php",
|
||||
"src/functions/normalize_uploaded_files.php",
|
||||
"src/functions/parse_cookie_header.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Zend\\Diactoros\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"description": "PSR HTTP Message implementations",
|
||||
"keywords": [
|
||||
"http",
|
||||
"psr",
|
||||
"psr-7"
|
||||
],
|
||||
"time": "2019-01-05T20:13:32+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
|
@ -45,6 +45,11 @@ return [
|
||||
'driver' => 'jwt',
|
||||
'provider' => 'users',
|
||||
],
|
||||
|
||||
'oauth' => [
|
||||
'driver' => 'passport',
|
||||
'provider' => 'users',
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|
@ -14,6 +14,13 @@ $menu['user'] = [
|
||||
['title' => 'general.player-manage', 'link' => 'user/player', 'icon' => 'fa-users'],
|
||||
['title' => 'general.my-reports', 'link' => 'user/reports', 'icon' => 'fa-flag'],
|
||||
['title' => 'general.profile', 'link' => 'user/profile', 'icon' => 'fa-user'],
|
||||
[
|
||||
'title' => 'general.developer',
|
||||
'icon' => 'fa-code-branch',
|
||||
'children' => [
|
||||
['title' => 'general.oauth-manage', 'link' => 'user/oauth/manage', 'icon' => 'fa-feather-alt'],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$menu['admin'] = [
|
||||
|
@ -3,3 +3,5 @@
|
||||
use Artisan;
|
||||
|
||||
Artisan::call('jwt:secret', ['--no-interaction' => true]);
|
||||
Artisan::call('migrate', ['--force' => true]);
|
||||
Artisan::call('passport:keys', ['--no-interaction' => true]);
|
||||
|
@ -36,6 +36,11 @@ export default [
|
||||
component: () => import('../views/user/Profile.vue'),
|
||||
el: '.content',
|
||||
},
|
||||
{
|
||||
path: 'user/oauth/manage',
|
||||
component: () => import('../views/user/OAuth.vue'),
|
||||
el: '.content',
|
||||
},
|
||||
{
|
||||
path: 'admin',
|
||||
module: [() => import('../views/admin/Dashboard')],
|
||||
|
228
resources/assets/src/views/user/OAuth.vue
Normal file
228
resources/assets/src/views/user/OAuth.vue
Normal file
@ -0,0 +1,228 @@
|
||||
<template>
|
||||
<section class="content">
|
||||
<el-button
|
||||
type="primary"
|
||||
class="btn-create-app"
|
||||
data-toggle="modal"
|
||||
data-target="#modal-create"
|
||||
>
|
||||
{{ $t('user.oauth.create') }}
|
||||
</el-button>
|
||||
<vue-good-table
|
||||
:rows="clients"
|
||||
:columns="columns"
|
||||
:search-options="tableOptions.search"
|
||||
:pagination-options="tableOptions.pagination"
|
||||
style-class="vgt-table striped"
|
||||
>
|
||||
<template #table-row="props">
|
||||
<span v-if="props.column.field === 'name'">
|
||||
{{ props.formattedRow[props.column.field] }}
|
||||
<a
|
||||
:title="$t('user.oauth.modifyName')"
|
||||
href="#"
|
||||
data-test="name"
|
||||
@click="modifyName(props.row)"
|
||||
>
|
||||
<i class="fas fa-edit btn-edit" />
|
||||
</a>
|
||||
</span>
|
||||
<span v-else-if="props.column.field === 'redirect'">
|
||||
{{ props.formattedRow[props.column.field] }}
|
||||
<a
|
||||
:title="$t('user.oauth.modifyUrl')"
|
||||
href="#"
|
||||
data-test="callback"
|
||||
@click="modifyCallback(props.row)"
|
||||
>
|
||||
<i class="fas fa-edit btn-edit" />
|
||||
</a>
|
||||
</span>
|
||||
<span v-else-if="props.column.field === 'operations'">
|
||||
<el-button type="danger" data-test="remove" @click="remove(props.row)">
|
||||
{{ $t('report.delete') }}
|
||||
</el-button>
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ props.formattedRow[props.column.field] }}
|
||||
</span>
|
||||
</template>
|
||||
</vue-good-table>
|
||||
|
||||
<div
|
||||
id="modal-create"
|
||||
class="modal fade"
|
||||
tabindex="-1"
|
||||
role="dialog"
|
||||
>
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button
|
||||
type="button"
|
||||
class="close"
|
||||
data-dismiss="modal"
|
||||
aria-label="Close"
|
||||
>
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 v-t="'user.oauth.create'" class="modal-title" />
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td v-t="'user.oauth.name'" class="key" />
|
||||
<td class="value">
|
||||
<el-input v-model="name" type="text" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td v-t="'user.oauth.redirect'" class="key" />
|
||||
<td class="value">
|
||||
<el-input v-model="callback" type="text" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<el-button data-dismiss="modal">{{ $t('general.close') }}</el-button>
|
||||
<el-button type="primary" data-test="create" @click="create">
|
||||
{{ $t('general.submit') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { VueGoodTable } from 'vue-good-table'
|
||||
import 'vue-good-table/dist/vue-good-table.min.css'
|
||||
import tableOptions from '../../components/mixins/tableOptions'
|
||||
import { walkFetch, init } from '../../scripts/net'
|
||||
|
||||
export default {
|
||||
name: 'OAuthApps',
|
||||
components: {
|
||||
VueGoodTable,
|
||||
},
|
||||
mixins: [
|
||||
tableOptions,
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
name: '',
|
||||
callback: '',
|
||||
clients: [],
|
||||
columns: [
|
||||
{
|
||||
field: 'id', label: this.$t('user.oauth.id'), type: 'number',
|
||||
},
|
||||
{ field: 'name', label: this.$t('user.oauth.name') },
|
||||
{
|
||||
field: 'secret',
|
||||
label: this.$t('user.oauth.secret'),
|
||||
sortable: false,
|
||||
globalSearchDisabled: true,
|
||||
},
|
||||
{
|
||||
field: 'redirect',
|
||||
label: this.$t('user.oauth.redirect'),
|
||||
sortable: false,
|
||||
globalSearchDisabled: true,
|
||||
},
|
||||
{
|
||||
field: 'operations',
|
||||
label: this.$t('admin.operationsTitle'),
|
||||
sortable: false,
|
||||
globalSearchDisabled: true,
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
async fetchData() {
|
||||
this.clients = await this.$http.get('/oauth/clients')
|
||||
},
|
||||
async create() {
|
||||
const client = await this.$http.post('/oauth/clients', {
|
||||
name: this.name,
|
||||
redirect: this.callback,
|
||||
})
|
||||
if (client.id) {
|
||||
$('#modal-create').modal('hide')
|
||||
this.clients.unshift(client)
|
||||
} else {
|
||||
this.$message.warning(client.message)
|
||||
}
|
||||
},
|
||||
async modifyName(client) {
|
||||
let name
|
||||
try {
|
||||
const { value } = await this.$prompt('', {
|
||||
title: this.$t('user.oauth.name'),
|
||||
inputValue: client.name,
|
||||
})
|
||||
name = value
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
await this.modify(client, { name })
|
||||
},
|
||||
async modifyCallback(client) {
|
||||
let redirect
|
||||
try {
|
||||
const { value } = await this.$prompt('', {
|
||||
title: this.$t('user.oauth.redirect'),
|
||||
inputValue: client.redirect,
|
||||
})
|
||||
redirect = value
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
await this.modify(client, { redirect })
|
||||
},
|
||||
async modify(client, modified) {
|
||||
const request = new Request(
|
||||
`/oauth/clients/${client.id}`,
|
||||
Object.assign({}, init, {
|
||||
body: JSON.stringify(Object.assign({ name: client.name, redirect: client.redirect }, modified)),
|
||||
method: 'PUT',
|
||||
})
|
||||
)
|
||||
request.headers.set('Content-Type', 'application/json')
|
||||
const result = await walkFetch(request)
|
||||
if (result.id) {
|
||||
Object.assign(client, modified)
|
||||
} else {
|
||||
this.$message.warning(result.message)
|
||||
}
|
||||
},
|
||||
async remove(client) {
|
||||
try {
|
||||
await this.$confirm(this.$t('user.oauth.confirmRemove'), { type: 'warning' })
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
const request = new Request(
|
||||
`/oauth/clients/${client.id}`,
|
||||
Object.assign({}, init, { method: 'DELETE' })
|
||||
)
|
||||
await walkFetch(request)
|
||||
this.$delete(this.clients, this.clients.findIndex(({ id }) => id === client.id))
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
.btn-create-app
|
||||
margin-bottom 5px
|
||||
margin-right 10px
|
||||
</style>
|
@ -5,16 +5,6 @@ import { showAjaxError } from '@/scripts/notify'
|
||||
|
||||
jest.mock('@/scripts/notify')
|
||||
|
||||
;(window as Window & { Request: any }).Request = class {
|
||||
headers: Map<string, string>
|
||||
|
||||
constructor(public url: string, init: Request) {
|
||||
this.url = url
|
||||
Object.assign(this, init)
|
||||
this.headers = new Map(Object.entries(init.headers))
|
||||
}
|
||||
}
|
||||
|
||||
test('the GET method', async () => {
|
||||
const json = jest.fn().mockResolvedValue({})
|
||||
window.fetch = jest.fn().mockResolvedValue({
|
||||
|
@ -1,3 +1,4 @@
|
||||
/* eslint-disable max-classes-per-file */
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import 'jest-extended'
|
||||
import Vue from 'vue'
|
||||
@ -20,6 +21,14 @@ window.Headers = class extends Map {
|
||||
}
|
||||
}
|
||||
|
||||
window.Request = class {
|
||||
constructor(url, init) {
|
||||
this.url = url
|
||||
Object.assign(this, init)
|
||||
this.headers = new Map(Object.entries(init.headers || {}))
|
||||
}
|
||||
}
|
||||
|
||||
const noop = () => undefined
|
||||
// eslint-disable-next-line no-console
|
||||
Object.keys(console).forEach(method => (console[method] = noop))
|
||||
|
138
resources/assets/tests/views/user/OAuth.test.ts
Normal file
138
resources/assets/tests/views/user/OAuth.test.ts
Normal file
@ -0,0 +1,138 @@
|
||||
import Vue from 'vue'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { MessageBoxData } from 'element-ui/types/message-box'
|
||||
import { flushPromises } from '../../utils'
|
||||
import { walkFetch } from '@/scripts/net'
|
||||
import OAuth from '@/views/user/OAuth.vue'
|
||||
|
||||
jest.mock('@/scripts/net', () => ({
|
||||
walkFetch: jest.fn(),
|
||||
init: {},
|
||||
}))
|
||||
|
||||
test('basic render', async () => {
|
||||
Vue.prototype.$http.get.mockResolvedValue([
|
||||
{ id: 1 },
|
||||
])
|
||||
const wrapper = mount(OAuth)
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.findAll('[data-test=remove]')).toHaveLength(1)
|
||||
})
|
||||
|
||||
test('create app', async () => {
|
||||
Object.assign(window, { $: () => ({ modal() {} }) })
|
||||
Vue.prototype.$http.get.mockResolvedValue([])
|
||||
Vue.prototype.$http.post
|
||||
.mockResolvedValueOnce({ message: 'fail' })
|
||||
.mockResolvedValueOnce({ id: 1, name: 'name' })
|
||||
const wrapper = mount(OAuth)
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
const button = wrapper.find('[data-test=create]')
|
||||
const inputs = wrapper.findAll('.value')
|
||||
inputs.at(0).find('input')
|
||||
.setValue('name')
|
||||
inputs.at(1).find('input')
|
||||
.setValue('https://example.com/')
|
||||
|
||||
button.trigger('click')
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(Vue.prototype.$http.post).toBeCalledWith(
|
||||
'/oauth/clients',
|
||||
{ name: 'name', redirect: 'https://example.com/' }
|
||||
)
|
||||
expect(Vue.prototype.$message.warning).toBeCalledWith('fail')
|
||||
|
||||
button.trigger('click')
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.text()).toContain('name')
|
||||
})
|
||||
|
||||
test('modify name', async () => {
|
||||
Vue.prototype.$http.get.mockResolvedValue([
|
||||
{ id: 1, name: 'old' },
|
||||
])
|
||||
walkFetch
|
||||
.mockResolvedValueOnce({ message: 'fail' })
|
||||
.mockResolvedValueOnce({ id: 1, name: 'new-name' })
|
||||
Vue.prototype.$prompt
|
||||
.mockRejectedValueOnce('')
|
||||
.mockResolvedValue({ value: 'new-name' } as MessageBoxData)
|
||||
const wrapper = mount(OAuth)
|
||||
await wrapper.vm.$nextTick()
|
||||
const button = wrapper.find('[data-test=name]')
|
||||
|
||||
button.trigger('click')
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(walkFetch).not.toBeCalled()
|
||||
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(walkFetch).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
url: '/oauth/clients/1',
|
||||
body: JSON.stringify({ name: 'new-name' }),
|
||||
method: 'PUT',
|
||||
})
|
||||
)
|
||||
expect(Vue.prototype.$message.warning).toBeCalledWith('fail')
|
||||
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(wrapper.text()).toContain('new-name')
|
||||
})
|
||||
|
||||
test('modify redirect', async () => {
|
||||
Vue.prototype.$http.get.mockResolvedValue([
|
||||
{ id: 1, redirect: 'https://example.com/' },
|
||||
])
|
||||
walkFetch
|
||||
.mockResolvedValueOnce({ message: 'fail' })
|
||||
.mockResolvedValueOnce({ id: 1, redirect: 'https://example.net/' })
|
||||
Vue.prototype.$prompt
|
||||
.mockRejectedValueOnce('')
|
||||
.mockResolvedValue({ value: 'https://example.net/' } as MessageBoxData)
|
||||
const wrapper = mount(OAuth)
|
||||
await wrapper.vm.$nextTick()
|
||||
const button = wrapper.find('[data-test=callback]')
|
||||
|
||||
button.trigger('click')
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(walkFetch).not.toBeCalled()
|
||||
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(walkFetch).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
url: '/oauth/clients/1',
|
||||
body: JSON.stringify({ redirect: 'https://example.net/' }),
|
||||
method: 'PUT',
|
||||
})
|
||||
)
|
||||
expect(Vue.prototype.$message.warning).toBeCalledWith('fail')
|
||||
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(wrapper.text()).toContain('https://example.net/')
|
||||
})
|
||||
|
||||
test('remove app', async () => {
|
||||
Vue.prototype.$http.get.mockResolvedValue([
|
||||
{ id: 1, name: 'name' },
|
||||
])
|
||||
Vue.prototype.$confirm
|
||||
.mockRejectedValueOnce('cancel')
|
||||
.mockResolvedValue('confirm')
|
||||
|
||||
const wrapper = mount(OAuth)
|
||||
await wrapper.vm.$nextTick()
|
||||
const button = wrapper.find('[data-test=remove]')
|
||||
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(walkFetch).not.toBeCalled()
|
||||
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(wrapper.text()).toContain('No data')
|
||||
})
|
@ -78,6 +78,12 @@ logout:
|
||||
success: You are now logged out.
|
||||
fail: No valid session.
|
||||
|
||||
oauth:
|
||||
authorization:
|
||||
title: Authorization
|
||||
introduction: A 3rd-party application ":name" is requesting permission to access your account.
|
||||
button: Authorize
|
||||
|
||||
nickname: Nickname
|
||||
email: Email
|
||||
identification: Email or player name
|
||||
|
@ -223,6 +223,15 @@ user:
|
||||
message: You must verify your email address before using the skin hosting service. Haven't received the email?
|
||||
resend: Click here to send again.
|
||||
sending: Sending...
|
||||
oauth:
|
||||
id: Client ID
|
||||
name: App Name
|
||||
secret: Client Secret
|
||||
redirect: Callback URL
|
||||
modifyName: Modify app name.
|
||||
modifyUrl: Modify callback URL.
|
||||
create: Create New App
|
||||
confirmRemove: Are you sure to delete this app? You won't be able to undo this.
|
||||
|
||||
admin:
|
||||
operationsTitle: Operations
|
||||
|
@ -13,6 +13,8 @@ back: Back
|
||||
dashboard: Dashboard
|
||||
my-closet: Closet
|
||||
my-reports: Reports
|
||||
developer: Developers
|
||||
oauth-manage: OAuth2 Apps
|
||||
player-manage: Players
|
||||
user-manage: Users
|
||||
report-manage: Reports
|
||||
@ -39,6 +41,7 @@ pause: Pause
|
||||
reset: Reset
|
||||
|
||||
submit: Submit
|
||||
cancel: Cancel
|
||||
op-success: Operated successfully.
|
||||
|
||||
notice: Notice
|
||||
|
@ -78,6 +78,12 @@ logout:
|
||||
success: 登出成功
|
||||
fail: 未找到已保存的登录信息
|
||||
|
||||
oauth:
|
||||
authorization:
|
||||
title: 授权
|
||||
introduction: 第三方应用 :name 正在向您请求获取权限。
|
||||
button: 授权
|
||||
|
||||
nickname: 昵称
|
||||
email: Email
|
||||
identification: Email 或角色名
|
||||
|
@ -221,6 +221,15 @@ user:
|
||||
message: 你必须验证你的邮箱才能正常使用本站的皮肤托管等功能。没有收到验证邮件?
|
||||
resend: 点击这里再次发送。
|
||||
sending: 正在发送……
|
||||
oauth:
|
||||
id: 客户端 ID
|
||||
name: 应用名
|
||||
secret: 客户端 Secret
|
||||
redirect: 回调 URL
|
||||
modifyName: 更改应用名
|
||||
modifyUrl: 更改回调 URL
|
||||
create: 创建应用
|
||||
confirmRemove: 确认要删除这个应用吗?此操作不可撤销。
|
||||
|
||||
admin:
|
||||
operationsTitle: 更多操作
|
||||
|
@ -13,6 +13,8 @@ back: 返回
|
||||
dashboard: 仪表盘
|
||||
my-closet: 我的衣柜
|
||||
my-reports: 我的举报
|
||||
developer: 开发者
|
||||
oauth-manage: OAuth2 应用
|
||||
player-manage: 角色管理
|
||||
user-manage: 用户管理
|
||||
report-manage: 举报管理
|
||||
@ -39,6 +41,7 @@ pause: 暂停
|
||||
reset: 重置
|
||||
|
||||
submit: 提交
|
||||
cancel: 取消
|
||||
op-success: 操作成功
|
||||
|
||||
notice: 提示
|
||||
|
12
resources/views/user/oauth.blade.php
Normal file
12
resources/views/user/oauth.blade.php
Normal file
@ -0,0 +1,12 @@
|
||||
@extends('user.master')
|
||||
|
||||
@section('title', trans('general.oauth-manage'))
|
||||
|
||||
@section('content')
|
||||
<div class="content-wrapper">
|
||||
<section class="content-header">
|
||||
<h1>@lang('general.oauth-manage')</h1>
|
||||
</section>
|
||||
<section class="content"></section>
|
||||
</div>
|
||||
@endsection
|
39
resources/views/vendor/passport/authorize.blade.php
vendored
Normal file
39
resources/views/vendor/passport/authorize.blade.php
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
@extends('auth.master')
|
||||
|
||||
@section('title', trans('auth.oauth.authorization.title'))
|
||||
|
||||
@section('content')
|
||||
<div class="login-box">
|
||||
<div class="login-logo">
|
||||
<a href="{{ url('/') }}">{{ option_localized('site_name') }}</a>
|
||||
</div>
|
||||
|
||||
<div class="login-box-body">
|
||||
<p class="login-box-msg">
|
||||
@lang('auth.oauth.authorization.introduction', ['name' => $client->name])
|
||||
</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<form method="post" action="{{ route('passport.authorizations.approve') }}">
|
||||
@csrf
|
||||
<input type="hidden" name="state" value="{{ $request->state }}">
|
||||
<input type="hidden" name="client_id" value="{{ $client->id }}">
|
||||
<button type="submit" class="btn btn-success btn-block btn-flat">
|
||||
@lang('auth.oauth.authorization.button')
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<form method="post" action="{{ route('passport.authorizations.deny') }}">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<input type="hidden" name="state" value="{{ $request->state }}">
|
||||
<input type="hidden" name="client_id" value="{{ $client->id }}">
|
||||
<button class="btn btn-default btn-block btn-flat">@lang('general.cancel')</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
@ -6,11 +6,11 @@ Route::prefix('auth')->group(function () {
|
||||
Route::post('refresh', 'AuthController@jwtRefresh')->middleware('auth:jwt');
|
||||
});
|
||||
|
||||
Route::prefix('user')->middleware('auth:jwt')->group(function () {
|
||||
Route::prefix('user')->middleware('auth:jwt,oauth')->group(function () {
|
||||
Route::put('sign', 'UserController@sign');
|
||||
});
|
||||
|
||||
Route::prefix('players')->middleware('auth:jwt')->group(function () {
|
||||
Route::prefix('players')->middleware('auth:jwt,oauth')->group(function () {
|
||||
Route::get('', 'PlayerController@listAll');
|
||||
Route::post('', 'PlayerController@add');
|
||||
Route::delete('{pid}', 'PlayerController@delete');
|
||||
|
@ -82,6 +82,9 @@ Route::group([
|
||||
Route::post('/closet/add', 'ClosetController@add');
|
||||
Route::post('/closet/remove', 'ClosetController@remove');
|
||||
Route::post('/closet/rename', 'ClosetController@rename');
|
||||
|
||||
// OAuth2 Management
|
||||
Route::view('/oauth/manage', 'user.oauth');
|
||||
});
|
||||
|
||||
/*
|
||||
|
@ -83,9 +83,12 @@ class SetupControllerTest extends TestCase
|
||||
|
||||
public function testInfo()
|
||||
{
|
||||
$this->get('/setup/info')
|
||||
->assertViewIs('setup.wizard.info');
|
||||
|
||||
$this->get('/setup/info')->assertViewIs('setup.wizard.info');
|
||||
Schema::dropIfExists('oauth_auth_codes');
|
||||
Schema::dropIfExists('oauth_access_tokens');
|
||||
Schema::dropIfExists('oauth_clients');
|
||||
Schema::dropIfExists('oauth_personal_access_clients');
|
||||
Schema::dropIfExists('oauth_refresh_tokens');
|
||||
Artisan::call('migrate:refresh');
|
||||
Schema::drop('users');
|
||||
$this->get('/setup/info')->assertSee('already exist');
|
||||
@ -167,6 +170,10 @@ class SetupControllerTest extends TestCase
|
||||
->with('jwt:secret', ['--no-interaction' => true])
|
||||
->once()
|
||||
->andReturn(true);
|
||||
Artisan::shouldReceive('call')
|
||||
->with('passport:keys', ['--no-interaction' => true])
|
||||
->once()
|
||||
->andReturn(true);
|
||||
Artisan::shouldReceive('call')
|
||||
->with('migrate', ['--force' => true])
|
||||
->once()
|
||||
|
Loading…
Reference in New Issue
Block a user