Laravel 5 RESTful API - Understanding resource concepts - php

I am developing a RESTful API with Laravel 5. I have some resource in my routes.php file and everything works properly.
But now I had added auth.basic middleware and I want introduce the user roles and I got confused.
In my Controller I have a constructor to call 2 middleware, auth.basic and roles middleware, but cannot continue because of lack of knowledge.
What do I need? Well, I need to set the user roles who can access every controller, but cant achieve this. Im the Controller I would like to access the user to check his role and compare it with the role stablished on the Controller, but I don't know how to access the user, can you help me?
EDIT:
I put this on the constructor of the Controller
public function __construct(Request $request)
{
$actions = $request->route()->setAction( ['roles' => ['admin', 'seller', 'buyer']]);
$this->middleware('auth.basic');
$this->middleware('roles');
}
Basically I inject the request in the controller constructor and then I set an action called roles.
Then I call middleware auth.basic to set the user.
And last call middleware roles which checks the user role against the roles array in the request, if it has the role or if he is root, then result is true, else I return error:
return response([
'error' => [
'code' => 'INSUFFICIENT_ROLE',
'description' => 'You are not authorized to access this resource.'
]
], 401);
Now I have the error that always get:
{"error":{"code":"INSUFFICIENT_ROLE","description":"You are not authorized to access this resource."}}
Thats because User model dont returns a Role. See my class:
class User extends Model implements AuthenticatableContract,
AuthorizableContract,
CanResetPasswordContract
{
use Authenticatable, Authorizable, CanResetPassword;
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['username', 'email', 'password', 'role_id'];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = ['password', 'remember_token'];
//Comprobacion del rol del usuario
public function hasRole($roles)
{
$this->have_role = $this->getUserRole();
// Check if the user is a root account
if($this->have_role->name == 'root') {
return true;
}
if(is_array($roles)){
foreach($roles as $need_role){
if($this->checkIfUserHasRole($need_role)) {
return true;
}
}
} else{
return $this->checkIfUserHasRole($roles);
}
return false;
}
private function getUserRole()
{
return $this->role()->getResults();
}
private function checkIfUserHasRole($need_role)
{
return (strtolower($need_role)==strtolower($this->have_role->name)) ? true : false;
}
//User relation with role
public function role(){
return $this->belongsTo('App\Role');
}
}
Whats wrong?

