feat: OAuth scope (#287)

Co-authored-by: Pig Fang <g-plane@hotmail.com>
This commit is contained in:
Asnxthaony 2021-04-18 15:31:57 +08:00 committed by GitHub
parent 5fb4240a47
commit 387fe81a60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 188 additions and 26 deletions

View File

@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Laravel\Passport\Exceptions\MissingScopeException;
use Throwable;
class Handler extends ExceptionHandler
@ -30,6 +31,8 @@ class Handler extends ExceptionHandler
if (Str::endsWith($model, 'Texture')) {
$exception = new ModelNotFoundException(trans('skinlib.non-existent'));
}
} elseif ($exception instanceof MissingScopeException) {
return json($exception->getMessage(), 403);
}
return parent::render($request, $exception);

View File

@ -65,5 +65,7 @@ class Kernel extends HttpKernel
'setup' => \App\Http\Middleware\CheckInstallation::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \App\Http\Middleware\CheckUserVerified::class,
'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,
'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
];
}

19
app/Models/Scope.php Normal file
View File

@ -0,0 +1,19 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id
* @property string $name
* @property string $description
*/
class Scope extends Model
{
public $timestamps = false;
protected $fillable = [
'name', 'description',
];
}

View File

@ -0,0 +1,54 @@
<?php
namespace App\Providers;
use App\Models\Scope;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Laravel\Passport\Passport;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
];
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Passport::routes();
$defaultScopes = [
'User.Read' => 'auth.oauth.scope.user.read',
'Notification.Read' => 'auth.oauth.scope.notification.read',
'Notification.ReadWrite' => 'auth.oauth.scope.notification.readwrite',
'Player.Read' => 'auth.oauth.scope.player.read',
'Player.ReadWrite' => 'auth.oauth.scope.player.readwrite',
'Closet.Read' => 'auth.oauth.scope.closet.read',
'Closet.ReadWrtie' => 'auth.oauth.scope.closet.readwrite',
'UsersManagement.Read' => 'auth.oauth.scope.users-management.read',
'UsersManagement.ReadWrite' => 'auth.oauth.scope.users-management.readwrite',
'PlayersManagement.Read' => 'auth.oauth.scope.players-management.read',
'PlayersManagement.ReadWrite' => 'auth.oauth.scope.players-management.readwrite',
'ClosetManagement.Read' => 'auth.oauth.scope.closet-management.read',
'ClosetManagement.ReadWrite' => 'auth.oauth.scope.closet-management.readwrite',
'ReportsManagement.Read' => 'auth.oauth.scope.reports-management.read',
'ReportsManagement.ReadWrite' => 'auth.oauth.scope.reports-management.readwrite',
];
$scopes = Scope::all()->pluck('description', 'name')->all();
Passport::tokensCan(array_merge($defaultScopes, $scopes));
Passport::setDefaultScope(['User.Read']);
}
}

View File

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateScopeTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (!Schema::hasTable('scopes')) {
Schema::create('scopes', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->unique();
$table->string('description');
});
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('scopes');
}
}

View File

@ -64,6 +64,31 @@ oauth:
title: Authorization
introduction: A 3rd-party application ":name" is requesting permission to access your account.
button: Authorize
permissions: Permissions
scope:
user:
read: Sign you in and read your profile
notification:
read: Allows the app to read your notifications.
readwrite: Allows the app to send notifications.
player:
read: Allows the app to read your players.
readwrite: Allows the app to create, read, update and delete your players.
closet:
read: Allows the app to read your closet items.
readwrite: Allows the app to create, read, update and delete your closet items.
users-management:
read: Allows the app to read site's users.
readwrite: Allows the app to create, read, update and delete site's users.
players-management:
read: Allows the app to read site's players.
readwrite: Allows the app to create, read, update and delete site's players.
closet-management:
read: Allows the app to read user's of your site closet items.
readwrite: Allows the app to create, read, update and delete user's closet items.
reports-management:
read: Allows the app to read user's reports.
readwrite: Allows the app to read and review user's reports.
email: Email
register-link: Register a new account

View File

