Authorization check in Blade Template - laravel

I need to control access to the menu items. Here is the Gate Function that is created inside AuthServiceProvider. How do I access it inside a blade template
AuthServiceProvider
Gate::define('isAdmin',function($user){
return $user->type === 'admin';
});
Gate::define('isGeneralUser',function($user){
return $user->type === 'user';
});
Gate::define('isPaidUser',function($user){
return $user->type === 'paid';
});
Gate::define('isSubscriber',function($user){
return $user->type === 'subscriber';
});
Menu
<ul class="dropdown-menu">
<li><a class="hvr-sweep-to-right" href="{{route('weekly-trades')}}">Weekly Trades</a></li>
<li><a class="hvr-sweep-to-right" href="{{route('daily-trade')}}">Daily Trades</a></li>
<li><a class="hvr-sweep-to-right" href="{{route('videos-articles-archive')}}">Videos & Articles Archive</a></li>
<li><a class="hvr-sweep-to-right" href="{{route('blog-list')}}">Surplus forex Blogs</a>
</li>
</ul>
</ul>

You can easily create the if blade directives and show the routes for each role you have, like this:
Blade::if('isRole', function ($role) {
return Auth::check() && Auth::user()->type === $role;
});
Then in your blade template you can filter routes like so:
#isRole('agent')
// Agent routes
#elseisRole('admin')
// Admin routes
#else
// Other routes
#endisRole
P.S: I assumed you want to show routes for the logged in user
EDIT: You actually created Gates which restrict any users to do the given action, it doesn't mean you restricted them access to pages necessarily. You just said that admin users can't do isAdmin action, which doesn't make much sense :)
Update: For checking multiple roles you can change your Blade::if to accept an array of roles instead of a single one:
Blade::if('isRole', function ($roles = []) {
if (empty($roles) || !Auth::check()) {
return false;
}
return in_array(Auth::user()->type, $roles);
});
Then you can use the directive as:
#isRole(['agent', 'admin'])
// User is either agent or admin
#elseisRole(['admin'])
// User is admin
#else
// User is something else
#endisRole

however, it is late but it is useful for someone, here what you want to just admin see write inside the condition
#can('isAdmin')
<!-- The Current User is admin -->
#endcan

Related

if else condition in routes laravel

