console

package
v1.76.2 Latest Latest
Warning

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

Go to latest
Published: Oct 12, 2026 License: MIT Imports: 29 Imported by: 0

Documentation

Overview

Package console is the Slack module's half of the console: what an operator does to Slack through the console (connecting a workspace, creating and installing Apps, defining channels and Slack Connect channels) runs here, on the module's own records and credentials, as the caller the console authorized (docs/decisions/0071, v1.76 D-v176-1 b).

The console keeps what is the browser's: it signs the person in, takes the coarse role, signs the state of an install and pins the browser to it, and renders the page a callback lands on. It asks this package for the rest through modcall, as the caller class `console` and no other; Register puts the methods on the module's server. Authorization against the module's own records (who owns a workspace) runs here, on the caller snapshot, through internal/consoleauthz.

A handler returns a Connect error for what the person is told, as the console always did; Register turns it into what crosses the wire: a refusal the transport carries (forbidden, unauthenticated), a consolecall.Problem for every other answer to the question asked, and nothing but a code for a failure of the module's own.

Index

Constants

View Source
const (
	// CatalogueCallbackPath is where Slack sends the browser back to after an
	// owner installs a catalogue App: a route of the console.
	CatalogueCallbackPath = "/connect/slack/catalogue/callback"
	// CatalogueBind prefixes the App's id in a catalogue flow's signed state,
	// so no other flow's state can finish it, and the other way round.
	CatalogueBind = "slack-catalogue:"
)

The states a workspace's connection is shown in, and a catalogue App's.

View Source
const (
	MethodBeginWorkspaceConnect = "console.begin_workspace_connect"
	MethodCompleteWorkspace     = "console.complete_workspace"
	MethodDisconnectWorkspace   = "console.disconnect_workspace"
	MethodChangeOwner           = "console.change_owner"
	MethodRequestPass           = "console.request_pass"
	MethodGetStatus             = "console.get_status"
	MethodConfirmRemovals       = "console.confirm_removals"

	MethodListApps          = "console.list_apps"
	MethodCreateApp         = "console.create_app"
	MethodInstallApp        = "console.install_app"
	MethodCompleteCatalogue = "console.complete_catalogue"

	MethodListChannels        = "console.list_channels"
	MethodCreateChannel       = "console.create_channel"
	MethodUpdateChannel       = "console.update_channel"
	MethodDeleteChannel       = "console.delete_channel"
	MethodListSharedChannels  = "console.list_shared_channels"
	MethodCreateSharedChannel = "console.create_shared_channel"
	MethodUpdateSharedChannel = "console.update_shared_channel"
	MethodDeleteSharedChannel = "console.delete_shared_channel"
)

The console methods of the Slack module, as `slack.<method>`. Each answers the caller class `console` alone (consolecall.Handle).

View Source
const Module = "slack"

Module is the module name the methods are registered under.

View Source
const WorkspaceBind = "slack-workspace:"

WorkspaceBind prefixes the workspace key in a workspace connect's signed state, so no other flow's state can finish it, and the other way round.

View Source
const WorkspaceCallbackPath = "/connect/slack/workspace/callback"

WorkspaceCallbackPath is where Slack sends the browser back to after an owner installs the roster's App into a workspace: a route of the console.

Variables

This section is empty.

Functions

func AppSettingsURL

func AppSettingsURL(appID string) string

AppSettingsURL is where an App is managed and deleted.

func CodeOf

func CodeOf(p *consolecall.Problem) connect.Code

CodeOf is the Connect code a Problem names, a failed precondition when it names none.

func NewServer

func NewServer(c *Service) *modcall.Server

NewServer is the module's server with the console methods on it: what a deployment that has not split the module out calls in its own process.

func Register

func Register(s *modcall.Server, c *Service)

Register puts the console methods on the module's server.

func RegisterLazy

func RegisterLazy(s *modcall.Server, log *slog.Logger, open func(context.Context) (*Service, error))

RegisterLazy puts the console methods on the server over a service that open assembles on the first call of a container and returns again for the rest: a scheduled pass needs none of it. A service that cannot be opened is a call the transport answers unavailable, with no detail, and the next call tries again.

func ValidAppID

func ValidAppID(id string) bool

ValidAppID reports whether id is a catalogue App's id.

Types

type APIOption

type APIOption = slackapp.Option

APIOption configures the calls to Slack: a test points them at a fake.

