akveo / ngx-admin

Customizable admin dashboard template based on Angular 10+
https://akveo.github.io/ngx-admin/
MIT License
25.24k stars 7.95k forks source link

Feature, Menu disable, hidden #1132

Closed Mr-Sloth closed 5 years ago

Mr-Sloth commented 7 years ago

To make it easier working with the project as starter it would be nice if we have 2 new options in pages.menu.

hidden: true, false

disable: true

I build this ugly by my own, but I´m sure more people would have a use of it.

Pages.menu and pages.routing contain the Pages. To remove one you have to change both files.

m0uj commented 7 years ago

Nice suggestions @darklinki, I have one improvement, if a user is not allowed to access a Menu Entry, this entry should be hidden !

sharath1608 commented 7 years ago

Have a look at #151

Mr-Sloth commented 7 years ago

Ah thank you @sharath1608 . Would be nice to have this as example in the project. Disaple one entry and set the style different is also working.

sharath1608 commented 7 years ago

@darklinki Agree, not sure if it's part of the doc . But just to illustrate it, you can hide a menu item like this

{
        path: 'metrics/:id',
        data: {
          menu: {
            title: 'Metrics',
            icon: 'ion-stats-bars',
            hidden:true,
            selected: false,
            expanded: false,
            order: 200,
          }
        }
      },
m0uj commented 7 years ago

I am implementing user role management, to hide menu entries for unauthorized users. I will share my contributions as soon as I am done.

Bengejd commented 7 years ago

@m0uj any update on the role management?

supun19 commented 7 years ago

I also need role base manu hidden machanism. @m0uj have you any update

Mr-Sloth commented 7 years ago

If he doesn´t replys until next week, I will build a simple one cause I would need it anyway in a few weeks. You guys only need a simple role management or also a login/register system ?

crossRT commented 7 years ago

I have the same requirement for the hiding the menu based on user role. some of the menus are only available for super admin. I did protect the route, but I need some mechanism to hide the menu as well. @darklinki do you have any updates?

Newan commented 7 years ago

it is possible to implementate it easy.

First solution, in pages.component ngOnInit() before you send the array to the service you can manipulate it with your user permissions

Second solution, in the BaMenuService, there are a lot of functions to manipulate the menu like selectMenuItem. easy implement one funktion to skip items

Last solution, implement a function in the itemComponent to hide himself if the user have no permissions

Mr-Sloth commented 7 years ago

@crossRT I have a working solution, kinda like newmans solution.

I think I read something about this feature in the ngx branch.

crossRT commented 7 years ago

this is how I achieve it. in baMenuService.ts line 111, I change prepared.hidden to true when logged in user doesn't have the role.

// hide menu when user doesn't have the role and permissions.
if (prepared.roles && prepared.roles.length > 0) {
  const role = this.authService.getUser().role;
  if (prepared.roles.indexOf(role) === -1) {
    prepared.hidden = true;
  }
}

and here's the menu:

{
  path: 'user',
  data: {
    menu: {
      title: 'kuber.user',
      icon: 'ion-android-person-add',
      selected: false,
      expanded: false,
      order: 0,
      roles: ['super_admin']
    }
  }
},
vishnuhminds8 commented 6 years ago

@crossRT, Its working like a charm..thanks

lukele1607 commented 6 years ago

disable: true

Menu points should be written in grey. This would make it possible to show user permisions already in the menu. E.g user X is not allowed to acces dashboard so its written in grey and he can´t click on it.

....Please implement this feature

umairm638 commented 6 years ago

Hi all, I've tried your solutions with some changes according to my project. It's working but with one major issue in it I'm facing is that I've to refresh browser for menu to hide or show according to user role. My pages-menu.ts file is:

import { NbMenuItem } from '@nebular/theme';
import * as _ from 'lodash';
import decode from 'jwt-decode';

const roles = [1, 2, 3, 4, 5, 6, 7];
const token = localStorage.getItem('auth_app_token');
// decode the token to get its payload
let tokenPayload: any = [];
if (token) {
  tokenPayload = decode(token);
}

export const MENU_ITEMS: NbMenuItem[] = [
  {
    title: 'Utahrunning',
    link: '#',
    children: [
      {
        title: 'Ask An Expert Question',
        icon: 'nb-compose',
        link: '/admin/askquestion',
        hidden: !findRole([1, 2, 7], Number(tokenPayload.userRole)),
      },
  {
    title: 'Dashboard',
    icon: 'nb-home',
    link: '/admin/dashboard',
    hidden: !findRole([1, 2], Number(tokenPayload.userRole)),
    home: true,
  },
  {
    title: 'Settings',
    icon: 'ion-wrench',
    link: '/admin/settings',
    hidden: !findRole([1], Number(tokenPayload.userRole)),
    children: [
      {
        title: 'Search',
        link: '/admin/settings/search',
      },
      {
        title: 'Notification',
        link: '/admin/settings/notification',
      },
    ]
  },
]
},
];

