ci_repotoken.go RawHistoryFolder

1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package usecase
4
5import (
6	"crypto/rand"
7	"encoding/hex"
8	"slices"
9
10	"bnema.dev/ermite/internal/domain"
11	"bnema.dev/ermite/internal/ports"
12)
13
14// Job repository tokens (ADR 0025, CI-2). A job gets one at its assignment:
15// "ermitegh_" and 32 random bytes, stored hashed, read-only on the repository
16// of its run. Auth.Authenticate resolves it to a principal shaped like a
17// stored token, so git over HTTP, the API and the REST subset need no rule of
18// their own. It is written in the batch of the assignment, deleted in the
19// batch that ends the job (CompleteJob, a cancellation that concludes, a lost
20// runner, a timeout, the queue and log limits) or that takes the assignment
21// back, and with the run when the run is deleted. Its expiry is the deadline of
22// the job plus a few minutes, whatever happens.
23
24func newRepoTokenID() string {
25	var raw [8]byte
26	_, _ = rand.Read(raw[:])
27	return hex.EncodeToString(raw[:])
28}
29
30// repoTokenExpiry is when a token of the job expires at the latest: the
31// deadline of the job and the grace.
32func (c *CI) repoTokenExpiry(j domain.CIJob) (expires domain.CIRepoToken) {
33	expires.ExpiresAt = c.deadline(j).Add(domain.RepoTokenGrace)
34	return expires
35}
36
37// issueRepoToken adds a repository token for j to the next flush and returns
38// its secret. j must be assigned (its AssignedAt is set).
39func (rs *runState) issueRepoToken(j *domain.CIJob) string {
40	return rs.issueRepoTokenFor(j, rs.run.Repo, "")
41}
42
43// issueRepoTokenFor is issueRepoToken for a token that reads repo. A sha makes
44// it the archive token of that commit of repo, which downloads that archive and
45// nothing else.
46func (rs *runState) issueRepoTokenFor(j *domain.CIJob, repo domain.RepoName, sha string) string {
47	c := rs.c
48	now := c.now()
49	at := *j
50	if at.AssignedAt.IsZero() {
51		at.AssignedAt = now
52	}
53	secret := newToken(domain.RepoTokenPrefix)
54	t := c.repoTokenExpiry(at)
55	t.ID, t.Hash, t.Repo = newRepoTokenID(), domain.HashTokenSecret(secret), repo
56	t.RunID, t.RunAttempt, t.JobID, t.Archive, t.Sha, t.CreatedAt = j.RunID, j.RunAttempt, j.ID, sha != "", sha, now
57	if sha == "" {
58		// The job's own token: what it may do on the run's repository follows
59		// from the permissions of the job, lowered to read when the run is not
60		// trusted (D1, D3).
61		t.Permissions = domain.TokenPermissions(j.Permissions, rs.run.Trusted)
62	}
63	rs.tokens = append(rs.tokens, t)
64	return secret
65}
66
67// revokeTokens deletes, with the next flush, every repository token of j, and
68// forgets the archive tokens kept in memory.
69func (rs *runState) revokeTokens(j *domain.CIJob) {
70	ref := ports.CIJobRef{RunID: j.RunID, RunAttempt: j.RunAttempt, JobID: j.ID}
71	if !slices.Contains(rs.revoke, ref) {
72		rs.revoke = append(rs.revoke, ref)
73	}
74	rs.c.mu.Lock()
75	delete(rs.c.archiveTokens, jobRef(*j))
76	rs.c.mu.Unlock()
77}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 4

Tags 45

↑↓ moveenter openesc close