52 lines
2.2 KiB
Markdown
52 lines
2.2 KiB
Markdown
|
|
# ADR-001: Bit-Set Permissions
|
|||
|
|
|
|||
|
|
**Status:** Accepted
|
|||
|
|
**Date:** 2026-03-18
|
|||
|
|
|
|||
|
|
## Context
|
|||
|
|
|
|||
|
|
Applications need to represent and check whether a user holds a specific capability
|
|||
|
|
on a resource. Common approaches include: role strings (e.g. `"admin"`, `"editor"`),
|
|||
|
|
permission string lists, or bit-set integers. Role strings require the application to
|
|||
|
|
know which capabilities each role implies, making capability checks indirect and
|
|||
|
|
requiring either a lookup table or a case statement everywhere. Permission string
|
|||
|
|
lists are flexible but expensive to check (linear scan) and verbose to store.
|
|||
|
|
|
|||
|
|
## Decision
|
|||
|
|
|
|||
|
|
`Permission` is typed as `int64` and represents a named bit position (0–62).
|
|||
|
|
Applications define their own constants using this type:
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
const (
|
|||
|
|
Read rbac.Permission = 0
|
|||
|
|
Write rbac.Permission = 1
|
|||
|
|
Delete rbac.Permission = 2
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`PermissionMask` is also typed as `int64` and holds the resolved OR-combination of
|
|||
|
|
granted permission bits. It is returned by `PermissionProvider.ResolveMask` and
|
|||
|
|
checked with `PermissionMask.Has(p Permission) bool`.
|
|||
|
|
|
|||
|
|
The upper bound is 62 (not 63) to keep the value within the positive range of a
|
|||
|
|
signed 64-bit integer, avoiding sign-bit ambiguity. `Has` and `Grant` both return
|
|||
|
|
false/no-op for out-of-range values (`p < 0 || p >= 63`).
|
|||
|
|
|
|||
|
|
`Grant(p Permission) PermissionMask` is provided for constructing masks in tests and
|
|||
|
|
in-memory provider implementations, returning a new mask with the bit set without
|
|||
|
|
mutating the receiver.
|
|||
|
|
|
|||
|
|
## Consequences
|
|||
|
|
|
|||
|
|
- Permission checks are O(1) bitwise operations — no map lookup, no string comparison.
|
|||
|
|
- A single `int64` column stores up to 62 independent permission bits per user-resource
|
|||
|
|
pair in the database.
|
|||
|
|
- Applications must define their own named constants; this package does not enumerate
|
|||
|
|
domain permissions. This keeps `rbac` domain-agnostic.
|
|||
|
|
- The 62-bit limit is sufficient for all foreseeable use cases; if an application
|
|||
|
|
needs more than 62 orthogonal permissions on a single resource, a structural
|
|||
|
|
refactor (multiple resources or resource hierarchies) is the appropriate response.
|
|||
|
|
- `PermissionProvider` is the extension point for DB-backed resolution; the bit-set
|
|||
|
|
design does not constrain the storage schema.
|