forked from MHSanaei/3x-ui
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapi_token_cli_test.go
More file actions
148 lines (125 loc) · 4.39 KB
/
Copy pathapi_token_cli_test.go
File metadata and controls
148 lines (125 loc) · 4.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package main
// GetApiToken rotates a credential rather than displaying one, so these pin
// which token name it destroys — the whole point of the -tokenName flag.
import (
"flag"
"testing"
"github.com/mhsanaei/3x-ui/v3/internal/config"
"github.com/mhsanaei/3x-ui/v3/internal/database"
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
"github.com/mhsanaei/3x-ui/v3/internal/web/service/panel"
)
func newTokenCLIEnv(t *testing.T) {
t.Helper()
t.Setenv("XUI_DB_FOLDER", t.TempDir())
if err := database.InitDB(config.GetDBPath()); err != nil {
t.Fatalf("init db: %v", err)
}
t.Cleanup(func() { _ = database.CloseDB() })
}
func tokenNames(t *testing.T) []string {
t.Helper()
tokens, err := (&panel.ApiTokenService{}).List()
if err != nil {
t.Fatalf("list tokens: %v", err)
}
names := make([]string, 0, len(tokens))
for _, token := range tokens {
names = append(names, token.Name)
}
return names
}
func tokenRow(t *testing.T, name string) model.ApiToken {
t.Helper()
var row model.ApiToken
if err := database.GetDB().Where("name = ?", name).First(&row).Error; err != nil {
t.Fatalf("load token %q: %v", name, err)
}
return row
}
func hasName(names []string, want string) bool {
for _, name := range names {
if name == want {
return true
}
}
return false
}
// The bug: two callers sharing one hardcoded slot silently revoke each other.
// A named token must leave an differently-named one authenticating.
func TestGetApiTokenRotatesOnlyTheNamedToken(t *testing.T) {
newTokenCLIEnv(t)
svc := panel.ApiTokenService{}
weekly, err := svc.RecreateByName("weekly-report")
if err != nil {
t.Fatalf("seed weekly-report: %v", err)
}
GetApiToken(true, "ci-bot")
names := tokenNames(t)
if !hasName(names, "ci-bot") {
t.Fatalf("token names = %v, want ci-bot among them", names)
}
if !svc.Match(weekly.Token) {
t.Fatal("weekly-report was revoked by a call naming ci-bot")
}
}
// An explicit name has to win on both branches, or the same command would
// produce ci-bot on a populated panel and "install" on a fresh one.
func TestGetApiTokenUsesGivenNameOnEmptyDatabase(t *testing.T) {
newTokenCLIEnv(t)
GetApiToken(true, "ci-bot")
names := tokenNames(t)
if !hasName(names, "ci-bot") {
t.Fatalf("token names = %v, want ci-bot among them", names)
}
if hasName(names, installTokenName) {
t.Fatalf("token names = %v, want no %s when a name was given", names, installTokenName)
}
}
// install.sh records the token it gets on a fresh panel. A later bare
// -getApiToken must rotate the fallback slot and leave that record valid.
func TestGetApiTokenPreservesInstallTokenWhenRotating(t *testing.T) {
newTokenCLIEnv(t)
GetApiToken(true, "")
installed := tokenRow(t, installTokenName)
GetApiToken(true, "")
names := tokenNames(t)
if !hasName(names, cliFallbackTokenName) {
t.Fatalf("token names = %v, want %s among them", names, cliFallbackTokenName)
}
if got := tokenRow(t, installTokenName); got.Id != installed.Id {
t.Fatalf("%s row id = %d, want %d — the installer's token was replaced", installTokenName, got.Id, installed.Id)
}
if got := tokenRow(t, installTokenName); got.Token != installed.Token {
t.Fatalf("the %s token hash changed, so the recorded credential stopped working", installTokenName)
}
}
// `-getApiToken true -tokenName ci-bot` parses tokenName as "", because flag
// stops at the positional. The command must not then rotate the shared slot.
func TestGetApiTokenWarnsOnIgnoredPositionalArgs(t *testing.T) {
set := flag.NewFlagSet("setting", flag.ContinueOnError)
var getApiToken bool
var tokenName string
set.BoolVar(&getApiToken, "getApiToken", false, "")
set.StringVar(&tokenName, "tokenName", "", "")
if err := set.Parse([]string{"-getApiToken", "true", "-tokenName", "ci-bot"}); err != nil {
t.Fatalf("parse: %v", err)
}
if tokenName != "" {
t.Fatalf("tokenName = %q; this test guards the case where flag drops it", tokenName)
}
if got := set.Args(); len(got) == 0 {
t.Fatal("leftover arguments must be visible so the CLI can warn instead of silently rotating cli-fallback")
}
}
func TestGetApiTokenTrimsName(t *testing.T) {
newTokenCLIEnv(t)
if _, err := (&panel.ApiTokenService{}).RecreateByName("seed"); err != nil {
t.Fatalf("seed: %v", err)
}
GetApiToken(true, " ")
names := tokenNames(t)
if !hasName(names, cliFallbackTokenName) {
t.Fatalf("token names = %v, want a whitespace-only name to fall back to %s", names, cliFallbackTokenName)
}
}