Skip to main content

internal/api/portalactions.go

internal/api · 317 lines · 19 declarations · source

Declarations​

type portalAction​

type portalAction struct {
// name is the ledger's name for the operation, so an action here and a call to the management
// API are one identifier in the record rather than two that have to be correlated.
name string
path string
// confirm is the form field whose value must equal the target's identifier before an
// irreversible action runs. Empty for anything the operator can simply do again.
confirm string
// recordedByStore is set for an action whose store writes its ledger row in the same transaction
// as the change, so the row and the change cannot disagree. perform then records only a refusal
// the store never saw. Recording the outcome again would count one operation twice on the
// overview and in the breakdown (#57).
recordedByStore bool
run func(*Portal, *http.Request, credential.Grant) (string, string, error)
}

portalAction is one thing the portal can do, and what it costs to be wrong about it.

source

var portalActions​

var portalActions = []portalAction{
{name: domain.AuditProjectCreate, path: "/actions/create", run: (*Portal).create},
{name: domain.AuditProjectSuspend, path: "/actions/suspend", run: (*Portal).suspend},
{name: domain.AuditProjectResume, path: "/actions/resume", run: (*Portal).resume},
{name: domain.AuditFormationUnpark, path: "/actions/unpark", recordedByStore: true, run: (*Portal).unpark},
{name: domain.AuditCredentialIssue, path: "/actions/issue", run: (*Portal).issue},
// Irreversible: the token is gone and every client holding it stops.
{name: domain.AuditCredentialRevoke, path: "/actions/revoke", confirm: "id", run: (*Portal).revoke},
{name: domain.AuditAuditSeal, path: "/actions/seal", run: (*Portal).seal},
}

portalActions is the set, declared as data so a test can walk it — the same reason the memory surface's operations are a table. An action added without a ledger name, or without the confirm rule its reversibility calls for, is caught by a test rather than by an operator.

source

method Portal.mountActions​

func (p *Portal) mountActions(mux *http.ServeMux)

mountActions registers every declared action. One loop, so a route cannot exist without its declaration and its declaration cannot exist without a route.

source

method Portal.perform​

func (p *Portal) perform(w http.ResponseWriter, r *http.Request, grant credential.Grant, session string, action portalAction)

perform runs one action, records it, and carries its outcome to the page that follows.

source

method Portal.back​

func (p *Portal) back(r *http.Request) string

back is where the operator is sent afterwards: the project page they acted from, or the instance page. Built from the form's project field and validated as a project name, never taken from a referer or a redirect parameter — either of those is a caller choosing where a signed-in browser goes next.

source

method Portal.create​

func (p *Portal) create(r *http.Request, _ credential.Grant) (string, string, error)

create provisions a project's storage. Reversible in the sense that matters: an empty project can be suspended and ignored, and nothing anybody owns is at stake in one existing.

source

method Portal.suspend​

func (p *Portal) suspend(r *http.Request, _ credential.Grant) (string, string, error)

source

method Portal.resume​

func (p *Portal) resume(r *http.Request, _ credential.Grant) (string, string, error)

source

method Portal.unpark​

func (p *Portal) unpark(r *http.Request, grant credential.Grant) (string, string, error)

source

method Portal.issue​

func (p *Portal) issue(r *http.Request, _ credential.Grant) (string, string, error)

source

method Portal.revoke​

func (p *Portal) revoke(r *http.Request, _ credential.Grant) (string, string, error)

source

method Portal.seal​

func (p *Portal) seal(r *http.Request, _ credential.Grant) (string, string, error)

source

var errInvalidPortalTarget​

var errInvalidPortalTarget = errors.New("the form named something this action cannot act on")

errInvalidPortalTarget is a form naming something this action cannot act on.

One error for every shape of bad input, because the page says the same thing to all of them: a refusal that distinguished "no such project" from "not a project name" would answer a question about what exists to whoever is guessing.

source

type recordedRefusal​

type recordedRefusal struct{ error }

recordedRefusal is a refusal the store has already written to the ledger, so perform does not write it again. It unwraps to the refusal, so every check on what kind of refusal it was still holds.

source

method recordedRefusal.Unwrap​

func (e recordedRefusal) Unwrap() error

source

func recordableProject​

func recordableProject(posted string) string

recordableProject is the posted project name if it is a valid one, and empty otherwise — the only form of a caller-supplied project that may reach the ledger or a log.

source

func validProjectName​

func validProjectName(name string) bool

validProjectName accepts what a project is allowed to be called, and nothing else.

Checked here rather than trusted from the page. Every one of these values is interpolated into a redirect and passed to a store that will use it as a scope, and a form field is a thing anybody can post — the page it came from does not make it safe.

source

func validUUID​

func validUUID(value string) bool

validUUID accepts the canonical form and nothing else, so an identifier reaching a store is one shape rather than whatever a form posted.

source

func PortalActionCount​

func PortalActionCount() int

PortalActionCount is how many actions the portal declares.

Exported for the test that walks them. The memory surface's guard tests enumerate its operations for the same reason: a set that is checked one entry at a time grows an entry nobody checks, and the entry nobody checks is the one whose ledger row is missing.

source