v0.33.0 cf26365

ci_oidc_test.go RawHistoryFolder

1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package domain_test
4
5import (
6	"strings"
7	"testing"
8	"time"
9
10	"github.com/stretchr/testify/assert"
11
12	"bnema.dev/ermite/internal/domain"
13)
14
15func TestIsOIDCRequestToken(t *testing.T) {
16	t.Parallel()
17	ok := domain.OIDCRequestTokenPrefix + strings.Repeat("aB3-_", 8) + "xyz"
18	assert.True(t, domain.IsOIDCRequestToken(ok))
19	assert.False(t, domain.IsRepoToken(ok), "not a repository token")
20	for _, bad := range []string{"", domain.OIDCRequestTokenPrefix, ok + "x", strings.Replace(ok, "ermiteoidc_", "ermitegh_", 1)} {
21		assert.False(t, domain.IsOIDCRequestToken(bad), bad)
22	}
23}
24
25// Only a trusted run with id-token: write gets ID tokens: GitHub's rule, plus
26// ermite's trust rule (an untrusted run's writes are lowered, and id-token has
27// no read).
28func TestIDTokenAllowed(t *testing.T) {
29	t.Parallel()
30	write := map[string]string{"id-token": "write", "contents": "read"}
31	assert.True(t, domain.IDTokenAllowed(write, true))
32	assert.False(t, domain.IDTokenAllowed(write, false), "untrusted")
33	assert.False(t, domain.IDTokenAllowed(map[string]string{"id-token": "read"}, true), "read is not a level of id-token")
34	assert.False(t, domain.IDTokenAllowed(nil, true), "the default has no id-token")
35	assert.False(t, domain.IDTokenAllowed(map[string]string{"contents": "write"}, true))
36	all := domain.ResolvePermissions(&domain.PermissionSpec{All: domain.PermissionsWriteAll}, nil)
37	assert.True(t, domain.IDTokenAllowed(all, true), "write-all")
38	readAll := domain.ResolvePermissions(&domain.PermissionSpec{All: domain.PermissionsReadAll}, nil)
39	assert.False(t, domain.IDTokenAllowed(readAll, true), "read-all")
40}
41
42func TestOIDCSubject(t *testing.T) {
43	t.Parallel()
44	tests := []struct{ event, env, ref, want string }{
45		{"push", "", "refs/heads/main", "repo:o/r:ref:refs/heads/main"},
46		{"push", "", "refs/tags/v1", "repo:o/r:ref:refs/tags/v1"},
47		{"workflow_dispatch", "", "refs/heads/dev", "repo:o/r:ref:refs/heads/dev"},
48		{"pull_request", "", "refs/pull/1/merge", "repo:o/r:pull_request"},
49		{"pull_request_target", "", "refs/heads/main", "repo:o/r:pull_request"},
50		{"pull_request", "prod", "refs/pull/1/merge", "repo:o/r:environment:prod"},
51		{"push", "Prod", "refs/heads/main", "repo:o/r:environment:Prod"},
52	}
53	for _, tt := range tests {
54		assert.Equal(t, tt.want, domain.OIDCSubject("o/r", tt.event, tt.env, tt.ref), tt)
55	}
56}
57
58func TestOIDCKeyPublished(t *testing.T) {
59	t.Parallel()
60	at := time.Date(2026, 10, 16, 0, 0, 0, 0, time.UTC)
61	assert.True(t, domain.OIDCKey{}.Published(at), "an active key")
62	k := domain.OIDCKey{RetiredAt: at}
63	assert.True(t, k.Published(at.Add(24*time.Hour-time.Second)))
64	assert.False(t, k.Published(at.Add(24*time.Hour)))
65	assert.Equal(t, "oidc/key/abc", domain.OIDCKeyPath("abc"))
66}
67
68// An ID token request token is nobody outside the token endpoint.
69func TestOIDCRequestTokenHasNoRights(t *testing.T) {
70	t.Parallel()
71	p := domain.RepoTokenPrincipal(domain.CIRepoToken{ID: "x", OIDC: true, Permissions: map[string]string{"contents": "write"}})
72	assert.True(t, p.IsAnonymous())
73	assert.Empty(t, p.Scopes())
74}

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 2

Tags 44

↑↓ moveenter openesc close