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.
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.
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.
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.
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.
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.
method Portal.suspend
func (p *Portal) suspend(r *http.Request, _ credential.Grant) (string, string, error)
method Portal.resume
func (p *Portal) resume(r *http.Request, _ credential.Grant) (string, string, error)
method Portal.unpark
func (p *Portal) unpark(r *http.Request, grant credential.Grant) (string, string, error)
method Portal.issue
func (p *Portal) issue(r *http.Request, _ credential.Grant) (string, string, error)
method Portal.revoke
func (p *Portal) revoke(r *http.Request, _ credential.Grant) (string, string, error)
method Portal.seal
func (p *Portal) seal(r *http.Request, _ credential.Grant) (string, string, error)
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.
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.
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.
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.
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.