Problem · Array

Role-Based Network Access Control

Learn this problem
MediumOkta logoOktaFULLTIMEPHONE SCREEN

Problem statement

Implement a role-based network access-control system by processing a finite ordered batch of operations.

Entities

  • A user may have many roles.
  • A role may have many permissions.
  • A permission has an identifier, an effect of ALLOW or DENY, and zero or more required property pairs.
  • A permission is applicable to a check only when every required key-value pair exactly matches a property supplied by that check. A permission with no required properties is always applicable.

Operations

Each row of operations is one of the following forms:

  • ["CREATE_USER", userId]
  • ["CREATE_ROLE", roleId]
  • ["CREATE_PERMISSION", permissionId, effect, k, key1, value1, ..., keyk, valuek]
  • ["ASSIGN_PERMISSION", roleId, permissionId]
  • ["ASSIGN_ROLE", userId, roleId]
  • ["CHECK", userId, k, key1, value1, ..., keyk, valuek]

User, role, and permission identifiers are unique when created. Repeating an existing role assignment or permission assignment is an idempotent no-op.

An assignment or check that references an entity that has not been created is invalid: append ERROR to the result and do not mutate any state. All other non-check operations are valid and produce no result.

Access decision

For a valid CHECK, evaluate every applicable permission reachable through all roles assigned to the user:

  1. If any applicable permission has effect DENY, append DENY.
  2. Otherwise, if at least one applicable permission has effect ALLOW, append ALLOW.
  3. Otherwise, append DENY.

Return the emitted results in operation order. The judged batch represents one serial history; concurrent updates and distributed replication are outside the returned result contract.

Function

evaluateAccess(operations: String[][]) → String[]

Examples

Example 1

operations = [["CREATE_USER","alice"],["CREATE_ROLE","engineer"],["CREATE_PERMISSION","prod-read","ALLOW","1","env","prod"],["ASSIGN_PERMISSION","engineer","prod-read"],["ASSIGN_ROLE","alice","engineer"],["CHECK","alice","1","env","prod"],["CHECK","alice","1","env","dev"]]return = ["ALLOW","DENY"]

The permission applies to the first check because env=prod matches, so access is allowed. It does not apply to the second check, so the default decision is DENY.

Example 2

operations = [["CREATE_USER","bob"],["CREATE_ROLE","member"],["CREATE_ROLE","security"],["CREATE_PERMISSION","read-docs","ALLOW","1","resource","docs"],["CREATE_PERMISSION","block-suspended","DENY","2","resource","docs","status","suspended"],["ASSIGN_PERMISSION","member","read-docs"],["ASSIGN_PERMISSION","security","block-suspended"],["ASSIGN_ROLE","bob","member"],["ASSIGN_ROLE","bob","security"],["CHECK","bob","2","resource","docs","status","active"],["CHECK","bob","2","resource","docs","status","suspended"],["CHECK","bob","1","resource","code"]]return = ["ALLOW","DENY","DENY"]

The allow applies to active document access. For suspended document access, both permissions apply and the deny wins. No permission applies to the code resource, so access defaults to DENY.

Example 3

operations = [["CREATE_USER","u"],["CREATE_ROLE","r"],["CREATE_PERMISSION","p","ALLOW","0"],["ASSIGN_PERMISSION","r","p"],["ASSIGN_PERMISSION","r","p"],["ASSIGN_ROLE","u","r"],["ASSIGN_ROLE","u","r"],["CHECK","u","0"],["ASSIGN_ROLE","u","missing"],["CHECK","missing","0"]]return = ["ALLOW","ERROR","ERROR"]

Duplicate assignments do not change the state. The unconditional allow makes the valid check succeed. The unknown role assignment and unknown user check each emit ERROR.

Constraints

  • 1 <= operations.length <= 10^5.
  • The total number of strings across all operation rows is at most 2 * 10^5.
  • Every row has exactly the length required by its operation, and every k is a valid non-negative integer.
  • User, role, and permission identifiers are non-empty and unique within their entity type when created.
  • Each permission effect is ALLOW or DENY.
  • Property keys are unique within one permission or check, and all identifiers, keys, and values are non-null strings.
  • Only references to entities that have not yet been created are invalid; operation names and row encodings are otherwise valid.

More Okta problems

drafts saved locally
public String[] evaluateAccess(String[][] operations) {
  // write your code here
}
operations[["CREATE_USER","alice"],["CREATE_ROLE","engineer"],["CREATE_PERMISSION","prod-read","ALLOW","1","env","prod"],["ASSIGN_PERMISSION","engineer","prod-read"],["ASSIGN_ROLE","alice","engineer"],["CHECK","alice","1","env","prod"],["CHECK","alice","1","env","dev"]]
expected["ALLOW", "DENY"]
checking account