function findRole(allowedRoles, userRole) {
  if (Number.isNaN(userRole)) {
    return false;
  }
  console.log('testing -- ' + userRole);
  return (_.find(allowedRoles, function (
    item: any
  ) {
    return item == userRole;
  })) ? true : false;
} 

After login findRole didn't run and I've to refresh browser then correct menu loads up. If I logout and login from different user having different role then it shows menu appeared for previously logged in user and again I've to refresh. How can I handle this or if I'm missing anything? Thanks!

rovidiu commented 5 years ago

Hi all, I've tried your solutions with some changes according to my project. It's working but with one major issue in it I'm facing is that I've to refresh browser for menu to hide or show according to user role. My pages-menu.ts file is:

import { NbMenuItem } from '@nebular/theme';
import * as _ from 'lodash';
import decode from 'jwt-decode';

const roles = [1, 2, 3, 4, 5, 6, 7];
const token = localStorage.getItem('auth_app_token');
// decode the token to get its payload
let tokenPayload: any = [];
if (token) {
  tokenPayload = decode(token);
}

export const MENU_ITEMS: NbMenuItem[] = [
  {
    title: 'Utahrunning',
    link: '#',
    children: [
      {
        title: 'Ask An Expert Question',
        icon: 'nb-compose',
        link: '/admin/askquestion',
        hidden: !findRole([1, 2, 7], Number(tokenPayload.userRole)),
      },
  {
    title: 'Dashboard',
    icon: 'nb-home',
    link: '/admin/dashboard',
    hidden: !findRole([1, 2], Number(tokenPayload.userRole)),
    home: true,
  },
  {
    title: 'Settings',
    icon: 'ion-wrench',
    link: '/admin/settings',
    hidden: !findRole([1], Number(tokenPayload.userRole)),
    children: [
      {
        title: 'Search',
        link: '/admin/settings/search',
      },
      {
        title: 'Notification',
        link: '/admin/settings/notification',
      },
    ]
  },
]
},
];

function findRole(allowedRoles, userRole) {
  if (Number.isNaN(userRole)) {
    return false;
  }
  console.log('testing -- ' + userRole);
  return (_.find(allowedRoles, function (
    item: any
  ) {
    return item == userRole;
  })) ? true : false;
} 

After login findRole didn't run and I've to refresh browser then correct menu loads up. If I logout and login from different user having different role then it shows menu appeared for previously logged in user and again I've to refresh. How can I handle this or if I'm missing anything? Thanks!

Did you managed to find the issue here - regarding the cached menu on logout and login with another role?

Thanks, Ovi

Prefix1802 commented 5 years ago

Hi @rovidiu did you have a look at https://akveo.github.io/nebular/docs/security/acl-configuration--usage

Because when i look at your code i think you have the wrong approach.

hosseinGanjyar commented 5 years ago

Hi @rovidiu did you have a look at https://akveo.github.io/nebular/docs/security/acl-configuration--usage

Because when i look at your code i think you have the wrong approach.

Hello, I wrote that document, but *How to use _nbIsGranted_ directive for menu**? Same @umairm638 issue, I problem with showing menu during switch user? Can you show a sample/code to us?

anarbek commented 5 years ago

I solved this by creating a menu service:

`import { Injectable } from '@angular/core'; import { NbMenuItem } from '@nebular/theme'; import { NbAccessChecker } from '@nebular/security';

@Injectable({ providedIn: 'root' }) export class MenuService { constructor(private accessChecker: NbAccessChecker) { } findMenuItem(dataToFind:string, menuItems: NbMenuItem[]):NbMenuItem { return menuItems.find(t=>t.data == dataToFind); }

getSubMenuItem = function (data:string, subMenuItems:NbMenuItem[]) { if (subMenuItems) { for (var i = 0; i < subMenuItems.length; i++) { if (subMenuItems[i].data == data) { return subMenuItems[i]; } var found = this.getSubMenuItem(data, subMenuItems[i].children); if (found) return found; } } };

setMenuItemVisibility(dataToFind:string, permission:string, resource:string, menuItems: NbMenuItem[]){ var menuItem = this.getSubMenuItem(dataToFind, menuItems); console.log('menu searched:' + dataToFind); console.log('menu found:' + menuItem); if(menuItem == null)return; console.log('setting auth for menu:' + menuItem.data); this.accessChecker.isGranted(permission, resource).subscribe(res=>{ menuItem.hidden = !res }); }

} `

