Skip to main content

internal/api/portalactions.go

internal/api · 272 lines · 17 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
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", 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

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