I create a policy called LetterPolicy , this is the code
namespace App\Policies;
use App\Letter;
use App\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class LetterPolicy
{
use HandlesAuthorization;
/**
* Create a new policy instance.
*
* #return void
*/
public function __construct()
{
//
}
public function update(User $user, Letter $letter)
{
return($user->id === $letter->user_id || $user->role_id===1 ) ;
}
}
and this is authserviceprovider
namespace App\Providers;
use App\Letter;
use App\Policies\LetterPolicy;
use App\Policies\UserPolicy;
use App\User;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* #var array
*/
protected $policies = [
'App\Model' => 'App\Policies\ModelPolicy',
User::class => UserPolicy::class,
Letter::class => LetterPolicy::class,
];
/**
* Register any authentication / authorization services.
*
* #return void
*/
public function boot()
{
$this->registerPolicies();
//
}
}
And in the following code I check for the user
class LetterController extends Controller
{
protected $user;
public function __construct()
{
$this->middleware(function ($request, $next){
$this->user = Auth::user();
return $next($request);
});
}
public function edit(Letter $letter)
{
if($this->user->can('update', $letter)){
//edit
}
else
abort('403', 'Access Denied');
}
The code is working well in localhost but on the remote server it reports the access denied error. I created this policy after deploying the site on the server so I create a route /clear-cache with code
Route::get('/clear-cache', function() {
$exitCode = \Illuminate\Support\Facades\Artisan::call('cache:clear');
});
To clear the cache after creating the policy. But it still reports the 403 error. What is the problem?
I tried dd($this->user->id === $letter->user_id || $this->user->role_id===1 ); in the COntroller and it returned false. I tried dd($this->user->id == $letter->user_id || $this->user->role_id==1 ); and it was true. Now it works but I don't know why!!!
Fjarlaegur's answer was the key. I had the same problem: in localhost there was no issue, but in production server somehow every authorization failed and it was because of the comparison operator. Changed from === to == and all as good.
Related
I have the following policy called UserPolicy.
I want only admin users to access/edit the users data, even though I have set the return value to true(for testing) I still get 403 response no matter what.
namespace App\Policies;
use App\Models\Auth\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class UserPolicy
{
use HandlesAuthorization;
public function viewAny(User $user)
{
// return $user->admin();
return true;
}
}
I have registered the policy as follows
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
use App\Models\Auth\User;
use App\Policies\UserPolicy;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* #var array
*/
protected $policies = [
User::class => UserPolicy::class,
];
/**
* Register any authentication / authorization services.
*
* #return void
*/
public function boot()
{
$this->registerPolicies();
//
}
}
The following code is how I use it in the controller
if (Gate::denies('viewAny')) {
return response('Not Authorized!', 403);
}
You should use authorize() method in your controller and pass User class as the second parameter. It will point the request to the targeted policy.
authorize() method is by default provided by Illuminate\Foundation\Auth\Access\AuthorizesRequests trait in your base controller.
Your controller could be like below:
try {
$this->authorize('viewAny', User::class);
} catch (AuthorizationException $e) {
return response('Not Authorized!', 403);
}
I have created a user policy under App\Policies\UserPolicy for the UserController to only allow users with id 2 to access . However right now even the users with id 1 is able to access without throwing any error.
/**
* Determine whether the user can create models.
*
* #param \App\Models\Auth\User $user
* #return mixed
*/
public function create(User $user)
{
return $user->id === 2 ? Response::allow()
: Response::deny('You do not own this users.');;
}
Route :
Route::get('user/createProfile' , [UserController::class, 'showCreateProfileForm'])->name('user.profile.createProfile');
Controller :
UserController.php
public function showCreateProfileForm()
{
$user = Auth::user();
$this->authorize('create' , $user);
return view('backend.auth.user.profile.create');
}
Provider : AuthServiceProvider.php
<?php
namespace App\Providers;
use Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use App\Models\Auth\User;
use App\Policies\UserPolicy;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* #var array
*/
protected $policies = [
User::class => UserPolicy::class,
];
/**
* Register any authentication / authorization services.
*/
public function boot()
{
$this->registerPolicies();
}
}
When I return $this->authorize('create' , $user); from the UserController#showCreateProfileForm , it keeps returning
{
"allowed": true,
"message": null,
"code": null
}
Just found out that there was this method in AuthServiceProvider#boot
Gate::before(function ($user, $ability) {
if ($user->isSuperAdmin()) {
return true;
}
});
Hence, all my authorization kept failing.
The solution would be changing it to
Gate::after(function ($user, $ability) {
if ($user->isSuperAdmin()) {
return true;
}
});
Thank you
Unable to find answers anywhere
I am using Laravel 5.5 policy to restrict a user from listing properties that are not registered by the user (authenticated via API).
I have two classes User and HostProperty. Additionally, I have registered a policy for the user to access their hosted property list by ID.
Here are my models.
The Main Problem is not able to call on controller method - which throws above error:
$authUser = auth('api')->user();
if ($authUser->can('access', $property)) {
return response()->json(['success' => 'success']);
} else {
return response()->json(['error' => 'error']);
}
User.php
namespace App;
use Illuminate\Notifications\Notifiable;
use Cartalyst\Sentinel\Users\EloquentUser;
use Illuminate\Database\Eloquent\SoftDeletes;
use Laravel\Passport\HasApiTokens;
use Illuminate\Auth\Authenticatable as AuthenticableTrait;
use Illuminate\Contracts\Auth\Authenticatable;
class User extends EloquentUser implements Authenticatable
{
use HasApiTokens, Notifiable;
use SoftDeletes;
use AuthenticableTrait;
protected $guarded=[];
protected $dates = ['deleted_at'];
protected $hidden = [
'password', 'remember_token',
];
//hosts relation
public function hostproperty()
{
return $this->hasMany('App\Models\Hosts\HostProperty','user_id');
}
}
HostProperty.php
namespace App\Models\Hosts;
use Illuminate\Database\Eloquent\Model;
class HostProperty extends Model
{
public $timestamps = true;
protected $guarded=[];
protected $hidden = [
'user_id',
];
public function user()
{
return $this->belongsTo('App\User','user_id');
}
}
HostPropertyPolicy
namespace App\Policies\Host;
use App\User;
use App\Models\Hosts\HostProperty;
use Illuminate\Auth\Access\HandlesAuthorization;
class HostPropertyPolicy
{
use HandlesAuthorization;
/**
* Create a new policy instance.
*
* #return void
*/
public function __construct()
{
//
}
public function access(User $user, HostProperty $HostProperty)
{return TRUE;
//return $user->id === $HostProperty->user_id;
}
}
AuthServiceProvider
namespace App\Providers;
use Illuminate\Support\Facades\Gate;
use Laravel\Passport\Passport;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use App\Models\Hosts\HostProperty;
use App\Policies\Host\HostPropertyPolicy;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* #var array
*/
protected $policies = [
'App\Model' => 'App\Policies\ModelPolicy',
HostProperty::class=>HostPropertyPolicy::class,
];
/**
* Register any authentication / authorization services.
*
* #return void
*/
public function boot()
{
$this->registerPolicies();
Passport::routes();
}
}
HostPropertyController
use App\User;
use App\Models\Hosts\HostProperty;
use App\Http\Controllers\Controller;
class HostPropertyController extends Controller
{
public function listOneProperty($propertyId)
{
$authUser = auth('api')->user();
$property=HostProperty::with('user')->find($propertyId);
if ($authUser->can('access', $property)) {
return response()->json(['success' => 'success']);
} else {
return response()->json(['error' => 'error']);
}
}
}
Route
Route::get('listOneProperty/{propertyId}', array('as' => 'listOneProperty.get', 'uses' => 'HostPropertyController#listOneProperty'));
Please note: I am calling from API - the above route is for API, I am not able to use the policy on the API routes. I keep getting the above error while calling this route.
I tried
$this->authorize('access', $property);
However, since API doesn't store login session the above could not be completed so I again tried with
$authUser = auth('api')->user();
$authUser->authorize('access', $property);
Does not work either. I have tried all I can but still, I cannot get it done right.
If someone has an example of using Laravel policy in API authenticated by Passport it would be helpful for anybody looking to get this done right.
With regards
Looks like
$authUser = auth('api')->user();
is returning a query instead of a User model.
Please make sure that $authUser is a User model before calling ->can()
However, since API doesn't store login session the above could not be completed so I again tried with
Authentication is typically handled by middleware in Laravel. If you're using Passport, you should be able to use the auth:api middleware to authenticate requests, as long as you're sending the correct Authorization header and token.
Try changing this
$authUser = auth('api')->user();
to this
$authUser = auth()->user();
And add the auth middleware to your route:
Route::get('listOneProperty/{propertyId}', 'HostPropertyController#listOneProperty')
->name('listOneProperty.get')
->middleware('auth:api');
If you're consuming the api from your own web views and you want it to work with the current session, check out the Consuming Your API With JavaScript section of the Passport docs:
https://laravel.com/docs/5.7/passport#consuming-your-api-with-javascript
I'm trying to figure out why my custom request class cannot be called by one of my methods.
I created my class with, php artisan make:request ValidateUserSecretRequest.
This created my custom request file in the Http/Requests folder as expected.
However, ValidateUserSecretRequest called within my Auth\LoginController.php, , I get Class App\Http\Controllers\Auth\ValidateUserSecretRequest does not exist.
Here's the controller, with unnecessary methods removed:
namespace App\Http\Controllers\Auth;
use Cache;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use App\Http\Requests\ValidateSecretUserRequest;
use App\Http\Controllers\Controller;
class LoginController extends Controller
{
use AuthenticatesUsers;
public function __construct()
{
$this->middleware('guest')->except('logout');
}
public function postValidateToken(ValidateUserSecretRequest $request)
{
// get user id and create cache key
$userId = $request->session()->pull('2fa:user:id');
$key = $userId . ':' . $request->totp;
// use cache to store token to blacklist
Cache::add($key, true, 4);
// login and redirect user
Auth::loginUsingId($userId);
return redirect()->intended($this->redirectTo);
}
And my custom request class:
namespace App\Http\Requests;
use Cache;
use Crypt;
use Google2FA;
use App\User;
use App\Http\Requests\Request;
use Illuminate\Validation\Factory as ValidatonFactory;
use Illuminate\Foundation\Http\FormRequest;
class ValidateUserSecretRequest extends FormRequest
{
/**
*
* #var \App\User
*/
private $user;
/**
* Create a new FormRequest instance.
*
* #param \Illuminate\Validation\Factory $factory
* #return void
*/
public function __construct(ValidatonFactory $factory)
{
$factory->extend(
'valid_token',
function ($attribute, $value, $parameters, $validator) {
$secret = Crypt::decrypt($this->user->google2fa_secret);
return Google2FA::verifyKey($secret, $value);
},
'Not a valid token'
);
$factory->extend(
'used_token',
function ($attribute, $value, $parameters, $validator) {
$key = $this->user->id . ':' . $value;
return !Cache::has($key);
},
'Cannot reuse token'
);
}
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
try {
$this->user = User::findOrFail(
session('2fa:user:id')
);
} catch (Exception $exc) {
return false;
}
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'totp' => 'bail|required|digits:6|valid_token|used_token',
];
}
}
I've tried:
composer dump-autoload
composer update
scratching my head multiple times
recreating the custom request with artisan with a different name, same problem
What the hell is going on here?
You have written "use App\Http\Requests\ValidateSecretUserRequest;" while you are using "ValidateUserSecretRequest" class, There is a typo.
Error in use App\Http\Requests\ValidateSecretUserRequest; You can check it again.
You get Class App\Http\Controllers\Auth\ValidateUserSecretRequest does not exist since it doesn't really exists. App\Http\Controllers\Auth\ prefix in the error means that it uses current namespace.
Look at ValidateUserSecretRequest - you should swap User and Secret to write correct class name.
You have the class name wrong
You have it as
App\Http\Requests\ValidateSecretUserRequest;
but actually it is ValidateUserSecretRequest
I am trying to use Laravel's HTTP Basic Authentication in a Lumen project.
On the routes.php file, I set the auth.basic middleware for the rout I need to authenticate:
$app->get('/test', ['middleware' => 'auth.basic', function() {
return "test stuff";
}]);
On bootstrap.php I have registered the middleware and the auth service provider:
$app->routeMiddleware([
'auth.basic' => Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
]);
[...]
$app->register(App\Providers\AuthServiceProvider::class);
But when I try to test the route by visiting http://lumen/test I get the following error:
Fatal error: Call to undefined method Illuminate\Auth\RequestGuard::basic() in C:\source\lumen\vendor\illuminate\auth\Middleware\AuthenticateWithBasicAuth.php on line 38
Does anyone know how can I get the code of the guard for basic authentication?
Thanks.
Ran into a similar problem, wanted to use basic auth for users in the database, so ended up writing my own AuthServiceProvider and registered that in bootstrap/app.php
Here is the class, maybe it will help you in your case.
<?php
namespace App\Providers;
use App\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\ServiceProvider;
class HttpBasicAuthServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
/**
* Boot the authentication services for the application.
*
* #return void
*/
public function boot()
{
$this->app['auth']->viaRequest('api', function ($request) {
$email = $_SERVER['PHP_AUTH_USER'];
$password = $_SERVER['PHP_AUTH_PW'];
if ($email && $password) {
$user = User::whereEmail($email)->first();
if (Hash::check($password, $user->password)) {
return $user;
}
}
return null;
});
}
}