Attribute-Based Access Control (ABAC) system

What is ABAC?

ABAC (Attribute-Based Access Control) is a flexible access control model that makes authorization decisions based on attributes associated with users, resources, actions, and environmental conditions.

ABAC attributes

User attributes: Department, job title, location, ...

Resource attributes: Owner, creation date, file type, sensitivity, ...

Action attributes: Read, write, delete, ...

Environmental attributes: Time of day, location, ...

How were the polilies created?

ABAC uses policies written as rules that combine these attributes. For example, a policy might state: "Allow read access to financial documents if the user is in the Finance department AND it's during business hours AND the request comes from a corporate network."

The advantages

Granular control: Can create particular access rules based on multiple criteria.

Dynamic decisions: Access can change based on real-time conditions like time or location.

Scalability: Doesn't require creating numerous roles for every possible permission combination.

Flexibility: Can easily accommodate new attributes and complex business rules.

Example

Resource

type Todo = {
  id: string
  title: string
  userId: string
  completed: boolean
  invitedUsers: string[]
}

User

type Role = "admin" | "moderator" | "user"
type User = { blockedBy: string[]; roles: Role[]; id: string; name: string }

Action can performance with the resource

type Permissions = {
  todos: {
    dataType: Todo
    action: "view" | "create" | "update" | "delete"
  }
}

Permission checker

type PermissionCheck<Key extends keyof Permissions> =
  | boolean
  | ((user: User, data: Permissions[Key]["dataType"]) => boolean)

type RolesWithPermissions = {
  [R in Role]: Partial<{
    [Key in keyof Permissions]: Partial<{
      [Action in Permissions[Key]["action"]]: PermissionCheck<Key>
    }>
  }>
}
// ABAC Permission Rules
const ROLES = {
  admin: {
    todos: {
      view: true,
      create: true,
      update: true,
      delete: true,
    },
  },
  moderator: {
    todos: {
      view: true,
      create: true,
      update: true,
      delete: (user, todo) => todo.completed,
    },
  },
  user: {
    todos: {
      view: true,
      create: true,
      update: (user, todo) =>
        todo.userId === user.id || todo.invitedUsers.includes(user.id),
      delete: (user, todo) =>
        (todo.userId === user.id || todo.invitedUsers.includes(user.id)) &&
        todo.completed,
    },
  },
} as const satisfies RolesWithPermissions

// Permission checker function
function hasPermission<Resource extends keyof Permissions>(
  user: User,
  resource: Resource,
  action: Permissions[Resource]["action"],
  data?: Permissions[Resource]["dataType"]
) {
  return user.roles.some(role => {
    const permission = (ROLES as RolesWithPermissions)[role][resource]?.[action]
    if (permission == null) return false

    if (typeof permission === "boolean") return permission
    return data != null && permission(user, data)
  })
}

Action button

const ActionButton = ({ action, resource, data, children, className = "" }) => {
    const canPerform = hasPermission(currentUser, resource, action, data);
    
    return (
      <button
        className={`px-2 py-1 rounded text-sm flex items-center gap-1 ${
          canPerform 
            ? `${className} hover:opacity-80` 
            : 'bg-gray-200 text-gray-400 cursor-not-allowed'
        }`}
        disabled={!canPerform}
        onClick={() => canPerform && alert(`${action} ${resource} - Action would be performed`)}
      >
        {children}
      </button>
    );
  };

Sample data

const sampleUsers: User[] = [
  { id: "1", name: "John (Admin)", roles: ["admin"] },
  { id: "2", name: "Jane (Moderator)", roles: ["moderator"] },
  { id: "3", name: "Bob (User)", roles: ["user"] },
  { id: "4", name: "Alice (User)", roles: ["user"] }
];

const sampleTodos: Todo[] = [
  { id: "1", title: "Complete project", userId: "1", completed: false },
  { id: "2", title: "Review code", userId: "2", completed: true },
  { id: "3", title: "Team meeting", userId: "3", completed: false },
  { id: "4", title: "Deploy to production", userId: "4", completed: true }
];

Todo Permission Rules

  • Admin
    • Full access to all todos
    • Can view, create, update, and delete
  • Moderator
    • Can manage all todos
    • Can only delete completed todos
  • User
    • Can view all todos
    • Can only edit own todos
    • Can edit invited todos

Application demo: https://claude.ai/public/artifacts/9cdc4ba6-d46c-46c0-9829-8b48dd997624

Conclution

ABAC is particularly useful in environments with complex security requirements, such as healthcare systems that need to consider patient relationships, government systems with classification levels, or enterprises with diverse departments and data sensitivity levels. However, it can be more complex to implement and manage than simpler access control models.