Documentation
¶
Overview ¶
Package hubaccess implements the hub-access half of the provider contract: the hub REST capabilities a provider may exercise with the delegated user token the hub hands it in place of the caller's bearer.
A provider requests capabilities in its CatalogEntry (spec.hubAccess) from a closed set this package owns; it never names routes. A tenant accepts them when enabling the provider, which records a Grant for the provider. The Gate admits a delegated call only for a route that maps to a capability the provider both declares and was granted in that workspace, and marks the request with the accepted limits (tenant.DelegatedCall). The person the token stands for is still authorized by the tenant middleware and handlers exactly as if they had called: a provider never exceeds that person.
Index ¶
- Constants
- func Allowed(grant *tenancyv1alpha1.Grant, declared providersv1alpha1.ProviderHubAccess, ...) (tenancyv1alpha1.GrantedCapability, bool, bool)
- func Declared(declared []providersv1alpha1.ProviderHubAccess, req Requirement) (providersv1alpha1.ProviderHubAccess, bool)
- func DelegatedUserResolver(human tenant.UserResolver) tenant.UserResolver
- func FromDeclaration(d providersv1alpha1.ProviderHubAccess) tenancyv1alpha1.GrantedCapability
- func GrantName(orgUUID, wsUUID, provider, providerOrgUUID string) string
- func Granted(grant *tenancyv1alpha1.Grant, req Requirement) (tenancyv1alpha1.GrantedCapability, bool)
- func MayDecide(scope providersv1alpha1.ProviderHubAccessScope, workspaceRole, orgRole string) bool
- type Caller
- type Decision
- type Gate
- type GrantKey
- type GrantReader
- type Limits
- type MergeFunc
- type ProviderLookup
- type Requirement
- type Store
- func (s *Store) Delete(ctx context.Context, key GrantKey) error
- func (s *Store) Get(ctx context.Context, key GrantKey) (*tenancyv1alpha1.Grant, error)
- func (s *Store) Put(ctx context.Context, key GrantKey, ...) (*tenancyv1alpha1.Grant, error)
- func (s *Store) Record(ctx context.Context, key GrantKey, merge MergeFunc, decidedBy string) (*tenancyv1alpha1.Grant, error)
- type VerifyFunc
Constants ¶
const DefaultGrantCacheTTL = 15 * time.Second
DefaultGrantCacheTTL bounds how long a replica serves a cached grant (or the absence of one). Enable and Disable on the same replica update the cache immediately; another replica sees the change within this window.
const DefaultInvitesPerHour = 60
DefaultInvitesPerHour bounds memberships.invite calls per provider per Organization on one replica.
Variables ¶
This section is empty.
Functions ¶
func Allowed ¶
func Allowed(grant *tenancyv1alpha1.Grant, declared providersv1alpha1.ProviderHubAccess, platformProvider, platformDefault bool) (tenancyv1alpha1.GrantedCapability, bool, bool)
Allowed is the gate's answer for one declared capability: accepted capabilities are allowed, declined ones are not, and an undecided one is allowed only for a platform provider under the platform default. The returned entry carries the limits to combine with the declaration.
func Declared ¶
func Declared(declared []providersv1alpha1.ProviderHubAccess, req Requirement) (providersv1alpha1.ProviderHubAccess, bool)
Declared returns the provider's declaration for req, if any.
func DelegatedUserResolver ¶
func DelegatedUserResolver(human tenant.UserResolver) tenant.UserResolver
DelegatedUserResolver wraps the human resolver for tenant.Middleware: a request the gate admitted resolves to the person the delegated token stands for; anything else is the human resolver's decision.
func FromDeclaration ¶
func FromDeclaration(d providersv1alpha1.ProviderHubAccess) tenancyv1alpha1.GrantedCapability
FromDeclaration is the grant entry recorded when a tenant accepts a declared capability.
func GrantName ¶
GrantName is the deterministic Grant name for one provider in one workspace. The subject kind and the provider owner are part of it, so an org-owned provider and the platform provider it shadows never share a grant, and neither collides with a grant for another kind of subject.
func Granted ¶
func Granted(grant *tenancyv1alpha1.Grant, req Requirement) (tenancyv1alpha1.GrantedCapability, bool)
Granted returns the grant entry for req, if any.
func MayDecide ¶
func MayDecide(scope providersv1alpha1.ProviderHubAccessScope, workspaceRole, orgRole string) bool
MayDecide reports whether a person with these roles may accept or decline a capability at scope: org scope needs an org admin, workspace scope a workspace or org admin.
Types ¶
type Caller ¶
type Caller struct {
// User is the person a delegated token stands for.
User string
OrgUUID string
WorkspaceUUID string
// Delegated is true for a delegated user token; Provider and Legacy are
// meaningful only then.
Delegated bool
Provider string
// ProviderOrgUUID is the provider's owner org ("" = platform), covered by
// the hub's proof unless Legacy.
ProviderOrgUUID string
// Legacy marks a token whose proof predates the owner org; the owner is
// then resolved the way the proxy resolved it when issuing.
Legacy bool
}
Caller is a verified workload identity presented with the tenant headers.
type Decision ¶
type Decision int
Decision is what a grant says about one capability.
func Decide ¶
func Decide(grant *tenancyv1alpha1.Grant, req Requirement) (Decision, tenancyv1alpha1.GrantedCapability)
Decide returns the grant's decision for req, and the accepted entry when the decision is Accepted.
type Gate ¶
type Gate struct {
// Human resolves the caller's own credential (OIDC, static token, kcp
// ServiceAccount). When it succeeds the request is not a delegated call.
Human tenant.UserResolver
Verify VerifyFunc
Providers ProviderLookup
Grants GrantReader
// PlatformDefault grants a platform provider the capabilities it declares
// in a workspace where no decision was recorded yet (no grant object).
// Platform providers are operator-installed and, under the default
// --provider-delegated-tokens=off, already hold the caller's own bearer;
// this keeps them working across the upgrade. Org-owned providers always
// need an explicit grant.
PlatformDefault bool
// InvitesPerHour bounds memberships.invite per (provider, org); 0 uses
// DefaultInvitesPerHour.
InvitesPerHour int
// contains filtered or unexported fields
}
Gate admits provider calls made with a delegated user token to the hub REST routes the provider contract allows, and nothing else.
func (*Gate) Middleware ¶
Middleware wraps the tenant-scoped routes. It runs before tenant.Middleware and only acts on requests whose bearer is a ServiceAccount token that the human resolver does not accept — i.e. a delegated token; for every other request it is a pass-through and the tenant middleware decides as before.
type GrantKey ¶
type GrantKey struct {
OrgUUID, WorkspaceUUID, Provider, ProviderOrgUUID string
}
GrantKey identifies one provider in one workspace.
type GrantReader ¶
type GrantReader interface {
Get(ctx context.Context, key GrantKey) (*tenancyv1alpha1.Grant, error)
}
GrantReader reads recorded grants.
type Limits ¶
Limits are the constraints in force for one admitted capability.
func Effective ¶
func Effective(declared providersv1alpha1.ProviderHubAccess, granted tenancyv1alpha1.GrantedCapability) Limits
Effective combines a declaration with its grant: the narrower of the two wins, so neither a catalog update nor an old grant can widen access. A role cap is always "member" — admin is never grantable through a provider.
type MergeFunc ¶
type MergeFunc func(prev *tenancyv1alpha1.Grant) (accepted []tenancyv1alpha1.GrantedCapability, declined []tenancyv1alpha1.CapabilityRef)
MergeFunc computes the decisions to record from the stored grant (nil when none exists).
type ProviderLookup ¶
type ProviderLookup interface {
Get(name string) (providers.Provider, bool)
GetForOrg(orgUUID, name string) (providers.Provider, bool)
}
ProviderLookup is the part of the provider registry the gate needs.
type Requirement ¶
type Requirement struct {
Capability providersv1alpha1.ProviderHubCapability
Scope providersv1alpha1.ProviderHubAccessScope
}
Requirement is the capability a route needs, at a scope.
func Match ¶
func Match(method, urlPath string) (Requirement, bool)
Match maps a hub REST request to the capability it requires. Only the routes listed here are reachable with a delegated token; everything else (role changes, removals, workspace membership writes, org and workspace management) is not part of the closed set and is refused.
GET /api/orgs/{org}/memberships memberships.read org
GET /api/orgs/{org}/workspaces/{ws}/memberships memberships.read workspace
POST /api/orgs/{org}/memberships memberships.invite org
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store reads and writes provider Grants in root:railgrid:system:tenants, with a short cache on the read path the gate uses per request.
func NewStore ¶
func NewStore(client *railgridclient.Client) *Store
NewStore returns a Store over the hub's tenancy client (the one bound to root:railgrid:system:tenants).
func (*Store) Delete ¶
Delete removes the grant for key. Deleting a grant that does not exist is not an error.
func (*Store) Put ¶
func (s *Store) Put(ctx context.Context, key GrantKey, accepted []tenancyv1alpha1.GrantedCapability, declined []tenancyv1alpha1.CapabilityRef, decidedBy string) (*tenancyv1alpha1.Grant, error)
Put records exactly these decisions for key, replacing any earlier ones.
func (*Store) Record ¶
func (s *Store) Record(ctx context.Context, key GrantKey, merge MergeFunc, decidedBy string) (*tenancyv1alpha1.Grant, error)
Record reads the stored grant for key straight from the API (not the cache), merges the new decisions into it with merge, and writes the result. When there is no stored grant and merge decides nothing, nothing is written: every capability stays undecided.