mirror of
https://github.com/Noooste/garage-ui.git
synced 2026-08-12 22:47:02 +00:00
feat(oidc): add fine grained access control (#91)
* feat(access-control): fine grain tokens * fix(docs): clean wording * test(access-control): add tests for team extraction from access tokens and bucket info permissions * test(vocabulary): add test for ExpandGlob function to reject non-glob patterns * feat(helm): add multi-user access control documentation and schema support
This commit is contained in:
@@ -225,6 +225,45 @@ config:
|
||||
# ... additional OIDC settings
|
||||
```
|
||||
|
||||
#### Multi-User Access Control (optional)
|
||||
|
||||
Scope what each OIDC user can see and do, based on the teams in their token
|
||||
claims. **Absent by default**, so every authenticated user keeps full access.
|
||||
When set, authorization becomes default-deny.
|
||||
|
||||
> **Not a security boundary.** This is UI-layer policy only. Anyone holding the
|
||||
> Garage admin token or raw S3 keys bypasses it. See
|
||||
> [docs/access-control.md](../../docs/access-control.md) for the full model and
|
||||
> permission vocabulary.
|
||||
|
||||
```yaml
|
||||
config:
|
||||
auth:
|
||||
oidc:
|
||||
enabled: true
|
||||
# OIDC claim (go-jmespath) listing the user's teams.
|
||||
team_attribute_path: "groups"
|
||||
# admin_role stays optional once access_control is set: unmatched users
|
||||
# are denied rather than promoted to admin.
|
||||
access_control:
|
||||
presets:
|
||||
bucket_readonly: [bucket.list, bucket.read, object.list, object.read]
|
||||
bucket_owner: ["preset:bucket_readonly", bucket.create, bucket.update,
|
||||
bucket.delete, object.write, object.delete]
|
||||
teams:
|
||||
- name: backend
|
||||
claim_values: ["garage-team-backend"] # matched against the team_attribute_path claim
|
||||
bindings:
|
||||
- bucket_prefixes: ["backend-"]
|
||||
permissions: ["preset:bucket_owner"]
|
||||
- bucket_prefixes: ["shared-"]
|
||||
permissions: ["preset:bucket_readonly"]
|
||||
cluster_permissions: [cluster.status, cluster.health]
|
||||
```
|
||||
|
||||
Admin-password and Garage-admin-token logins are always full admin in v1; only
|
||||
OIDC users can be scoped to a team.
|
||||
|
||||
#### CORS Configuration
|
||||
|
||||
```yaml
|
||||
|
||||
@@ -322,11 +322,24 @@
|
||||
"description": "Path to roles in the OIDC token claims",
|
||||
"default": "resource_access.garage-ui.roles"
|
||||
},
|
||||
"team_attribute_path": {
|
||||
"type": "string",
|
||||
"description": "go-jmespath path to the team/group claim used by config.access_control (same convention as role_attribute_path). Required only when access_control.teams is set; empty disables team resolution",
|
||||
"default": ""
|
||||
},
|
||||
"admin_role": {
|
||||
"type": "string",
|
||||
"description": "Role name that grants admin privileges",
|
||||
"default": "admin"
|
||||
},
|
||||
"admin_roles": {
|
||||
"type": "array",
|
||||
"description": "Additional admin role names. A user is granted admin if ANY of their roles matches admin_role or any entry here",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": []
|
||||
},
|
||||
"tls_skip_verify": {
|
||||
"type": "boolean",
|
||||
"description": "Skip TLS certificate verification (only for testing)",
|
||||
@@ -429,6 +442,77 @@
|
||||
"default": "json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"access_control": {
|
||||
"type": "object",
|
||||
"description": "Optional team-based access control (issue #33). When present (even empty) authorization is default-deny: OIDC users get only what their teams grant, and users matching no team are denied everywhere. When omitted, every authenticated user has full access. UI-layer policy only, NOT a security boundary: anyone holding the Garage admin token or S3 keys bypasses it. Requires auth.oidc.team_attribute_path when teams are set. See docs/access-control.md",
|
||||
"properties": {
|
||||
"presets": {
|
||||
"type": "object",
|
||||
"description": "Named, reusable permission lists referenced from bindings/cluster_permissions via the 'preset:<name>' syntax. Presets may reference other presets",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"teams": {
|
||||
"type": "array",
|
||||
"description": "Maps IdP claim values to bucket-prefix bindings and cluster-level permissions",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["name", "claim_values"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Unique team name"
|
||||
},
|
||||
"claim_values": {
|
||||
"type": "array",
|
||||
"description": "Values matched (exact string, no wildcards) against the team_attribute_path claim",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"bindings": {
|
||||
"type": "array",
|
||||
"description": "Prefix-scoped permission grants over buckets whose names match one of the prefixes",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["bucket_prefixes", "permissions"],
|
||||
"properties": {
|
||||
"bucket_prefixes": {
|
||||
"type": "array",
|
||||
"description": "Bucket-name prefixes this binding applies to. Use '*' to match every bucket",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"permissions": {
|
||||
"type": "array",
|
||||
"description": "Prefix-scoped permissions, preset references (preset:<name>), or trailing-star globs (e.g. bucket.*, object.*)",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"minItems": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"cluster_permissions": {
|
||||
"type": "array",
|
||||
"description": "Global (cluster-level) permissions granted to the team, e.g. cluster.status, cluster.health",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -113,6 +113,11 @@ config:
|
||||
role_attribute_path: "resource_access.garage-ui.roles"
|
||||
admin_role: "admin"
|
||||
admin_roles: []
|
||||
# Team-based access control (optional). OIDC claim (go-jmespath, same
|
||||
# convention as role_attribute_path) listing the user's teams. Required
|
||||
# only when config.access_control.teams is set; leave empty to disable.
|
||||
# See config.access_control below and docs/access-control.md.
|
||||
team_attribute_path: ""
|
||||
# TLS settings
|
||||
tls_skip_verify: false
|
||||
# Session settings
|
||||
@@ -147,6 +152,21 @@ config:
|
||||
# Options: json, text
|
||||
format: "json"
|
||||
|
||||
# access_control:
|
||||
# presets:
|
||||
# bucket_readonly: [bucket.list, bucket.read, object.list, object.read]
|
||||
# bucket_owner: ["preset:bucket_readonly", bucket.create, bucket.update,
|
||||
# bucket.delete, object.write, object.delete]
|
||||
# teams:
|
||||
# - name: backend
|
||||
# claim_values: ["garage-team-backend"] # matched against the team_attribute_path claim
|
||||
# bindings:
|
||||
# - bucket_prefixes: ["backend-"]
|
||||
# permissions: ["preset:bucket_owner"]
|
||||
# - bucket_prefixes: ["shared-"]
|
||||
# permissions: ["preset:bucket_readonly"]
|
||||
# cluster_permissions: [cluster.status, cluster.health]
|
||||
|
||||
# Pod annotations
|
||||
podAnnotations: {}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user