From your question, I got the following:
How do I handle auth middleware...
Well, let's assume that you have two middlewares auth.basic, auth.admin
You can then have your routes be like:
Route::post('/api/getResponse', ['middleware' => 'auth', function () {
$var = "you have access to this route";
return json_encode($var);
}]);
Here, you set whether or not, and, who has access to this particular route, in this case, only people that have admin rights would be able to access it.
If, for example, you do not have a middleware for "admin" you can create it by running the artisan command php artisan make:middleware admin and then you put your logic inside of the file that has been created. In this instance, the logic would check to see if the user (signed in) has admin rights.
EDIT:
As you pointed out in your reply:
I dont user Route::post, I use Route::resource for handling RESTful API requests
You can therefore use a group, see:
Route::group(['middleware' => 'admin'], function () {
Route::resource('API_USER', 'API_USER_CONTROLLER');
});
This will then allow you to use your admin group as GROUP and therefore, all your routes that have access can go in here. In the past, I have just created separate groups for all of my user groups, i.e. admin would have it's own, user would have its own and moderator would have it's own. But, I believe you can use the following:
Route::group(['before' => 'auth|admin'], function()
{
}
This group reads: should be open to auth users OR admin but I have not fully tested this.

Found the solution!!!!! Thanks Phorce for guiding me, you gave me the idea basics. I post it here for everyone who needs. How to get Role Authentication for a RESTful API with Laravel 5.
Explanation. In the controller of the route I call a constructor for the middleware, first add the attribute roles (sets which roles can access this route) with an injected $request object. Then I call middleware auth.basic for requesting user, and then another middleware to check roles. Ando thats it! All working.
Middleware:
<?php
namespace App\Http\Middleware;
use Closure;
class CheckRole
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
//return $next($request);
// Get the required roles from the route
$roles = $this->getRequiredRoleForRoute($request->route());
// Check if a role is required for the route, and
// if so, ensure that the user has that role.
//print "HasRole:".$request->user()->hasRole($roles).".";
if($request->user()->hasRole($roles) || !$roles)
{
return $next($request);
}
return response([
'error' => [
'code' => 'INSUFFICIENT_ROLE',
'description' => 'You are not authorized to access this resource.'
]
], 401);
}
private function getRequiredRoleForRoute($route)
{
$actions = $route->getAction();
//print "actinos:".print_r($actions);
return isset($actions['roles']) ? $actions['roles'] : null;
}
}
User Model
class User extends Model implements AuthenticatableContract,
AuthorizableContract,
CanResetPasswordContract
{
use Authenticatable, Authorizable, CanResetPassword;
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['username', 'email', 'password', 'role_id'];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = ['password', 'remember_token'];
protected $have_role;
protected $profile;
//Comprobacion del rol del usuario
public function hasRole($roles)
{
$this->have_role = $this->getUserRole();
//$this->have_role = $this->role()->getResults();
// Check if the user is a root account
if($this->have_role->nombre == 'root') {
return true;
}
if(is_array($roles)){
foreach($roles as $need_role){
if($this->checkIfUserHasRole($need_role)) {
return true;
}
}
} else{
return $this->checkIfUserHasRole($roles);
}
return false;
}
private function getUserRole()
{
return $this->role()->getResults();
}
private function checkIfUserHasRole($need_role)
{
if($need_role === $this->have_role->nombre){
return true;
}else{
return false;
}
//return (strtolower($need_role)==strtolower($this->have_role->name)) ? true : false;
}
//Relaciones de user
public function role(){
return $this->belongsTo('App\Role');
}
}
Routes:
Route::resource('perfiles','PerfilesUsuariocontroller',[ 'only'=>['index','show'] ]);
Controller Constructor method
public function __construct(Request $request)
{
$actions = $request->route()->setAction( ['roles' => ['root', 'admin', 'seller']]);
$this->middleware('auth.basic');
$this->middleware('roles');
}

Related

Laravel delete linked model's data by user_id in booted function of User model

