I'm submitting a ... (check one with "x")
I could not find any documentation to integrate the menu with ACL.
How to accomplish hiding certain elements in the menu list based on permissions
Hi @amanmamgain, you need to filter menu items before passing them into the menu component.
What I ended up doing this like nnixaa mentiones:
export class NbMenuItemWithPermissions extends NbMenuItem {
permissionRequired?: string;
featureFlag?: string
children?: NbMenuItemWithPermissions[];
}
export const MENU_ITEMS: NbMenuItemWithPermissions[] = [
{
title: 'Dashboard',
icon: 'nb-home',
link: '/pages/dashboard',
home: true,
},
{
title: 'My Pages',
icon: 'nb-compose',
link: '/pages/my-pages',
permissionRequired: 'VIEW_PAGES',
featureFlag: 'MyFlag'
}
]
Then in my Pages component, I just filter those out based on an API call to get the user's info, and what feature flags I have enabled, ex.
constructor(quorumService: QuorumService, menuService: NbMenuService) {
// Filter out menu items based on Togglz
service.user().subscribe((userData) => {
this.userData = userData;
let filteredItems = MENU_ITEMS.filter((item) => {
if(item.children) {
item.children = item.children.filter((childItem) => this.itemAllowed(childItem));
}
return this.itemAllowed(item);
});
menuService.addItems(filteredItems);
});
}
itemAllowed(menuItem) {
if(menuItem.featureFlag && !this.flags[menuItem.featureFlag]) {
return false;
}
if(menuItem.permissionRequired && !(this.userData.userType === 'ADMIN' || this.userData.permissions.includes(menuItem.permissionRequired))){
return false;
}
return true;
}
Is there a way to filter the Menu based on NbSecurity NbAccessChecker.isGranted() Method?
It could be iterate items of Menu
@Component({
selector: 'ngx-pages',
template: `
<ngx-sample-layout>
<nb-menu [items]="menu"></nb-menu>
<router-outlet></router-outlet>
</ngx-sample-layout>
`,
})
export class PagesComponent implements OnInit {
menu = MENU_ITEMS;
constructor(private accessChecker: NbAccessChecker) {
}
ngOnInit() {
this.authMenuItems();
}
authMenuItems() {
this.menu.forEach(item => {
this.authMenuItem(item);
});
}
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;
}
});
}
}
}
and MENU_ITEMS carry data object with permission and resource.
export const MENU_ITEMS: NbMenuItem[] = [
...
{
title: 'UI Features',
icon: 'nb-keypad',
link: '/pages/ui-features',
data: {
permission: 'view',
resource: 'ui-features'
},
}
...
];
Reference: https://akveo.github.io/nebular/docs/security/acl-configuration--usage#usage
I have the same problem.
I followed @pickonefish solution, but it doesn't work.
My configuration:
app.module:
NbSecurityModule.forRoot({
accessControl: {
user: {
view: ['customer-care', 'sent']
},
admin: {
view: ['*']
}
}
})
pages.menu:
import { NbMenuItem } from '@nebular/theme';
export const MENU_ITEMS: NbMenuItem[] = [
{
title: 'Inbox',
icon: 'download-outline',
link: '/pages/customer-care',
home: true,
data: {
permission: 'view',
resource: 'customer-care'
}
},
{
title: 'Sent',
icon: 'upload-outline',
link: '/pages/customer-care/sent',
data: {
permission: 'view',
resource: 'sent'
}
},
{
title: 'Mail Template',
icon: 'file-text-outline',
link: '/pages/mail-templates',
data: {
permission: 'view',
resource: 'mail-templates'
}
}]
pages.component is the same that @pickonefish
Where is the wrong part?
Thanks
Massimo
@viandanteoscuro Try to delete array in view if you set all permissions for admin ['*'] => '*'
NbSecurityModule.forRoot({
accessControl: {
user: {
view: ['customer-care', 'sent']
},
admin: {
view: '*'
}
}
})
@pickonefish thank you for saving my time :)
Hi all.
I'm trying to implement @brgaulin solution that for my purpose is suitable.
In my pages.module.ts providers section is like this:
providers: [NbAuthService,
{ provide: NbRoleProvider, useClass: RoleProvider },
PagesMenu]
My pages.component.ts has constructor defined like this:
constructor( private pagesMenu: PagesMenu,
private tokenService: NbTokenService,
protected initUserService: InitUserService, private roleService:RoleProvider
)
but when page is loaded i get :
"core.js:6228 ERROR Error: Uncaught (in promise): NullInjectorError: R3InjectorError(PagesModule)[RoleProvider -> RoleProvider -> RoleProvider -> RoleProvider]:
NullInjectorError: No provider for RoleProvider!"
for completeness my role.provider.ts is like that:
`import { NbAuthService, NbAuthOAuth2JWTToken } from '@nebular/auth';
import { NbRoleProvider } from '@nebular/security';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { Injectable } from '@angular/core';
@Injectable()
export class RoleProvider extends NbRoleProvider {
constructor(private authService: NbAuthService) {
super();
}
getLowerCaseRoles(roles: any): string | string[] {
if (Array.isArray(roles)) {
roles = roles.map(element => {
return element.toLowerCase();
});
} else {
roles = roles.toLowerCase();
}
return roles;
}
getRole(): Observable
.pipe(
map((token: NbAuthOAuth2JWTToken) => {
const payload = token.getAccessTokenPayload();
return !!(token.isValid() && payload && payload['role']) ? this.getLowerCaseRoles(payload['role']) : 'guest';
}),
);
}
}
`
Any idea on what I'm missing?
Thanks for help
Not sure. But the code I posted was for nebular 2 or 3. If you are on latest there might be some changes.
Most helpful comment
It could be iterate items of Menu
and
MENU_ITEMScarrydataobject withpermissionandresource.Reference: https://akveo.github.io/nebular/docs/security/acl-configuration--usage#usage