Documentation
¶
Index ¶
- Constants
- Variables
- func Collections() []string
- func MachineID(identityID, machineName string) string
- func NewHandler(options HandlerOptions) http.Handler
- func NewInstallationStores(backend githubapp.DocumentStore) (InstallationStateStore, InstallationBindingStore, ...)
- func NewMachineStores(backend githubapp.DocumentStore) (MachineCredentialStore, MachineCredentialResolver, MachineSnapshotStore)
- func NewRepositoryEventStore(backend githubapp.DocumentStore) (RepositoryEventStore, RepositoryEventStatusStore)
- type AppInstallationVerifier
- type EnqueueResult
- type EnrolledIdentity
- type EnrolledMachine
- type GitHubAppInstallationVerifier
- type GitHubIdentityVerifier
- type GitHubOAuthVerifier
- type HandlerOptions
- type IdentityInstallationBinding
- type InstallationBindingStore
- type InstallationConnectRequest
- type InstallationConnectResponse
- type InstallationConnectionService
- func (service InstallationConnectionService) AuthorizeOpener(ctx context.Context, viewer Viewer, ...) error
- func (service InstallationConnectionService) Begin(ctx context.Context, viewer Viewer, request InstallationConnectRequest) (InstallationConnectResponse, error)
- func (service InstallationConnectionService) CompleteOAuth(ctx context.Context, state, code, browserContinuation, oauthCredential string) (InstallationRedirect, error)
- func (service InstallationConnectionService) CompleteSetup(ctx context.Context, state string, installationID int64, ...) (InstallationRedirect, error)
- func (service InstallationConnectionService) Continue(ctx context.Context, state, openerChallenge string) (InstallationContinuation, error)
- type InstallationContinuation
- type InstallationLifecycleAction
- type InstallationLifecycleEvent
- type InstallationLifecycleStore
- type InstallationOpenerAuthorizationRequest
- type InstallationRedirect
- type InstallationState
- type InstallationStateDigest
- type InstallationStateStore
- type Machine
- type MachineBearerResolver
- type MachineCredentialBinding
- type MachineCredentialResolver
- type MachineCredentialStore
- type MachineEnrollmentRequest
- type MachineEnrollmentResponse
- type MachineEnrollmentService
- type MachineScope
- type MachineSnapshotService
- type MachineSnapshotStore
- type MachineSnapshotStoreResult
- type MachineTokenDigest
- type PendingRefresh
- type ProjectionProcessor
- type RepositoryEntitlementResolver
- type RepositoryEventService
- func (service RepositoryEventService) Acknowledge(ctx context.Context, machine Machine, request repositoryevent.AckRequest) (repositoryevent.AckResponse, error)
- func (service RepositoryEventService) EnqueueWebhook(ctx context.Context, delivery WebhookDelivery) (EnqueueResult, error)
- func (service RepositoryEventService) Poll(ctx context.Context, machine Machine, cursor string, limit int, ...) (repositoryevent.PollResponse, error)
- type RepositoryEventStatusStore
- type RepositoryEventStore
- type StatusConnection
- type StatusDelivery
- type StatusDeliveryMarker
- type StatusError
- type StatusInstallation
- type StatusMachine
- type StatusResponse
- type StatusService
- type StoredMachineSnapshot
- type VerifiedGitHubIdentity
- type VerifiedInstallation
- type VerifiedRepository
- type Viewer
- type ViewerResolver
- type WebhookDelivery
Constants ¶
const ( WebhookPath = APIPrefix + "/github/webhook" // MinimumWebhookSecretBytes is the configuration-readiness floor for a // high-entropy GitHub webhook secret. MinimumWebhookSecretBytes = 32 )
const ( InstallationConnectPath = APIPrefix + "/github/installations/connect" InstallationContinuePath = APIPrefix + "/github/installations/continue" InstallationAuthorizePath = APIPrefix + "/github/installations/authorize" InstallationSetupPath = APIPrefix + "/github/installations/setup" InstallationCallbackPath = APIPrefix + "/github/installations/callback" )
const APIPrefix = "/v0/workbench"
const (
MachineEnrollmentPath = APIPrefix + "/machines/enroll"
)
const StatusPath = APIPrefix + "/github/status"
Variables ¶
var ( ErrInvalidInstallationState = errors.New("workbench github app installation state is invalid") )
Functions ¶
func Collections ¶ added in v0.124.5
func Collections() []string
Collections lists every collection path shape the hub-owned stores write to, in declaration order: a root collection always precedes the subcollections nested beneath it.
A schemaless engine (Firestore, OpenVaultDB, dalgo2memory) never needs this — a collection springs into existence on first write. A schema-first engine does: inGitDB refuses a write to a collection that has no definition on disk, and a nested path must be declared as a subcollection of its root. The dynamic segments in a real path (a machine id, an identity digest, a generation) are document ids, not collection names, so the shapes here are the complete and finite set.
Keep this in step with the collection constants and path helpers in installation_store.go, repository_event_store.go, machine_credential_store.go and machine_snapshot_store.go; the store tests assert every one of them is listed.
func MachineID ¶ added in v0.124.5
MachineID derives the stable machine document id from the identity and the machine name. A self-hosting daemon needs it to recognise the machine it already enrolled without keeping a second record of the id.
func NewHandler ¶
func NewHandler(options HandlerOptions) http.Handler
func NewInstallationStores ¶
func NewInstallationStores(backend githubapp.DocumentStore) (InstallationStateStore, InstallationBindingStore, RepositoryEntitlementResolver, InstallationLifecycleStore)
NewInstallationStores wires the provider-owned installation stores to a host's github.com/sneat-dev/wb/api/githubapp DocumentStore. The returned binding store also implements RepositoryEntitlementResolver and InstallationLifecycleStore, so the same value can be assigned to every matching field on InstallationConnectionService, RepositoryEventService, and StatusService.
func NewMachineStores ¶ added in v0.124.5
func NewMachineStores(backend githubapp.DocumentStore) (MachineCredentialStore, MachineCredentialResolver, MachineSnapshotStore)
NewMachineStores wires the hub-owned machine stores to a host's github.com/sneat-dev/wb/api/githubapp DocumentStore. One value satisfies both MachineCredentialStore and MachineCredentialResolver, so the same backing records answer enrollment and bearer resolution.
The returned values are what MachineEnrollmentService.Store, NewMachineBearerResolver and MachineSnapshotService.Store expect, which is every persistence a loopback hub needs before an App is involved.
func NewRepositoryEventStore ¶
func NewRepositoryEventStore(backend githubapp.DocumentStore) (RepositoryEventStore, RepositoryEventStatusStore)
NewRepositoryEventStore wires the provider-owned repository event store to a host's github.com/sneat-dev/wb/api/githubapp DocumentStore. The returned value implements both RepositoryEventStore and RepositoryEventStatusStore.
Types ¶
type AppInstallationVerifier ¶
type AppInstallationVerifier interface {
VerifyAppInstallation(context.Context, int64) (VerifiedInstallation, error)
}
type EnqueueResult ¶
type EnrolledIdentity ¶
type EnrolledMachine ¶
type GitHubAppInstallationVerifier ¶
type GitHubAppInstallationVerifier struct {
Client *http.Client
AppID int64
PrivateKeyPEM []byte
APIBaseURL string
Now func() time.Time
}
func (GitHubAppInstallationVerifier) VerifyAppInstallation ¶
func (verifier GitHubAppInstallationVerifier) VerifyAppInstallation(ctx context.Context, installationID int64) (VerifiedInstallation, error)
type GitHubIdentityVerifier ¶
type GitHubOAuthVerifier ¶
type GitHubOAuthVerifier struct {
Client *http.Client
ClientID string
ClientSecret string
CallbackURL string
OAuthBaseURL string
APIBaseURL string
}
func (GitHubOAuthVerifier) ExchangeGitHubOAuthCode ¶
func (GitHubOAuthVerifier) Validate ¶
func (verifier GitHubOAuthVerifier) Validate() error
func (GitHubOAuthVerifier) VerifyGitHubIdentity ¶
func (verifier GitHubOAuthVerifier) VerifyGitHubIdentity(ctx context.Context, token string) (VerifiedGitHubIdentity, error)
type HandlerOptions ¶
type HandlerOptions struct {
ViewerResolver ViewerResolver
MachineBearer MachineBearerResolver
Enrollment *MachineEnrollmentService
Snapshots *MachineSnapshotService
Installations *InstallationConnectionService
RepositoryEvents *RepositoryEventService
Status *StatusService
Projection ProjectionProcessor
WebhookSecret []byte
AllowedOrigin string
}
type InstallationBindingStore ¶
type InstallationBindingStore interface {
IdentityHasInstallation(context.Context, string, int64) (bool, error)
// CompleteIdentityInstallationBinding atomically verifies and consumes the
// pending OAuth state and upserts only the explicitly selected installation.
// It must reject a different GitHub user when the Workbench identity already
// has bindings. A replay or state mismatch returns ErrInvalidInstallationState.
CompleteIdentityInstallationBinding(context.Context, InstallationStateDigest, InstallationState, time.Time, IdentityInstallationBinding) error
ListIdentityInstallationBindings(context.Context, string) ([]IdentityInstallationBinding, error)
}
type InstallationConnectRequest ¶
type InstallationConnectRequest struct {
InstallationID int64 `json:"installation_id,omitempty"`
}
type InstallationConnectionService ¶
type InstallationConnectionService struct {
States InstallationStateStore
AppVerifier AppInstallationVerifier
OAuthVerifier GitHubIdentityVerifier
Bindings InstallationBindingStore
InstallURL string
OAuthAuthorizeURL string
OAuthClientID string
OAuthCallbackURL string
SuccessURL string
StateSecret []byte
StateLifetime time.Duration
Random io.Reader
Now func() time.Time
}
func (InstallationConnectionService) AuthorizeOpener ¶
func (service InstallationConnectionService) AuthorizeOpener(ctx context.Context, viewer Viewer, request InstallationOpenerAuthorizationRequest) error
func (InstallationConnectionService) Begin ¶
func (service InstallationConnectionService) Begin(ctx context.Context, viewer Viewer, request InstallationConnectRequest) (InstallationConnectResponse, error)
func (InstallationConnectionService) CompleteOAuth ¶
func (service InstallationConnectionService) CompleteOAuth(ctx context.Context, state, code, browserContinuation, oauthCredential string) (InstallationRedirect, error)
func (InstallationConnectionService) CompleteSetup ¶
func (service InstallationConnectionService) CompleteSetup(ctx context.Context, state string, installationID int64, browserContinuation string) (InstallationRedirect, error)
func (InstallationConnectionService) Continue ¶
func (service InstallationConnectionService) Continue(ctx context.Context, state, openerChallenge string) (InstallationContinuation, error)
type InstallationLifecycleAction ¶
type InstallationLifecycleAction string
const ( InstallationSuspended InstallationLifecycleAction = "suspended" InstallationRevoked InstallationLifecycleAction = "revoked" InstallationRepositoriesRemoved InstallationLifecycleAction = "repositories_removed" InstallationUserAccessRemoved InstallationLifecycleAction = "user_access_removed" GitHubUserAuthorizationRevoked InstallationLifecycleAction = "github_user_authorization_revoked" RepositoryUserAccessRemoved InstallationLifecycleAction = "repository_user_access_removed" )
type InstallationLifecycleEvent ¶
type InstallationLifecycleEvent struct {
DeliveryID string `firestore:"delivery_id"`
Action InstallationLifecycleAction `firestore:"action"`
InstallationID int64 `firestore:"installation_id"`
RepositoryIDs []int64 `firestore:"repository_ids,omitempty"`
RepositoryID int64 `firestore:"repository_id,omitempty"`
GitHubUserID int64 `firestore:"github_user_id,omitempty"`
}
type InstallationLifecycleStore ¶
type InstallationLifecycleStore interface {
// ApplyInstallationLifecycle is idempotent by DeliveryID and atomically
// removes or disables every affected entitlement before it returns success.
// A GitHubUserAuthorizationRevoked event applies to every binding for that
// GitHub user; RepositoryUserAccessRemoved applies only to its exact
// installation, repository, and GitHub user tuple.
ApplyInstallationLifecycle(context.Context, InstallationLifecycleEvent) error
}
type InstallationRedirect ¶
type InstallationState ¶
type InstallationState struct {
Kind installationStateKind `firestore:"kind"`
IdentityID string `firestore:"identity_id"`
InstallationID int64 `firestore:"installation_id,omitempty"`
BrowserContinuationDigest string `firestore:"browser_continuation_digest"`
OpenerChallengeDigest string `firestore:"opener_challenge_digest,omitempty"`
OpenerChallengeExpiresAt time.Time `firestore:"opener_challenge_expires_at,omitempty"`
OpenerAuthorizedAt time.Time `firestore:"opener_authorized_at,omitempty"`
OAuthAccessTokenCiphertext string `firestore:"oauth_access_token_ciphertext,omitempty"`
IssuedAt time.Time `firestore:"issued_at"`
ExpiresAt time.Time `firestore:"expires_at"`
}
type InstallationStateDigest ¶
type InstallationStateStore ¶
type InstallationStateStore interface {
// IssueInstallationState rejects an existing digest so a random-state
// collision cannot overwrite another pending authorization.
IssueInstallationState(context.Context, InstallationStateDigest, InstallationState) error
// ReadInstallationState returns a pending, unexpired state without consuming
// it. Store availability errors must remain distinguishable from
// ErrInvalidInstallationState so callers can safely retry transient failures.
ReadInstallationState(context.Context, InstallationStateDigest, time.Time) (InstallationState, error)
// TransitionInstallationState atomically verifies current against the stored
// pending state, consumes it, and issues next. A replay, expired state,
// mismatch, or next-digest collision returns ErrInvalidInstallationState.
TransitionInstallationState(context.Context, InstallationStateDigest, InstallationState, time.Time, InstallationStateDigest, InstallationState) error
// ReplaceInstallationState atomically verifies current against the stored
// pending state and replaces it under the same digest. It is used to retain
// an encrypted exchanged OAuth credential before any fallible post-exchange
// reads. A replay, expiry, or mismatch returns ErrInvalidInstallationState.
ReplaceInstallationState(context.Context, InstallationStateDigest, InstallationState, time.Time, InstallationState) error
}
type Machine ¶
type Machine struct {
ID string
Name string
IdentityID string
Scopes []MachineScope
}
type MachineBearerResolver ¶
func NewMachineBearerResolver ¶
func NewMachineBearerResolver(credentials MachineCredentialResolver, pepper []byte) MachineBearerResolver
type MachineCredentialBinding ¶
type MachineCredentialBinding struct {
MachineID string `firestore:"machine_id"`
MachineName string `firestore:"machine_name"`
IdentityID string `firestore:"identity_id"`
IdentityDisplayName string `firestore:"identity_display_name,omitempty"`
IssuedAt time.Time `firestore:"issued_at"`
Scopes []MachineScope `firestore:"scopes"`
}
type MachineCredentialResolver ¶
type MachineCredentialResolver interface {
ResolveMachineCredential(context.Context, MachineTokenDigest) (MachineCredentialBinding, error)
}
type MachineCredentialStore ¶
type MachineCredentialStore interface {
RotateMachineCredential(context.Context, MachineCredentialBinding, MachineTokenDigest) (MachineCredentialBinding, error)
}
type MachineEnrollmentRequest ¶
type MachineEnrollmentRequest struct {
Name string `json:"name"`
}
type MachineEnrollmentResponse ¶
type MachineEnrollmentResponse struct {
Machine EnrolledMachine `json:"machine"`
Identity EnrolledIdentity `json:"identity"`
Token string `json:"token"`
EnrolledAt time.Time `json:"enrolled_at"`
}
type MachineEnrollmentService ¶
type MachineEnrollmentService struct {
Store MachineCredentialStore
Pepper []byte
Random io.Reader
Now func() time.Time
}
func (MachineEnrollmentService) Enroll ¶
func (service MachineEnrollmentService) Enroll(ctx context.Context, viewer Viewer, request MachineEnrollmentRequest) (MachineEnrollmentResponse, error)
type MachineScope ¶
type MachineScope string
const ( ScopeSnapshotPublish MachineScope = "machine_snapshot:publish" ScopeSnapshotRead MachineScope = "machine_snapshot:read" ScopeEventsPoll MachineScope = "repository_events:poll" ScopeEventsAck MachineScope = "repository_events:ack" )
type MachineSnapshotService ¶
type MachineSnapshotService struct {
Store MachineSnapshotStore
Now func() time.Time
}
func (MachineSnapshotService) List ¶
func (service MachineSnapshotService) List(ctx context.Context, machine Machine) (machinesnapshot.ListResponse, error)
func (MachineSnapshotService) Publish ¶
func (service MachineSnapshotService) Publish(ctx context.Context, machine Machine, snapshot machinesnapshot.Snapshot) (machinesnapshot.Receipt, error)
type MachineSnapshotStore ¶
type MachineSnapshotStore interface {
StoreLatest(context.Context, StoredMachineSnapshot) (MachineSnapshotStoreResult, error)
ListLatest(context.Context) ([]StoredMachineSnapshot, error)
}
type MachineSnapshotStoreResult ¶
type MachineSnapshotStoreResult struct {
Current StoredMachineSnapshot
Updated bool
}
func ResolveLatestMachineSnapshot ¶
func ResolveLatestMachineSnapshot(current *StoredMachineSnapshot, candidate StoredMachineSnapshot) (MachineSnapshotStoreResult, error)
type MachineTokenDigest ¶
func DigestMachineToken ¶
func DigestMachineToken(token string, pepper []byte) (MachineTokenDigest, error)
type PendingRefresh ¶
type ProjectionProcessor ¶
type ProjectionProcessor interface {
ProcessProjection(context.Context, WebhookDelivery, string) error
}
ProjectionProcessor lets an existing dashboard projection subsystem consume the same already authenticated delivery while that subsystem is migrated.
type RepositoryEventService ¶
type RepositoryEventService struct {
Snapshots MachineSnapshotStore
Entitlements RepositoryEntitlementResolver
Lifecycle InstallationLifecycleStore
Store RepositoryEventStore
PollInterval time.Duration
Sleep func(context.Context, time.Duration) error
Now func() time.Time
}
func (RepositoryEventService) Acknowledge ¶
func (service RepositoryEventService) Acknowledge(ctx context.Context, machine Machine, request repositoryevent.AckRequest) (repositoryevent.AckResponse, error)
func (RepositoryEventService) EnqueueWebhook ¶
func (service RepositoryEventService) EnqueueWebhook(ctx context.Context, delivery WebhookDelivery) (EnqueueResult, error)
func (RepositoryEventService) Poll ¶
func (service RepositoryEventService) Poll(ctx context.Context, machine Machine, cursor string, limit int, wait time.Duration) (repositoryevent.PollResponse, error)
type RepositoryEventStatusStore ¶
type RepositoryEventStatusStore interface {
IdentityRepositoryEventStatus(context.Context, string) (*StatusDelivery, []PendingRefresh, []StatusError, error)
}
type RepositoryEventStore ¶
type RepositoryEventStore interface {
EnqueueForMachines(context.Context, repositoryevent.Event, []Machine) (EnqueueResult, error)
Poll(context.Context, Machine, string, int) (repositoryevent.PollResponse, error)
Acknowledge(context.Context, Machine, repositoryevent.AckRequest) (repositoryevent.AckResponse, error)
}
type StatusConnection ¶
type StatusDelivery ¶
type StatusDelivery struct {
LastReceived *StatusDeliveryMarker `json:"last_received,omitempty"`
LastAcknowledged *StatusDeliveryMarker `json:"last_acknowledged,omitempty"`
}
type StatusDeliveryMarker ¶
type StatusError ¶
type StatusInstallation ¶
type StatusInstallation struct {
ID string `json:"id"`
Account string `json:"account"`
AccountType string `json:"account_type,omitempty"`
State string `json:"state"`
RepositorySelection string `json:"repository_selection,omitempty"`
Repositories int `json:"repositories,omitempty"`
ManageURL string `json:"manage_url,omitempty"`
}
type StatusMachine ¶
type StatusResponse ¶
type StatusResponse struct {
GeneratedAt time.Time `json:"generated_at"`
Connection StatusConnection `json:"connection"`
Installations []StatusInstallation `json:"installations"`
Machines []StatusMachine `json:"machines"`
Delivery *StatusDelivery `json:"delivery,omitempty"`
PendingRefreshes []PendingRefresh `json:"pending_refreshes"`
Errors []StatusError `json:"errors"`
}
type StatusService ¶
type StatusService struct {
Bindings InstallationBindingStore
Snapshots MachineSnapshotStore
Events RepositoryEventStatusStore
AppName string
Now func() time.Time
}
func (StatusService) Read ¶
func (service StatusService) Read(ctx context.Context, viewer Viewer) (StatusResponse, error)
type StoredMachineSnapshot ¶
type VerifiedGitHubIdentity ¶
type VerifiedGitHubIdentity struct {
UserID int64 `json:"user_id"`
Login string `json:"login"`
Installations []VerifiedInstallation `json:"installations"`
}
func (VerifiedGitHubIdentity) Validate ¶
func (identity VerifiedGitHubIdentity) Validate() error
type VerifiedInstallation ¶
type VerifiedInstallation struct {
ID int64 `json:"id" firestore:"id"`
Account string `json:"account" firestore:"account"`
AccountType string `json:"account_type,omitempty" firestore:"account_type,omitempty"`
RepositorySelection string `json:"repository_selection" firestore:"repository_selection"`
Repositories []VerifiedRepository `json:"repositories" firestore:"repositories"`
State string `json:"state,omitempty" firestore:"state,omitempty"`
ManageURL string `json:"manage_url,omitempty" firestore:"manage_url,omitempty"`
}
type VerifiedRepository ¶
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package web embeds the built bench dashboard so a self-hosted hub serves the same pages the hosted instance does, with no Node runtime and no configuration.
|
Package web embeds the built bench dashboard so a self-hosted hub serves the same pages the hosted instance does, with no Node runtime and no configuration. |