type Authorizer

type Authorizer interface {
	Policy() *policy.Set
}

Authorizer is the policy in force, which the module reads.

type Book

type Book map[string]connection.Record

Book is every connected Slack workspace's record by key. What the policy leaves out of a workspace — which team it is, which directory owns it — is read here, from what connecting it recorded.

func (Book) May

func (b Book) May(id access.Identity, want access.Role, workspace string) bool

May reports whether an identity holds a role over one Slack workspace: by the recorded owner once it is connected. A workspace nobody has connected has no owner to be a viewer or an operator of; the one thing to do with it is connect it, so whoever could (consoleauthz.ResolveOwner) is let in.

func (Book) MayAct

func (b Book) MayAct(id access.Identity, workspace string) bool

MayAct is whether the caller passes Service.RequireSlack for the workspace: the operator of its recorded owner, or installation-wide, which is all anyone but the installation-wide operator can be of a workspace that has no owner.

func (Book) Owner

func (b Book) Owner(workspace string) string

Owner is the directory workspace recorded as a workspace's owner, "" when it has none or is not connected.

func (Book) Team

func (b Book) Team(workspace string) string

Team is the Slack team id recorded at the workspace's first install, "" before it.

type Catalogue

type Catalogue = slackcatalogue.Catalogue

Catalogue is every Slack App a deployment declares.

type CatalogueApps

type CatalogueApps interface {
	Put(ctx context.Context, record catalogueapp.Record, credentials catalogueapp.Credentials) error
	List(ctx context.Context) ([]catalogueapp.Record, error)
	// Get is one App's record and credentials, installed or not.
	Get(ctx context.Context, id string) (catalogueapp.Record, catalogueapp.Credentials, bool, error)
}

CatalogueApps is where catalogue Slack Apps are kept: every App's record, client credentials and bot token, by its catalogue id.

type ChannelRecords

type ChannelRecords interface {
	List(ctx context.Context) ([]connection.ChannelRecord, error)
	// Apply reads one record (nil when there is none), asks decide what it
	// becomes (nil deletes it) and writes that under the object's version.
	// decide is given every record as read, for a check across records.
	Apply(ctx context.Context, workspace, name string,
		decide func(current *reconcile.ConsoleChannel, all []connection.ChannelRecord) (*reconcile.ConsoleChannel, error)) error
}

ChannelRecords is where console channels' records are kept: the records the Slack controller reads.

type CompleteRequest

type CompleteRequest struct {
	// Bind is what the verified state binds: the workspace's or the App's key,
	// with the flow's prefix.
	Bind string `json:"bind"`
	// Code is Slack's one-time code for the bot token.
	Code string `json:"code,omitempty"`
	// Denied is the `error` Slack sent instead of a code, when the install was
	// not approved.
	Denied string `json:"denied,omitempty"`
	// Recheck says the caller is the person signed in now, whose role is asked
	// again by the rule of the call that began the flow. A flow finished by
	// someone who is not signed in here rests on the state alone.
	Recheck bool `json:"recheck,omitempty"`
}

CompleteRequest is what the console forwards of Slack's redirect, once it has checked the signed state and the cookie the flow was pinned with. The code is single use: a call that spends it is never repeated, so a lost answer is a lost install, to be started again.

type Completion

type Completion struct {
	Refused *Refusal `json:"refused,omitempty"`
}

Completion is what became of a redirect: nothing to say when it finished, or the refusal the console shows on the page the browser lands on.

type Deps

type Deps struct {
	// Log is where what is not an audit record goes; nil is the default logger.
	Log *slog.Logger
	// Authorizer is the policy in force.
	Authorizer Authorizer
	// SlackCatalogue is every Slack App the deployment declares. Nil declares
	// none.
	SlackCatalogue *slackcatalogue.Catalogue
	// SlackCatalogueApps is where catalogue Slack Apps are kept.
	SlackCatalogueApps CatalogueApps
	// SlackShared is where Slack Connect channel definitions are kept.
	SlackShared SharedRecords
	// SlackChannels is where console channels' records are kept.
	SlackChannels ChannelRecords
	// SlackStatus is what the Slack controller last reported.
	SlackStatus StatusReports
	// SlackWorkspaces is where connected workspaces and operators'
	// confirmations are kept.
	SlackWorkspaces Workspaces
	// SlackAPI configures the calls to Slack: a test points them at a fake.
	SlackAPI []slackapp.Option
	// Audit records what an identity did. Nil records nothing.
	Audit audit.Recorder
	// Trigger is told which workspace a write concerns, so the controller's pass
	// over it runs soon. A notification is a hint: failing to send one is
	// logged. Nil notifies nobody.
	Trigger port.Trigger
}

