token.go RawHistoryFolder
1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package domain
4
5import (
6 "crypto/sha256"
7 "encoding/hex"
8 "fmt"
9 "slices"
10 "time"
11)
12
13// TokenSecretPrefix starts every token secret.
14const TokenSecretPrefix = "ermite_"
15
16// Scope is one permission of a stored token.
17type Scope string
18
19// The scopes of ADR 0006. The last three only matter after v1 and are defined
20// now so that the token format never changes.
21const (
22 // ScopeRepoRead reads private repositories.
23 ScopeRepoRead Scope = "repo:read"
24 // ScopeRepoWrite pushes and creates a repository on first push.
25 ScopeRepoWrite Scope = "repo:write"
26 // ScopeRepoAdmin deletes a repository and changes its visibility or description.
27 ScopeRepoAdmin Scope = "repo:admin"
28 // ScopeMRWrite creates, comments on and closes merge requests.
29 ScopeMRWrite Scope = "mr:write"
30 // ScopeMRMerge merges.
31 ScopeMRMerge Scope = "mr:merge"
32 // ScopeCIWrite re-runs or cancels CI runs.
33 ScopeCIWrite Scope = "ci:write"
34 // ScopeSecretsWrite sets and deletes secrets and variables.
35 ScopeSecretsWrite Scope = "secrets:write"
36 // ScopeRunnerRegister registers a CI runner.
37 ScopeRunnerRegister Scope = "runner:register"
38 // ScopeTokenAdmin creates, lists and revokes stored tokens.
39 ScopeTokenAdmin Scope = "token:admin"
40)
41
42// AllScopes returns the nine scopes in a stable order.
43func AllScopes() []Scope {
44 return []Scope{
45 ScopeRepoRead, ScopeRepoWrite, ScopeRepoAdmin,
46 ScopeMRWrite, ScopeMRMerge,
47 ScopeCIWrite, ScopeSecretsWrite, ScopeRunnerRegister,
48 ScopeTokenAdmin,
49 }
50}
51
52// ParseScope parses the text form of a scope, such as "repo:read".
53func ParseScope(s string) (Scope, error) {
54 sc := Scope(s)
55 if slices.Contains(AllScopes(), sc) {
56 return sc, nil
57 }
58 return "", fmt.Errorf("%w: %q", ErrInvalidScope, s)
59}
60
61// String returns the scope as text.
62func (s Scope) String() string { return string(s) }
63
64// TokenHash is the SHA-256 of a token secret. Secrets are never stored.
65type TokenHash [sha256.Size]byte
66
67// HashTokenSecret returns the hash of a token secret.
68func HashTokenSecret(secret string) TokenHash { return sha256.Sum256([]byte(secret)) }
69
70// String returns the hash as lowercase hex.
71func (h TokenHash) String() string { return hex.EncodeToString(h[:]) }
72
73// Token is a stored credential: what it may do, where, and until when.
74type Token struct {
75 // ID identifies the token; it is not secret.
76 ID string
77 Label string
78 Hash TokenHash
79 // Scopes is never empty.
80 Scopes []Scope
81 // Repos limits the token to these repositories; empty means every one.
82 Repos []RepoName
83 // ExpiresAt is the zero time for a token that never expires.
84 ExpiresAt time.Time
85 CreatedAt time.Time
86 LastUsedAt time.Time // zero until first used
87}
88
89// Expired reports whether the token is no longer valid at now.
90func (t Token) Expired(now time.Time) bool {
91 return !t.ExpiresAt.IsZero() && !now.Before(t.ExpiresAt)
92}