Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement group-membership JavaScript policy in Keycloak

A 2-year old keycloak-user list question w/o an answer:

  1. there’s a protected resource called Project
  2. and an owner - a Project Manager
  3. Each project manager has access to only their own projects (owner-only policy).
  4. Project Managers in turn report to one or more Portfolio Managers. A Portfolio Manager should be able to access all his/her project managers' projects (portfolio-manager policy).

Let’s assume the system design if flexible and this fact who are the Portfolio Managers for a particular Project Manager can be either kept inside Keycloak (but not as keycloak groups) or in the client app itself. How can this be implemented as a JavaScrtipt-based authorization policy in Keycloak? I guess the request can be injected with this info somehow but can’t figure it out from the docs.

like image 501
milan Avatar asked Sep 04 '18 12:09

milan


1 Answers

It turned out to be rather easy. I've decided to keep the info about managers in another database, and then the app (service-nodejs) needs to pass this info as a claim to keycloak. I've tested this on the service-nodejs keycloak quickstart. Here are the relevant pieces:

// app.js route:

app.get('/service/project/:id',
  keycloak.enforcer(['Project'], {
      response_mode: 'permissions', 
      claims: (request) => {
          return { "portfolio.managers": ['alice', 'bob'] } //hard-coded
      }
  }), function(req, res) {
      res.json({ message: `got project "operation "` });
  });

The policy protecting the Project resource is an aggregated of OwnerOnly and PortfolioManagers:

// portfolio-managers-policy:

var context = $evaluation.getContext();
var identity = context.getIdentity();
var userid = identity.getAttributes().getValue('preferred_username').asString(0);
var managers =  context.getAttributes().getValue('portfolio.managers')

if (!managers) {
    print('managers not provided, cannot decide!');
    $evaluation.deny();
} else {
    // check if the user is one of the portfolio managers of this project:
    for (var i = 0; i < managers.size(); i++) {
        if (managers.asString(i) == userid) {
            $evaluation.grant();
            break;
        }
    }
}

Note that the service-nodejs keycloak client must be confidential in order to call the token endpoint.

like image 52
milan Avatar answered Nov 17 '22 05:11

milan