FEATURE #2048: add basics of permissions management
This commit is contained in:
@@ -16,6 +16,7 @@ use App\Models\Content\Content;
|
||||
use App\Models\Content\ContentHistory;
|
||||
use App\Models\FileManager\File;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Modules\Categories\Contracts\Repositories\Category as ICategory;
|
||||
use Modules\Categories\Repositories\Category;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Requests\LoginRequest;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
/**
|
||||
* Class Login
|
||||
*
|
||||
* @package App\Http\Controllers\Admin
|
||||
*
|
||||
* @todo use trait ThrottlesLogins for disabling brutal-force password cracking
|
||||
*/
|
||||
class Login extends Controller
|
||||
{
|
||||
/**
|
||||
* Display login form
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function getLogin()
|
||||
{
|
||||
return view('admin.loginForm');
|
||||
}
|
||||
|
||||
/**
|
||||
* Login handler
|
||||
*
|
||||
* @param LoginRequest $request
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function postLogin(LoginRequest $request)
|
||||
{
|
||||
$email = $request->email;
|
||||
|
||||
// try to find user
|
||||
$user = User::where('email', $email)
|
||||
->where('role', '!=', User::ROLE_VISITOR)
|
||||
->where('status', User::STATE_ACTIVE)
|
||||
->first();
|
||||
|
||||
if (!$user) {
|
||||
return $this->loginFailed();
|
||||
}
|
||||
|
||||
// try to log in user
|
||||
if (Hash::check($request->password, $user->password)) {
|
||||
Auth::login($user);
|
||||
|
||||
$redirect = $request->redirectTo ? $request->redirectTo : route('admin.dashboard');
|
||||
|
||||
return redirect()->intended($redirect);
|
||||
}
|
||||
|
||||
// login invalid, redirect and inform the user
|
||||
return $this->loginFailed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs out user
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function logout()
|
||||
{
|
||||
|
||||
// remove user information
|
||||
\Auth::logout();
|
||||
|
||||
// redirect to login form with info about successfull logout
|
||||
return redirect()
|
||||
->route('login')
|
||||
->with('success', trans('admin/common.logoutSuccessfull'));
|
||||
}
|
||||
|
||||
private function loginFailed()
|
||||
{
|
||||
return redirect()
|
||||
->route('login')
|
||||
->with('error', trans('auth.failed'))
|
||||
->withInput();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class Permissions extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
dd(Auth::user()->permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Http;
|
||||
|
||||
use App\Http\Middleware\Web\IncrementWidgetsViews;
|
||||
use Illuminate\Auth\Middleware\Authorize;
|
||||
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
|
||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||
use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode;
|
||||
@@ -54,6 +55,7 @@ class Kernel extends HttpKernel
|
||||
* @var array
|
||||
*/
|
||||
protected $routeMiddleware = [
|
||||
'can' => Authorize::class,
|
||||
'auth' => Middleware\Admin\Authenticate::class,
|
||||
'onlyAdmin' => Middleware\Admin\RequireAdminRole::class,
|
||||
'adminMenu' => Middleware\Admin\AdminMenuRegistrator::class,
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Permission extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'string_id',
|
||||
'parent_id',
|
||||
'group',
|
||||
'name',
|
||||
];
|
||||
|
||||
public function parent() {
|
||||
return $this->hasOne(Permission::class, 'parent_id');
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ class User extends Model implements AuthenticableInterface, CanResetPassword
|
||||
const ROLE_VISITOR = 'visitor';
|
||||
const ROLE_EDITOR = 'editor';
|
||||
const ROLE_ADMIN = 'admin';
|
||||
protected static $abilities;
|
||||
public $timestamps = true;
|
||||
protected $fillable = [
|
||||
'email',
|
||||
@@ -60,4 +61,25 @@ class User extends Model implements AuthenticableInterface, CanResetPassword
|
||||
{
|
||||
$this->notify(new ResetPasswordNotification($token));
|
||||
}
|
||||
|
||||
public function permissions()
|
||||
{
|
||||
return $this->belongsToMany(Permission::class, 'user_has_permissions');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current user has given ability
|
||||
*
|
||||
* @param $ability
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasAbility($ability)
|
||||
{
|
||||
if (empty(self::$abilities)) {
|
||||
self::$abilities = $this->permissions->pluck('string_id')->toArray();
|
||||
}
|
||||
|
||||
return in_array($ability, self::$abilities);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
|
||||
abstract class BasicPolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
public function before(User $user, $ability)
|
||||
{
|
||||
if ($user->role == User::ROLE_ADMIN) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\Content\Content;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
|
||||
class ContentPolicy extends BasicPolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the content.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @param \App\Models\Content\Content $content
|
||||
* @return mixed
|
||||
*/
|
||||
public function view(User $user, Content $content)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create contents.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @return mixed
|
||||
*/
|
||||
public function create(User $user)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the content.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @param \App\Models\Content\Content $content
|
||||
* @return mixed
|
||||
*/
|
||||
public function update(User $user, Content $content)
|
||||
{
|
||||
if (!$user->hasAbility('update-contents-'.$content->module->alias))
|
||||
return false;
|
||||
|
||||
if ($user->id != $content->user_id) {
|
||||
return $user->hasAbility('update-contents') && $user->hasAbility('update-all-contents');
|
||||
};
|
||||
|
||||
return $user->hasAbility('update-contents');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the content.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @param \App\Models\Content\Content $content
|
||||
* @return mixed
|
||||
*/
|
||||
public function delete(User $user, Content $content)
|
||||
{
|
||||
return $user->hasAbility('delete-contents');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\Module;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
|
||||
class ModulePolicy extends BasicPolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the module.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @param \App\Module $module
|
||||
* @return mixed
|
||||
*/
|
||||
public function view(User $user, Module $module)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create modules.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @param Module $module
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function create(User $user, Module $module)
|
||||
{
|
||||
return $user->hasAbility('create-contents-'.$module->alias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the module.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @param \App\Module $module
|
||||
* @return mixed
|
||||
*/
|
||||
public function update(User $user, Module $module)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the module.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @param \App\Module $module
|
||||
* @return mixed
|
||||
*/
|
||||
public function delete(User $user, Module $module)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
|
||||
class UserPolicy extends BasicPolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the user.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @param \App\Models\User $user
|
||||
* @return mixed
|
||||
*/
|
||||
public function view(User $user, User $user)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create users.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @return mixed
|
||||
*/
|
||||
public function create(User $user)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the user.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @param \App\Models\User $user
|
||||
* @return mixed
|
||||
*/
|
||||
public function update(User $user, User $user)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the user.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @param \App\Models\User $user
|
||||
* @return mixed
|
||||
*/
|
||||
public function delete(User $user, User $user)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -2,30 +2,36 @@
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
|
||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||
use App\Models\Content\Content;
|
||||
use App\Models\Module;
|
||||
use App\Models\User;
|
||||
use App\Policies\ContentPolicy;
|
||||
use App\Policies\ModulePolicy;
|
||||
use App\Policies\UserPolicy;
|
||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as IlluminateAuthServiceProvider;
|
||||
|
||||
class AuthServiceProvider extends ServiceProvider
|
||||
class AuthServiceProvider extends IlluminateAuthServiceProvider
|
||||
{
|
||||
/**
|
||||
* The policy mappings for the application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $policies = [
|
||||
'App\Model' => 'App\Policies\ModelPolicy',
|
||||
Content::class => ContentPolicy::class,
|
||||
User::class => UserPolicy::class,
|
||||
Module::class => ModulePolicy::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Register any application authentication / authorization services.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Auth\Access\Gate $gate
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot(GateContract $gate)
|
||||
public function boot()
|
||||
{
|
||||
$this->registerPolicies($gate);
|
||||
|
||||
$this->registerPolicies();
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,21 @@ class RouteServiceProvider extends ServiceProvider
|
||||
*/
|
||||
public function map()
|
||||
{
|
||||
$this->mapAdminRoutes();
|
||||
$this->mapWebRoutes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the "admin" routes for the application.
|
||||
* These routes all receive session state, CSRF protection, etc.
|
||||
* @return void
|
||||
*/
|
||||
protected function mapAdminRoutes()
|
||||
{
|
||||
Route::namespace($this->namespace.'\\Admin')
|
||||
->group(base_path('routes/admin.php'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the "web" routes for the application.
|
||||
* These routes all receive session state, CSRF protection, etc.
|
||||
@@ -31,7 +43,6 @@ class RouteServiceProvider extends ServiceProvider
|
||||
protected function mapWebRoutes()
|
||||
{
|
||||
Route::namespace($this->namespace)
|
||||
->middleware('web')
|
||||
->group(base_path('routes/web.php'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreatePermissionsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('permissions', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->integer('parent_id')
|
||||
->nullable()
|
||||
->unsigned();
|
||||
$table->string('string_id')
|
||||
->unique();
|
||||
$table->string('name');
|
||||
$table->string('group')
|
||||
->nullable(true);
|
||||
});
|
||||
|
||||
Schema::table('permissions', function (Blueprint $table) {
|
||||
$table->foreign('parent_id')
|
||||
->references('id')
|
||||
->on('permissions')
|
||||
->onDelete('set null')
|
||||
->onUpdate('cascade');
|
||||
});
|
||||
|
||||
Schema::create('user_has_permissions', function (Blueprint $table) {
|
||||
$table->integer('user_id');
|
||||
$table->integer('permission_id')
|
||||
->unsigned();
|
||||
});
|
||||
|
||||
Schema::table('user_has_permissions', function (Blueprint $table) {
|
||||
$table->foreign('user_id')
|
||||
->references('id')
|
||||
->on('users')
|
||||
->onDelete('cascade')
|
||||
->onUpdate('cascade');
|
||||
|
||||
$table->foreign('permission_id')
|
||||
->references('id')
|
||||
->on('permissions')
|
||||
->onDelete('cascade')
|
||||
->onUpdate('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('user_has_permissions');
|
||||
Schema::drop('permissions');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
|
||||
|
||||
Route::pattern('id', '\d+');
|
||||
Route::pattern('url', '[0-9\-a-z]+');
|
||||
|
||||
/*******************************************************************************/
|
||||
Auth::routes();
|
||||
$this->get('logout', 'Auth\LoginController@logout')
|
||||
->name('logout');
|
||||
|
||||
/**
|
||||
* This route group serves to handle request in admin
|
||||
*/
|
||||
|
||||
Route::group([
|
||||
'prefix' => LaravelLocalization::setLocale() . '/admin',
|
||||
'middleware' => ['bindings', 'admin']
|
||||
], function () {
|
||||
|
||||
// include all routes in routes/admin subfolder
|
||||
$files = File::allFiles('routes/admin');
|
||||
foreach ($files as $file) {
|
||||
require_once base_path($file);
|
||||
}
|
||||
|
||||
// display dashboard with some usefull information
|
||||
Route::get('/', 'Dashboard\Dashboard@index')
|
||||
->name('admin.dashboard');
|
||||
|
||||
// list of all actions
|
||||
Route::get('actions', 'ActionLog\Overview@index')
|
||||
->name('log.overview');
|
||||
|
||||
// search form with results
|
||||
Route::get('search', 'Search@getSearch')
|
||||
->name('search');
|
||||
});
|
||||
@@ -6,11 +6,13 @@ Route::group(['namespace' => 'Contents'], function () {
|
||||
|
||||
// form for creating new static page
|
||||
Route::get('content/new/{module}', 'CreateNewContent@create')
|
||||
->name('content.new');
|
||||
->name('content.new')
|
||||
->middleware(['can:create,module', 'can:create,' . \App\Models\Content\Content::class]);
|
||||
|
||||
// form for editing existing static page
|
||||
Route::get('content/edit/{page}', 'EditContent@edit')
|
||||
->name('content.edit');
|
||||
->name('content.edit')
|
||||
->middleware('can:update,page');
|
||||
|
||||
// post method for creating new static page
|
||||
Route::post('content/new/{module}', 'CreateNewContent@store')
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
Route::resource('permissions', 'Permissions');
|
||||
+1
-39
@@ -10,45 +10,8 @@ Route::pattern('id', '\d+');
|
||||
Route::pattern('url', '[0-9\-a-z]+');
|
||||
|
||||
/*******************************************************************************/
|
||||
Auth::routes();
|
||||
$this->get('logout', 'Auth\LoginController@logout')->name('logout');
|
||||
|
||||
Route::group(['prefix' => LaravelLocalization::setLocale(), 'middleware' => ['bindings']], function () {
|
||||
/**
|
||||
* This route group serves to handle request in admin
|
||||
*/
|
||||
Route::group([
|
||||
'middleware' => ['admin'],
|
||||
'namespace' => 'Admin',
|
||||
'prefix' => 'admin'
|
||||
], function () {
|
||||
|
||||
// include all routes in routes/admin subfolder
|
||||
$files = ['contents', 'fileManager', 'settings', 'templates', 'users', 'widgets'];
|
||||
foreach ($files as $file) {
|
||||
require_once __DIR__ . DIRECTORY_SEPARATOR . 'admin' . DIRECTORY_SEPARATOR . $file . '.php';
|
||||
}
|
||||
|
||||
// display dashboard with some usefull information
|
||||
Route::get('/', 'Dashboard\Dashboard@index')
|
||||
->name('admin.dashboard');
|
||||
|
||||
// list of all actions
|
||||
Route::get('actions', 'ActionLog\Overview@index')
|
||||
->name('log.overview');
|
||||
|
||||
// search form with results
|
||||
Route::get('search', 'Search@getSearch')
|
||||
->name('search');
|
||||
});
|
||||
|
||||
/**************************************************************************************/
|
||||
|
||||
/**
|
||||
* This section must be specified as last one
|
||||
*/
|
||||
Route::group(['middleware' => ['web']], function () {
|
||||
|
||||
Route::group(['prefix' => LaravelLocalization::setLocale(), 'middleware' => ['bindings', 'web']], function () {
|
||||
// Homepage
|
||||
Route::get('/', 'Visitor\Homepage@getIndex')
|
||||
->name('page.index');
|
||||
@@ -60,5 +23,4 @@ Route::group(['prefix' => LaravelLocalization::setLocale(), 'middleware' => ['bi
|
||||
// route for all content pages. This rule must be specified last (because it matches every route)
|
||||
Route::get('{url}', 'Visitor\ContentPage@getPage')
|
||||
->name('page.content');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user