Implement ROLE based access in Angular2 - javascript

I have an angular2 application and i have implemented the Registration and Login Modules. User role and other details are received when login in. Have no idea on how to properly manage access, based on the role of the user.
At the moment i'm hoping to use a Angular2 service to share the user role and other details through out the application and use "if" conditions to manage access, based on the Role.
Please provide me any information on how to properly do this.

I would approach this by building out an object to read from when the user is successfully logged in.
// when user logs in build out permissions object
permissions = {
dashboardOne: true,
dashboardTwo: true
}
then within your auth service, have a function that returns a boolean based on the user's permissions
userHasAccess = (() =>{
return {
toDashboardOne: () => {
return this.permissions.hasOwnProperty('dashboardOne');
},
toDashboardTwo: () => {
return this.permissions.hasOwnProperty('dashboardTwo');
}
}
})();
now throughout the app you can call the above function
if(this._authService.userHasAccess.toDashboardOne()){
// do something
}
I hope this helps get you started. cheers

You can try to use ngx-permissions library for controlling of permissions and roles in your angular application. The benefits it will remove elements from DOM, lazy loading and isolated modules supported(then, else syntax is supported).
Example of loading roles
import { Component, OnInit } from '#angular/core';
import { NgxPermissionsService } from 'ngx-permissions';
import { HttpClient } from '#angular/common/http';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
title = 'app';
constructor(private permissionsService: NgxPermissionsService,
private http: HttpClient) {}
ngOnInit(): void {
NgxRolesService
.addRole('ROLE_NAME', ['permissionNameA', 'permissionNameB'])
NgxRolesService.addRole('Guest', () => {
return this.sessionService.checkSession().toPromise();
});
NgxRolesService.addRole('Guest', () => {
return true;
});
}
}
Usage in templates
<ng-template [ngxPermissionsOnly]="['ADMIN']" (permissionsAuthorized)="yourCustomAuthorizedFunction()" (permissionsUnauthorized)="yourCustomAuthorizedFunction()">
<div>You can see this text congrats</div>
</ng-template>
<div *ngxPermissionsOnly="['ADMIN', 'GUEST']">
<div>You can see this text congrats</div>
</div>
<div *ngxPermissionsExcept="['ADMIN', 'JOHNY']">
<div>All will see it except admin and Johny</div>
</div>
for more information see wiki page

Related

Is any standard way to role based access file in angular?

I have developed 4 roles access projects in angular. The dashboard have different content pages. Whenever logged in the portal intially called dashboard page.
This dashboard content will shows based on logged user role. I have used ngSwitch. Anyone knows a different way implementation instead of using ngSwitch. Kindly share your answer. It's working but I want different solution
I have explained what i did,
defined 4 role
SuperAdmin, Admin, AdminUser, User
I have created 4 component files. follow this code component.ts file
export class DashboardComponent implements OnInit {
userRole: string;
constructor(private authService: AuthService) {
this.userRole = this.authService.userRole();
}
html file:
<div [ngSwitch]="userRole">
<app-header-component title="Dashboard" *ngSwitchCase="'SuperAdmin'">
</app-header-component>
<app-system-integrator-dashboard *ngSwitchCase="'Admin'">
</app-system-integrator-dashboard>
<app-organization-admin-dashboard *ngSwitchCase="'AdminUser'">
</app-organization-admin-dashboard>
<app-organization-user-dashboard *ngSwitchCase="'User'">
</app-organization-user-dashboard>
</div>
create directive like
/* Usage : *roleIsOneOf="[userType.ADMIN, userType.ANALYST, userType.SUPER_ANALYST]" */
#Directive({
selector: '[roleIsOneOf]',
})
export class RoleIsOneOfDirective {
constructor(private authService: AuthService,
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef) {
}
#Input() set roleIsOneOf(allowedRoles: Role[]) {
const userRole: Role = this.authService.userRole();
if (allowedRoles.includes(userRole)) {
this.viewContainer.createEmbeddedView(this.templateRef);
} else {
this.viewContainer.clear();
}
}
}

How do you query and display the authenticated user's order from the Firebase Realtime Database using AngularFire in an Angular app?