I've read part of the Laravel docs for events and closures for models, I've got various models in my project whereby a user may have data linked to them in another table by a user_id column, the user_id column that I have in my various tables is structured as an unsigned integer (I'm aware I could've gone with a foreignId column by kind of a legacy approach here)
It looks like:
$table->integer('user_id')->unsigned()->nullable()->index();
I'd like to delete user data by their ID within these other tables and rather than creating a delete function and grabbing each model I want to delete data against, I've utilised the closure booted function and what I believe to be an event to listen and delete related model data, but I experience an error when trying to delete my user account, other data in other tables isn't deleted, the error I get is:
Call to undefined method App\Models\User::releationship()
My user model looks like:
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Model;
use Tymon\JWTAuth\Contracts\JWTSubject;
class User extends Authenticatable implements JWTSubject, MustVerifyEmail
{
use Notifiable, SoftDeletes;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'first_name', 'last_name', 'email', 'password'
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token'
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime'
];
/**
* Get the identifier that will be stored in the subject claim of the JWT.
*
* #return mixed
*/
public function getJWTIdentifier()
{
return $this->getKey();
}
/**
* Return a key value array, containing any custom claims to be added to the JWT.
*
* #return array
*/
public function getJWTCustomClaims()
{
return [];
}
/**
* Route notifications for the Slack channel.
*
* #param \Illuminate\Notifications\Notification $notification
* #return string
*/
public function routeNotificationForSlack($notification)
{
$url = $this->slack_webhook;
$webhook = (isset($url) && !empty($url)) ? $url : null;
return $webhook;
}
/**
* The "booted" method of the model.
*
* #return void
*/
protected static function booted()
{
static::deleted(function ($model) {
$model->relationship()->delete();
});
}
}
And an example (of many) model I have, UptimeChecks looks like:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class UptimeChecks extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'uptime_checks';
/**
* Join user table
*/
public function user()
{
return $this->belongsTo('App\User');
}
}
All is then kicked off by a deleteAccount function in my API, which is deleting the user's account, but isn't deleting data in other tables. What am I missing and how could I do a check to make sure other data is deleted before confirming to the user that their account and linked data is gone?
/**
* Delete account
*
* #return Response
*/
public function deleteAccount(Request $request)
{
// attempt to delete account
try {
$user = User::findOrFail(Auth::id());
$user->delete();
// everything went okay!
return response()->json(['success' => true, 'message' => 'Your account has been deleted'], 200);
} catch (Exception $e) {
// catch the error
return response()->json(['success' => false, 'message' => 'We was unable to delete your account at this time'], 422);
}
}
In Laravel, when doing $model->relationship()->delete(); you will need to have the relationship defined and relationship() seems like it is copy pasted code snippet. Simply add the relationship to your User model.
class User extends Authenticatable implements JWTSubject, MustVerifyEmail
{
...
public function uptimeChecks() {
return $this->hasMany(UptimeChecks::class);
}
}
Now you can access and delete the relationship in your boot method.
$model->uptimeChecks()->delete();
You need to create a function in User.php
public function uptimeCheck()
{
return $this->hasOne('App\UptimeChecks');
}
and change the boot function
$model->uptimeCheck()->delete();
This way you need to do for all related relations.
This probably should be: $model->user()->delete() instead. There's nothing else.
If this shouldn't be the intention, reconsider the direction of the relationship.

Spatie User Permissions - Belongs To Many Companies, solution feedback

