How to allow nova resource action in Policy - laravel

Nova 2.0
Laravel 5.8
I have one nova resource Document ( contains file url, related foreign key and title ) for which I have defined policy with create and update false and all others set to true, the PDF is generated from another resource, so I don't need to allow it to be created or edited, now everything is working fine, but with another action on this Document resource I am trying to download these files, giving me error "Sorry you are not authorized to take this action", so how to allow this action on Policy.
DocumentPolicy class
<?php
namespace App\Policies;
use App\User;
use App\Models\Document;
use Illuminate\Auth\Access\HandlesAuthorization;
class DocumentPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any documents.
*
* #param \App\User $user
* #return mixed
*/
public function viewAny(User $user)
{
return true;
}
/**
* Determine whether the user can view the document.
*
* #param \App\User $user
* #param \App\Document $document
* #return mixed
*/
public function view(User $user, Document $document)
{
return true;
}
/**
* Determine whether the user can create documents.
*
* #param \App\User $user
* #return mixed
*/
public function create(User $user)
{
return false;
}
/**
* Determine whether the user can update the document.
*
* #param \App\User $user
* #param \App\Document $document
* #return mixed
*/
public function update(User $user, Document $document)
{
return false;
}
/**
* Determine whether the user can delete the document.
*
* #param \App\User $user
* #param \App\Document $document
* #return mixed
*/
public function delete(User $user, Document $document)
{
return true;
}
/**
* Determine whether the user can restore the document.
*
* #param \App\User $user
* #param \App\Document $document
* #return mixed
*/
public function restore(User $user, Document $document)
{
return true;
}
/**
* Determine whether the user can permanently delete the document.
*
* #param \App\User $user
* #param \App\Document $document
* #return mixed
*/
public function forceDelete(User $user, Document $document)
{
return true;
}
public function download(User $user, Document $document)
{
return true;
}
}

The reason why you are getting the error is because your update method returns false in your policy.
By default, if the update is false, Nova will not allow the action. To test this, you can try to set it to true and test it again.
To fix this, you'd have to change the way you are registering the action to add a custom callback to handle if the user can run the action or not like this:
public function actions(Request $request)
{
return [
(new DownloadDocument)->canRun(function ($request, $document) {
return $request->user()->can('download', $document);
}),
];
}
With this, it will check for the download method in your document policy instead of the update method for the action.
For more information: https://nova.laravel.com/docs/2.0/actions/registering-actions.html#authorizing-actions-per-resource

Related

403 this action is unauthorized in Laravel Policy

Postpolicy.php
/**
* Determine whether the user can update the model.
*
* #param \App\Models\User $user
* #param \App\Models\Post $post
* #return \Illuminate\Auth\Access\Response|bool
*/
public function update(User $user, Post $post)
{
return $user->id === $post->user_id;
}
PostController.php
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function edit(Request $request,Post $post)
{
$this->authorize('update',$post);
$id=$request->id;
$edit=Post::find($id);
if($edit){
return view('editform');
}
return abort(404);
}
I am trying to edit the post if the posts belong to logged-in users but I am getting this action is not authorized error whenever I press the edit button to get the edit form. Above are my codes and I don't know what is wrong with them. Any help will be really appreciated.

Laravel - How to Add Custom field in FORGOT PASSWORD