This is the function I am using to insert orders into the database, which works fine.
async createPackage(){
const itemsRef = this.afDatabase.database.ref(`delivery orders/${this.uid}`);
const userId =
itemsRef.push({packageName: this.packageName, packageSize: this.packageSize, packageDescription: this.packageDescription, packageFrom: this.packageFrom, packageTo: this.packageTo, deliveryDate: this.deliveryDate, receiverNumber: this.receiverNumber, paymentOption: this.paymentOption, UID: this.uid})
this.packageName = '';
this.packageDescription = '';
this.packageFrom = '';
this.packageTo = '';
this.deliveryDate = '';
this.paymentOption = '';
this.receiverNumber = '';
this.packageSize = '';
this.showAlert('Your package delivery order has been successfully taken.', 'You will be contacted by one of our Administrators soon.');
}
here is a screenshot of how it's been structured in the database, the parent node is the user uid for each user, while the child node is the id for each order by each user.
the problem I am having is how to query and display each user's order separately on their dashboard differently on the front end.
For context, the complete files for this answer can be found here: https://gist.github.com/nclarx/ef581b0e1a95a2d43531411fe91a9814
To Query the User's Orders
To query the user's data you need to use a function similar to the following:
getCurrentOrder(): Observable<Order[] | never> {
return this.afAuth.authState // authState is an observable
.pipe( // use pipe
switchMap((user) => { // switchMap gets authState and then lets you return a different observable
// The following returns an observable call to the real-time database:
return user ? this.afDatabase.list<Order>(`delivery-orders/${user.uid}`).valueChanges() // if the user is authenticated an observable with the Orders is returned
: EMPTY; // if the user is not authenticated an empty observable is returned
})
);
}
This isn't an ideal way to do authentication in a larger application. I suggest you look at this video on Authentication with AngularFire https://www.youtube.com/watch?v=qP5zw7fjQgo and https://fireship.io/lessons/angularfire-google-oauth/ to create an AuthService that can be used across your application.
Displaying the Orders
To display that information in an Angular component using Observables from AngularFire I suggest the following structure/pattern:
OrderService - contains methods for accessing the database (generate the service with the Angular CLI)
AppComponent - has OrderService injected and calls getCurrentUserOrder() when the component initialises ngOnInit()
Template: app.component.html - the template which uses the async pipe in a *ngFor directive to subscribe/unsubscribe to the observable automatically.
Handing an observable to the template and using the async pipe to subscribe to it is good practice because it means that you do not need to subscribe and unsubscribe from an observable manually.
The files can be found in their entirety here: https://gist.github.com/nclarx/ef581b0e1a95a2d43531411fe91a9814
See the comments in the code for important points about how this works.
The Service: OrderService.ts
import {Injectable} from '#angular/core';
import {EMPTY, Observable} from 'rxjs';
import {AngularFireAuth} from '#angular/fire/auth';
import {switchMap} from 'rxjs/operators';
import {AngularFireDatabase} from '#angular/fire/database';
export interface Order {
// write interfaces for all of your objects and use them
// when defining variables and function return types.
packageName: string;
packageSize: number;
packageDescription: string;
packageFrom: string;
packageTo: string;
deliveryDate: Date;
receiverNumber: number;
paymentOption: string;
UID: string;
}
export class OrderService {
constructor(private afAuth: AngularFireAuth, private afDatabase: AngularFireDatabase) {
}
getCurrentOrder(): Observable<Order[] | never> { // note the use of the interface: Order[], which means returning an array of Orders
return this.afAuth.authState // authState is an observable
.pipe( // use pipe
switchMap((user) => { // switchMap gets authState and then lets you return a different observable
// The following returns an observable call to the real-time database:
return user ? this.afDatabase.list<Order>(`delivery-orders/${user.uid}`).valueChanges() // if the user is authenticated an observable with the Orders is returned
: EMPTY; // if the user is not authenticated an empty observable is returned
// NOTE: this observable is not called until it is subscribed to in the template using the `async pipe`, see
// `app.component.html` where it has `*ngFor="let order of orders$ | async"` <== this is what kicks off the request to the database
})
);
}
}
The Component: app.component.ts
import {Component, OnInit} from '#angular/core';
import {Order, OrderService} from './order.service';
import {Observable} from 'rxjs';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
title = 'ng-fire-so-qtn';
orders$: Observable<Order[]>; // property to hold the observable which will have your array of Orders
constructor(public orderService: OrderService) { // inject the OrderService into the component
}
ngOnInit() {
this.orders$ = this.orderService.getCurrentOrder();
// When this component is initialised it will set the `orders$` property to the `Observable<Order[]>` so it is available in the template
}
}
The Template: app.component.html
<section>
<ul *ngIf="orders$ | async"> <!-- The *ngIf will hide the whole list until the data has arrived-->
<li *ngFor="let order of orders$ | async"> <!-- The *ngFor will loop over and create list items for the orders once the data has arrived-->
{{order.packageName}}: {{order.packageDescription}}
</li>
</ul>
</section>
Thanks everyone, i finally was able to query each user's submission from the firebase realtime database with angular with this few lines of code.
getCurrentUserOrder() {
return this.afAuth.authState.subscribe(user => {
if(user) {
this.userId = user.uid;
console.log(user.uid)
console.log(user.email)
}
this.afDatabase.list(`delivery orders/${this.userId}`).valueChanges().subscribe(
data => {
console.log(data);
this.orders = data;
}
);
});
after importing the AngularFireDatabase and the AngularFireAuth inside the component.On the frontend,
<section *ngFor="let order of orders">
<ion-card>
<ion-item>
<ion-icon name="cube" slot="end"></ion-icon>
<ion-label>{{order.packageName}}</ion-label>
</ion-item>
<ion-card-content>
</ion-card-content>
</ion-card>
</section>
this solves the entire problems completely.

Display ContentFul RichText in Angular

I have a blog feed in my Angular App connected with Contentful. Thanks to the Contentful javascript sdk.
https://www.contentful.com/developers/docs/javascript/tutorials/using-contentful-in-an-angular-project/
I'm trying to display the Title and the Text field. Here is my code:
import { Component, OnInit } from '#angular/core';
import {Observable} from 'rxjs';
import {ContentfulService} from '../../services/contentful/contentful.service';
import { Entry } from 'contentful';
#Component({
selector: 'app-blog',
templateUrl: './blog.component.html',
styleUrls: ['./blog.component.scss']
})
export class BlogComponent implements OnInit {
private posts: Entry<any>[] = [];
constructor(private postService: ContentfulService) {}
ngOnInit() {
this.postService.getPosts()
.then(posts => {
this.posts = posts;
console.log(this.posts);
});
}
}
And the html:
<div *ngFor="let post of posts">
{{ post.fields.title }}
<div>{{ post.fields.text }}</div>
</div>
The title field is displayed well because it is just a string field but the text field is RichText and display [object Object].
Indeed it contain several object. It seems like the Object is divided in several pieces.
https://www.contentful.com/developers/docs/concepts/rich-text/
Does somebody have already display Contentful RichText in an Angular App ?
Is there a specific way to do it?
First, you must install rich-text-html-renderer from your terminal:
npm install #contentful/rich-text-html-renderer
Then, you can import it from your Component:
import { documentToHtmlString } from '#contentful/rich-text-html-renderer';
and use it, simply like that:
_returnHtmlFromRichText(richText) {
if (richText === undefined || richText === null || richText.nodeType !== 'document') {
return '<p>Error</p>';
}
return documentToHtmlString(richText);
}
Finally, 'call the function' from your html like so:
<div [innerHtml]="_returnHtmlFromRichText(post.fields.text)">
</div>
You can also add some options to customise your rich text, more information here. Also, you should code a function similar to _returnHtmlFromRichText in your Contentful service to be able to reuse it later.
I created an Angular library that can render rich text using Angular components: https://github.com/kgajera/ngx-contentful-rich-text
Why use this over #contentful/rich-text-html-renderer? If you need to customize the default mark-up, it allows you to use your Angular components which you can't do using the documentToHtmlString function and [innerHTML].