I have a situation where a user can belong to many teams/companies and within that team/company they can have different roles and permissions depending on which one they are signed into. I have come up with the following solution and would love some feedback!
Note: Currently I am only using the model_has_roles table with Spatie permissions and always use $user->can('Permission') to check permissions.
Our company model has the following relationships and method
class Company extends Model
{
public function owner(): HasOne
{
return $this->hasOne(User::class, 'id', 'user_id');
}
public function users(): BelongsToMany
{
return $this->belongsToMany(
User::class, 'company_users', 'company_id', 'user_id'
)->using(CompanyUser::class);
}
public function addTeamMember(User $user)
{
$this->users()->detach($user);
$this->users()->attach($user);
}
}
We modify the pivot model to have the Spatie HasRoles trait. This allows us to assign a role to the CompanyUser as opposed to the Auth User. You also need to specify the default guard or Spatie permissions squarks.
class CompanyUser extends Pivot
{
use HasRoles;
protected $guard_name = 'web';
}
On the user model, I have created the HasCompanies Trait. This provides the relationships and provides a method for assigning the roles to the new company user. Additionally, it overwrites the gate can() method.
A user can belong to many companies, but can only have one active company at a time (i.e. the one they are viewing). We define this with the current_company_id column.
It is also important to ensure the pivot table ID is pulled across (which it will not be as standard) as this is now what we are using in the Spatie model_has_roles table.
trait HasCompanies
{
public function companies(): HasMany
{
return $this->hasMany(Company::class);
}
public function currentCompany(): HasOne
{
return $this->hasOne(Company::class, 'id', 'current_company_id');
}
public function teams(): BelongsToMany
{
return $this->belongsToMany(
Company::class, 'company_users', 'user_id', 'company_id'
)->using(CompanyUser::class)->withPivot('id');
}
public function switchCompanies(Company $company): void
{
$this->current_company_id = $company->id;
$this->save();
}
private function companyWithPivot(Company $company)
{
return $this->teams()->where('companies.id', $company->id)->first();
}
public function assignRolesForCompany(Company $company, ...$roles)
{
if($company = $this->companyWithPivot($company)){
/** #var CompanyUser $companyUser */
$companyUser = $company->pivot;
$companyUser->assignRole($roles);
return;
}
throw new Exception('Roles could not be assigned to company user');
}
public function hasRoleForCurrentCompany(string $roles, Company $company = null, string $guard = null): bool
{
if(! $company){
if(! $company = $this->currentCompany){
throw new Exception('Cannot check role for current company because it has not been set');
}
}
if($company = $this->companyWithPivot($company)){
/** #var CompanyUser $companyUser */
$companyUser = $company->pivot;
return $companyUser->hasRole($roles, $guard);
}
return false;
}
public function can($ability, $arguments = []): bool
{
if(isset($this->current_company_id)){
/** #var CompanyUser $companyUser */
$companyUser = $this->teams()->where('companies.id', $this->current_company_id)->first()->pivot;
if($companyUser->hasPermissionTo($ability)){
return true;
}
// Still run through the gate as this will check for gate bypass
return app(Gate::class)->forUser($this)->check('N/A', []);
}
return app(Gate::class)->forUser($this)->check($ability, $arguments);
}
}
Now we can do something like this:
Create the role & permission
/** #var Role $ownerRoll */
$ownerRoll = Role::create(['name' => 'Owner']);
/** #var Permission $permission */
$permission = Permission::create([
'name' => 'Create Company',
'guard_name' => 'web',
]);
$ownerRoll->givePermissionTo($permission);
Create a new company with an owning user and then switch this company to that owner's active company.
public function store(CompanyStoreRequest $request)
{
DB::transaction(function () use($request) {
/** #var User $owner */
$owner = User::findOrFail($request->user_id);
/** #var Company $company */
$company = $owner->companies()->create($request->validated());
$company->addTeamMember($owner);
$owner->assignRolesForCompany($company, 'Owner');
$owner->switchCompanies($company);
});
return redirect()->back();
}
So this all works, my main concerns are that:
We are overwriting the can method. There may be other authorization methods/gate functions that are not caught.
We have 2 sets of model_permissions. The Auth user and the company user. I think I need to build in some checks to ensure that only the correct kinds of users can be assigned to the roles. At this stage, all administrator users would have permissions assigned to their auth user, while any users who own a company should only have permissions on the company user model

Call to a member function hasRole() on null