I am trying to add one more field to forgot password which is STAFF ID & EMAIL. If STAFF ID and EMAIL is correct then the system should send reset password link.
It seems laravel default only allow email for forgot password. Is there anyways to add STAFF ID and verify both field before send email?
vendor/laravel/framework/src/Illuminate/Foundation/Auth/SendsPasswordResetEmails.php
<?php
namespace Illuminate\Foundation\Auth;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
trait SendsPasswordResetEmails
{
/**
* Display the form to request a password reset link.
*
* #return \Illuminate\Http\Response
*/
public function showLinkRequestForm()
{
return view('auth.passwords.email');
}
/**
* Send a reset link to the given user.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
*/
public function sendResetLinkEmail(Request $request)
{
$this->validateEmail($request);
// We will send the password reset link to this user. Once we have attempted
// to send the link, we will examine the response then see the message we
// need to show to the user. Finally, we'll send out a proper response.
$response = $this->broker()->sendResetLink(
$this->credentials($request)
);
return $response == Password::RESET_LINK_SENT
? $this->sendResetLinkResponse($request, $response)
: $this->sendResetLinkFailedResponse($request, $response);
}
/**
* Validate the email for the given request.
*
* #param \Illuminate\Http\Request $request
* #return void
*/
protected function validateEmail(Request $request)
{
$request->validate(['email' => 'required|email']);
}
/**
* Get the needed authentication credentials from the request.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
protected function credentials(Request $request)
{
return $request->only('email');
}
/**
* Get the response for a successful password reset link.
*
* #param \Illuminate\Http\Request $request
* #param string $response
* #return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
*/
protected function sendResetLinkResponse(Request $request, $response)
{
return back()->with('status', trans($response));
}
/**
* Get the response for a failed password reset link.
*
* #param \Illuminate\Http\Request $request
* #param string $response
* #return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
*/
protected function sendResetLinkFailedResponse(Request $request, $response)
{
return back()
->withInput($request->only('email'))
->withErrors(['email' => trans($response)]);
}
/**
* Get the broker to be used during password reset.
*
* #return \Illuminate\Contracts\Auth\PasswordBroker
*/
public function broker()
{
return Password::broker();
}
}
The proper way to do this is to override the PasswordBroker and DatabaseTokenRepository which is actually a lot of work for something that could have been achieved with a little modification to the canResetPasswordContract. The current implementation assumes resetting a password is all about the user and undermines the importance of getting the request information such as the ip address; and there's also the issue of efficient table indexing.
Nevertheless, I came up with a possible replacement of the shipped ForgotPasswordController that should be sufficient for most use cases to change the payload associated with reset password if you would like to use a different table structure without overriding everything.
Keep in mind that
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Str;
use Illuminate\Contracts\Auth\PasswordBroker;
use Illuminate\Http\Request;
use Carbon\Carbon;
use App\Models\PasswordReset;
use App\Models\User;
class ForgotPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset emails and
| includes a trait which assists in sending these notifications from
| your application to your users. Feel free to explore this trait.
|
*/
use SendsPasswordResetEmails;
//in minutes
protected $throttle = 60;
/**
* Send a reset link to the given user.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
*/
public function sendResetLinkEmail(Request $request)
{
$this->validateEmail($request);
// We will send the password reset link to this user. Once we have attempted
// to send the link, we will examine the response then see the message we
// need to show to the user. Finally, we'll send out a proper response.
$user = User::where($this->credentials($request))->first();
if (is_null($user)) {
return $this->sendResetLinkFailedResponse($request, PasswordBroker::INVALID_USER);
}
$reset = PasswordReset::where(
'email', $user->getEmailForPasswordReset()
)->first();
if ($reset && $this->tokenRecentlyCreated($reset)) {
return $this->sendResetLinkFailedResponse($request, PasswordBroker::RESET_THROTTLED);
}
$token = $this->createToken($request, $user, $reset);
//keep in mind that saved token is hashed version of this
$user->sendPasswordResetNotification($token);
return $this->sendResetLinkResponse($request, Password::RESET_LINK_SENT);
}
/**
* Create a ne password reset token
*
* #param \Illuminate\Http\Request $request
* #param Model $user
* #param Model $reset
*/
public function createToken($request, $user, $reset)
{
$email = $user->getEmailForPasswordReset();
if ($reset) {
$reset->delete();
}
// We will create a new, random token for the user so that we can e-mail them
// a safe link to the password reset form. Then we will insert a record in
// the database so that we can verify the token within the actual reset.
$token = $this->createNewToken();
PasswordReset::create([
'user_id' => $user->id,
'email' => $email,
'token' => bcrypt($token),
'created_at' => now(),
'ip_address' => $request->ip()
]);
return $token;
}
/**
* Create a new token for the user.
*
* #return string
*/
public function createNewToken()
{
return hash_hmac('sha256', Str::random(40), $this->getHashKey());
}
/**
* Replicate hash key used by DatabaseTokenRepository
*/
public function getHashKey()
{
$key = config('app.key');
if (Str::startsWith($key, 'base64:')) {
$key = base64_decode(substr($key, 7));
}
return $key;
}
/**
* Determine if the token was recently created.
*
* #param Model $token
* #return bool
*/
protected function tokenRecentlyCreated($token)
{
if ($this->throttle <= 0) {
return false;
}
return Carbon::parse($token->created_at)->addSeconds(
$this->throttle
)->isFuture();
}
}
Finally manage to add staff ID in credentials :)
<?php
namespace Illuminate\Foundation\Auth;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
trait SendsPasswordResetEmails
{
/**
* Display the form to request a password reset link.
*
* #return \Illuminate\Http\Response
*/
public function showLinkRequestForm()
{
return view('auth.passwords.email');
}
/**
* Send a reset link to the given user.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
*/
public function sendResetLinkEmail(Request $request)
{
$this->validateEmail($request);
// We will send the password reset link to this user. Once we have attempted
// to send the link, we will examine the response then see the message we
// need to show to the user. Finally, we'll send out a proper response.
$response = $this->broker()->sendResetLink(
$this->credentials($request)
);
return $response == Password::RESET_LINK_SENT
? $this->sendResetLinkResponse($request, $response)
: $this->sendResetLinkFailedResponse($request, $response);
}
/**
* Validate the email for the given request.
*
* #param \Illuminate\Http\Request $request
* #return void
*/
protected function validateEmail(Request $request)
{
$request->validate(['email' => 'required|email'],['StaffID' => 'required']);
}
/**
* Get the needed authentication credentials from the request.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
protected function credentials(Request $request)
{
return $request->only('email', 'StaffID');
}
/**
* Get the response for a successful password reset link.
*
* #param \Illuminate\Http\Request $request
* #param string $response
* #return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
*/
protected function sendResetLinkResponse(Request $request, $response)
{
return back()->with('status', trans($response));
}
/**
* Get the response for a failed password reset link.
*
* #param \Illuminate\Http\Request $request
* #param string $response
* #return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
*/
protected function sendResetLinkFailedResponse(Request $request, $response)
{
return back()
->withInput($request->only('email','StaffID'))
->withErrors(['email' => 'We cant find a user with that Staff ID and Email']);
}
/**
* Get the broker to be used during password reset.
*
* #return \Illuminate\Contracts\Auth\PasswordBroker
*/
public function broker()
{
return Password::broker();
}
}
Thanks :)

