MCPcopy Create free account
hub / github.com/bytebase/bytebase / TestAuditLogFormat

Function TestAuditLogFormat

backend/tests/login_audit_test.go:23–243  ·  view source on GitHub ↗

TestAuditLogFormat is both a regression test for the 3.17.0 bug where AuthService/Login (and Signup/ExchangeToken) silently dropped audit entries, AND a contract test for the shape of audit log entries returned by AuditLogService/SearchAuditLogs. Downstream consumers (SIEMs, compliance tooling, `doc

(t *testing.T)

Source from the content-addressed store, hash-verified

21// tooling, `docker logs | grep log_type:audit`) depend on this shape being
22// stable across releases — changes here are user-visible breaking changes.
23func TestAuditLogFormat(t *testing.T) {
24 a := require.New(t)
25 ctx := context.Background()
26 ctl := &controller{}
27 ctx, err := ctl.StartServerWithExternalPg(ctx)
28 a.NoError(err)
29 defer ctl.Close(ctx)
30
31 // --- Part 1: Login (workspace-scoped, allow_without_credential) ---
32 //
33 // Clear the token so the Login call runs without credentials — the exact
34 // path that regressed in 3.17.0 (Resources is empty, so the audit loop
35 // never fires unless the handler hands us the workspace).
36 adminToken := ctl.authInterceptor.token
37 ctl.authInterceptor.token = ""
38
39 loginResp, err := ctl.authServiceClient.Login(ctx, connect.NewRequest(&v1pb.LoginRequest{
40 Email: "demo@example.com",
41 Password: "1024bytebase",
42 }))
43 a.NoError(err)
44 workspace := loginResp.Msg.GetUser().GetWorkspace()
45 a.NotEmpty(workspace, "login response should carry the user's workspace")
46
47 // Restore the token so the SearchAuditLogs call below authenticates.
48 ctl.authInterceptor.token = adminToken
49
50 loginAuditLogs, err := ctl.auditLogServiceClient.SearchAuditLogs(ctx, connect.NewRequest(&v1pb.SearchAuditLogsRequest{
51 Parent: workspace,
52 Filter: `method == "/bytebase.v1.AuthService/Login"`,
53 OrderBy: "create_time desc",
54 }))
55 a.NoError(err)
56 a.NotEmpty(loginAuditLogs.Msg.AuditLogs, "Login must produce an audit entry under the caller's workspace (regression guard for 3.17.0)")
57
58 entry := loginAuditLogs.Msg.AuditLogs[0]
59 // Name: "workspaces/{id}/auditLogs/{uid}" — the resource name format is
60 // part of the API contract and also the parent users filter on.
61 a.Regexp(regexp.MustCompile(`^workspaces/[^/]+/auditLogs/[^/]+$`), entry.Name,
62 "audit log name must match workspaces/{id}/auditLogs/{uid}")
63 a.True(strings.HasPrefix(entry.Name, workspace+"/auditLogs/"),
64 "audit log must be parented under the login workspace")
65 a.NotNil(entry.CreateTime, "CreateTime must be set")
66 a.Equal("users/demo@example.com", entry.User, "User must be users/{email}")
67 a.Equal("/bytebase.v1.AuthService/Login", entry.Method,
68 "Method is part of the filter API contract and must be the full procedure name")
69 a.Equal(v1pb.AuditLog_INFO, entry.Severity, "successful Login is INFO severity")
70 a.Equal("demo@example.com", entry.Resource, "Login's Resource is the login email")
71 a.Nil(entry.Status, "successful Login has nil Status (code 0)")
72 a.NotNil(entry.Latency, "Latency must be recorded")
73
74 // Request JSON must round-trip back to LoginRequest, have the caller's
75 // email, and must NOT contain the plaintext password.
76 gotReq := &v1pb.LoginRequest{}
77 a.NoError(common.ProtojsonUnmarshaler.Unmarshal([]byte(entry.Request), gotReq),
78 "Request JSON must be valid protojson for LoginRequest")
79 a.Equal("demo@example.com", gotReq.Email)
80 a.Empty(gotReq.Password, "password must be redacted in the Request payload")

Callers

nothing calls this directly

Calls 15

CloseMethod · 0.95
GetUserMethod · 0.95
UnmarshalMethod · 0.80
LoginMethod · 0.65
GetWorkspaceMethod · 0.65
GetUserMethod · 0.65
SearchAuditLogsMethod · 0.65
EqualMethod · 0.65
UpdateSettingMethod · 0.65
StringMethod · 0.65
SignupMethod · 0.65

Tested by

no test coverage detected