I am new to Laravel
I have set up permissions and roles inside my application, and assigned these to users - however when I try to use hasRole or hasAnyRole it isn't working for me.
Here is my 'CheckRole' middleware:
<?php
namespace App\Http\Middleware;
use Closure;
class CheckRole
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
// Get the required roles from the route
$roles = $this->getRequiredRoleForRoute($request->route());
// Check if a role is required for the route, and
// if so, ensure that the user has that role.
if($request->user()->hasRole('Admin','Receiptionist','Manager','CEO','Root')
{
return $next($request);
}
return response([
'error' => [
'code' => 'INSUFFICIENT_ROLE',
'description' => 'You are not authorized to access this resource.'
]
], 401);
}
private function getRequiredRoleForRoute($route)
{
$actions = $route->getAction();
return isset($actions['roles']) ? $actions['roles'] : null;
}
}
Here is my user model:
public function role()
{
return $this->belongsToOne('App\Role', 'id', 'role_id');
}
public function hasRole($roles)
{
$this->have_role = $this->getUserRole();
// Check if the user is a root account
if($this->have_role->name == 'Root') {
return true;
}
if(is_array($roles)){
foreach($roles as $need_role){
if($this->checkIfUserHasRole($need_role)) {
return true;
}
}
} else{
return $this->checkIfUserHasRole($roles);
}
return false;
}
private function getUserRole()
{
return $this->role()->getResults();
}
private function checkIfUserHasRole($need_role)
{
return (strtolower($need_role)==strtolower($this->have_role->name)) ? true : false;
}
And here is my Role model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
protected $table = 'role';
protected $fillable = ['name'];
protected $primaryKey = 'id';
public $timestamps = false;
public function users()
{
return $this->belongsToMany('App\User', 'role_id', 'id');
}
}
I am trying to run this route:
Route::group(['middleware'=>['authen','roles'],'roles'=>['Root']],function(){
//for Root
Route::get('/createUser',function(){
echo "This is for Root test";
});
which is producing this error:
FatalThrowableError (E_ERROR)
Call to a member function hasRole() on null
If your code worked on first time then try to add into Kernel.php one line and will be everything all right I guess. Have nice code working on your project. :)
protected $middlewareGroups = [
'CheckRole' => [
\App\Http\Middleware\CheckRole::class,
\Illuminate\Auth\Middleware\Authenticate::class,
],
It means that you are trying to check the role of the user but you are not logged in before that method that is the reason that you are getting null.
I had the same issue, turns out the I was calling the middleware even when the user was not logged in, meaning that the Auth::user() or in your case $request->user() was empty.
Might I suggest you ensure that the user is logged in

Laravel - Custom authentification query

How can I add DESC to the default login sql query?
I mean on default is something like
select * from users where name = user_name limit 1.
How can I add
select * from users where name = user_name ORDER BY id DESC limit 1?
I know that the name column should contain unique values only, my login system it's different (some predefined users in another table) and I need multiple user registrations with the same name. I just want to login on the last record in the database. Please help me how can I customize the model provider in laravel? I don't know what files to modify for this to work.
This is my LoginController.php but you can ignore it (I added it because some users required it) just look at the default loginController from php artisan make:auth
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Illuminate\Support\Facades\Session;
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* #var string
*/
protected $redirectTo = '/home';
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
}
/**
* Check either username or email.
* #return string
*/
public function login(Request $request)
{
$this->validateLogin($request);
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
if ($this->attemptLogin($request)) {
return $this->sendLoginResponse($request);
}
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
public function username()
{
$identity = Session::get('table_id');
$fieldName = 'name';
request()->merge([$fieldName => $identity]);
return $fieldName;
}
/**
* Validate the user login.
* #param Request $request
*/
protected function validateLogin(Request $request)
{
$this->validate(
$request,
[
'password' => 'required|string',
],
[
'password.required' => 'Password is required',
]
);
}
/**
* #param Request $request
* #throws ValidationException
*/
protected function sendFailedLoginResponse(Request $request)
{
$request->session()->put('login_error', trans('auth.failed'));
throw ValidationException::withMessages(
[
'error' => [trans('auth.failed')],
]
);
}
protected function attemptLogin(Request $request)
{
$remember = true;
return $this->guard()->attempt(
$this->credentials($request), $remember
);
}
}
All methods in my LoginController overrides methods from vendor\laravel\framework\src\Illuminate\Foundation\Auth\AuthenticatesUsers.php
Replace LoginController with the following. I have removed username() method and replaced attemptLogin() method to fetch the last user in your database given your session value of 'table_id'.
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Illuminate\Support\Facades\Session;
use App\User;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* #var string
*/
protected $redirectTo = '/home';
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct(User $user)
{
$this->middleware('guest')->except('logout');
$this->user = $user;
}
/**
* Check either username or email.
* #return string
*/
public function login(Request $request)
{
$this->validateLogin($request);
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
if ($this->attemptLogin($request)) {
return $this->sendLoginResponse($request);
}
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
/**
* Validate the user login.
* #param Request $request
*/
protected function validateLogin(Request $request)
{
$this->validate(
$request,
[
'password' => 'required|string',
],
[
'password.required' => 'Password is required',
]
);
}
/**
* #param Request $request
* #throws ValidationException
*/
protected function sendFailedLoginResponse(Request $request)
{
$request->session()->put('login_error', trans('auth.failed'));
throw ValidationException::withMessages(
[
'error' => [trans('auth.failed')],
]
);
}
protected function attemptLogin(Request $request, User $user)
{
if (session()->has('table_id') != true) return redirect()->back()->withErrors(['error' => 'No username is set.']);
$userName = $user->where('name', session('table_id'))->orderBy('id', 'desc')->first()->name;
$remember = true;
if (Auth::attempt(['name' => $userName, 'password' => request('password')], $remember)) {
return redirect()->intended();
}
}
}
You should not change/delete any framework files and codes.
in top of your login controller just add this trait:
use AuthenticatesUsers;
then you can override all login functions.
for authenticating username/password just override attemptLogin() function.
So if I understand your question right, you want to change default sql query for selecting user when authenticating.
in attemptLogin method you call attempt and it is in StatefulGuard Interface and the implementation is in /vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php so you need to either override full attempt method, either the methods in it.
I found another solution that works but I believe it will mess something up (I'm not sure) that is why I voted Polaris's answer as the right one.
You can leave the default LoginController and modify the App/User.php like this:
It basically overrides the retrieveByCredentials method used in Illuminate\Auth\EloquentUserProvider; . The issue is that I believe this method is not normally accessed directly from the Users.php so you are not directly overriding it. But for some reason it works :)).
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Auth\EloquentUserProvider;
class User extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
public function retrieveByCredentials(array $credentials)
{
if (empty($credentials) ||
(count($credentials) === 1 &&
array_key_exists('password', $credentials))) {
return;
}
// First we will add each credential element to the query as a where clause.
// Then we can execute the query and, if we found a user, return it in a
// Eloquent User "model" that will be utilized by the Guard instances.
$query = $this->createModel()->newQuery();
foreach ($credentials as $key => $value) {
if (Str::contains($key, 'password')) {
continue;
}
if (is_array($value) || $value instanceof Arrayable) {
$query->whereIn($key, $value);
} else {
$query->where($key, $value);
}
}
return $query->orderBy('id', 'desc')->first();
}
}