Updating Angular DOM immediately after editing data and retrieving from the database

Am working on a Single page Application built using Angular 8 on the frontend and Laravel on the backend. It is a CRUD application, on the delete functionality, it is working well by deleting the user of the specific id on the database. After the user of the specific id is deleted, am fetching all the products from the database but I want to update the data on the U.I afresh with the new data (excluding the deleted resource).
Kindly assist?
Show.component.ts file
import { Component, OnInit , ViewChild, ElementRef} from '#angular/core';
import { SharedService } from 'src/app/Services/shared.service';
import { AuthService } from 'src/app/Services/auth.service';
import { Router } from '#angular/router';
import { Observable } from 'rxjs';
import { SnotifyService } from 'ng-snotify';
#Component({
selector: 'app-show',
templateUrl: './show.component.html',
styleUrls: ['./show.component.css']
})
export class ShowComponent implements OnInit {
public userData : any[];
public error = null;
constructor(
private Shared : SharedService,
private Auth:AuthService,
private router: Router,
private Notify:SnotifyService
) { }
//Update the data when the DOM loads
ngOnInit() {
this.Shared.checkAll$.subscribe(message => this.userData = message);
}
//Method called when the delete button is triggered from the html
//Inside it we submit the data to the backend via a service and get
//the response
deleteUser(id:number){
return this.Auth.delete(id).subscribe(
data => this.handleDeleteResponse(data),
error => this.handleDeleteError(error)
);
}
//data below contains data from the backend after successful deletion
handleDeleteResponse(data:any){
this.Notify.success(`Successfully Deleted in our records`, {timeout:4000});
}
handleDeleteError(error:any){
console.log(error);
}
}
In you’re handleDeleteResponse method, there is a data if the data is the userData this.userData = data or it’s simple delete the user id from the array in you’re Js in the subscription of your delete method.
Like:
this.userData = this.userData.filter(user => user.id !== idToDelete )
Method 1:
Define a Subject in your service and subscribe to that subject in the service to receive the data. In the component, change the lifecycle hook to 'onChanges'. As soon as the data in the Subject is received/updated (with the deleted records) ngChanges shall reflect it in the DOM.
Method 2:
Track the records on the front-end in the form of list and when the service gives the response of delete as success then delete that very record in the list using ID or any other unique identifier. In this case you need not to populate all the records again.
export class MyComponent implements OnInit, OnChanges {
ngOnChanges() {
// code here
}
ngOnInit() {
// code here
}
}

