Skip to content
quarry Artifact [f3e6feae08]
Login

Artifact [f3e6feae08]

Artifact f3e6feae08595c50623c4e30b315e1fde038c06e60b3c53c2ee6bc16d7c11326:


package api

import (
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"log"
	"strings"
	"time"

	"craftdesign.group/quarry/internal/oltp"
	"craftdesign.group/quarry/internal/ops"
)

// A sign-off's secret is what an approved call minted (a token's value)
// held for the asking agent to collect once. It is sealed at rest with
// the forge's secret key, the box CI secrets use, so it is never in the
// database or a backup in the clear; and it lapses: uncollected after
// signOffSecretLife it is cleared, and a worker token it held is
// revoked.
const (
	signOffSecretLife = oltp.SignOffSecretLife
	sealedPrefix      = "sealed:"
)

// signOffAsk says, inside a run of an approved request, who asked and
// which request: the settings log names them beside the approver.
type signOffAsk struct{ agent, id string }

type signOffAskKey struct{}

func withSignOffAsk(ctx context.Context, s SignOffRequest) context.Context {
	return context.WithValue(ctx, signOffAskKey{}, signOffAsk{agent: s.Agent, id: s.ID})
}

func signOffAskOf(ctx context.Context) (signOffAsk, bool) {
	a, ok := ctx.Value(signOffAskKey{}).(signOffAsk)
	return a, ok
}

func (d Deps) sealSignOffSecret(id, secret string) (string, error) {
	if secret == "" {
		return "", nil
	}
	if !d.SecretBox.Ready() {
		return "", fmt.Errorf("this forge has no secret key, so it cannot hold a credential for collection: %w", ops.ErrUnavailable)
	}
	sealed, err := d.SecretBox.Seal("signoff", id, "secret", secret)
	if err != nil {
		return "", err
	}
	return sealedPrefix + base64.StdEncoding.EncodeToString(sealed), nil
}

// openSignOffSecret reads what sealSignOffSecret stored. A row written
// before secrets were sealed holds plain JSON; it is handed over once
// like any other and the sweep clears the rest after the same limit.
func (d Deps) openSignOffSecret(id, stored string) (string, error) {
	raw, ok := strings.CutPrefix(stored, sealedPrefix)
	if !ok {
		return stored, nil
	}
	sealed, err := base64.StdEncoding.DecodeString(raw)
	if err != nil {
		return "", err
	}
	return d.SecretBox.Open("signoff", id, "secret", sealed)
}

// lapseRef names one credential an approved call minted, not secret.
type lapseRef struct {
	Repo  string `json:"repo"`
	Token string `json:"token"`
}

// lapseRefOf is the credential an op's answer minted, when it is one the
// sweep knows how to revoke.
func lapseRefOf(op, repo string, out any) []lapseRef {
	if op != "ci.worker.token.new" {
		return nil
	}
	if o, ok := out.(workerTokenNewOut); ok && o.Token.ID != "" {
		return []lapseRef{{Repo: repo, Token: o.Token.ID}}
	}
	return nil
}

// holdSignOffSecret records what an approved request answered: the
// credentials it minted (not secret), then the answer and the sealed
// secret. A forge that cannot seal revokes what was minted rather than
// hold it in the clear.
func (d Deps) holdSignOffSecret(ctx context.Context, s SignOffRequest, result, secret string, refs []lapseRef) error {
	sealed, err := d.sealSignOffSecret(s.ID, secret)
	if err != nil {
		for _, ref := range refs {
			d.revokeLapsed(ref, s, "no secret key to hold it")
		}
		return err
	}
	if len(refs) > 0 {
		raw, _ := json.Marshal(refs)
		if err := d.SignOffs.SetSignOffRefs(s.ID, string(raw)); err != nil {
			return err
		}
	}
	return d.SignOffs.FinishSignOff(s.ID, result, sealed)
}

// LapseSignOffSecrets clears the secrets nobody collected within their
// limit, revoking the worker tokens they held and logging each. It is
// the periodic upkeep's call; a second pass finds nothing.
func (d Deps) LapseSignOffSecrets(now time.Time) (int, error) {
	if d.SignOffs == nil {
		return 0, nil
	}
	lapsed, err := d.SignOffs.LapseSignOffSecrets(now.Add(-signOffSecretLife))
	for _, l := range lapsed {
		var refs []lapseRef
		if l.Refs != "" {
			if jerr := json.Unmarshal([]byte(l.Refs), &refs); jerr != nil {
				log.Printf("signoff %s: lapse refs: %v", l.ID, jerr)
			}
		}
		for _, ref := range refs {
			d.revokeLapsed(ref, SignOffRequest{ID: l.ID, Agent: l.Agent}, "lapsed uncollected")
		}
	}
	return len(lapsed), err
}

// revokeLapsed revokes one token its secret never reached anybody for,
// and says so in the repository's settings log.
func (d Deps) revokeLapsed(ref lapseRef, s SignOffRequest, why string) {
	if d.WorkerTokens == nil || ref.Repo == "" {
		return
	}
	own, err := d.ownWorkerTokens(ref.Repo)
	if err != nil {
		log.Printf("signoff %s: listing tokens to revoke: %v", s.ID, err)
		return
	}
	for _, t := range own {
		if t.ID != ref.Token || t.RevokedAt != "" {
			continue
		}
		if err := d.WorkerTokens.RevokeWorkerToken(t.ID); err != nil {
			log.Printf("signoff %s: revoking %s: %v", s.ID, t.ID, err)
			return
		}
		if d.SettingLog != nil {
			d.SettingLog.LogSetting(ref.Repo, "forge", "", "worker-token", tokenWords(t, ""),
				fmt.Sprintf("revoked: %s (request %s, asked by %s)", why, s.ID, s.Agent))
		}
	}
}