I want only user with same name with the url id can access using if condition
Example
User logged on with name jer
He should only access url with /User-Profile/jer
And not access other page /User-Profile/abc that are not equal to his
name
Doing something like Example:
if{id}!=={{auth->name}}
{
Route::get('NoPermission', 'Restriction#index');
}
else
{
Route::get('/User-Profile/{name}/', 'AccountController#index');
}
How can I compare {name} from url to {auth->name} ?
Route
Route::get('/User-Profile/{name}/', 'AccountController#index');
Blade
<a href="/dashboard/User-Profile/{{ Auth::user()->name }}">{{ Auth::user()->name
}}</a>
You can't access Auth like that in your routes, compare it in your AccountController instead:
public function index($name){
if($name != Auth::user->name()) abort(403);
else...
}
In a service provider (Doesn't really matter which one, but it would be clearer if done in the RouteServiceProvider), add a route binding in the boot method as documented in https://laravel.com/docs/6.x/routing#explicit-binding
public function boot()
{
// Declare binding 'name'.
Route::bind('name', function ($name) {
return App\User::where('name', $name)->first() ?? abort(404);
});
}
Then, use that binding in your routes file
// Use binding name and middleware auth to make sure this route can't be accessed by guest users.
Route::get('/User-Profile/{name}/', 'AccountController#index')->middleware('auth')->name('account_profile');
In your blade file, you can do the following
{{-- Make sure the link is only visible for authenticated users https://laravel.com/docs/6.x/blade#if-statements --}}
#auth
<a href="{{ route('account_profile', ['name' => auth()->user()->name]) }}</a>
#endauth
Allow acces to the page , but before showing content ,check if the url path is == to the id name .
Actually, you can check in your routes like this:
Route::get('/profile/{name}', function(String $name) {
if (!Auth::check() || $name !== Auth::user()->name) {
abort(404);
}
return view("view.auth.profile", ['profile => App\Profile::where('user_id', '=', Auth::id())->first()]);
});
However if you use
Route::get('/profile', 'AuthController#profile')->middleware('auth');
and use Auth::user() in your controller to select the correct profile.
The benefit here is that any unauthenticated users will be automatically redirected to your login page, and there's no need to include the name on your profile link.

Laravel #include controller data

I'm trying to receive data on a sidebar that is included in the blade template but i'm not getting any data delivered. I've tried adding #include('admin.sidebar',['message_counter' => $message_counter]) and in the sidebar view show as {{$message_counter}}. I'm getting a Undefined variable: message_counter.
My router:
Route::get('/admin/sidebar', [
'uses' => 'MessagesController#counter',
'as' => 'admin.sidebar'
]);
My controller
use App\Message;
public function counter()
{
$message_counter = Message::where('status', 0)->get();
return view('admin.sidebar')->with('message_counter', $message_counter);
}
My View
<span class="menu-collapsed">Messages <span class="badge badge-pill badge-primary ml-2"> {{$message_counter}} </span></span>
What i ultimately intend to do is to show the amount of unread messages in the sidebar of the administrator backend, which is #includein every page.
It may be because i'm accessing two different controllers everytime I enter any page on the admin backend.
I've looked into Including Sub-Views but i'm probably missing something silly or not understanding some key concept, help is appreciated!
Thank you!
Note: I think this is inconvenient and unrecommendable. This is just to answer the question, you can scroll down to see other answers or approach.
Controller
public function counter()
{
$message_counter = Message::where('status', 0)->get();
return view('admin.sidebar');
}
View
#php
$message_counter = App\Message::where('status', 0)->get();
#endphp
Messages <span class="badge badge-pill badge-primary ml-2"> {{$message_counter}} </span></span>
You can try like this way
In Route:
Route::get('/admin/sidebar', 'MessagesController#counter');
In Controller
use App\Message;
public function counter()
{
$message_counter = Message::where('status', 0)->get();
return view('admin.sidebar', compact('message_counter));
}
And your view is ok.. Try this and if it is not working please let me know....
With a View Composer: add this to App\Providers\AppServiceProvider#boot()
View::composer('admin.sidebar', function ($view) {
$message_counter = Message::where('status', 0)->get();
$view->with([''message_counter' => $message_counter]);
});

Vue.js with Laravel Permission

I am in the process of integrating Laravel Permission API with Vue.JS frontend. I am using https://github.com/spatie/laravel-permission library for Laravel Permission. I am not understanding how can I check permission in the Vue JS front End (In Laravel blade I am using #Can to check the permission).
I will do a ajax call to check for permissions instead , something like this, but of cours eyou need to modify it to cater your needs.
Routes:
Route::get('/permission/{permissionName}', 'PermissionController#check');
Controller:
function check($permissionName) {
if (! Auth::user()->hasPermissionTo($permissionName)) {
abort(403);
}
return response('', 204);
}
Vue: (if you wanna do this synchronously), this is a simple example (Vue global mixin), you can turn this into Vue directive or component.
Vue.mixin("can", (permissionName) => {
let hasAccess;
axios.get(`/permission/${permissionName}`)
.then(()=> {
hasAccess = true;
}
.catch(()=> {
hasAccess = false;
};
return hasAccess;
});
And then everytime you wanna check permission, you can just do
<el-input v-if="can('write-stuff')"> </el-input>
I'm literally working on this exact same thing. I'm thinking of adding a custom Vue directive that would check against the Laravel.permissions array.
It might even be as simple as
Vue.directive('can', function (el, binding) {
return Laravel.permissions.indexOf(binding) !== -1;
})
I haven't tested this code. Just brainstorming here.
<button v-can="editStuff">You can edit this thing</button>
I can hold permissions this way:
window.Laravel = <?php echo json_encode([
'csrfToken' => csrf_token(),
'userId' => Auth::user()->id,
'permissions' => Auth::user()->permissions()->pluck('name')->toArray()
]); ?>
Just stumbled upon this problem and I would like to share what I found and implemented.
Add an accessor on the User model the spatie/laravel-permission is using
public function getAllPermissionsAttribute() {
$permissions = [];
foreach (Permission::all() as $permission) {
if (Auth::user()->can($permission->name)) {
$permissions[] = $permission->name;
}
}
return $permissions;
}
On your global page or layout page pass the permission from the accessor to the javascript.
<script type="text/javascript">
#auth
window.Permissions = {!! json_encode(Auth::user()->allPermissions, true) !!};
#else
window.Permissions = [];
#endauth
</script>
Create a global directive on resources/js/app.js
Vue.directive('can', function (el, binding, vnode) {
if(Permissions.indexOf(binding.value) !== -1){
return vnode.elm.hidden = false;
}else{
return vnode.elm.hidden = true;
}
})
Here you are checking if the permission you supplied on the directive is on the permission array from laravel.
If found then it will hide the element else show, this function is like a v-if.
Use it like this on your component - "add_items" is your permission
<button type="button" v-can="'add_items'"></button>
This solution is from this but instead of a mixin, I use a directive.
Got the idea of directive from #Ismoil Shifoev comment above.
You can use this format in Vuejs for Laravel Permission:
<div v-if="can('edit post')">
<!-- Edit post form -->
</div>
<div v-if="is('super-admin')">
<!-- Show admin tools -->
</div>
add function to User Model to get all user permissions&roles like this:
class User extends Authenticatable
{
// ...
public function jsPermissions()
{
return json_encode([
'roles' => $this->getRoleNames(),
'permissions' => $this->getAllPermissions()->pluck('name'),
]);
}
}
pass this data to JavaScript in HTML header:
<script type="text/javascript">
window.Laravel = {
csrfToken: "{{ csrf_token() }}",
jsPermissions: {!! auth()->check()?auth()->user()->jsPermissions():null !!}
}
</script>
in app.js file add global Vuejs can function to check user permissions and is function to check user roles:
Vue.prototype.can = function(value){
return window.Laravel.jsPermissions.permissions.includes(value);
}
Vue.prototype.is = function(value){
return window.Laravel.jsPermissions.roles.includes(value);
}
https://github.com/ahmedsaoud31/laravel-permission-to-vuejs
I would go with Ralph solution. But I find myself better using. This function to fetch the Permissions.
public function getAllPermissionsAttribute() {
return Auth::user()->getAllPermissions()->pluck('name');
}
Just a bit cleaner, and since I tend to use Roles instead of particular permissions for each User, this solution should work as well.

Compare route name in blade

I am using Laravel 5.2 and Blade templating, currently I am using this code to send the user to their own profile
href="{{ route('profile.index', ['username' => Auth::user()->username]) }}
This code is in an #if statement in blade, I was wondering how I would be able to check to make sure the user is on their own profile before I show them elements they should only be able to see on their own profile?
Just use check similar to this in your controller:
if (Auth::check()) // Checks if user authenticated
{
$userId = Auth::user()->id; // Gets user ID
// Do some stuff
}
return view('profile', compact('profileInfo'));
In this case any user will see only he's own profile.

laravel login link with return back redirection

I create main menu with login link for quest users.
#if(Auth::guest())
<li>Login</li>
#endif
What is the best way to make back redirection after login?
Now I made the next crutch:
// menu.blade.php
#if(Auth::guest())
<?php
if (Route::getCurrentRoute()->getName() != 'auth::login')
Session::put('login_back_url', Request::url());
?>
<li>Login</li>
#endif
// AuthController.php
public function __construct()
{
...
if (Session::has('login_back_url'))
$this->redirectPath = Session::get('login_back_url');
else
$this->redirectPath = route('home');
...
}
You can use:
use Redirect;
Redirect::back();

Resources