Al finalizar este tutorial el estudiante estará en capacidad de incluir el manejo de roles en el front de la aplicación Angular. Esto permite restringir el acceso a servicios de acuerdo con el rol del usuario.
Un servicio para ingresar a la aplicación, indicando el rol. Como consecuencia del rol, la aplicación front desplegará solo las funcionalidades permitidas para ese rol.
En este tutorial desarrollamos una aplicación simple que permite mostrar una lista de clientes y crear un nuevo cliente. Se tienen tres roles: GUEST, ADMIN y CLIENT. Todos los roles pueden ver la lista de clientes pero solo ADMIN puede crear un nuevo cliente. La aplicación ofrece un Login
y un SignUp
donde el usuario puede indicar el rol que va a tener para acceder la aplicación.
La Figura 1 muestra la interacción de un usuario no logueado, es decir GUEST, donde puede ver la opción de Listar Clientes
y el SigUp
y Login
.
Figura 1. Interacción de un usuario no logueado. |
La Figura 2 muestra un usuario logueado como ADMIN y puede ver la opción Add new client
y el Logout
.
Figura 2. Interacción de un usuario no logueado. |
Para realizar este taller Ud. debe haber tener claro:
Para desarrollar el tutorial y manejar los roles, vamos a utilizar un módulo externo que está definido y documentado en: NgxPermissions.
https://github.com/AlexKhymenko/ngx-permissions/wiki
Si bien en este tutorial no hacemos un verdadero módulo de autenticación, necesitamos simularla y, para propósitos de mostrarle al usuario solo los servicios sobre los que tiene permisos, al registrarse o al ingresar al sistema le vamos a pedir el rol.
El diseño de este módulo se muestra en la Figura 3. Podemos ver los elementos del módulo AuthModule
que tiene dos componentes: AuthLoginComponent
y AuthSignUpComponent
, un modelo para el usuario User
(con los datos de su username, password
y rol)
y un servicio AuthService
que define las funciones de login
, signUp
y logout
y otras funciones para configurar los roles. La figura siguiente muestra los elementos del módulo de autenticación y su relación con el módulo de la aplicación principal.
Figura 3. Diseño del módulo. |
Desde una aplicación nueva o desde la aplicación sobre la que quiere crear el módulo de autenticación, cree, utilizando Angular-cli el módulo auth
.
Utilizando el Angular-cli, sobre el módulo auth
creado en el paso anterior, cree un componente llamado auth-login
(authLoginComponent
).
Este componente tiene una forma, como la que se muestra en la Figura 4, donde se captura el usuario, el password y el rol.
Figura 4. Vista del componente. |
Para la forma utilizamos las forma reactivas de Angular, de la librería ReactiveForm
(ver explicación sobre las formas en el tutorial ng-formularios).
El código del template del componente se puede ver en el siguiente listado:
<div class="container-fluid">
<div class="col-4 p-3">
<div class="h4">Login</div>
<form [formGroup]="userForm" (ngSubmit)="!userForm.invalid && login(userForm.value)">
<div class="form-group mx-sm-3 mb-2">
<label for="name">
Name
</label>
<input novalidate id="name" class="form-control" formControlName="name" placeholder="Your username or your email">
<div class="alert alert-danger alert-dismissible fade show"
*ngIf="userForm.get('name').hasError('required') && userForm.get('name').touched">
Name required
</div>
</div>
<div class="form-group mx-sm-3 mb-2">
<label for="password">
Password
</label>
<input id="password" type="password" class="form-control" formControlName="password"
placeholder="Your password">
<div class="alert alert-danger alert-dismissible fade show"
*ngIf="userForm.get('password').hasError('required') && userForm.get('password').touched">
Password required
</div>
</div>
<div class="form-group mx-sm-3 mb-2">
<label for="role">
Role
</label>
<select class="form-control" id="role" rows="3" formControlName="role">
<option [value]="e" *ngFor="let e of roles">{{e}}</option>
</select>
<div class="alert alert-danger alert-dismissible fade show"
*ngIf="userForm.get('role').hasError('required') && userForm.get('role').touched">
Role required
</div>
</div>
<div class="row m-3">
<button type="submit" class="btn btn-primary" [disabled]="!userForm.valid">Create</button>
<button type="button" class="btn btn-danger ml-3" (click)="cancelCreation()">Cancel</button>
</div>
</form>
</div>
</div>
Como se puede ver en el código, cuando el usuario da clic en Login, se invoca la función login(userForm.value)
que está definida en el componente. El parámetro corresponde con la estructura de User
definida (name, password, role
).
La función login
está definida en el servicio del módulo AuthService y la invoca el componente.
Desde el componente se invoca el servicio pasando de parámetro el role del usuario:
...
export class AuthLoginComponent implements OnInit {
userForm: FormGroup;
user: User;
roles: string[];
constructor(
private formBuilder: FormBuilder,
private toastrService: ToastrService,
private authService: AuthService,
) {
}
login(user): void {
this.authService.login(user.role);
this.toastrService.success('Successfully login')
}
ngOnInit() {
this.userForm = this.formBuilder.group({
name: ['', [Validators.required]],
password: ['', [Validators.required]],
role: ['Client', [Validators.required]],
});
this.user = new User();
this.roles = ['Administrator', 'Client'];
}
}
Declaración de la función login
en el servicio AuthService
se ocupa de cambiar el rol del usuario en el localStorage
. Esto lo vemos en los pasos siguientes, primero revisaremos cómo configurar los roles y permisos, luego la implementación del servicio y luego cómo usar esta información de los templates HTML para restringir o no los accesos.
En este tutorial mostramos un uso muy básico de roles y permisos en el front, con el propósito de mostrar al usuario final sólo los servicios sobre los que tiene permiso.
En el ejemplo tenemos tres roles:
Cuando alguien se registra o hace login, en realidad la única información importante que tomamos es su rol.
La configuración de lo que el usuario ve en la aplicación front-end depende únicamente de su rol.
Para definir los permisos sobre los elementos de la interfaz usuario vamos a utilizar el módulo externo NgxPermissions.
Debemos instalar ese paquete en nuestro proyecto:
npm install ngx-permissions --save
La opción `--save` actualiza el `package.json`.
NgxPermissionsModule
En el módulo principal importamos este módulo.
...
import { NgxPermissionsModule} from 'ngx-permissions';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
AuthModule,
ClientModule,
NgxPermissionsModule.forRoot(),
ToastrModule.forRoot({
timeOut: 10000,
positionClass: 'toast-bottom-right',
preventDuplicates: true,
}),
BrowserAnimationsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
En esta clase se define los métodos para inicializar los roles y los permisos. Un permiso es una acción (atómica) que un usuario puede hacer dentro de la aplicación. Por ejemplo, edit_author_permission,
create_editorial_permission
.
Un rol es un conjunto nombrado de permisos. Varios roles pueden compartir un mismo permiso. Los roles son asignados a los usuarios.
La estrategia utilizada en este ejemplo es la siguiente:
En el localStorage se guarda bajo la llave "role" el role del usuario según se haya autenticado. Si no se ha autenticado no se guarda nada.
En el HTML se restringe el despliegue de elementos utilizando `*ngxPermissionsOnly`.
Por ejemplo:
*ngxPermissionsOnly="['ADMIN']"
significa que solo el rol `ADMIN` tiene permisos.
*ngxPermissionsOnly="['ADMIN', 'CLIENT']"
significa que solo rol `ADMIN` y el rol `CLIENT `tienen permisos.
En las rutas también se define el acceso a ellas de acuerdo con el rol. Por ejemplo:
const routes: Routes = [{
path: 'clients',
children: [
{
path: 'list',
component: ClientListComponent
},
{
path: 'add',
component: ClientCreateComponent,
canActivate: [NgxPermissionsGuard],
data: {
permissions: {
only: ['ADMIN']
}
}
}
]
},
{
path: 'auth',
children: [
{
path: 'login',
component: AuthLoginComponent,
canActivate: [NgxPermissionsGuard],
data: {
permissions: {
only: ['GUEST']
}
}
},
{
path: ':sign-up',
component: AuthSignUpComponent,
canActivate: [NgxPermissionsGuard],
data: {
permissions: {
only: ['GUEST']
}
}
}
]
}];
Significa que para acceder a la ruta `clients/add
` se necesita el rol `ADMIN`.