Restrict Staffs to Login in Laravel in both system

I've create a 2 (two) system and 2 subdomain with the same Database
but the staff they can login in both system, and i want to disable the staff not to login in the other system.
please let me know if anyone can do this. im new to Laravel World
Thanks
Consider adding a instance_id field to the User model.
On the first application, add to app/Http/Controllers/Auth/LoginController.php:
/**
* Attempt to log the user into the application.
*
* #param \Illuminate\Http\Request $request
* #return bool
*/
protected function attemptLogin(Request $request)
{
$success = $this->guard()->attempt(
$this->credentials($request), $request->filled('remember')
);
return ($success && $this->userExistsInApplication());
}
/**
* Determine if the user exists for the specified application
*
* #return bool
*/
private function userExistsInApplication()
{
if ($this->guard()->user()->instance_id == env('APP_INSTANCE'))
return true;
$this->guard()->user()->logout();
return false;
}
Add to .env:
APP_INSTANCE=1
On the second application, add to app/Http/Controllers/Auth/LoginController.php:
/**
* Attempt to log the user into the application.
*
* #param \Illuminate\Http\Request $request
* #return bool
*/
protected function attemptLogin(Request $request)
{
$success = $this->guard()->attempt(
$this->credentials($request), $request->filled('remember')
);
return ($success && $this->userExistsInApplication());
}
/**
* Determine if the user exists for the specified application
*
* #return bool
*/
private function userExistsInApplication()
{
if ($this->guard()->user()->instance_id == env('APP_INSTANCE'))
return true;
$this->guard()->user()->logout();
return false;
}
Add to .env:
APP_INSTANCE=2
Set which instance the user is able to log into based on their value set in instance_id.

