FEATURE #2048: add basics of permissions management

This commit is contained in:
Michal Kváček
2017-08-10 23:18:53 +02:00
parent f702ba3a29
commit cccdbda543
18 changed files with 483 additions and 147 deletions
@@ -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;
-87
View File
@@ -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)
{
//
}
}
+2
View File
@@ -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,
+19
View File
@@ -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');
}
}
+22
View File
@@ -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);
}
}
View File
+20
View File
@@ -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;
}
}
+66
View File
@@ -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');
}
}
+61
View File
@@ -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)
{
//
}
}
+58
View File
@@ -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)
{
//
}
}
+14 -8
View File
@@ -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();
//
}
}
+12 -1
View File
@@ -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');
}
}
+39
View File
@@ -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');
});
+4 -2
View File
@@ -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')
+3
View File
@@ -0,0 +1,3 @@
<?php
Route::resource('permissions', 'Permissions');
+1 -39
View File
@@ -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');
@@ -61,4 +24,3 @@ Route::group(['prefix' => LaravelLocalization::setLocale(), 'middleware' => ['bi
Route::get('{url}', 'Visitor\ContentPage@getPage')
->name('page.content');
});
});