From b28ace970c2acfa613be8d374cd33a2e29bad51c Mon Sep 17 00:00:00 2001 From: Nabeel Shahzad Date: Thu, 20 Jun 2019 16:52:37 -0400 Subject: [PATCH] Add interface to additional roles/permissions --- ...17_06_08_0001_roles_permissions_tables.php | 5 - ...019_06_19_220910_add_readonly_to_roles.php | 37 ++++ app/Database/seeds/DatabaseSeeder.php | 21 +- app/Database/seeds/permissions.yml | 42 ++++ app/Database/seeds/sample.yml | 4 + .../Controllers/Admin/RolesController.php | 185 ++++++++++++++++++ .../Controllers/Auth/RegisterController.php | 2 +- app/Http/Requests/CreateRoleRequest.php | 19 ++ app/Http/Requests/UpdateRoleRequest.php | 32 +++ app/Models/Role.php | 22 +++ app/Repositories/PermissionsRepository.php | 48 +++++ app/Repositories/RoleRepository.php | 55 ++++++ app/Routes/admin.php | 8 +- app/Services/UserService.php | 7 +- docker-compose.yml | 1 - .../Http/Controllers/InstallerController.php | 48 +++-- resources/views/admin/menu.blade.php | 49 ++++- resources/views/admin/roles/create.blade.php | 11 ++ resources/views/admin/roles/edit.blade.php | 17 ++ resources/views/admin/roles/fields.blade.php | 51 +++++ resources/views/admin/roles/index.blade.php | 19 ++ resources/views/admin/roles/table.blade.php | 20 ++ resources/views/admin/roles/users.blade.php | 17 ++ storage/debugbar/.gitignore | 0 storage/docker/.gitignore | 0 storage/framework/cache/.gitignore | 0 storage/framework/sessions/.gitignore | 0 storage/framework/views/.gitignore | 0 tests/RegistrationTest.php | 2 +- 29 files changed, 677 insertions(+), 45 deletions(-) create mode 100644 app/Database/migrations/2019_06_19_220910_add_readonly_to_roles.php create mode 100644 app/Database/seeds/permissions.yml create mode 100644 app/Http/Controllers/Admin/RolesController.php create mode 100644 app/Http/Requests/CreateRoleRequest.php create mode 100644 app/Http/Requests/UpdateRoleRequest.php create mode 100644 app/Repositories/PermissionsRepository.php create mode 100644 app/Repositories/RoleRepository.php create mode 100644 resources/views/admin/roles/create.blade.php create mode 100644 resources/views/admin/roles/edit.blade.php create mode 100644 resources/views/admin/roles/fields.blade.php create mode 100644 resources/views/admin/roles/index.blade.php create mode 100644 resources/views/admin/roles/table.blade.php create mode 100644 resources/views/admin/roles/users.blade.php mode change 100644 => 100755 storage/debugbar/.gitignore mode change 100644 => 100755 storage/docker/.gitignore mode change 100644 => 100755 storage/framework/cache/.gitignore mode change 100644 => 100755 storage/framework/sessions/.gitignore mode change 100644 => 100755 storage/framework/views/.gitignore diff --git a/app/Database/migrations/2017_06_08_0001_roles_permissions_tables.php b/app/Database/migrations/2017_06_08_0001_roles_permissions_tables.php index 1116927d..033e4cc1 100644 --- a/app/Database/migrations/2017_06_08_0001_roles_permissions_tables.php +++ b/app/Database/migrations/2017_06_08_0001_roles_permissions_tables.php @@ -74,11 +74,6 @@ class RolesPermissionsTables extends Migration 'name' => 'admin', 'display_name' => 'Administrators', ], - [ - 'id' => 2, - 'name' => 'user', - 'display_name' => 'Pilot', - ], ]; $this->addData('roles', $roles); diff --git a/app/Database/migrations/2019_06_19_220910_add_readonly_to_roles.php b/app/Database/migrations/2019_06_19_220910_add_readonly_to_roles.php new file mode 100644 index 00000000..6f05512f --- /dev/null +++ b/app/Database/migrations/2019_06_19_220910_add_readonly_to_roles.php @@ -0,0 +1,37 @@ +boolean('read_only'); + }); + + // Set the two main roles as read-only + DB::table('roles') + ->whereIn('name', ['admin', 'user']) + ->update(['read_only' => true]); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down(): void + { + Schema::table('roles', static function (Blueprint $table) { + $table->dropColumn('read_only'); + }); + } +} diff --git a/app/Database/seeds/DatabaseSeeder.php b/app/Database/seeds/DatabaseSeeder.php index 126a8d35..06c1f459 100755 --- a/app/Database/seeds/DatabaseSeeder.php +++ b/app/Database/seeds/DatabaseSeeder.php @@ -5,17 +5,16 @@ use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder { - /** - * Map these other environments to a specific seed file - * - * @var array - */ - public static $seed_mapper = [ + private static $seed_mapper = [ 'local' => 'dev', 'qa' => 'dev', 'staging' => 'dev', ]; + private static $always_seed = [ + 'permissions', + ]; + /** * Run the database seeds. * @@ -28,6 +27,7 @@ class DatabaseSeeder extends Seeder $env = self::$seed_mapper[$env]; } + Log::info('Seeding from environment '.$env); $path = database_path('seeds/'.$env.'.yml'); if (!file_exists($path)) { @@ -36,5 +36,14 @@ class DatabaseSeeder extends Seeder $svc = app(DatabaseService::class); $svc->seed_from_yaml_file($path); + + // Always seed/sync these + foreach (self::$always_seed as $file) { + Log::info('Importing '.$file); + $path = database_path('seeds/'.$file.'.yml'); + if (file_exists($path)) { + $svc->seed_from_yaml_file($path); + } + } } } diff --git a/app/Database/seeds/permissions.yml b/app/Database/seeds/permissions.yml new file mode 100644 index 00000000..06f1f724 --- /dev/null +++ b/app/Database/seeds/permissions.yml @@ -0,0 +1,42 @@ +# All of the different permissions that can be assigned to roles +--- +permissions: + - name: admin-access + display_name: Admin Access + description: Access the admin panel + - name: airlines + display_name: Airlines + description: Create/edit airlines + - name: airports + display_name: Airports + description: Create/edit airports + - name: addons + display_name: Addons + description: Edit/view addons + - name: awards + display_name: Awards + description: Create/edit award classes + - name: flights + display_name: Flights + description: Create/edit flights + - name: fleet + display_name: Fleet + description: Create/edit subfleets and fleets + - name: fares + display_name: Fares + description: Create/edit fares + - name: finances + display_name: Finances + description: Create/view finance related items + - name: pireps + display_name: PIREPs + description: Accept/reject/edit PIREPs + - name: ranks + display_name: Ranks + description: Create/edit ranks + - name: users + display_name: Users + description: Create/edit users + - name: settings + display_name: Settings + description: Edit VA settings diff --git a/app/Database/seeds/sample.yml b/app/Database/seeds/sample.yml index d750e867..b2a66172 100644 --- a/app/Database/seeds/sample.yml +++ b/app/Database/seeds/sample.yml @@ -9,6 +9,10 @@ airlines: created_at: now updated_at: now +roles: + - name: fleet-only + display_name: Edit Fleet + users: - id: 1 name: Admin User diff --git a/app/Http/Controllers/Admin/RolesController.php b/app/Http/Controllers/Admin/RolesController.php new file mode 100644 index 00000000..d0d32460 --- /dev/null +++ b/app/Http/Controllers/Admin/RolesController.php @@ -0,0 +1,185 @@ +permsRepo = $permsRepo; + $this->rolesRepo = $rolesRepo; + } + + /** + * Display a listing of the Airlines. + * + * @param Request $request + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + * + * @throws \Prettus\Repository\Exceptions\RepositoryException + */ + public function index(Request $request) + { + $this->rolesRepo->pushCriteria(new RequestCriteria($request)); + $roles = $this->rolesRepo->findWhere(['read_only' => false]); + + return view('admin.roles.index', [ + 'roles' => $roles, + ]); + } + + /** + * Show the form for creating a new Airlines. + */ + public function create() + { + return view('admin.roles.create', [ + 'permissions' => $this->permsRepo->all(), + ]); + } + + /** + * Store a newly created Airlines in storage. + * + * @param CreateRoleRequest $request + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + * + * @throws \Prettus\Validator\Exceptions\ValidatorException + */ + public function store(CreateRoleRequest $request) + { + $input = $request->all(); + + // Create a slug using the display name provided + $input['name'] = str_slug($input['display_name']); + + $this->rolesRepo->create($input); + + Flash::success('Role saved successfully.'); + return redirect(route('admin.roles.index')); + } + + /** + * Display the specified role + * + * @param int $id + * + * @return mixed + */ + public function show($id) + { + $roles = $this->rolesRepo->findWithoutFail($id); + + if (empty($roles)) { + Flash::error('Role not found'); + return redirect(route('admin.roles.index')); + } + + return view('admin.roles.show', [ + 'roles' => $roles, + ]); + } + + /** + * Show the form for editing the specified roles + * + * @param int $id + * + * @return Response + */ + public function edit($id) + { + $role = $this->rolesRepo->findWithoutFail($id); + + if (empty($role)) { + Flash::error('Role not found'); + return redirect(route('admin.role.index')); + } + + return view('admin.roles.edit', [ + 'role' => $role, + 'permissions' => $this->permsRepo->all(), + ]); + } + + /** + * Update the specified Airlines in storage. + * + * @param int $id + * @param UpdateRoleRequest $request + * + * @return Response + *@throws \Prettus\Validator\Exceptions\ValidatorException + * + */ + public function update($id, UpdateRoleRequest $request) + { + $role = $this->rolesRepo->findWithoutFail($id); + + if (empty($role)) { + Flash::error('Role not found'); + return redirect(route('admin.roles.index')); + } + + $this->rolesRepo->update($request->all(), $id); + + // Update the permissions, filter out null/invalid values + $perms = collect($request->permissions)->filter(static function ($v, $k) { + return $v; + }); + + $role->permissions()->sync($perms); + + Flash::success('Roles updated successfully.'); + return redirect(route('admin.roles.index')); + } + + /** + * Remove the specified Airlines from storage. + * + * @param int $id + * + * @return Response + */ + public function destroy($id) + { + $roles = $this->rolesRepo->findWithoutFail($id); + + if (empty($roles)) { + Flash::error('Role not found'); + return redirect(route('admin.roles.index')); + } + + $this->rolesRepo->delete($id); + + Flash::success('Role deleted successfully.'); + return redirect(route('admin.roles.index')); + } +} diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php index 22cad1d9..7bbf337b 100755 --- a/app/Http/Controllers/Auth/RegisterController.php +++ b/app/Http/Controllers/Auth/RegisterController.php @@ -122,7 +122,7 @@ class RegisterController extends Controller } $user = User::create($opts); - $user = $this->userService->createPilot($user); + $user = $this->userService->createUser($user); Log::info('User registered: ', $user->toArray()); diff --git a/app/Http/Requests/CreateRoleRequest.php b/app/Http/Requests/CreateRoleRequest.php new file mode 100644 index 00000000..8ed7bf8f --- /dev/null +++ b/app/Http/Requests/CreateRoleRequest.php @@ -0,0 +1,19 @@ + 'boolean', + ]; + + /** + * Validation rules + * + * @var array + */ + public static $rules = [ + 'display_name' => 'required', + ]; } diff --git a/app/Repositories/PermissionsRepository.php b/app/Repositories/PermissionsRepository.php new file mode 100644 index 00000000..ff385a87 --- /dev/null +++ b/app/Repositories/PermissionsRepository.php @@ -0,0 +1,48 @@ + 'like', + ]; + + public function model(): string + { + return Permission::class; + } + + /** + * Return the list of roles formatted for a select box + * + * @param boolean $add_blank + * + * @return array + */ + public function selectBoxList($add_blank = false): array + { + $retval = []; + $items = $this->all(); + + if ($add_blank) { + $retval[''] = ''; + } + + foreach ($items as $i) { + $retval[$i->id] = $i->name; + } + + return $retval; + } +} diff --git a/app/Repositories/RoleRepository.php b/app/Repositories/RoleRepository.php new file mode 100644 index 00000000..0267cfd3 --- /dev/null +++ b/app/Repositories/RoleRepository.php @@ -0,0 +1,55 @@ + 'like', + ]; + + public function model(): string + { + return Role::class; + } + + /** + * Return the list of roles formatted for a select box + * + * @param boolean $include_read_only + * @param boolean $add_blank + * + * @return array + */ + public function selectBoxList($include_read_only = true, $add_blank = false): array + { + $retval = []; + + $where = []; + if ($include_read_only) { + $where['read_only'] = true; + } + + $items = $this->findWhere($where); + + if ($add_blank) { + $retval[''] = ''; + } + + foreach ($items as $i) { + $retval[$i->id] = $i->name; + } + + return $retval; + } +} diff --git a/app/Routes/admin.php b/app/Routes/admin.php index 4fe64921..b802fd03 100644 --- a/app/Routes/admin.php +++ b/app/Routes/admin.php @@ -4,10 +4,14 @@ */ Route::group([ 'namespace' => 'Admin', 'prefix' => 'admin', 'as' => 'admin.', - 'middleware' => ['role:admin'], -], function () { + 'middleware' => ['ability:admin,admin-access'], +], static function () { + // CRUD for airlines Route::resource('airlines', 'AirlinesController'); + // CRUD for roles + Route::resource('roles', 'RolesController'); + Route::get('airports/export', 'AirportController@export')->name('airports.export'); Route::match(['get', 'post', 'put'], 'airports/fuel', 'AirportController@fuel'); Route::match(['get', 'post'], 'airports/import', 'AirportController@import')->name('airports.import'); diff --git a/app/Services/UserService.php b/app/Services/UserService.php index f69b65c9..fcc59515 100644 --- a/app/Services/UserService.php +++ b/app/Services/UserService.php @@ -51,7 +51,7 @@ class UserService extends Service * * @return mixed */ - public function createPilot(User $user, array $groups = null) + public function createUser(User $user, array $groups = null) { // Determine if we want to auto accept if (setting('pilots.auto_accept') === true) { @@ -63,9 +63,10 @@ class UserService extends Service $user->save(); // Attach the user roles - $role = Role::where('name', 'user')->first(); - $user->attachRole($role); + // $role = Role::where('name', 'user')->first(); + // $user->attachRole($role); + // Attach any additional roles if (!empty($groups) && \is_array($groups)) { foreach ($groups as $group) { $role = Role::where('name', $group)->first(); diff --git a/docker-compose.yml b/docker-compose.yml index 6688b9c9..4d7127cf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,5 @@ --- version: '3' - services: app: build: diff --git a/modules/Installer/Http/Controllers/InstallerController.php b/modules/Installer/Http/Controllers/InstallerController.php index 62462340..072c427e 100644 --- a/modules/Installer/Http/Controllers/InstallerController.php +++ b/modules/Installer/Http/Controllers/InstallerController.php @@ -124,6 +124,10 @@ class InstallerController extends Controller /** * Step 1. Check the modules and permissions + * + * @param Request $request + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ public function step1(Request $request) { @@ -151,6 +155,10 @@ class InstallerController extends Controller /** * Step 2. Database Setup + * + * @param Request $request + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ public function step2(Request $request) { @@ -186,13 +194,13 @@ class InstallerController extends Controller // Now write out the env file $attrs = [ 'SITE_NAME' => $request->post('site_name'), - 'SITE_URL' => $request->post('site_url'), - 'DB_CONN' => $request->post('db_conn'), - 'DB_HOST' => $request->post('db_host'), - 'DB_PORT' => $request->post('db_port'), - 'DB_NAME' => $request->post('db_name'), - 'DB_USER' => $request->post('db_user'), - 'DB_PASS' => $request->post('db_pass'), + 'SITE_URL' => $request->post('site_url'), + 'DB_CONN' => $request->post('db_conn'), + 'DB_HOST' => $request->post('db_host'), + 'DB_PORT' => $request->post('db_port'), + 'DB_NAME' => $request->post('db_name'), + 'DB_USER' => $request->post('db_user'), + 'DB_PASS' => $request->post('db_pass'), 'DB_PREFIX' => $request->post('db_prefix'), ]; @@ -264,12 +272,12 @@ class InstallerController extends Controller public function usersetup(Request $request) { $validator = Validator::make($request->all(), [ - 'airline_name' => 'required', - 'airline_icao' => 'required|unique:airlines,icao', + 'airline_name' => 'required', + 'airline_icao' => 'required|unique:airlines,icao', 'airline_country' => 'required', - 'name' => 'required', - 'email' => 'required|email|unique:users,email', - 'password' => 'required|confirmed' + 'name' => 'required', + 'email' => 'required|email|unique:users,email', + 'password' => 'required|confirmed' ]); if ($validator->fails()) { @@ -283,8 +291,8 @@ class InstallerController extends Controller */ $attrs = [ - 'icao' => $request->get('airline_icao'), - 'name' => $request->get('airline_name'), + 'icao' => $request->get('airline_icao'), + 'name' => $request->get('airline_name'), 'country' => $request->get('airline_country'), ]; @@ -297,17 +305,17 @@ class InstallerController extends Controller */ $attrs = [ - 'name' => $request->get('name'), - 'email' => $request->get('email'), - 'api_key' => Utils::generateApiKey(), - 'airline_id' => $airline->id, + 'name' => $request->get('name'), + 'email' => $request->get('email'), + 'api_key' => Utils::generateApiKey(), + 'airline_id' => $airline->id, 'home_airport_id' => 'KAUS', 'curr_airport_id' => 'KAUS', - 'password' => Hash::make($request->get('password')) + 'password' => Hash::make($request->get('password')) ]; $user = User::create($attrs); - $user = $this->userService->createPilot($user, ['admin']); + $user = $this->userService->createUser($user, ['admin']); Log::info('User registered: ', $user->toArray()); # Set the intial admin e-mail address diff --git a/resources/views/admin/menu.blade.php b/resources/views/admin/menu.blade.php index 8e597189..25833a33 100644 --- a/resources/views/admin/menu.blade.php +++ b/resources/views/admin/menu.blade.php @@ -10,15 +10,28 @@
@@ -30,15 +43,37 @@
@@ -50,9 +85,11 @@
diff --git a/resources/views/admin/roles/create.blade.php b/resources/views/admin/roles/create.blade.php new file mode 100644 index 00000000..053590e4 --- /dev/null +++ b/resources/views/admin/roles/create.blade.php @@ -0,0 +1,11 @@ +@extends('admin.app') +@section('title', 'Add Airline') +@section('content') +
+
+ {{ Form::open(['route' => 'admin.airlines.store']) }} + @include('admin.airlines.fields') + {{ Form::close() }} +
+
+@endsection diff --git a/resources/views/admin/roles/edit.blade.php b/resources/views/admin/roles/edit.blade.php new file mode 100644 index 00000000..10fb640a --- /dev/null +++ b/resources/views/admin/roles/edit.blade.php @@ -0,0 +1,17 @@ +@extends('admin.app') +@section('title', "Edit \"$role->display_name\"") +@section('content') +
+
+ {{ Form::model($role, ['route' => ['admin.roles.update', $role->id], 'method' => 'patch']) }} + @include('admin.roles.fields') + {{ Form::close() }} +
+
+ +
+
+ @include('admin.roles.users') +
+
+@endsection diff --git a/resources/views/admin/roles/fields.blade.php b/resources/views/admin/roles/fields.blade.php new file mode 100644 index 00000000..5336f468 --- /dev/null +++ b/resources/views/admin/roles/fields.blade.php @@ -0,0 +1,51 @@ +
+ +
+
+
+  Name +
+
+
+
+ {{ Form::text('display_name', null, ['class' => 'form-control']) }} +