and used it within pages.component.ts : `import { Component } from '@angular/core';

import { MENU_ITEMS } from './pages-menu'; import {MenuService} from '../services/menu.service' import { NbAccessChecker } from '@nebular/security';

@Component({ selector: 'ngx-pages', template: `

, }) export class PagesComponent { menu = MENU_ITEMS; constructor( private accessChecker: NbAccessChecker, private menuService:MenuService){ console.log('menu items auth'); menuService.setMenuItemVisibility('createuser', 'create', 'users', this.menu); } } you should define 'data' property for your menu items like : { icon: 'nb-plus', title: 'Create User', link: '/pages/users/create-user', data: 'createuser' },

sriharikrishna33 commented 5 years ago

i am facing issue with hiding menu,in our project they are five roles,i need hide based on user role,can any one help me with that

ghost commented 4 years ago

Same here, I have to refresh the browser in order to load the correct menu for current user logged.

initMenu() {
    let userRole: string;
    this.roleProvider.getRole().subscribe(role => userRole = role);
    this.pagesMenu
      .getMenu(userRole)
      .pipe(takeWhile(() => this.alive))
      .subscribe(menu => {
        this.menu = menu;
      });
  }

userRole does not change its value when you log in with different user (and different role) unless you refresh the browser.

Prefix1802 commented 4 years ago

274

Prefix1802 commented 4 years ago

I am using custom authentication. If you want to use NbAccessChecker instead of custom authentication check out the commented code.

I have this in my pages.component.ts

import { Component } from '@angular/core'; import { MENU_ITEMS } from './pages-menu'; import { AuthService } from '../core/auth/services/auth.service'; import { NbAccessChecker } from '@nebular/security'; import { NbMenuItem } from '@nebular/theme';

@Component({ selector: 'ngx-pages', styleUrls: ['pages.component.scss'], template: `

`, }) export class PagesComponent {

menu = MENU_ITEMS;

constructor(private accessChecker: NbAccessChecker, public auth: AuthService) {

}

ngOnInit() { this.authMenuItems(); }

authMenuItems() { this.menu.forEach(item => { this.authMenuItem(item); }); }

authMenuItem(menuItem: NbMenuItem) { if (menuItem.data && menuItem.data['expectedRoles']) { menuItem.hidden = !this.auth.isAuthorized(menuItem.data['expectedRoles']);

} else {
  menuItem.hidden = true;
}
if (!menuItem.hidden && menuItem.children != null) {
  menuItem.children.forEach(item => {
    if (item.data && item.data['expectedRoles']) {
      item.hidden = !this.auth.isAuthorized(menuItem.data['expectedRoles']);

    } else {
      // if child item do not config any `data.permission` and `data.resource` just inherit parent item's config
      item.hidden = menuItem.hidden;
    }
  });
}

}

// authMenuItem(menuItem: NbMenuItem) { // if (menuItem.data && menuItem.data['permission'] && menuItem.data['resource']) { // this.accessChecker.isGranted(menuItem.data['permission'], menuItem.data['resource']).subscribe(granted => { // menuItem.hidden = !granted; // }); // } else { // menuItem.hidden = true; // } // if (!menuItem.hidden && menuItem.children != null) { // menuItem.children.forEach(item => { // if (item.data && item.data['permission'] && item.data['resource']) { // this.accessChecker.isGranted(item.data['permission'], item.data['resource']).subscribe(granted => { // item.hidden = !granted; // }); // } else { // // if child item do not config any data.permission and data.resource just inherit parent item's config // item.hidden = menuItem.hidden; // } // }); // } // }

}

ghost commented 4 years ago

Thanks!

I was able to achieve it by getting the user role with a custom service rather than getRole provided by RoleProvider.

getCurrentUser(): Observable<User> {
    return this.authService.isAuthenticated().pipe(
      switchMap(authenticated => {
        return authenticated ? this.api.getCurrent() : of(null);
      }),
    );
  }
initMenu() {
    this.usersService.getCurrentUser().subscribe(user => {
      this.pagesMenu
        .getMenu(user.role)
        .pipe(takeWhile(() => this.alive))
        .subscribe(menu => {
          this.menu = menu;
        });
    });
  }
raoulsigne commented 4 years ago

@sharath1608 you saved my day I was able to dynamically add this HIDDEN attribute on every menu item and their children

Thanks