Laravel Policies Via blade template - laravel

I got an error when i want to use Policies to limit users access, when user access the system as guest, the system won't show edit button vice versa if user as admin the system will show the edit button. But i got an error when user are logged in as Admin and no error when user are not logged in. This are my error messages
oo few arguments to function App\Policies\InverterPolicy::update(), 1 passed in /Applications/XAMPP/xamppfiles/htdocs/PROJECT/ta/vendor/laravel/framework/src/Illuminate/Auth/Access/Gate.php on line 691 and exactly 2 expected
This are my blade
#can('update',App\Inverter::class)
<button type="button" class="btn btn-warning" name="button">Edit</button>
#endcan
This are my Controllers
public function update(Request $request, Inverter $inverter)
{
$this->authorize('update',$inverter);
$data = $request->validate([
'name'=>'bail|required|max:191',
'warehouse_id'=>'bail|required|numeric',
'company_id'=>'bail|required|numeric',
'length'=>'numeric',
'width'=>'numeric',
'height'=>'numeric',
'maxInputPower'=>'numeric',
'maxInputVoltage'=>'numeric',
'maxInputCurrent'=>'numeric',
'MPPTOperatingRange'=>'numeric',
'parallelInput'=>'numeric',
'MPPTTrackers'=>'numeric',
'nominalOutputPower'=>'numeric',
'maxOutputPower'=>'numeric',
'nominalOutputCurrent'=>'numeric',
'maxOutputCurrent'=>'numeric',
'ACFrequencyRange'=>'numeric',
'THDI'=>'numeric',
'efficiency'=>'numeric',
'MPPTEfficiency'=>'numeric',
'euroEfficiency'=>'numeric',
]);
Inverter::find($inverter->id)->update($data);
return redirect(action('InverterController#index'));
}
this are my policies
public function update(User $user, Inverter $inverter)
{
return in_array($user->role,[
'Admin',
]);
}

When you call the can() method on a User using, as the second parameter, the class name instead of an instance, you're actually calling the method without the second parameter at all. Just make the $inverter nullable in your policy and it should be fixed:
public function update(User $user, Inverter $inverter = null)
{
return in_array($user->role,[
'Admin',
]);
}

Related

Laravel 7: How can I restrict URL by user?

I'm a newbie who is learning Laravel 7. I have developed a small web application with Laravel 7. But today I noticed one problem. That all the URLs are global, means all users can access all the URLs of my website. Suppose User A created data and it shows in a table where the edit and delete buttons exist with every row. The edit URL is like: localhost/records/edit/5. The problem is, that other logged-in users can access this edit page also. Like this, all the URLs are accessible by any logged-in users which is very bad.
I hope you understand what I'm saying. I have almost 250+ web routes. Is there any easy way to restrict the routes?
User can access their own data only. How can I do that?
Thanks
You'll have to register policies and ensure users cannot access parts of the website without the correct authorization.
See the docs on how to write policies and implement them.
Sample code:
Policy:
class RecordPolicy
{
public function delete(User $user, Record $record)
{
return $user->id === $record->user_id;
}
}
Controller
class RecordController
{
public function destroy(Record $record)
{
// Authorize the delete action before actually deleting the record
$this->authorize('delete', $record);
$record->delete();
}
}
Records index
#foreach($records as $record)
<div>
{{ $record->name }}
{{-- Only show delete button if the authorized user can actually delete the record --}}
#can('delete', $record)
<form action="{{ route('records.destroy', compact('record') }}" method="POST">
#csrf
#method('DELETE')
<button type="submit">Delete record</button>
</form>
#endcan
</div>
#endforeach
store user_id when new record added > Add created_by field in user_table DB
when user run URL > get logged-in user user_id from session and check in DB for their record > if record not found then redirect to home page with message otherwise continue.
If i understand you correctly you want to restrict routes to specific user.
Create a roles table
Columns (id, name)
(1 = Super Admin, 2 = Admin, 3 = User)
Assign Roles To User While Creating new User
i.e add role_id to users table.
$user = User::create([
'name' => 'First Admin',
'email' => 'admin#admin.com',
'password' => Hash::make('Admin#1234'),
'role_id' => 2 // For admin role
]);
Then Create Middlewares for each role and restrict routes for specific users.
Admin Middleware: AdminMiddleware.php
public function handle(Request $request, Closure $next)
{
$allowedRoles = [2];
if (!in_array(Auth::user()->role_id, $allowedRoles))
{
return redirect()->back()->with('error',__('Sorry, you are not authorized to access that location.'));
}
return $next($request);
}
In Kernel.php
'admin' => \App\Http\Middleware\AdminMiddleware::class,
Route::group(['middleware' => 'admin'], function(){
// All admin Routes
});
You Can also Use Spatie package for this.
https://spatie.be/docs/laravel-permission/v5/basic-usage/middleware
Just Check If Role is allowed to use that route:
Route::group(['middleware' => ['auth', 'role:admin']], function () {
// All routes available for admin
});

