Role-Based Access Control

MD
R
Markdown

Industry Standard Principles: Role-based access control (RBAC) RBAC is enough for many use cases. ABAC is the next step Evolution from RBAC: Attribute-based access control (ABAC) References: https://developer.okta.com/books/api-security/authz/attribute-based/#authz-attribute-based https://developer.okta.com/books/api-security/authz/role-based/#authz-role-based https://auth0.com/docs/authorization/concepts/rbac https://auth0.com/docs/users/concepts/overview-user-metadata https://auth0.com/docs/extensions/authorization-extension/v2 https://community.auth0.com/t/how-to-manage-organization-level-access/20993/5 https://auth0.com/

Precedence Rules:

In RBAC systems, if a user has multiple roles, the policies of each role are aggregated to form the user's permissions. a) the most restrictive policy takes precedence (BLOCKS) b) if one role allows a certain action while another denies it, the action will typically be denied. c) if a user doesn't have any policies, they essentially have no permissions and cannot perform any actions: Permissions in RBAC are explicitly granted

Example

Admin: This is typically the most privileged role. "policies": [ { "name": "Allow All", "effect": "Allow", "service": [ "suppliers", "users", "roles" ], "action": "*" } ]

Manager: A Manager can read, write, and update bookings, but cannot delete them. They can also manage users but not roles. "policies": [ { "name": "Allow Bookings Management", "effect": "Allow", "service": [ "suppliers" ], "action": "read,write,update" }, { "name": "Allow Users Management", "effect": "Allow", "service": [ "users" ], "action": "read,write,update" } ]

Staff: A Staff member can read and update bookings but cannot create new ones or delete existing ones. "policies": [ { "name": "Allow Bookings Update", "effect": "Allow", "service": [ "suppliers" ], "action": "read,update" } ]

Guest: A Guest can only read bookings, they can't create, update or delete them. "policies": [ { "name": "Allow Bookings Read", "effect": "Allow", "service": [ "suppliers" ], "action": "read" } ]

Example middlware for Node.js.

function enforcePolicies(req, res, next) {
  // Get the user's role(s) from the request. This might be encoded in a JWT, 
  // provided in a header, etc. depending on your auth strategy.
  const roles = getUserRoles(req);

  // Determine the action and resource from the request. This could be based 
  // on the HTTP method (GET, POST, DELETE, etc.) and the request URL.
  const action = determineAction(req);
  const resource = determineResource(req);

  // Check the user's roles to see if any of them allow the action on the resource.
  for (const role of roles) {
    const policies = getRolePolicies(role);
    for (const policy of policies) {
      if (policy.allows(action, resource)) {
        // If the policy allows the action, proceed to the next middleware.
        return next();
      }
    }
  }

  // If none of the user's roles allow the action, return an error response.
  return res.status(403).json({ error: 'Forbidden' });
}

Organization Role Model

type Role {
  Role: WRITE
  Policies: []
  User: userId
}

type OrganizationRole {
  Organization: organizationId
  Role: WRITE
  User: userId
}

Authorization Map

Special: /ping/is-org-admin User - OrganizationRole('Coke', 'WRITE') - Role('WRITE', policies: [])

+------------------------+------+-------+--------+----------+----------+ | endpoints | user | admin | org-w | org-r | no-login | +------------------------+------+-------+--------+----------+----------+ | /users/login | OK | OK | OK | OK | OK | | /ping | OK | OK | OK | OK | OK | | /ping/permit-all | OK | OK | OK | OK | OK | | /ping/deny-all | 401 | 401 | 401 | 401 | OK | | /ping/is-authenticated | OK | OK | OK | 401 | OK | | /ping/has-any-role | 401 | OK | OK | 401 | OK | | /ping/write-org | 401 | 401 | OK | 401 | OK | | /ping/read-org | 401 | 401 | OK | OK | OK | +------------------------+------+-------+--------+----------+----------+

Authorization Middleware Functions

.authorizeRequests()
  .antMatchers("/foo/bar").hasRole("BAR")
  .antMatchers("/foo/spam").hasRole("SPAM")
  .antMatchers("/foo/spam").hasOrganizationRole("WRITE")
 .anyRequest().isAuthenticated();

Authorization Decorator

 @Secured("ROLE_USER")
 public String secure() {
    return "Hello Security";
  }

Verifier

async hasRole(userId: string) {
  if (type === SecuredType.HAS_ROLES && roles.length) {
    const userRoles = await this.userRoleRepository.find({ where: { userId } });
    const roleIds = userRoles.map(ur => ur.roleId);
    let valid = true;
    for (const role of roles)
      if (!roleIds.includes(role)) {
        valid = false;
        break;
      }
    if (valid) return;
  }
}

Security Notes

Log everything. Authorization decisions must be reviewed and adjusted based on new use cases, usage patterns, and bad actors. Auditing becomes more important as you grow

ABAC

User scopes: read:name, edit:name, read:email, edit:email Playlist scopes: read:playlist, edit:playlist, edit:description, sort:playlist

Middleware Architecture

  • Policy Enforcement Point (PEP) - protecting all the resources and all requests are routed to this point to make a decision, creates an authorization specific request.
  • Policy Decision Point (PDP) - consumes the authorization request sent from PEP, breaks it down, and evaluates all the attributes

Runtime Flow:

  • PEP requests edit:playlist, edit:description scopes along with some identifying information like user id
  • PDP uses this information to lookup policy, user info, and returns allow or deny.

OAuth2.0

The OAuth 2.0 framework is specifically designed for ABAC that works for many use cases, especially for APIs.

Created on 8/19/2019