{{ $errors->first('display_name') }}

+
+
+
+
+
+ + +
+
+
+  Permissions +
+
+
+
+ @foreach($permissions as $p) +
+ +
+ @endforeach +
+
+
+
+
+
+
+ +
+
+ {{ Form::button('Save', ['type' => 'submit', 'class' => 'btn btn-success']) }} + Cancel +
+
+
diff --git a/resources/views/admin/roles/index.blade.php b/resources/views/admin/roles/index.blade.php new file mode 100644 index 00000000..88933c82 --- /dev/null +++ b/resources/views/admin/roles/index.blade.php @@ -0,0 +1,19 @@ +@extends('admin.app') + +@section('title', 'Roles') +@section('actions') +
  • + + + Add New +
  • +@endsection + +@section('content') +
    +
    + @include('admin.roles.table') +
    +
    +@endsection + diff --git a/resources/views/admin/roles/table.blade.php b/resources/views/admin/roles/table.blade.php new file mode 100644 index 00000000..ccfbb406 --- /dev/null +++ b/resources/views/admin/roles/table.blade.php @@ -0,0 +1,20 @@ + + + + + + + @foreach($roles as $role) + + + + + @endforeach + +
    Name
    {{ $role->display_name }} + {{ Form::open(['route' => ['admin.roles.destroy', $role->id], 'method' => 'delete']) }} + + {{ Form::button('', ['type' => 'submit', 'class' => 'btn btn-sm btn-danger btn-icon', 'onclick' => "return confirm('Are you sure?')"]) }} + {{ Form::close() }} +
    diff --git a/resources/views/admin/roles/users.blade.php b/resources/views/admin/roles/users.blade.php new file mode 100644 index 00000000..6ca62dd2 --- /dev/null +++ b/resources/views/admin/roles/users.blade.php @@ -0,0 +1,17 @@ +
    + +
    +
    +
    +  Users +
    +
    +
    +
    + TO DO +
    +
    +
    +
    +
    +
    diff --git a/storage/debugbar/.gitignore b/storage/debugbar/.gitignore old mode 100644 new mode 100755 diff --git a/storage/docker/.gitignore b/storage/docker/.gitignore old mode 100644 new mode 100755 diff --git a/storage/framework/cache/.gitignore b/storage/framework/cache/.gitignore old mode 100644 new mode 100755 diff --git a/storage/framework/sessions/.gitignore b/storage/framework/sessions/.gitignore old mode 100644 new mode 100755 diff --git a/storage/framework/views/.gitignore b/storage/framework/views/.gitignore old mode 100644 new mode 100755 diff --git a/tests/RegistrationTest.php b/tests/RegistrationTest.php index 04178f2f..1f5f43c5 100644 --- a/tests/RegistrationTest.php +++ b/tests/RegistrationTest.php @@ -22,7 +22,7 @@ class RegistrationTest extends TestCase setting('pilots.auto_accept', true); $user = factory(App\Models\User::class)->create(); - $user = $userSvc->createPilot($user); + $user = $userSvc->createUser($user); $this->assertEquals(UserState::ACTIVE, $user->state);