├── .gitattributes ├── .gitignore ├── README.md ├── composer.json └── src ├── Commands └── SimplePassportConfiguration.php ├── Core ├── Paginator.php ├── PartialList.php ├── UserProfiles.php └── UserServices.php ├── Http ├── Controllers │ ├── ProfileController.php │ └── RoleController.php └── Middleware │ └── RoleMiddleware.php ├── Rules ├── ProfileExists.php └── RoleExists.php ├── SecurityStarterServiceProvider.php ├── config └── security-starter.php ├── database └── migrations │ ├── 2019_02_22_150438_create_profile_table.php │ ├── 2019_02_22_150448_create_role_table.php │ ├── 2019_02_22_150500_create_profile_role_table.php │ └── 2019_02_22_150512_create_user_profiles_table.php ├── models ├── Profile.php └── Role.php └── routes └── api.php /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.css linguist-vendored 3 | *.scss linguist-vendored 4 | *.js linguist-vendored 5 | CHANGELOG.md export-ignore 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Due to a time constraint, unfortunately this repository is no longer maintained.

2 |
3 | Security-Starter is a ready to use package that provide to you a complete CRUD REST system for a User/Profile/Role architecture, and also provide a middleware to specify roles that the user must have to access your routes, and it's based on the **heloufir/simple-passport** package that offers to you a **forgot password** system, you can refer to this [link](https://github.com/heloufir/simple-passport) to know more about this package. 4 | 5 | ![Security starter architecture](https://lh3.googleusercontent.com/-ZPq7gXOK7gM/XHO22Ns2z4I/AAAAAAAAEsQ/6lu1zpoi_n81rEEqGlSG4btyNST6Up9wgCLcBGAs/s0/2019-02-25_102913.png "2019-02-25_102913.png") 6 | 7 | # A full implementation 8 | 9 | You can find a complete implementation of this repository in [Ngx Security Starter](https://github.com/heloufir/ngx-security-starter) 10 | 11 | # Installation 12 | 13 | composer require heloufir/security-starter 14 | 15 | # Configuration 16 | 17 | **Method 1.** You can configure this package automatically, by using the command `php artisan starter:config` (if you want to configure it manually, go to **Method 2**) 18 | When executing this command you will be asked to answer some questions, and at the very end you will need to complete 3 steps manually : 19 | 20 | - Add Laravel\Passport\HasApiTokens trait to the User model 21 | 22 | ```php 23 | \Heloufir\SecurityStarter\Http\Middleware\RoleMiddleware::class` to the $routeMiddleware in Kernel 56 | 57 | **File: app/Http/Kernel.php** 58 | ```php 59 | protected $routeMiddleware = [ 60 | // ... 61 | 'roles' => \Heloufir\SecurityStarter\Http\Middleware\RoleMiddleware::class 62 | ]; 63 | ``` 64 | 65 | **Method 2.** (Manual configuration) 66 | 67 | First, you need to publish the **heloufir/simple-passport**: 68 | 69 | php artisan vendor:publish --provider=Heloufir\SimplePassport\SimplePassportServiceProvider 70 | 71 | After, you need to publish the **heloufir/security-starter**: 72 | 73 | php artisan vendor:publish --provider=Heloufir\SecurityStarter\SecurityStarterServiceProvider 74 | 75 | Then, if you want to customize the **profiles**, **roles**, **profile_roles** and **user_profiles** tables you need first to update the file **config/security-starter.php**, then go to next step. 76 | 77 | Launch migrations to add **laravel/passport** tables and **heloufir/security-starter** tables: 78 | 79 | php artisan migrate 80 | 81 | Then, install **laravel/passport** oauth clients, by executing: 82 | 83 | php artisan passport:install 84 | 85 | Add Laravel\Passport\HasApiTokens trait to the User model 86 | 87 | ```php 88 | \Heloufir\SecurityStarter\Http\Middleware\RoleMiddleware::class` to the $routeMiddleware in Kernel 121 | 122 | **File: app/Http/Kernel.php** 123 | ```php 124 | protected $routeMiddleware = [ 125 | // ... 126 | 'roles' => \Heloufir\SecurityStarter\Http\Middleware\RoleMiddleware::class 127 | ]; 128 | ``` 129 | 130 | > Don't forget to update the guards in your **auth.php** configuration file for the `api` to **passport** 131 | 132 | ```php 133 | 'guards' => [ 134 | 'web' => [ 135 | 'driver' => 'session', 136 | 'provider' => 'users', 137 | ], 138 | 139 | 'api' => [ 140 | 'driver' => 'passport', // <- Here 141 | 'provider' => 'users', 142 | ], 143 | ], 144 | ``` 145 | 146 | That's all, the installation and configuration of **security-starter** is done. 147 | 148 | You can check the [wiki](https://github.com/heloufir/security-starter/wiki) for more information about this package. 149 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "heloufir/security-starter", 3 | "description": "Seucirty starter is a full implementation of laravel/passport and heloufir/simple-passport, containing all the implementations of the authentication and forgot password systems, which allows you to start your project from a good foundation, and only worry about the business logic of your application.", 4 | "type": "package", 5 | "require": { 6 | "heloufir/simple-passport": "^1.0" 7 | }, 8 | "license": "MIT", 9 | "authors": [ 10 | { 11 | "name": "EL OUFIR Hatim", 12 | "email": "eloufirhatim@gmail.com" 13 | } 14 | ], 15 | "extra": { 16 | "laravel": { 17 | "providers": [ 18 | "Heloufir\\SecurityStarter\\SecurityStarterServiceProvider" 19 | ] 20 | } 21 | }, 22 | "autoload": { 23 | "psr-4": { 24 | "Heloufir\\SecurityStarter\\": "src/" 25 | } 26 | }, 27 | "minimum-stability": "dev", 28 | "prefer-stable" : true 29 | } 30 | -------------------------------------------------------------------------------- /src/Commands/SimplePassportConfiguration.php: -------------------------------------------------------------------------------- 1 | option('trust')) { 40 | $this->step(); 41 | } else { 42 | if ($this->confirm('You wan\'t to publish the SimplePassport provider?')) { 43 | $this->step(1); 44 | } 45 | if ($this->confirm('You wan\'t to publish the SecurityStarter provider?')) { 46 | $this->step(2); 47 | } 48 | if ($this->confirm('You wan\'t to customize SecurityStarter tables names?')) { 49 | $this->step(3); 50 | } 51 | if ($this->confirm('You wan\'t to migrate laravel/passport and heloufir/simple-passport migrations?')) { 52 | try { 53 | $this->step(4); 54 | } catch (\Exception $e) { 55 | $this->error($e->getMessage()); 56 | $this->line(''); 57 | $this->error('>>>>>> Please fix the above error, and execute the starter:config command again!'); 58 | $this->line(''); 59 | return false; 60 | } 61 | } 62 | if ($this->confirm('You wan\'t to install laravel/passport keys?')) { 63 | $this->step(5); 64 | } 65 | } 66 | $this->line(''); 67 | $this->line('***************************************************************************************************************************'); 68 | $this->line('Almost Done! You still need to do the following steps:'); 69 | $this->line(''); 70 | $this->line(' 1. Add Laravel\Passport\HasApiTokens trait to the User model'); 71 | $this->line(' 2. Add Heloufir\SecurityStarter\Core\UserProfiles trait to the User model'); 72 | $this->line(' 3. Add \'roles\' => \Heloufir\SecurityStarter\Http\Middleware\RoleMiddleware::class to the $routeMiddleware in Kernel'); 73 | $this->line('***************************************************************************************************************************'); 74 | $this->line(''); 75 | return true; 76 | } 77 | 78 | /** 79 | * Configuration steps 80 | * 81 | * @param int $step 82 | * The configuration step to do 83 | * >> Default value is -1, in this case all steps are executed 84 | * 85 | * @author EL OUFIR Hatim 86 | */ 87 | private function step(int $step = -1) 88 | { 89 | switch ($step) { 90 | case 1: 91 | $this->call('vendor:publish', [ 92 | '--provider' => 'Heloufir\SimplePassport\SimplePassportServiceProvider' 93 | ]); 94 | $this->call('config:cache'); 95 | break; 96 | case 2: 97 | $this->call('vendor:publish', [ 98 | '--provider' => 'Heloufir\SecurityStarter\SecurityStarterServiceProvider' 99 | ]); 100 | $this->call('config:cache'); 101 | break; 102 | case 3: 103 | $profiles = $this->ask('Name of the "profiles" table? (default: ' . config('security-starter.tables.profiles') . ')') ?? config('security-starter.tables.profiles'); 104 | $roles = $this->ask('Name of the "roles" table? (default: ' . config('security-starter.tables.roles') . ')') ?? config('security-starter.tables.roles'); 105 | $profileRole = $this->ask('Name of the "profile_roles" table? (default: ' . config('security-starter.tables.associations.profile_roles') . ')') ?? config('security-starter.tables.associations.profile_roles'); 106 | $userProfiles = $this->ask('Name of the "user_profiles" table? (default: ' . config('security-starter.tables.associations.user_profiles') . ')') ?? config('security-starter.tables.associations.user_profiles'); 107 | $this->updateConfigFile('security-starter.tables.profiles', $profiles); 108 | $this->updateConfigFile('security-starter.tables.roles', $roles); 109 | $this->updateConfigFile('security-starter.tables.associations.profile_roles', $profileRole); 110 | $this->updateConfigFile('security-starter.tables.associations.user_profiles', $userProfiles); 111 | $this->call('config:cache'); 112 | break; 113 | case 4: 114 | $this->call('migrate'); 115 | break; 116 | case 5: 117 | $this->call('passport:install'); 118 | break; 119 | case -1: 120 | $this->step(1); 121 | $this->step(2); 122 | $this->step(4); 123 | $this->step(5); 124 | break; 125 | default: 126 | break; 127 | } 128 | } 129 | 130 | /** 131 | * Update configuration file 132 | * 133 | * @param string $old 134 | * The old value to update 135 | * @param string $new 136 | * The new value to set 137 | * 138 | * @author EL OUFIR Hatim 139 | */ 140 | private function updateConfigFile(string $old, string $new) 141 | { 142 | $content = file_get_contents(config_path('security-starter.php')); 143 | $search = '/\=\>\s*\'' . config($old) . '\'\,/m'; 144 | $replace = '=> \'' . $new . '\','; 145 | $content = preg_replace($search, $replace, $content); 146 | file_put_contents(config_path('security-starter.php'), $content); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/Core/Paginator.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | public static function paginate(Builder $builder, Request $request = null): array 24 | { 25 | if ($request != null && $request->has('size')) { 26 | $count = $builder->count(); 27 | $size = $request->size; 28 | $page = 1; 29 | if ($request->has('page')) { 30 | $page = $request->page; 31 | } 32 | $builder 33 | ->skip(($page - 1) * $size) 34 | ->take($size); 35 | $result = new PartialList($builder->get(), $count, $page, $size); 36 | } else { 37 | $result = new PartialList($builder->get(), $builder->count(), 1, $builder->count()); 38 | } 39 | return $result->toArray(); 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /src/Core/PartialList.php: -------------------------------------------------------------------------------- 1 | 51 | */ 52 | public function __construct(Collection $data, int $count, int $page, int $size) 53 | { 54 | $this->data = $data; 55 | $this->count = $count; 56 | $this->page = $page; 57 | $this->size = $size; 58 | } 59 | 60 | /** 61 | * Return an array containing the partial list information 62 | * 63 | * @return array 64 | * 65 | * @author EL OUFIR Hatim 66 | */ 67 | public function toArray(): array 68 | { 69 | return [ 70 | 'data' => $this->data, 71 | 'count' => $this->count, 72 | 'page' => $this->page, 73 | 'size' => $this->size 74 | ]; 75 | } 76 | 77 | } -------------------------------------------------------------------------------- /src/Core/UserProfiles.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | public function profiles() 17 | { 18 | return $this->belongsToMany(Profile::class, config('security-starter.tables.associations.user_profiles'), 'refUser', 'refProfile'); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/Core/UserServices.php: -------------------------------------------------------------------------------- 1 | 20 | */ 21 | public function attacheProfilesToUser(int $user, array $profiles) 22 | { 23 | DB::table(config('security-starter.tables.associations.user_profiles')) 24 | ->where('refUser', $user) 25 | ->delete(); 26 | foreach ($profiles as $profile) { 27 | DB::table(config('security-starter.tables.associations.user_profiles')) 28 | ->insert([ 29 | 'refUser' => $user, 30 | 'refProfile' => $profile 31 | ]); 32 | } 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /src/Http/Controllers/ProfileController.php: -------------------------------------------------------------------------------- 1 | 27 | */ 28 | public function index(Request $request): JsonResponse 29 | { 30 | $query = Profile::query(); 31 | $query->with(['roles']); 32 | return response()->json(self::paginate($query, $request), 200); 33 | } 34 | 35 | /** 36 | * Store a newly created resource in storage. 37 | * 38 | * @param Request $request 39 | * The request object 40 | * 41 | * @return JsonResponse 42 | * 43 | * @author EL OUFIR Hatim 44 | */ 45 | public function store(Request $request): JsonResponse 46 | { 47 | $rules = [ 48 | 'code' => [ 49 | 'required', 50 | 'max:255', 51 | 'unique:' . config('security-starter.tables.profiles') . ',code' 52 | ], 53 | 'designation' => [ 54 | 'required', 55 | 'max:255' 56 | ], 57 | 'roles' => [ 58 | 'array' 59 | ] 60 | ]; 61 | $validator = Validator::make($request->all(), $rules); 62 | if ($validator->fails()) { 63 | return response()->json(collect($validator->getMessageBag())->flatten()->toArray(), 403); 64 | } 65 | $profile = new Profile(); 66 | $profile->code = $request->get('code'); 67 | $profile->designation = $request->get('designation'); 68 | $profile->save(); 69 | if ($request->has('roles')) { 70 | foreach ($request->get('roles') as $role) { 71 | DB::table(config('security-starter.tables.associations.profile_roles')) 72 | ->insert([ 73 | 'refProfile' => $profile->id, 74 | 'refRole' => $role 75 | ]); 76 | } 77 | } 78 | return response()->json(Profile::where('id', $profile->id)->with(['roles'])->first(), 200); 79 | } 80 | 81 | /** 82 | * Display the specified resource. 83 | * 84 | * @param int $id 85 | * The profile id 86 | * 87 | * @return JsonResponse 88 | * 89 | * @author EL OUFIR Hatim 90 | */ 91 | public function show($id): JsonResponse 92 | { 93 | $query = Profile::query(); 94 | $query->where('id', $id); 95 | $query->with(['roles']); 96 | return response()->json($query->first(), $query->count() == 0 ? 404 : 200); 97 | } 98 | 99 | /** 100 | * Update the specified resource in storage. 101 | * 102 | * @param Request $request 103 | * The request object 104 | * @param int $id 105 | * The profile id 106 | * 107 | * @return JsonResponse 108 | */ 109 | public function update(Request $request, $id): JsonResponse 110 | { 111 | $rules = [ 112 | 'code' => [ 113 | 'required', 114 | 'max:255', 115 | new ProfileExists($id), 116 | 'unique:' . config('security-starter.tables.profiles') . ',code,' . $id 117 | ], 118 | 'designation' => [ 119 | 'required', 120 | 'max:255' 121 | ], 122 | 'roles' => [ 123 | 'array' 124 | ] 125 | ]; 126 | $validator = Validator::make($request->all(), $rules); 127 | if ($validator->fails()) { 128 | return response()->json(collect($validator->getMessageBag())->flatten()->toArray(), 403); 129 | } 130 | $profile = Profile::where('id', $id)->first(); 131 | $profile->code = $request->get('code'); 132 | $profile->designation = $request->get('designation'); 133 | $profile->save(); 134 | DB::table(config('security-starter.tables.associations.profile_roles')) 135 | ->where('refProfile', $id) 136 | ->delete(); 137 | if ($request->has('roles')) { 138 | foreach ($request->get('roles') as $role) { 139 | DB::table(config('security-starter.tables.associations.profile_roles')) 140 | ->insert([ 141 | 'refProfile' => $profile->id, 142 | 'refRole' => $role 143 | ]); 144 | } 145 | } 146 | return response()->json(Profile::where('id', $profile->id)->with(['roles'])->first(), 200); 147 | } 148 | 149 | /** 150 | * Remove the specified resource from storage. 151 | * 152 | * @param Request $request 153 | * The request object 154 | * @param int $id 155 | * The profile id 156 | * 157 | * @return JsonResponse 158 | * 159 | * @author EL OUFIR Hatim 160 | */ 161 | public function destroy(Request $request, int $id): JsonResponse 162 | { 163 | $rules = [ 164 | 'id' => [ 165 | new ProfileExists($id) 166 | ] 167 | ]; 168 | $request->request->add(['id' => $id]); 169 | $validator = Validator::make($request->all(), $rules); 170 | if ($validator->fails()) { 171 | return response()->json(collect($validator->getMessageBag())->flatten()->toArray(), 403); 172 | } 173 | DB::table(config('security-starter.tables.associations.profile_roles')) 174 | ->where('refProfile', $id) 175 | ->delete(); 176 | DB::table(config('security-starter.tables.associations.user_profiles')) 177 | ->where('refProfile', $id) 178 | ->delete(); 179 | return response()->json(Profile::where('id', $id)->delete(), 200); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/Http/Controllers/RoleController.php: -------------------------------------------------------------------------------- 1 | 27 | */ 28 | public function index(Request $request): JsonResponse 29 | { 30 | $query = Role::query(); 31 | return response()->json(self::paginate($query, $request), 200); 32 | } 33 | 34 | /** 35 | * Store a newly created resource in storage. 36 | * 37 | * @param Request $request 38 | * The request object 39 | * 40 | * @return JsonResponse 41 | * 42 | * @author EL OUFIR Hatim 43 | */ 44 | public function store(Request $request): JsonResponse 45 | { 46 | $rules = [ 47 | 'code' => [ 48 | 'required', 49 | 'max:255', 50 | 'unique:' . config('security-starter.tables.roles') . ',code' 51 | ], 52 | 'designation' => [ 53 | 'required', 54 | 'max:255' 55 | ] 56 | ]; 57 | $validator = Validator::make($request->all(), $rules); 58 | if ($validator->fails()) { 59 | return response()->json(collect($validator->getMessageBag())->flatten()->toArray(), 403); 60 | } 61 | $role = new Role(); 62 | $role->code = $request->get('code'); 63 | $role->designation = $request->get('designation'); 64 | $role->save(); 65 | return response()->json(Role::where('id', $role->id)->first(), 200); 66 | } 67 | 68 | /** 69 | * Display the specified resource. 70 | * 71 | * @param int $id 72 | * The role id 73 | * 74 | * @return JsonResponse 75 | * 76 | * @author EL OUFIR Hatim 77 | */ 78 | public function show($id): JsonResponse 79 | { 80 | $query = Role::query(); 81 | $query->where('id', $id); 82 | return response()->json($query->first(), $query->count() == 0 ? 404 : 200); 83 | } 84 | 85 | /** 86 | * Update the specified resource in storage. 87 | * 88 | * @param Request $request 89 | * The request object 90 | * @param int $id 91 | * The role id 92 | * 93 | * @return JsonResponse 94 | */ 95 | public function update(Request $request, $id): JsonResponse 96 | { 97 | $rules = [ 98 | 'code' => [ 99 | 'required', 100 | 'max:255', 101 | new RoleExists($id), 102 | 'unique:' . config('security-starter.tables.roles') . ',code,' . $id 103 | ], 104 | 'designation' => [ 105 | 'required', 106 | 'max:255' 107 | ] 108 | ]; 109 | $validator = Validator::make($request->all(), $rules); 110 | if ($validator->fails()) { 111 | return response()->json(collect($validator->getMessageBag())->flatten()->toArray(), 403); 112 | } 113 | $role = Role::where('id', $id)->first(); 114 | $role->code = $request->get('code'); 115 | $role->designation = $request->get('designation'); 116 | $role->save(); 117 | return response()->json(Role::where('id', $role->id)->first(), 200); 118 | } 119 | 120 | /** 121 | * Remove the specified resource from storage. 122 | * 123 | * @param Request $request 124 | * The request object 125 | * @param int $id 126 | * The role id 127 | * 128 | * @return JsonResponse 129 | * 130 | * @author EL OUFIR Hatim 131 | */ 132 | public function destroy(Request $request, int $id): JsonResponse 133 | { 134 | $rules = [ 135 | 'id' => [ 136 | new RoleExists($id) 137 | ] 138 | ]; 139 | $request->request->add(['id' => $id]); 140 | $validator = Validator::make($request->all(), $rules); 141 | if ($validator->fails()) { 142 | return response()->json(collect($validator->getMessageBag())->flatten()->toArray(), 403); 143 | } 144 | DB::table(config('security-starter.tables.associations.profile_roles')) 145 | ->where('refRole', $id) 146 | ->delete(); 147 | return response()->json(Role::where('id', $id)->delete(), 200); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/Http/Middleware/RoleMiddleware.php: -------------------------------------------------------------------------------- 1 | user() == null) { 22 | return response()->json(['error' => 'unauthorized', 'message' => 'The request does not contains token'], 401); 23 | } 24 | $this->hasRole($request->user(), $roles); 25 | return $next($request); 26 | } 27 | 28 | /** 29 | * Check if the user has a list of roles, based on a type (any or all) 30 | * 31 | * @param $user 32 | * The user object 33 | * @param array $roles 34 | * The roles array 35 | * 36 | * @return bool 37 | * 38 | * @author EL OUFIR Hatim 39 | */ 40 | private function hasRole($user, array $roles): bool 41 | { 42 | $roles = collect($roles); 43 | switch ($roles->first()) { 44 | case 'any': 45 | $roles->forget(0); 46 | return $this->any($user, $roles); 47 | case 'all': 48 | $roles->forget(0); 49 | return $this->all($user, $roles); 50 | default: 51 | return $this->any($user, $roles); 52 | } 53 | } 54 | 55 | /** 56 | * Check if the user has any role of a collection of roles 57 | * 58 | * @param $user 59 | * The user object 60 | * @param Collection $roles 61 | * The roles collection 62 | * 63 | * @return bool 64 | * 65 | * @author EL OUFIR Hatim 66 | */ 67 | private function any($user, Collection $roles): bool 68 | { 69 | $result = false; 70 | foreach ($user->profiles as $profile) { 71 | $result = $result || $profile->roles->pluck('code')->intersect($roles)->count() != 0; 72 | } 73 | return $result; 74 | } 75 | 76 | /** 77 | * Check if the user has all roles of a collection of roles 78 | * 79 | * @param $user 80 | * The user object 81 | * @param Collection $roles 82 | * The roles collection 83 | * 84 | * @return bool 85 | * 86 | * @author EL OUFIR Hatim 87 | */ 88 | private function all($user, Collection $roles): bool 89 | { 90 | $results = collect(); 91 | foreach ($user->profiles as $profile) { 92 | $intersection = $profile->roles->pluck('code')->intersect($roles); 93 | foreach ($intersection as $item) { 94 | if (!$results->contains($item)) { 95 | $results->push($item); 96 | } 97 | } 98 | } 99 | dd($results->count() == $roles->count()); 100 | return $results->count() == $roles->count(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/Rules/ProfileExists.php: -------------------------------------------------------------------------------- 1 | id = $id; 15 | } 16 | 17 | /** 18 | * Determine if the validation rule passes. 19 | * 20 | * @param string $attribute 21 | * @param mixed $value 22 | * @return bool 23 | */ 24 | public function passes($attribute, $value) 25 | { 26 | return Profile::where('id', $this->id)->count() != 0; 27 | } 28 | 29 | /** 30 | * Get the validation error message. 31 | * 32 | * @return string 33 | */ 34 | public function message() 35 | { 36 | return trans('validation.exists', ['attribute' => 'profile']); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Rules/RoleExists.php: -------------------------------------------------------------------------------- 1 | id = $id; 15 | } 16 | 17 | /** 18 | * Determine if the validation rule passes. 19 | * 20 | * @param string $attribute 21 | * @param mixed $value 22 | * @return bool 23 | */ 24 | public function passes($attribute, $value) 25 | { 26 | return Role::where('id', $this->id)->count() != 0; 27 | } 28 | 29 | /** 30 | * Get the validation error message. 31 | * 32 | * @return string 33 | */ 34 | public function message() 35 | { 36 | return trans('validation.exists', ['attribute' => 'role']); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/SecurityStarterServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->make(ProfileController::class); 22 | 23 | // Register RoleController 24 | $this->app->make(RoleController::class); 25 | } 26 | 27 | /** 28 | * Bootstrap services. 29 | * 30 | * @return void 31 | */ 32 | public function boot() 33 | { 34 | // Register package commands 35 | $this->commands([ 36 | SimplePassportConfiguration::class 37 | ]); 38 | 39 | // Register package routes 40 | $this->loadRoutesFrom(__DIR__ . '/routes/api.php'); 41 | 42 | // Register package migrations 43 | $this->loadMigrationsFrom(__DIR__ . '/database/migrations'); 44 | 45 | // Publish package sources 46 | $this->publishes([ 47 | __DIR__ . '/config/security-starter.php' => config_path('security-starter.php') 48 | ]); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/config/security-starter.php: -------------------------------------------------------------------------------- 1 | > You can change these values before starting the migration command. 14 | | 15 | */ 16 | 'tables' => [ 17 | 'profiles' => 'profiles', 18 | 'roles' => 'roles', 19 | 'associations' => [ 20 | 'profile_roles' => 'profile_roles', 21 | 'user_profiles' => 'user_profiles' 22 | ] 23 | ], 24 | 25 | /* 26 | |-------------------------------------------------------------------------- 27 | | Privileges 28 | |-------------------------------------------------------------------------- 29 | | 30 | | This value represents the roles that the use must have to access 31 | | respectively the profiles and roles REST resources 32 | | >> Please refer to [https://github.com/heloufir/security-starter] to have 33 | | an idea on how to use this package 34 | | 35 | */ 36 | 'privileges' => [ 37 | 'profiles' => 'any,ROLE_PROFILES', 38 | 'roles' => 'any,ROLE_ROLES' 39 | ] 40 | 41 | ]; -------------------------------------------------------------------------------- /src/database/migrations/2019_02_22_150438_create_profile_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('code', 255)->unique(); 19 | $table->string('designation', 255); 20 | $table->timestamps(); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::dropIfExists(config('security-starter.tables.profiles')); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/database/migrations/2019_02_22_150448_create_role_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('code', 255)->unique(); 19 | $table->string('designation', 255); 20 | $table->timestamps(); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::dropIfExists(config('security-starter.tables.roles')); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/database/migrations/2019_02_22_150500_create_profile_role_table.php: -------------------------------------------------------------------------------- 1 | integer('refProfile')->unsigned(); 18 | $table->foreign('refProfile')->references('id')->on(config('security-starter.tables.profiles')); 19 | $table->integer('refRole')->unsigned(); 20 | $table->foreign('refRole')->references('id')->on(config('security-starter.tables.roles')); 21 | $table->primary(['refProfile', 'refRole']); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::table(config('security-starter.tables.associations.profile_roles'), function (Blueprint $table) { 33 | $table->dropForeign(['refProfile']); 34 | $table->dropForeign(['refRole']); 35 | }); 36 | Schema::dropIfExists(config('security-starter.tables.associations.profile_roles')); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/database/migrations/2019_02_22_150512_create_user_profiles_table.php: -------------------------------------------------------------------------------- 1 | integer('refUser')->unsigned(); 18 | $table->foreign('refUser')->references('id')->on(app(config('auth.providers.users.model'))->getTable() ?: 'users'); 19 | $table->integer('refProfile')->unsigned(); 20 | $table->foreign('refProfile')->references('id')->on(config('security-starter.tables.profiles')); 21 | $table->primary(['refProfile', 'refUser']); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::table(config('security-starter.tables.associations.user_profiles'), function (Blueprint $table) { 33 | $table->dropForeign(['refUser']); 34 | $table->dropForeign(['refProfile']); 35 | }); 36 | Schema::dropIfExists(config('security-starter.tables.associations.user_profiles')); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/models/Profile.php: -------------------------------------------------------------------------------- 1 | table = config('security-starter.tables.profiles'); 22 | } 23 | 24 | /** 25 | * Get roles related to this profile 26 | * 27 | * @return BelongsToMany 28 | * 29 | * @author EL OUFIR Hatim 30 | */ 31 | public function roles(): BelongsToMany 32 | { 33 | return $this->belongsToMany(Role::class, config('security-starter.tables.associations.profile_roles'), 'refProfile', 'refRole'); 34 | } 35 | 36 | /** 37 | * Get users related to this profile 38 | * 39 | * @return BelongsToMany 40 | * 41 | * @author EL OUFIR Hatim 42 | */ 43 | public function users(): BelongsToMany 44 | { 45 | return $this->belongsToMany(config('auth.providers.users.model'), config('security-starter.tables.associations.user_profiles'), 'refProfile', 'refUser'); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/models/Role.php: -------------------------------------------------------------------------------- 1 | table = config('security-starter.tables.roles'); 22 | } 23 | 24 | /** 25 | * Get profiles related to this role 26 | * 27 | * @return BelongsToMany 28 | * 29 | * @author EL OUFIR Hatim 30 | */ 31 | public function profiles(): BelongsToMany 32 | { 33 | return $this->belongsToMany(Profile::class, config('security-starter.tables.associations.profile_roles'), 'refRole', 'refProfile'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/routes/api.php: -------------------------------------------------------------------------------- 1 | 'api', 'middleware' => ['auth:api']], function () { 4 | Route::resource('profiles', 'Heloufir\SecurityStarter\Http\Controllers\ProfileController') 5 | ->except(['create', 'edit']) 6 | ->middleware(['roles:' . config('security-starter.privileges.profiles')]); 7 | Route::resource('roles', 'Heloufir\SecurityStarter\Http\Controllers\RoleController') 8 | ->except(['create', 'edit']) 9 | ->middleware(['roles:' . config('security-starter.privileges.roles')]); 10 | }); --------------------------------------------------------------------------------