| 1 | // SPDX-License-Identifier: AGPL-3.0-or-later |
| 2 | |
| 3 | package policy |
| 4 | |
| 5 | import policydb "github.com/tenseleyFlow/shithub/internal/auth/policy/sqlc" |
| 6 | |
| 7 | // Role models the per-collaborator grant. The five values mirror |
| 8 | // GitHub's tiers; the order matters because RoleAtLeast depends on it. |
| 9 | type Role string |
| 10 | |
| 11 | const ( |
| 12 | // RoleNone is the zero value used by callers when no row exists. |
| 13 | RoleNone Role = "" |
| 14 | RoleRead Role = "read" |
| 15 | RoleTriage Role = "triage" |
| 16 | RoleWrite Role = "write" |
| 17 | RoleMaintain Role = "maintain" |
| 18 | RoleAdmin Role = "admin" |
| 19 | ) |
| 20 | |
| 21 | // roleRank maps a role to its position in the lattice. Higher rank → |
| 22 | // strictly more powerful. RoleNone is below everything; RoleAdmin is |
| 23 | // the top. |
| 24 | func roleRank(r Role) int { |
| 25 | switch r { |
| 26 | case RoleAdmin: |
| 27 | return 5 |
| 28 | case RoleMaintain: |
| 29 | return 4 |
| 30 | case RoleWrite: |
| 31 | return 3 |
| 32 | case RoleTriage: |
| 33 | return 2 |
| 34 | case RoleRead: |
| 35 | return 1 |
| 36 | default: |
| 37 | return 0 |
| 38 | } |
| 39 | } |
| 40 | |
| 41 | // RoleAtLeast reports whether `have` grants at least `want`. RoleNone |
| 42 | // grants nothing. |
| 43 | func RoleAtLeast(have, want Role) bool { |
| 44 | return roleRank(have) >= roleRank(want) && roleRank(want) > 0 |
| 45 | } |
| 46 | |
| 47 | // roleFromDB translates the sqlc-generated enum to our domain type. |
| 48 | // We mirror values rather than re-using the DB type so the policy |
| 49 | // package's API doesn't require callers to import policydb. |
| 50 | func roleFromDB(r policydb.CollabRole) Role { |
| 51 | return Role(r) |
| 52 | } |
| 53 | |
| 54 | // roleToDB is the inverse, used by callers that mutate collaborators. |
| 55 | func roleToDB(r Role) policydb.CollabRole { |
| 56 | return policydb.CollabRole(r) |
| 57 | } |
| 58 |