Skip to content
quarry Artifact [d11c96dac4]
Login

Artifact [d11c96dac4]

Artifact d11c96dac4a6421d238c463d6409a07b84e1a09b71d9080736ee490abf3f3f0e:


// agent.go — which accounts are machine-operated, and who answers for
// them.
//
// A forge that cannot say this has a record it cannot read back. On
// this one, in a single day: eleven tickets filed by an automated
// campaign under a person's name, permanent in every clone, indexed
// forever as that person's words; a sync credential minted in the
// operator's name by a machine; and two agent sessions writing all day
// with nothing on any page to say either was a machine.
//
// The mark is on the ACCOUNT, not the write. A write-level mark is more
// honest and much larger — it would have to reach the artifact format —
// and the account-level one is what makes the write-level one worth
// building later: you cannot ask "which of this account's writes were
// machine-made" until you can ask "is this account a machine".
package oltp

import (
	"database/sql"
	"errors"
	"fmt"
)

// Operator names the person answerable for a machine-operated account.
// Empty means the account is a person's own.
func (d *DB) Operator(name string) string {
	var op string
	if err := d.db.QueryRow(`SELECT operator FROM account WHERE name = ?`, name).Scan(&op); err != nil {
		return ""
	}
	return op
}

// SetOperator marks an account as machine-operated and records who runs
// it; an empty operator clears the mark.
//
// The GATE is the caller's, not this function's — see api's account.agent
// for the asymmetry: declaring yourself an agent is always allowed and
// undeclaring is a site admin's call. Stated there rather than here
// because it is a policy about callers, and this is a table.
func (d *DB) SetOperator(name, operator string) error {
	if name == "" {
		return errors.New("oltp: empty account name")
	}
	if operator != "" && !validLogin(operator) {
		return fmt.Errorf("oltp: operator %q is not an account name", operator)
	}
	if operator != "" {
		var one int
		err := d.db.QueryRow(`SELECT 1 FROM account WHERE name = ?`, operator).Scan(&one)
		if errors.Is(err, sql.ErrNoRows) {
			// An operator who is not an account is a name nobody can
			// be asked about, which is the thing this exists to end.
			return fmt.Errorf("oltp: no account named %q to answer for %q", operator, name)
		} else if err != nil {
			return err
		}
	}
	res, err := d.w.Exec(`UPDATE account SET operator = ? WHERE name = ?`, operator, name)
	if err != nil {
		return err
	}
	if n, _ := res.RowsAffected(); n == 0 {
		return fmt.Errorf("oltp: no such account %q", name)
	}
	return nil
}