hubaccess

package
v0.2.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 16, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

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

View Source
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.

View Source
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

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

FromDeclaration is the grant entry recorded when a tenant accepts a declared capability.

func GrantName

func GrantName(orgUUID, wsUUID, provider, providerOrgUUID string) string

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

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.

const (
	// Undecided: nobody entitled to decide it has.
	Undecided Decision = iota
	Accepted
	Declined
)

func Decide

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

func (g *Gate) Middleware(next http.Handler) http.Handler

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.

func (GrantKey) Name

func (k GrantKey) Name() string

Name is the grant's object name.

type GrantReader

type GrantReader interface {
	Get(ctx context.Context, key GrantKey) (*tenancyv1alpha1.Grant, error)
}

GrantReader reads recorded grants.

type Limits

type Limits struct {
	MaxRole     string
	AllowInvite bool
}

Limits are the constraints in force for one admitted capability.

func Effective

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

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

func (s *Store) Delete(ctx context.Context, key GrantKey) error

Delete removes the grant for key. Deleting a grant that does not exist is not an error.

func (*Store) Get

func (s *Store) Get(ctx context.Context, key GrantKey) (*tenancyv1alpha1.Grant, error)

Get returns the grant for key, or nil when none is recorded.

func (*Store) Put

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.

type VerifyFunc

type VerifyFunc func(*http.Request) (Caller, error)

VerifyFunc verifies the request's bearer as a workload identity bound to the tenant named by X-Railgrid-Org / X-Railgrid-Workspace.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL