Host the gRPC API and add token admin views

This commit is contained in:
Joe Julian
2026-03-30 07:50:34 -07:00
parent 84c188129e
commit 9afddd7a93
9 changed files with 1175 additions and 23 deletions
+109
View File
@@ -19,6 +19,7 @@ import (
"gioui.org/widget"
"git.julianfamily.org/keepassgo/apiapproval"
"git.julianfamily.org/keepassgo/apiaudit"
"git.julianfamily.org/keepassgo/apitokens"
"git.julianfamily.org/keepassgo/clipboard"
"git.julianfamily.org/keepassgo/passwords"
@@ -150,6 +151,114 @@ func TestUIChildGroupsComeFromVaultModel(t *testing.T) {
}
}
func TestUIAPITokenLifecycleManagement(t *testing.T) {
t.Parallel()
u := newUIWithSession("desktop", &session.Manager{})
u.masterPassword.SetText("correct horse battery staple")
if err := u.createVaultAction(); err != nil {
t.Fatalf("createVaultAction() error = %v", err)
}
u.showAPITokensSection()
u.apiTokenName.SetText("Browser Extension")
u.apiTokenClientName.SetText("firefox")
u.apiTokenExpiresAt.SetText("2026-04-01T15:04:05Z")
if err := u.issueAPITokenAction(); err != nil {
t.Fatalf("issueAPITokenAction() error = %v", err)
}
if strings.TrimSpace(u.apiTokenSecret) == "" {
t.Fatal("apiTokenSecret = empty, want one-time secret")
}
tokens := u.apiTokens()
if len(tokens) != 1 {
t.Fatalf("len(apiTokens()) = %d, want 1", len(tokens))
}
if tokens[0].Name != "Browser Extension" || tokens[0].ClientName != "firefox" {
t.Fatalf("issued token = %#v, want Browser Extension/firefox", tokens[0])
}
if err := u.rotateAPITokenAction(); err != nil {
t.Fatalf("rotateAPITokenAction() error = %v", err)
}
if strings.TrimSpace(u.apiTokenSecret) == "" {
t.Fatal("apiTokenSecret after rotate = empty, want one-time secret")
}
if err := u.disableAPITokenAction(); err != nil {
t.Fatalf("disableAPITokenAction() error = %v", err)
}
disabled, ok := u.selectedAPIToken()
if !ok || !disabled.Disabled {
t.Fatalf("selectedAPIToken() = %#v, want disabled token", disabled)
}
}
func TestUIAPITokenPolicyRulesCanBeAddedAndRemoved(t *testing.T) {
t.Parallel()
u := newUIWithSession("desktop", &session.Manager{})
u.masterPassword.SetText("correct horse battery staple")
if err := u.createVaultAction(); err != nil {
t.Fatalf("createVaultAction() error = %v", err)
}
u.showAPITokensSection()
u.apiTokenName.SetText("CLI")
u.apiTokenClientName.SetText("grpc-cli")
if err := u.issueAPITokenAction(); err != nil {
t.Fatalf("issueAPITokenAction() error = %v", err)
}
u.apiPolicyOperation.SetText(string(apitokens.OperationListEntries))
u.apiPolicyPath.SetText("Root / Internet")
u.apiPolicyAllow.Value = true
u.apiPolicyGroupScopeW.Value = true
if err := u.addAPIPolicyRuleAction(); err != nil {
t.Fatalf("addAPIPolicyRuleAction() error = %v", err)
}
token, ok := u.selectedAPIToken()
if !ok || len(token.Policies) != 1 {
t.Fatalf("selectedAPIToken().Policies = %#v, want 1 rule", token.Policies)
}
if token.Policies[0].Resource.Kind != apitokens.ResourceGroup {
t.Fatalf("rule kind = %q, want group", token.Policies[0].Resource.Kind)
}
if err := u.removeAPIPolicyRuleAction(0); err != nil {
t.Fatalf("removeAPIPolicyRuleAction() error = %v", err)
}
token, ok = u.selectedAPIToken()
if !ok || len(token.Policies) != 0 {
t.Fatalf("selectedAPIToken().Policies after remove = %#v, want empty", token.Policies)
}
}
func TestUIAPIAuditSectionShowsRecordedEvents(t *testing.T) {
t.Parallel()
u := newUIWithSession("desktop", &session.Manager{})
u.auditLog = apiaudit.New(10)
u.auditLog.Record(apiaudit.Event{
Type: apiaudit.EventApprovalAllowed,
TokenName: "Browser Extension",
ClientName: "firefox",
Message: "approved",
})
u.showAPIAuditSection()
events := u.apiAuditEvents()
if len(events) != 1 {
t.Fatalf("len(apiAuditEvents()) = %d, want 1", len(events))
}
if events[0].TokenName != "Browser Extension" {
t.Fatalf("apiAuditEvents()[0].TokenName = %q, want %q", events[0].TokenName, "Browser Extension")
}
}
func TestUISelectedEntryFollowsApplicationStateSelection(t *testing.T) {
t.Parallel()