How to pass array parameter to middleware in laravel

i don't know the syntax of php 7 i'm actually new to it
i'm trying to pass roles like ['admin','user','cmc'] to route middleware like shown below how do i do it properly
this is my route
Route::get('/dashboard','HomeController#dashboard')->name('dashboard')->middleware("roles:['admin','user']");
//how do i pass array of roles in it
//role middleware
<?php
namespace App\Http\Middleware;
use Closure;
use App\Role;
use Illuminate\Support\Facades\Log;
class Roles
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next,$role)
{
Log::info($role);
if($request->user()==null){
return response('unauthorised Access detected',401);
}
//check for any role passed from array
if($request->user()->hasAnyRole($role)){
return $next($request);
}
return response('unauthorised Access detected',401);
}
}
//usermodel
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
public function role(){
return $this->hasMany(Role::class);
}
public function hasANyRole($roles){
if(is_array($roles)){
foreach($roles as $role){
if($this->hasRole($role)){
return true;
}
}return false;
}else{
if($this->hasRole($roles)){
return true;
}
return false;
}
}
public function hasRole($role){
if($this->role()->where('role',$role)->first()){
return true;
}
return false;
}
public function getRole(){
return $this->role()->select('role')->get();
}
}
how do i pass role like ['admin','user','cmc'] some thing like this into the middleware of role
the problem is i can only get the first value in the array and the rest is not there
can some one get me out of this
I had a similar situation where I wanted to check if a user is an owner or admin before visiting a route; so I didn't need duplicate routes, and needed to pass an array instead of set single parameters.
I went down the route of sending a single paramter but using an | as a delimiter to explode on the middlewhere class.
In the route I had this for the route group
Route::group(['middleware' => ['checkRole:admin|owner']], function () {
and then in the middlewhere I used explode
$roles = explode('|', $permitted_roles);
Simple looped through the roles array to check if the user had one of the roles :) Hope this helps. Simple and easy for what I needed.

Categories