To Introduction

Microservice Facade

Step 4. Authorization

Once we’ve established who our user is, we need to implement some way of controlling what operations our user can perform, based on the rights he/she has been assigned. In this tutorial, we will be taking a look at how to install access limitations that are based on user roles. The roles themselves are stored in the Roles microservice and are loaded into a UserSession by the loadSession interceptor we discussed in the previous step.

Our Authorizer class was made to provide flexible access management. We will be using this class to limit access to certain operations in our facade’s RESTful services. This class’s implementation can be found in the Authorize.ts file, located in the folder services/version1. Its code is as follows:

/src/services/version1/Authorize.ts

let _ = require('lodash');

import { UnauthorizedException } from 'pip-services3-commons-node';
import { HttpResponseSender } from 'pip-services3-rpc-node';

import { BasicAuthManager } from 'pip-services3-rpc-node';
import { RoleAuthManager } from 'pip-services3-rpc-node';

export class AuthorizerV1 {
    private basicAuth = new BasicAuthManager();
    private roleAuth = new RoleAuthManager();

    // Anybody who entered the system
    public anybody(): (req: any, res: any, next: () => void) => void {
        return this.basicAuth.anybody();
    }

    // Only registered and authenticated users
    public signed(): (req: any, res: any, next: () => void) => void {
        return this.basicAuth.signed();
    }

    // System administrator
    public admin(): (req: any, res: any, next: () => void) => void {
         return this.roleAuth.userInRole('admin');
    }

    // Only the user session owner
    public owner(idParam: string = 'user_id'): (req: any, res: any, next: () => void) => void {
        return (req, res, next) => {
            let user = req.user;
            let partyId = req.params[idParam] || req.param(idParam);
            if (user == null) {
                HttpResponseSender.sendError(
                    req, res,
                    new UnauthorizedException(
                        null, 'NOT_SIGNED',
                        'User must be signed in to perform this operation'
                    ).withStatus(401)
                );
            } else if (partyId == null) {
                HttpResponseSender.sendError(
                    req, res,
                    new UnauthorizedException(
                        null, 'NO_USER_ID',
                        'User id is not defined'
                    ).withStatus(401)
                );
            } else {
                let isOwner = partyId == user.id;
                if (!isOwner) {
                    HttpResponseSender.sendError(
                        req, res,
                        new UnauthorizedException(
                            null, 'NOT_OWNER', 'Only user owner access is allowed'
                        ).withDetails('user_id', partyId).withStatus(403)
                    );
                } else {
                    next();
                }
            }
        };
    }
}

Let’s take a closer look at each of these methods:

  • anybody - allows everyone access, even unauthorized users.
  • signed - access is granted only to authorized users.
  • admin - access is granted only to users with the Administrator role.
  • owner - access is granted only for the session owner.

The logic pretty much boils down to making a decision about whether we should allow further access, or send an answer with the corresponding error. In case of the latter, the error is based on the information provided by the clients and the information about the user that is embedded into the interceptor’s request for the active session.

Setting specific access levels to certain resources is configured when registering routes in the service. The service’s implementation is described in Step 5 - REST services and versioning.