Global function available through all app Angular2

I have a PermissionService, which provide user roles. At the server-side data will not be uploaded if the user is not corresponds on role. The back-end is written with asp.net web api, which will use attributes to secure data. On upload page will be static upload user roles, the idea is just to show or hide elements on page which depending from user role.
The PermissionsService check avaiable role in its array. There are methods like isSeller(), isManager(). And all what i want is to provide accessibility from each view. For now i have this implementation.
permission.service
import { Injectable } from "#angular/core";
export enum Roles {
Admin,
Manager,
Moderator,
Seller
}
interface IPermissionDictionary {
[key: string]: boolean;
}
#Injectable()
export class PermissionService {
private permissions: IPermissionDictionary = {};
public constructor() {
this.emitPermissions();
}
private emitPermissions(): void {
let selector = document.querySelectorAll("#roles > span");
let availableRoles = Array.from(selector).map(element => element.textContent);
for (let role in Roles) {
if (!/^\d+$/.test(role)) { // for strings types in Roles
this.permissions[role] = availableRoles.indexOf(role) > -1;
}
}
}
public isInRole(role: string): boolean {
return this.permissions[role];
}
public isAdmin() {
return this.isInRole(Roles[Roles.Admin]);
}
public isSeller() {
return this.isInRole(Roles[Roles.Seller]);
}
public isManager() {
return this.isInRole(Roles[Roles.Manager]);
}
public isModerator() {
return this.isInRole(Roles[Roles.Moderator]);
}
}
app.component
import { Component } from "#angular/core";
import { ROUTER_DIRECTIVES } from "#angular/router";
import { PermissionService } from "./share/permission.service";
import { HomeComponent } from "./home/home.component";
import { OrderComponent } from "./order/order.component";
#Component({
selector: "admin-panel",
templateUrl: "../app/app.template.html",
directives: [ROUTER_DIRECTIVES],
precompile: [HomeComponent, OrderComponent]
})
export class AppComponent {
constructor(private permissionService: PermissionService) {
}
}
main.ts
import { bootstrap } from "#angular/platform-browser-dynamic";
import { AppComponent } from "./app.component";
import { APP_ROUTES_PROVIDER } from "./app.routes";
import { HTTP_PROVIDERS } from '#angular/http';
import { PermissionService } from "./share/permission.service";
bootstrap(AppComponent, [APP_ROUTES_PROVIDER, HTTP_PROVIDERS, PermissionService]);
For now to access the method of PermissionService need to inject it in component constructor. And in template is is use like
<div *ngIf="permissionService.isAdmin()">will show if you are admin</div>
But every time to inject my service in each component where i want to use it seems for me strange. And i just want to get access it from every part of my app like:
<div *ngIf="isAdmin()">will show if you are admin</div>
I think the person who asked this question has another version of Angular2 (perhaps a pre-release?), but in the latest version if you need to export a service for all the app you do it in the following way.
First, in your main.ts you must have a class that you bootstrap, like this:
platformBrowserDynamic().bootstrapModule(AppModule);
In this class "AppModule" (or whatever it is in your case), you should be able to add a global service provider in this way:
...
import {GlobalService} from './global-service.service'
#NgModule({
...
providers: [MyGlobalService],
...
})
export class AppModule{ ...}
In this way MyGlobalService is available for all other components.
Hopefully this will be useful to someone :).
Some option could be to create top level super class with the permission methods and then just subclass in view .ts. Not sure if this suits you as you still need to import super class into your components and extend it. It can also violate the "is-a".

Categories