Trying to authorize a user to update a post if the user id and the post user_id matches. I have a custom callback for authorization in the AuthServiceProvider which checks for 'Authorization' header, which is an API key in the boot() function.
$this->app['auth']->viaRequest('api', function ($request) {
if($request->header('Authorization')) {
$user = $this->getUserFromAuthorizationHeader($request);
if (!empty($user)) {
$request->request->add(['userid' => $user->id]);
}
return $user;
}
});
The function getUserFromAuthorizationHeader gets a Request $request parameter and extracts the Authorization header, which is an api key, and then returns a User object.
I defined a gate update-post which checks the user that is returned from the callback and the post passed when calling the gate update-post from a controller.
Gate::define('update-post', function($user, $post){
Log::info($user);
return $user->id == $post->user_id;
});
The way I am calling the Gate in my PostController is by the following
...
$user = $this->getUserFromRequest($request);
if(Gate::denies('update-post', $post)) {
return response("Unauthorized.", 401);
}
...
I logged - using Log:: info() - the $user and $post variables in my Gate and I can successfully see the correct user and post objects being passed, but I get the error Call to a member function parameter() on array and I can't understand why exactly I am getting it.
You probably need to convert into collection before comparing if you are getting the array like this
$post = collect($post);
$user = collect($user);
Gate::define('update-post', function($user, $post){
Log::info($user);
return $user->id == $post->user_id;
});
Doc Reference
Related
I am trying to use a Gate to see if the currently authenticated user matches the user_id column of the "posts" table.
However, when attempting to use the Gate inside my controller, it is giving me the following error, and I am at a loss.
App\Providers\AuthServiceProvider::App\Providers\{closure}(): Argument #2 ($post) must be of type App\Models\Post, App\Models\User given, called in [path]\vendor\laravel\framework\src\Illuminate\Auth\Access\Gate.php on line 535
Thanks.
My Controller:
class updatePost extends Controller
{
public function updatePost(Request $request, Post $post) {
if (Gate::allows('updatePost', auth()->user(), $post)) {
$post->title = $request->input('title');
$post->body = $request->input('body');
$post->save();
return redirect()->route('readPost', ['id' => $post->id]);
} else {
echo 'ERROR';
}
}
}
My Gate:
Gate::define('updatePost', function (User $user, Post $post) {
return $user->id === $post->user_id;
});
The define callback always receives the logged in user as first parameter followed but the parameters given by allows().
Try this
if (Gate::allows('updatePost', $post)) {
// your code
}
I actually am not able to understand why I am getting the following error.
App\Models\User::team must return a relationship instance, but "null" was returned. Was the "return" keyword used?
I am basically creating test cases for simple orders for ecommerce.
User Modal
public function team(): BelongsTo|null
{
if (!empty($this->team_id)) {
return $this->belongsTo(Team::class);
}
return null;
}
Test case
public function test_order_status_update()
{
$order = $this->create_order($this->books->id, $this->appUser->id, $this->address->id);
$response = $this->actingAs($this->user)->put('orders/' . $order->json('order.id'), [
'order_status' => 'ordered',
]);
$response->assertRedirect('orders/' . $order->json('order.id'))->assertSessionHas('success');
}
In addition, I have another feature in my application called pages access control, which controls page access for multiple users (admin, developer, and users).
I have implemented this feature manually using middleware.
Middlware.php
public function handle(Request $request, Closure $next)
{
//teams 1-Developer 2-Admin 3-Management 4-Marketing 5-Audit 6-Sales 7-Bookstores 8-Delivery 9-User
$team = $request->user()->team;
if ($team->id == 1 || $team->id == 2) {
return $next($request);
}
$pages = auth()->user()->pages->merge(auth()->user()->team->pages);
$currentRouteName = $request->route()->getName();
$pages->contains('route_name', $currentRouteName) ?: abort(403);
return $next($request);
}
Based on the error above, I believe the actingAs function is unable to obtain authenticated user information, which is why my test failed.
How can I fix this?
Simply don't check your team_id:
public function team(): BelongsTo
{
return $this->belongsTo(Team::class);
}
Laravel tries to be smart. If team_id isn't set, it will just return null. However, if you don't return the BelongsTo, the magic code of Laravel will trip when you try to access user->team
I'm doing this query in the payment controller and i need to get a post request from the route.
Controller:
class PaymentController extends Controller
{
public function apiPaymentByUserId($date_from, $date_to) {
$payments = DB::table("casefiles, payments")
->select("payments.*")
->where("casefiles.id", "=", 'payments.casefile_id')
->where("casefiles.user_id", "=", Auth::id())
->where("payments.created_at", ">=", $data_from)
->where("payments.updated_at", "<=", $data_to)
->get();
return response()->json([
'success' => true,
'response' => $payments
]);
}
}
Route:
Route::post('/payments/{date_from}/{date_to}', 'Api\PaymentController#apiPaymentByUserId');
How to pass multiple parameters in this post route? Thank you
For post request no need to pass param in url .You will get in request
So route will be
Route::post('/payments', 'Api\PaymentController#apiPaymentByUserId');
and controller method
public function apiPaymentByUserId(Request $request)
{
$date_from = $request->date_from;
$date_to = $request->date_to;
}
If you do not want to change your url, try this in your controller apiPaymentByUserId() method, inject the Request object along with the other path variables like like:
public function apiPaymentByUserId(Illuminate\Http\Request $request, $date_from, $date_to) {
// ... you can access the request body with the methods available in $request object depending on your needs.
}
For POST request no need to pass param in url . Send the Dates as FORM values sent via POST method along with the rest of the FORM values (if any, you're already POSTing in the FORM). You will get all FORM values sent via POST method in Request $request object instance, passed in Controller/Method.
So route will be:
Route::post('/payments', 'Api\PaymentController#apiPaymentByUserId');
and controller method:
public function apiPaymentByUserId(Request $request)
{
$date_from = $request->date_from;
$date_to = $request->date_to;
}
Currently I'm working on a project where I made it so that when a user types a correct password in form field, it will give them the items from the given section.
The main problem i'm having is that to do this I need to capture the request and therefore the route has to be a post method instead of a get as such:
public function index(Request $request)
{
$id = $request->input('id');
$password = $request->input('password');
$result = DB::table('scrumboards')->find($id);
if ($result->key == $password) {
$scrumboard = $result;
$items = DB::table('backlogs')->get();
return view('scrumboard', ['items' => $items, 'scrumboard' => $scrumboard]);
} else {
$scrumboard = $result;
return redirect('home');
}
}
and the route as such:
Route::post('/scrumboard', 'ScrumboardController#index');
By doing this, request errors wont work since It wants to redirect back but can't since this is a post method.
Any way I can avoid this clash?
Routes can have multiple HTTP verbs. Define your route as
Route::match(['get', 'post'], '/scrumboard', 'ScrumboardController#index');
to make it available as GET and POST route.
I need to restrict the access to some parts of the application depending on the user logged in. I mean for example to let a user edit only its own posts on a blog application.
Is there a better approach than in every function of the controller, if the user is not the owner of the required post, redirect to some error page?
For example if my routes are /post/{post_id}/edit, /post/{post_id}/preview, /post/{post_id}/delete, can I somehow declare a general function in the PostController like:
if(Post::find($post_id)->user_id != Auth::user()->id){
return View::make('access-error');
}
Thanks!
In your controller you can do something like this:
public $check = ['edit', 'preview', 'delete'];
public function callAction($method, $parameters) {
if(in_array($method, $this->check, true) &&
$post_id = $parameters['post_id'] &&
Post::find($post_id)->user_id != Auth::user()->id) {
return View::make('access-error');
}
return parent::callAction($method, $parameters);
}
You could throw a 401 error and catch it elsewhere to display a custom page
App::abort(401);
http://laravel.com/docs/4.2/errors#handling-404-errors