Documentation
¶
Overview ¶
Package workloadroles is the single source of truth for the canned roles a sandbox's workload identity token can be minted with.
A role is a coarse allow-list of (resource, action) pairs — resource is a lowercased RPC interface name, action a lowercased method name, matching what rpc.Authorizer.Authorize receives. It answers "may this workload ever call this method", never "against which app": per-app confinement is enforced separately by rpc.AllowApp inside the handlers.
That split is why ClusterScoped matters. An app-scoped role only ever grants methods that call rpc.AllowApp, so a token minted for app X can reach the method but only for X. A cluster-scoped role is not confined to one app, so it may also grant cluster-wide methods — and the authenticator signals that by leaving the identity's bound app empty, which makes rpc.AllowApp permit every app.
The package has no internal dependencies so both the authorizer (pkg/oidcauth) and the authenticator (pkg/workloadidentity) can import it without a cycle.
This map is not the permanent model. RFD-67 (MIR-891) moves authorization to {Resource, Action, Instance} with a Scope on each permission, at which point this catalog becomes a set of policy presets rather than a Go map and the per-handler rpc.AllowApp calls fall away. The token carries the role *name*, not a resolved permission set, so that backend swap needs no token-format change and no reissue — read the map as the current implementation, not the enduring shape.
Index ¶
Constants ¶
const ( RoleNone = "none" RoleAppReadonly = "app-readonly" RoleAppDeployer = "app-deployer" RoleAppDebugger = "app-debugger" RoleAppAdmin = "app-admin" RoleClusterReadonly = "cluster-readonly" RoleClusterDeployer = "cluster-deployer" RoleClusterDebugger = "cluster-debugger" RoleClusterAdmin = "cluster-admin" )
Role names. These are the values stored on an app and carried in the token's role claim.
const Default = RoleAppReadonly
Default is the role an app runs under when it hasn't chosen one. It preserves roughly the pre-role behavior (own-app reads) so nothing regresses; apps opt up from here.
Variables ¶
var Roles = map[string]Role{ RoleNone: {Perms: perms{}}, RoleAppReadonly: {Perms: appRead}, RoleAppDeployer: {Perms: merge(appRead, appDeploy)}, RoleAppDebugger: {Perms: merge(appRead, execBlock)}, RoleAppAdmin: {Perms: merge(appRead, appDeploy, appConfig, execBlock)}, RoleClusterReadonly: {Perms: merge(appRead, clusterRead), ClusterScoped: true}, RoleClusterDeployer: { Perms: merge(appRead, clusterRead, appDeploy, appConfig), ClusterScoped: true, }, RoleClusterDebugger: { Perms: merge(appRead, clusterRead, execBlock, perms{ "admin": set("invoke", "listmethods", "describemethods"), "internalhttp": set("dorequest"), }), ClusterScoped: true, }, RoleClusterAdmin: {Perms: clusterAdminPerms(), ClusterScoped: true}, }
Roles is the catalog. Composed from the blocks above so a method appears in exactly one place.
Functions ¶
func IsAppScoped ¶
IsAppScoped reports whether name is a known role confined to its own app. Used to reject cluster-scoped roles from app-owner-controlled sources (.miren/app.toml). An unknown name is not app-scoped.
Types ¶
type Role ¶
type Role struct {
// Perms is the allow-list. Treat as read-only.
Perms perms
// ClusterScoped reports whether the role reaches beyond its own app. When
// false the identity is confined to its origin app by rpc.AllowApp; when
// true it is not.
ClusterScoped bool
}
Role is a permission set. Perms[resource][action] == true means allowed.