Stripe error "The resource ID cannot be null or whitespace"

I am trying to create a subscription but get the error "The resource ID cannot be null or whitespace". I have stripe and cashier installed and migrated.
<?php
namespace App\Http\Controllers;
require_once('../vendor/autoload.php');
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Laravel\Cashier\Cashier;
use \Stripe\Stripe;
use Exception;
use Stripe_Error;
class SubscriptionController extends Controller {
public function __construct() {
$this->middleware('auth');
}
public function retrievePlans() {
$key = \config('services.stripe.secret');
$stripe = new \Stripe\StripeClient($key);
$plansraw = $stripe->plans->all();
$plans = $plansraw->data;
foreach($plans as $plan) {
$prod = $stripe->products->retrieve(
$plan->product,[]
);
$plan->product = $prod;
}
return $plans;
}
public function showSubscription() {
$plans = $this->retrievePlans();
$user = Auth::user();
return view('subscribe', [
'user'=>$user,
'intent' => $user->createSetupIntent(),
'plans' => $plans
]);
}
public function processSubscription(Request $request)
{
$user = Auth::user();
$paymentMethod = $request->input('payment_method');
$user->createOrGetStripeCustomer();
$user->addPaymentMethod($paymentMethod);
$plan = $request->input('plan');
try {
$user->newSubscription('default', $plan)->create($paymentMethod, [
'email' => $user->email
]);
} catch (\Exception $e) {
return back()->withErrors(['message' => 'Error creating subscription. ' . $e->getMessage()]);
}
return redirect('dashboard');
}
}
I keep getting the error when creating a user $user->createOrGetStripeCustomer();
In the user table, while creating a new user just kept the stripe-id field as NULL. It should be either a stripe id or must be set as NULL. If you just keep it blank will get the error
Another important point, stripe_id filed is highly case sensitive so keep it as utf8mb4_ci encoding
I had that same error. And in my case was because the following:
Subscribe user 1 (and thus was created as customer in Stripe)
Delete customer from the Stripe dashboard
Try to subscribe user 1 again (in the next 10 minutes or so after delete from the customers Stripe list)
This stop happening when I subscribe a different user that has never been in the customers list.
Perphaps and this is just me doing a long shot: Stripe needs a bit of time between delete and re-subscribe.
I also looked in docs but nothing came up about this error.
only this: https://stripe.com/docs/error-codes#resource-missing
I was trying to integrate subscription with stripe using cashier in Laravel 8 but I was getting the same error. For me, I have already stripe_id field as NULL by default and kept utf8mb4_ci encoding.
After debugging many things, I found that "payment_method" was not coming in post and found issue with below code.
<button id="card-button" data-secret="{{ $intent->client_secret }}" class="btn btn-lg btn-success btn-block">SUBMIT</button>
It was submitting the form before JavaScript code get fully executed to get the payment method and payment method was not getting posted. So adding type="button" resolved my issue. Below is the submit button code that resolved my issue.
<button type="button" id="card-button" data-secret="{{ $intent->client_secret }}" class="btn btn-lg btn-success btn-block">SUBMIT</button>
Modify processSubscription function to check if the user has a payment method already before creating one to him like this:
if(! $user->hasPaymentMethod())
$user->addPaymentMethod($paymentMethod);

Assign specific roles from where the user clicks

