v2.2.1 99f5ba4

auth.go RawHistoryFolder

1// Package domain contains pure business types without external dependencies.
2package domain
3
4import (
5	"fmt"
6	"strings"
7	"time"
8)
9
10// AuthType represents the type of authentication configured for the registry.
11type AuthType string
12
13const (
14	// AuthTypePassword uses bcrypt-hashed password authentication.
15	AuthTypePassword AuthType = "password"
16	// AuthTypeToken uses JWT token-based authentication.
17	AuthTypeToken AuthType = "token"
18)
19
20// Token represents a generated authentication token stored in the secrets backend.
21type Token struct {
22	ID        string
23	Subject   string
24	Scopes    []string
25	IssuedAt  time.Time
26	ExpiresAt time.Time // Zero value means never expires
27	Revoked   bool
28}
29
30// TokenClaims represents the JWT claims for a token.
31type TokenClaims struct {
32	ID        string   `json:"jti"`
33	Subject   string   `json:"sub"`
34	Scopes    []string `json:"scopes"`
35	IssuedAt  int64    `json:"iat"`
36	ExpiresAt int64    `json:"exp,omitempty"` // 0 means never expires
37	Issuer    string   `json:"iss"`
38}
39
40// IsExpired checks if the token has expired.
41func (t *Token) IsExpired() bool {
42	if t.ExpiresAt.IsZero() {
43		return false // Never expires
44	}
45	return time.Now().After(t.ExpiresAt)
46}
47
48// SecretsBackend represents the type of secrets storage backend.
49type SecretsBackend string
50
51const (
52	// SecretsBackendPass uses the pass password manager.
53	SecretsBackendPass SecretsBackend = "pass"
54	// SecretsBackendSops uses SOPS for encrypted secrets.
55	SecretsBackendSops SecretsBackend = "sops"
56	// SecretsBackendUnsafe stores secrets in plain text (development only).
57	SecretsBackendUnsafe SecretsBackend = "unsafe"
58)
59
60// Scope action constants for registry operations.
61const (
62	ScopeActionPull = "pull"
63	ScopeActionPush = "push"
64	ScopeActionAll  = "*"
65)
66
67// Scope represents a Docker registry scope (repository:name:actions).
68// Format follows the Docker Registry v2 token scope specification.
69type Scope struct {
70	Type    string   // "repository" for repository access
71	Name    string   // repository name or "*" for all
72	Actions []string // ["pull", "push", "*"]
73}
74
75// ParseScope parses a Docker v2 scope string into a Scope struct.
76// Format: type:name:action1,action2 (e.g., "repository:myrepo:push,pull")
77func ParseScope(s string) (*Scope, error) {
78	parts := strings.SplitN(s, ":", 3)
79	if len(parts) != 3 {
80		return nil, fmt.Errorf("invalid scope format: %s", s)
81	}
82
83	scope := &Scope{
84		Type:    parts[0],
85		Name:    parts[1],
86		Actions: strings.Split(parts[2], ","),
87	}
88
89	// Trim whitespace from actions
90	for i, action := range scope.Actions {
91		scope.Actions[i] = strings.TrimSpace(action)
92	}
93
94	return scope, nil
95}
96
97// CanAccess checks if the scope grants the requested action on the repository.
98// Supports exact matches and wildcard matching for repository names.
99func (s *Scope) CanAccess(repoName string, action string) bool {
100	if s.Type != "repository" {
101		return false
102	}
103
104	// Check repository name match
105	if s.Name != "*" && s.Name != repoName {
106		// Check prefix match for org-level scopes (e.g., "myorg/*" matches "myorg/app")
107		if !strings.HasSuffix(s.Name, "/*") {
108			return false
109		}
110		prefix := strings.TrimSuffix(s.Name, "/*")
111		if !strings.HasPrefix(repoName, prefix+"/") {
112			return false
113		}
114	}
115
116	// Check action permission
117	for _, a := range s.Actions {
118		if a == "*" || a == action {
119			return true
120		}
121	}
122
123	return false
124}
125
126// String returns the scope as a string in Docker v2 format.
127func (s *Scope) String() string {
128	return fmt.Sprintf("%s:%s:%s", s.Type, s.Name, strings.Join(s.Actions, ","))
129}

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 7

Tags 106

↑↓ moveenter openesc close