v2.31.0-dev21 b59533b

run_auth_config_test.go RawHistoryFolder

1package app
2
3import (
4	"context"
5	"os"
6	"path/filepath"
7	"runtime"
8	"testing"
9
10	"github.com/bnema/zerowrap"
11	"github.com/spf13/viper"
12	"github.com/stretchr/testify/assert"
13	"github.com/stretchr/testify/require"
14	"golang.org/x/sys/unix"
15
16	"github.com/bnema/gordon/internal/domain"
17)
18
19func TestBuildAuthConfig_UsesConfigEnabledFlag(t *testing.T) {
20	t.Setenv(TokenSecretEnvVar, "test-token-secret-that-is-at-least-32-bytes-long")
21
22	cfg := Config{}
23	cfg.Auth.Enabled = false
24	cfg.Auth.Type = "token"
25
26	authCfg, err := buildAuthConfig(context.Background(), cfg, domain.AuthTypeToken, domain.SecretsBackendUnsafe, t.TempDir(), zerowrap.Default())
27	require.NoError(t, err)
28	assert.False(t, authCfg.Enabled, "expected auth config enabled=false")
29}
30
31func TestResolveSecretsBackend_RejectsMissingBackend(t *testing.T) {
32	_, err := resolveSecretsBackend("")
33	require.Error(t, err)
34	assert.Contains(t, err.Error(), "auth.secrets_backend is required")
35}
36
37func TestResolveSecretsBackend_RejectsUnknownBackend(t *testing.T) {
38	_, err := resolveSecretsBackend("vault")
39	require.Error(t, err)
40	assert.Contains(t, err.Error(), `unsupported auth.secrets_backend "vault"`)
41}
42
43func TestResolveAuthType_RejectsPassword(t *testing.T) {
44	cfg := Config{}
45	cfg.Auth.Type = "password"
46	_, err := resolveAuthType(cfg)
47	require.Error(t, err)
48	assert.Contains(t, err.Error(), "unsupported auth.type")
49}
50
51func TestResolveAuthType_AcceptsToken(t *testing.T) {
52	cfg := Config{}
53	cfg.Auth.Type = "token"
54	authType, err := resolveAuthType(cfg)
55	require.NoError(t, err)
56	assert.Equal(t, domain.AuthTypeToken, authType)
57}
58
59func TestResolveAuthType_AcceptsEmpty(t *testing.T) {
60	cfg := Config{}
61	authType, err := resolveAuthType(cfg)
62	require.NoError(t, err)
63	assert.Equal(t, domain.AuthTypeToken, authType)
64}
65
66func TestLoadConfig_DoesNotDefaultSecretsBackendToUnsafe(t *testing.T) {
67	v := viper.New()
68
69	err := loadConfig(v, "")
70	require.NoError(t, err)
71	assert.Empty(t, v.GetString("auth.secrets_backend"), "expected empty default for auth.secrets_backend")
72}
73
74func TestLoadSecretUnsafeRejectsTraversalAndAbsolutePaths(t *testing.T) {
75	dataDir := t.TempDir()
76	secretsDir := filepath.Join(dataDir, "secrets")
77	require.NoError(t, os.MkdirAll(secretsDir, 0700))
78	require.NoError(t, os.WriteFile(filepath.Join(secretsDir, "token"), []byte("safe-secret"), 0600))
79	outside := filepath.Join(dataDir, "outside")
80	require.NoError(t, os.WriteFile(outside, []byte("outside-secret"), 0600))
81
82	for _, path := range []string{"../outside", filepath.Join(dataDir, "outside")} {
83		t.Run(path, func(t *testing.T) {
84			_, err := loadSecret(context.Background(), domain.SecretsBackendUnsafe, path, dataDir, zerowrap.Default())
85			require.Error(t, err)
86			assert.Contains(t, err.Error(), "invalid secret path")
87		})
88	}
89}
90
91func TestLoadSecretUnsafeAllowsRelativePathUnderSecretsDir(t *testing.T) {
92	dataDir := t.TempDir()
93	secretsDir := filepath.Join(dataDir, "secrets", "auth")
94	require.NoError(t, os.MkdirAll(secretsDir, 0700))
95	require.NoError(t, os.WriteFile(filepath.Join(secretsDir, "token"), []byte("safe-secret"), 0600))
96
97	secret, err := loadSecret(context.Background(), domain.SecretsBackendUnsafe, "auth/token", dataDir, zerowrap.Default())
98	require.NoError(t, err)
99	assert.Equal(t, "safe-secret", secret)
100}
101
102func TestLoadSecretUnsafeRejectsMissingCandidate(t *testing.T) {
103	dataDir := t.TempDir()
104	secretsDir := filepath.Join(dataDir, "secrets")
105	require.NoError(t, os.MkdirAll(secretsDir, 0700))
106
107	_, err := loadSecret(context.Background(), domain.SecretsBackendUnsafe, "missing", dataDir, zerowrap.Default())
108	require.Error(t, err)
109	assert.Contains(t, err.Error(), "failed to read secret file")
110}
111
112func TestLoadSecretUnsafeRejectsIntermediateSymlinkEscape(t *testing.T) {
113	dataDir := t.TempDir()
114	secretsDir := filepath.Join(dataDir, "secrets")
115	outsideDir := filepath.Join(dataDir, "outside")
116	require.NoError(t, os.MkdirAll(secretsDir, 0700))
117	require.NoError(t, os.MkdirAll(outsideDir, 0700))
118	require.NoError(t, os.WriteFile(filepath.Join(outsideDir, "token"), []byte("outside-secret"), 0600))
119	require.NoError(t, os.Symlink(outsideDir, filepath.Join(secretsDir, "linkdir")))
120
121	_, err := loadSecret(context.Background(), domain.SecretsBackendUnsafe, "linkdir/token", dataDir, zerowrap.Default())
122	require.Error(t, err)
123	assert.Contains(t, err.Error(), "failed to open secret path component")
124}
125
126func TestLoadSecretUnsafeRejectsNonRegularFile(t *testing.T) {
127	if runtime.GOOS == "windows" {
128		t.Skip("unix mkfifo is not available on windows")
129	}
130	dataDir := t.TempDir()
131	secretsDir := filepath.Join(dataDir, "secrets")
132	require.NoError(t, os.MkdirAll(secretsDir, 0700))
133	require.NoError(t, unix.Mkfifo(filepath.Join(secretsDir, "token"), 0600))
134
135	_, err := loadSecret(context.Background(), domain.SecretsBackendUnsafe, "token", dataDir, zerowrap.Default())
136	require.Error(t, err)
137	assert.Contains(t, err.Error(), "secret must be a regular file")
138}
139
140func TestLoadSecretUnsafeRejectsSymlinkEscape(t *testing.T) {
141	dataDir := t.TempDir()
142	secretsDir := filepath.Join(dataDir, "secrets")
143	require.NoError(t, os.MkdirAll(secretsDir, 0700))
144	outside := filepath.Join(dataDir, "outside")
145	require.NoError(t, os.WriteFile(outside, []byte("outside-secret"), 0600))
146	require.NoError(t, os.Symlink(outside, filepath.Join(secretsDir, "token")))
147
148	_, err := loadSecret(context.Background(), domain.SecretsBackendUnsafe, "token", dataDir, zerowrap.Default())
149	require.Error(t, err)
150	assert.Contains(t, err.Error(), "failed to read secret file")
151}

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