Deps is everything the module's console methods need. A store that is nil is a deployment that keeps nothing of that kind, and the method says so.

type Refusal

type Refusal struct {
	// Status is the HTTP status of the page: a 4xx, never a 5xx, which a CDN in
	// front would replace with a page of its own.
	Status  int    `json:"status"`
	Summary string `json:"summary"`
	// Detail is what Slack said.
	Detail string `json:"detail,omitempty"`
	// Reason is the short sentence the log and the audit record carry.
	Reason string   `json:"reason"`
	Causes []string `json:"causes,omitempty"`
}

Refusal is a redirect that could not finish. It is told as data, since the words are the ones the person reads on the page.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service is the Slack module's console methods. It reads its Deps afresh at every call, so a deployment that connects a store late still serves it.

func NewService

func NewService(deps func() Deps) *Service

NewService is the service over deps, read at each call.

func Static

func Static(deps Deps) *Service

Static is a service over deps that never change.

func (*Service) BeginSlackWorkspaceConnect

BeginSlackWorkspaceConnect creates the roster's App in a workspace, or prepares the reinstall of one created earlier, and returns Slack's authorize URL.

The configuration token is read from the request into one local, used for one call, and dropped: it is not written to the Secret or a record, not put into the signed state, not in any log line or audit record, and not in any error returned (errors from Slack never carry it).

func (*Service) Book

func (c *Service) Book(ctx context.Context) (Book, error)

Book is the connected workspaces' records.

func (*Service) ChangeSlackWorkspaceOwner

ChangeSlackWorkspaceOwner changes the directory recorded as a connected workspace's owner, or removes it. The installation-wide operator alone: an owner that could hand its workspace to another, or take one, would make "who may operate this" a thing its operators decide.

func (*Service) CompleteCatalogue

func (c *Service) CompleteCatalogue(ctx context.Context, caller consolecall.Caller, req CompleteRequest) (Completion, error)

CompleteCatalogue finishes a catalogue App's install: the console has verified where Slack sent the owner after installing it, and forwards the one-time code for the bot token.

The token is kept only if it belongs to the workspace the policy names for the App. Any other workspace is refused: the token is dropped without being stored, and the refusal is recorded.

func (*Service) CompleteWorkspace

func (c *Service) CompleteWorkspace(ctx context.Context, caller consolecall.Caller, req CompleteRequest) (Completion, error)

CompleteWorkspace finishes a workspace's install: the console has verified where Slack sent the owner after installing the roster's App, and forwards the one-time code for the bot token.

The first install records which Slack team the workspace is. Every later install must belong to the same team; a token for any other is revoked and dropped without being stored, and the refusal is recorded.

func (*Service) ConfirmSlackRemovals

ConfirmSlackRemovals lets exactly the removal set the operator saw go ahead. The fingerprint must be the one the latest report shows for that gate — the workspace's, or the named channel's: confirming a set that has since changed would confirm people nobody looked at.

func (*Service) CreateSlackApp

CreateSlackApp creates a catalogue App in Slack.

The configuration token is read from the request into one local, used for one call, and dropped: it is not written to the Secret or a record, not put into the signed state, not in any log line or audit record, and not in any error returned (errors from Slack never carry it).

func (*Service) CreateSlackChannel

CreateSlackChannel puts a channel under management: operator of the workspace's owner, or of the installation.

func (*Service) CreateSlackSharedChannel

CreateSlackSharedChannel defines a channel: operator of the host workspace's owner, or of the installation.

func (*Service) DeleteSlackChannel

DeleteSlackChannel forgets the record. The channel stays in Slack, unless the request asks for it to be archived too.

func (*Service) DeleteSlackSharedChannel

DeleteSlackSharedChannel forgets the record. The channel stays in Slack.

func (*Service) DisconnectSlackWorkspace

DisconnectSlackWorkspace revokes the bot token and forgets the connection.

func (*Service) GetSlackStatus

GetSlackStatus is every Slack workspace the caller may view: the ones the policy declares, and any the controller reports on or a connection exists for that it no longer does (which only the installation-wide role sees, for want of an owner). A workspace's owner and team are the ones its connection recorded.