Laravel, Show(), Edit (), update functions not working

In the code below methods show, edit update are not working.
<?php
namespace App\Http\Controllers\admins;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\contact;
use Image;
use Auth;
use Storage;
use File;
class ContactController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
$contact = Contact::orderby('created_at', 'desc')->paginate(5);
//$agent=Agent::orderby('id','desc')->paginate(5);
return view('admin.messages.index', ['contacts' => $contact]);
}
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create()
{
return back()->with('success', 'Message can only be created by Users end.');
}
/** * Display the specified resource.
*
* #param \App\contact $contact
* #return \Illuminate\Http\Response
*/
public function show(contact $contact)
{
dd(['contact' => $contact]);
//return back()->with('success','Message Contents Are Already Shown');
}
/**
* Show the form for editing the specified resource.
*
* #param \App\contact $contact
* #return \Illuminate\Http\Response
*/
public function edit(contact $contact)
{
return view('admin.messages.edit', compact('contact'));
}
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param \App\contact $contact
* #return \Illuminate\Http\Response
*/
public function update(Request $request, contact $contact)
{
dd($request);
}
/**
* Remove the specified resource from storage.
*
* #param \App\contact $contact
* #return \Illuminate\Http\Response
*/
public function destroy(contact $contact)
{
return back()->with('success', 'Message history can not be Deleted. ');
}
}
Assuming you are using a slug in a route like contacts/{ slugĀ }
public function show(contact $contact)
{
dd(['contact' => $contact]);
//return back()->with('success','Message Contents Are Already Shown');
}
Receives an id not a contact... you are initializing/declaring in the function parameter as contact thats why it somehow gets casted to a contact... but it's an id you should do something like:
public function show($id)
{
$contact = Contact::findOrFail($id);
dd(['contact' => $contact]);
//return back()->with('success','Message Contents Are Already Shown');
}

Laravel 5.5, Default route for index not working for resource controller

I have a resource Controller named CmsPagesController,
class CmsPagesController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
return 'something';
// $pages=CmsPages::all();
// return view('backend.pages.cms.list')->with('pages',$pages);
}
public function list(){
return '123';
}
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create()
{
return view('backend.pages.cms.add');
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->validate($request, [
'name' => 'required',
'slug' => 'required',
'description'=> 'required'
]);
$Page=new CmsPages;
$Page->name= $request->input('name');
$Page->slug= $request->input('slug');
$Page->description= $request->input('description');
$Page->copyright= $request->input('copyright');
$Page->keywords= $request->input('keywords');
$Page->save();
return redirect('/admin/pages')->with('success','Page Added Successfully');
}
/**
* Display the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param int $id
* #return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
}
}
My Add function and store function is working perfect, I can render add page and can store data to database but my index function is not working, I have placed a sample return statement but that is not executed as well. what am I missing
Following is my route
Route::resource('pages','CmsPagesController');
I was facing the same issue, that is index function wasn't returning anything, not even any error.
I had a UsersController made as a resource.
And route as:
Route::resource('users', 'UsersController');
When you try to hit the following URL
...public/users/index
it won't return anything.
It worked for me when I tried
...public/users
the index function is automatically called. You don't have have to write /index in the the url.

Resources