I would like to assign roles depending on which button the user clicks:
For example:
- If you click on I want to be an Advisor, redirect to the Laravel registration form and assign the role of advisor.
- If the user clicks on I want to be a Buyer, they redirect to the Laravel registration form and assign the buyer role.
But I do not know how to do it.
I have this code in my 'RegisterController':
protected function create(array $data)
{
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
//'password' => Hash::make($data['password']), //mutador en User model
'password' => $data['password'],
'surname1' => $data['surname1'],
'surname2' => $data['surname2'],
'comunidad_id' => $data['cbx_comunidad'],
'provincia_id' => $data['cbx_provincia'],
'municipio_id' => $data['cbx_municipio'],
]);
//dd(Request::url());
// $user->assignRole('Asesor');
//quiero asignar varios roles depende de el botón que clicken
return $user;
}
For now, what I have done is to add such a parameter, in the view that calls the view 'register':
href="{{ route('register','Asesor') }}"
and in the view 'register' send it by post in a hidden:
<div class="form-group">
<?php
$pos = strpos(Request::fullUrl(), '?');
$cadena = substr (Request::fullUrl() , $pos+1, strlen(Request::fullUrl()) );
?>
<input type="hidden" name="role" id="role" value="{{ $cadena }}">
</div>
Then in the controller I do this:
if ($data['role'] == 'Asesor=')
{
$user->assignRole('Asesor');
}
return $user;
But I don't know if it's the right way to act.
I think, you could work with events like this:
In your EventServiceProvider class, create an item inside your property $listen:
'App\Events\User\Created' => ['App\Listeners\User\AssignRoles'],
After that, you going to run the command:
php artisan event:generate
Now, you need to turn on this event in your User class declaring protected property $dispatchesEvents, like this:
protected $dispatchesEvents = ['created' => 'App\Events\User\Created'];
After all call create method in your User class, the created event is going to be called and run AssignRoles logic.
In your App\Events\User\Created class you need to inject User into __construct method, like this:
public $user;
public function __construct(User $user)
{
$this->user = $user;
}
Remember to put the complete path of User class!
This is the object that is going to be filled with data coming from User::create method.
Inside your Listener AssignRoles you have the event linked with the user filled and you can get it this way:
public function handle(Created $event)
{
$event->user;
// ...
}
Inside your Listener AssignRoles you can get all Requested params in your __construct method:
private $request;
public function __construct(Illuminate\Http\Request $request)
{
$this->request = $request;
}
With requested params in your hand you can apply the logic depending on the clicked button inside handle method:
public function handle(Created $event)
{
$event->user;
// here is the best place to do all the logic about roles that is going to be attached in this user. E.g:
switch($role = $this->request->role) {
case $role == 'Asesor':
$event->user->roles()->assignRole('Asesor');
break;
case $role == 'Buyer':
$event->user->roles()->assignRole('Buyer');
break;
}
}
To send role param into Request you need to create a form with hidden element,
<input type='hidden' name='role' />
create more than one submit button to fill role hidden element
<input type='submit' value='I want to be an Advisor' onClick='setRole("Advisor")' />
<input type='submit' value='I want to be a Buyer' onClick='setRole("Buyer")' />
And, finally you need a logic to setRole js method. Good Look. ;-)
For assign Role to user.
Controller function will be like.
/* assign role */
if(is_array($request['role']))
{
foreach($request['role'] as $d)
{
$user->roles()->attach(Role::where('id',$d)->first());
}
}
return redirect()->route();

i want to store restaurant id when saving my review blade

I have created a review blade for restaurants i need it to save the restaurants id .i have a variable called restaurant_id which stores the restaurants review comment.This is my review blade
<html>
{!!Form::open(['action' =['ReviewsController#store'],'method'=>'POST','enctype'=>'multipart/form-data'])!!}
<div class="form-group">
{{Form::textarea('value',null,['id'=>'value','placeholder'=>'Insert
here','name'=>'value'])}}
</div>
{{Form::submit('submit',['class'=>'btn btn-
primary','type'=>'submit','id'=>'submit'])}}
{!!Form::close()!!}
</html>
i also have user_id which stores the id the currently logged in user but it gives me an error
ErrorException (E_NOTICE)
Trying to get property 'id' of non-object
This is the review controller function that stores the review
public function store(Request $request )
{
// $restaurant=Restaurant::find($id);
$review=new Review;
$review->user_id=auth()->user()->id;
$review->restaurant_id=$request->route('id');
$review->value=$request->input('value');
$review->save();
}
This is the link to the review blade template on the restaurant
Add review
$review->user_id=auth()->user()->id;
This line assumes the existence of a logged in user. If your application supports reviews from non-authenticated users, you need to account for auth()->user() being null instead of a User object:
$review->user_id = auth()->user() ? auth()->user()->id : null;
Your code, specifically:
auth()->user()->id;
is assuming there is always an authenticated user. Currently there isn't, therefore you are:
Trying to get property 'id' of non-object
since auth()->user() is a non-object and you're attempting to get the property id from it.
You will have to do some kind of authentication check assuming you only want authenticated users doing what you want done.
public function store(Request $request)
{
if (! Auth::check() {
// Do something here if the user isn't authenticated, such as
// return back()->withInput();
}
$review = new Review;
$review->user_id = auth()->user()->id;
$review->restaurant_id = $request->route('id');
$review->value=$request->input('value');
$review->save();
}

Laravel 5.4 show activation pending message on login form

I am working on Laravel 5.4 project. I love the login provided by Laravel and it works fine with both login or register.
I add below code to Auth/LoginController.php. It allows only activated users (status=1) to successfully login, but not pending users or blocked users (status =0 or something else).
protected function credentials(\Illuminate\Http\Request $request)
{
return ['email' => $request->{$this->username()}, 'password' => $request->password, 'status' => 1];
}
Anyway, to protect spam I would like to allow only activated users to login. For those whose account are not activated, I would like to show the pending message on the login form. Also, I would like to do the same thing for blocked users.
Could you please advise me how to achieve this?
This way Laravel would only pick only users by the credentials you specify, if you want to check status the user has and what view to show you can overwrite the authenticated() method of the login controller. It will have access to the already logged in user so note that you have to logout it the status is invalid.
protected function authenticated(Request $request, $user)
{
if ( $user->status == 0 ) {
auth()->logout();
return back()->withErrors(['email' => 'You are blocked or not activated.']);
}
return redirect()->intended($this->redirectPath());
}

Resources