func (*Service) InstallSlackApp

InstallSlackApp starts installing, or reinstalling, a created App: the response is Slack's authorize URL carrying signed state, and the header pins the flow to this browser.

func (*Service) ListSlackApps

ListSlackApps is every declared App, and every App created from an entry no longer declared, with where each stands.

func (*Service) ListSlackChannels

ListSlackChannels is every record the caller may see, the channels the bots see that nothing manages, and the choices a form offers.

func (*Service) ListSlackSharedChannels

ListSlackSharedChannels is every record the caller may see, with where the controller has each.

func (*Service) RequestSlackPass

RequestSlackPass asks the Slack controller to pass over a workspace now. The request is a marker the controller notices within its poll (see the controller's watch); a second one under a minute after the first is refused, so the button cannot be leaned on.

func (*Service) RequireAnySlack

func (c *Service) RequireAnySlack(ctx context.Context, caller consolecall.Caller, want access.Role) (access.Identity, Book, error)

RequireAnySlack is the gate on a page that lists Slack workspaces and then shows only the ones the caller may see: the installation-wide role, or the role over the directory that owns at least one Slack workspace, or the operator role over any directory when a declared workspace is still to be connected (the caller may be the one to connect it). A scoped role over a directory that owns none has nothing here to see, and is refused rather than shown an empty page that looks like an answer.

func (*Service) RequireSlack

func (c *Service) RequireSlack(ctx context.Context, caller consolecall.Caller, want access.Role, workspace string) (access.Identity, error)

RequireSlack is consoleauthz.RequireOwner for one Slack workspace: the operator of its recorded owning directory, or the installation-wide role.

func (*Service) UpdateSlackChannel

UpdateSlackChannel changes mode, ignore and sources. The caller's role is asked about the STORED workspace, which a request cannot change.

func (*Service) UpdateSlackSharedChannel

UpdateSlackSharedChannel changes with, from (directory groups) and private. The caller's role is asked about the STORED host, which a request cannot change.

func (*Service) WorkspaceStore

func (c *Service) WorkspaceStore() (Workspaces, error)

WorkspaceStore is where connected workspaces are kept, or the refusal where the deployment keeps none.

type SharedRecords

type SharedRecords interface {
	List(ctx context.Context) ([]connection.SharedRecord, error)
	// Apply reads one record (nil when there is none), asks decide what it
	// becomes (nil deletes it) and writes that under the object's version.
	Apply(ctx context.Context, name string, decide func(current *reconcile.SharedChannel) (*reconcile.SharedChannel, error)) error
}

SharedRecords is where Slack Connect channel definitions are kept: the records the Slack controller reads.

type StatusReports

type StatusReports interface {
	Reports(ctx context.Context) (map[string]string, error)
}

StatusReports is what the Slack controller last reported, one document per workspace.

type Workspaces

type Workspaces interface {
	// Put writes a record and its credential.
	Put(ctx context.Context, record connection.Record, credential connection.Credential) error
	List(ctx context.Context) ([]connection.Record, error)
	// Get is one workspace's record and credential; found is false when
	// there is no credential.
	Get(ctx context.Context, workspace string) (connection.Record, connection.Credential, bool, error)
	// SetOwner changes only a record's owner, under the object's version;
	// found is false when the workspace has no record.
	SetOwner(ctx context.Context, workspace, owner string) (previous string, found bool, err error)
	// RequestPass keeps an operator's request for a pass now; kept is false,
	// with the time of the last request, when that was under a minute ago.
	RequestPass(ctx context.Context, request connection.PassRequest) (kept bool, last time.Time, err error)
	// PassRequests are the last request for each workspace.
	PassRequests(ctx context.Context) (map[string]connection.PassRequest, error)
	// Delete forgets a workspace and what belongs to it.
	Delete(ctx context.Context, workspace string) error
	PutConfirmation(ctx context.Context, confirmation connection.Confirmation) error
	// Confirmations are every confirmation by connection.ConfirmationKey.
	Confirmations(ctx context.Context) (map[string]connection.Confirmation, error)
}

Workspaces is where connected Slack workspaces are kept: each one's record, which the console shows, and credential, which only the Slack controller acts with. It also keeps the operators' confirmations of removal sets.

Jump to

Keyboard shortcuts

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