@ -8,6 +8,18 @@
<p class="login-box-msg">
{{ trans('auth.oauth.authorization.introduction', {name: client.name}) }}
</p>
{% if scopes %}
<div class="scopes">
<p>
<strong>{{ trans('auth.oauth.authorization.permission') }}</strong>
</p>
<ul>
{% for scope in scopes %}
<li>{{ trans(scope.description) }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
<div class="d-flex justify-content-between">
<form method="post" action="{{ route('passport.authorizations.approve', {}, false) }}">
{{ csrf_field() }}

View File

@ -3,34 +3,42 @@
Route::any('', 'HomeController@apiRoot');
Route::prefix('user')->middleware('auth:oauth')->group(function () {
Route::get('', 'UserController@user');
Route::get('', 'UserController@user')->middleware(['scope:User.Read']);
Route::get('notifications', 'NotificationsController@all');
Route::post('notifications/{id}', 'NotificationsController@read');
Route::middleware(['scope:Notification.Read'])->group(function () {
Route::get('notifications', 'NotificationsController@all');
Route::post('notifications/{id}', 'NotificationsController@read');
});
});
Route::prefix('players')->middleware('auth:oauth')->group(function () {
Route::get('', 'PlayerController@list');
Route::post('', 'PlayerController@add');
Route::delete('{player}', 'PlayerController@delete');
Route::put('{player}/name', 'PlayerController@rename');
Route::put('{player}/textures', 'PlayerController@setTexture');
Route::delete('{player}/textures', 'PlayerController@clearTexture');
Route::get('', 'PlayerController@list')->middleware(['scope:Player.Read,Player.ReadWrite']);
Route::middleware(['scope:Player.ReadWrite'])->group(function () {
Route::post('', 'PlayerController@add');
Route::delete('{player}', 'PlayerController@delete');
Route::put('{player}/name', 'PlayerController@rename');
Route::put('{player}/textures', 'PlayerController@setTexture');
Route::delete('{player}/textures', 'PlayerController@clearTexture');
});
});
Route::prefix('closet')->middleware('auth:oauth')->group(function () {
Route::get('', 'ClosetController@getClosetData');
Route::post('', 'ClosetController@add');
Route::put('{tid}', 'ClosetController@rename');
Route::delete('{tid}', 'ClosetController@remove');
Route::get('', 'ClosetController@getClosetData')->middleware(['scope:Closet.Read,Closet.ReadWrite']);
Route::middleware(['scope:Closet.ReadWrite'])->group(function () {
Route::post('', 'ClosetController@add');
Route::put('{tid}', 'ClosetController@rename');
Route::delete('{tid}', 'ClosetController@remove');
});
});
Route::prefix('admin')
->middleware(['auth:oauth', 'role:admin'])
->group(function () {
Route::prefix('users')->group(function () {
Route::get('', 'UsersManagementController@list')->name('list');
Route::prefix('{user}')->group(function () {
Route::get('', 'UsersManagementController@list')->name('list')->middleware(['scope:UsersManagement.Read,UsersManagement.ReadWrite']);
Route::prefix('{user}')->middleware(['scope:UsersManagement.ReadWrite'])->group(function () {
Route::put('email', 'UsersManagementController@email')->name('email');
Route::put('verification', 'UsersManagementController@verification')->name('verification');
Route::put('nickname', 'UsersManagementController@nickname')->name('nickname');
@ -42,23 +50,28 @@ Route::prefix('admin')
});
Route::prefix('players')->group(function () {
Route::get('', 'PlayersManagementController@list');
Route::put('{player}/name', 'PlayersManagementController@name');
Route::put('{player}/owner', 'PlayersManagementController@owner');
Route::put('{player}/textures', 'PlayersManagementController@texture');
Route::delete('{player}', 'PlayersManagementController@delete');
Route::get('', 'PlayersManagementController@list')->middleware(['scope:PlayersManagement.Read,PlayersManagement.ReadWrite']);
Route::middleware(['scope:PlayersManagement.ReadWrite'])->group(function () {
Route::put('{player}/name', 'PlayersManagementController@name');
Route::put('{player}/owner', 'PlayersManagementController@owner');
Route::put('{player}/textures', 'PlayersManagementController@texture');
Route::delete('{player}', 'PlayersManagementController@delete');
});
});
Route::prefix('closet')->group(function () {
Route::get('{user}', 'ClosetManagementController@list');
Route::post('{user}', 'ClosetManagementController@add');
Route::delete('{user}', 'ClosetManagementController@remove');
Route::get('{user}', 'ClosetManagementController@list')->middleware(['scope:ClosetManagement.Read,ClosetManagement.ReadWrite']);
Route::middleware(['scope:ClosetManagement.ReadWrite'])->group(function () {
Route::post('{user}', 'ClosetManagementController@add');
Route::delete('{user}', 'ClosetManagementController@remove');
});
});
Route::prefix('reports')->group(function () {
Route::get('', 'ReportController@manage');
Route::put('{report}', 'ReportController@review');
Route::get('', 'ReportController@manage')->middleware(['scope:ReportsManagement.Read,ReportsManagement.ReadWrite']);
Route::put('{report}', 'ReportController@review')->middleware(['scope:ReportsManagement.ReadWrite']);
});
Route::post('notifications', 'NotificationsController@send');
Route::post('notifications', 'NotificationsController@send')->middleware(['scope:Notification.ReadWrite']);
});