Skip to content

Commit 4f2c4a0

Browse files
abhishekSingh1007Harness
authored andcommitted
fix: [PL-64132]: Added option to fetch the Audit Trail for multiple users and actions (#57)
* fix: [PL-64132]: Added Enum for actions * fix: [PL-64132]: Added ActionsEnum, Updated description * fix: [PL-64132]: Deleted prompts.txt File * fix: [PL-64132]: Added option to fetch for multiple users and actions
1 parent 3852780 commit 4f2c4a0

File tree

4 files changed

+108
-30
lines changed

4 files changed

+108
-30
lines changed

client/audit.go

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package client
33
import (
44
"context"
55
"fmt"
6+
"strings"
67

78
"github.com/harness/harness-mcp/client/dto"
89
)
@@ -16,7 +17,7 @@ type AuditService struct {
1617
}
1718

1819
// ListUserAuditTrail fetches the audit trail.
19-
func (a *AuditService) ListUserAuditTrail(ctx context.Context, scope dto.Scope, userID string, page int, size int, startTime int64, endTime int64, opts *dto.ListAuditEventsFilter) (*dto.AuditOutput[dto.AuditListItem], error) {
20+
func (a *AuditService) ListUserAuditTrail(ctx context.Context, scope dto.Scope, userIDList string, actionsList string, page int, size int, startTime int64, endTime int64, opts *dto.ListAuditEventsFilter) (*dto.AuditOutput[dto.AuditListItem], error) {
2021
if opts == nil {
2122
opts = &dto.ListAuditEventsFilter{}
2223
}
@@ -28,12 +29,47 @@ func (a *AuditService) ListUserAuditTrail(ctx context.Context, scope dto.Scope,
2829

2930
addScope(scope, params)
3031

31-
// Required fields
3232
opts.FilterType = "Audit"
33-
opts.Principals = []dto.AuditPrincipal{{
34-
Type: "USER",
35-
Identifier: userID,
36-
}}
33+
if strings.TrimSpace(userIDList) != "" {
34+
rawIDs := strings.Split(userIDList, ",")
35+
userIDs := make([]string, 0)
36+
for _, id := range rawIDs {
37+
id = strings.TrimSpace(id)
38+
if id != "" {
39+
userIDs = append(userIDs, id)
40+
}
41+
}
42+
43+
if len(userIDs) > 0 {
44+
principals := make([]dto.AuditPrincipal, 0, len(userIDs))
45+
for _, uid := range userIDs {
46+
principals = append(principals, dto.AuditPrincipal{
47+
Type: "USER",
48+
Identifier: uid,
49+
})
50+
}
51+
opts.Principals = principals
52+
}
53+
}
54+
55+
if strings.TrimSpace(actionsList) != "" {
56+
rawActions := strings.Split(actionsList, ",")
57+
actions := make([]string, 0)
58+
for _, action := range rawActions {
59+
action = strings.ToUpper(strings.TrimSpace(action))
60+
if action != "" {
61+
if _, ok := dto.AllowedActions[action]; ok {
62+
actions = append(actions, action)
63+
} else {
64+
return nil, fmt.Errorf("Invalid action: %s\n", action)
65+
}
66+
}
67+
}
68+
69+
if len(actions) > 0 {
70+
opts.Actions = actions
71+
}
72+
}
3773

3874
opts.Scopes = []dto.AuditResourceScope{{
3975
AccountIdentifier: scope.AccountID,

client/dto/audit.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,53 @@
11
package dto
22

3+
var AllowedActions = map[string]struct{}{
4+
"CREATE": {},
5+
"UPDATE": {},
6+
"RESTORE": {},
7+
"DELETE": {},
8+
"FORCE_DELETE": {},
9+
"UPSERT": {},
10+
"INVITE": {},
11+
"RESEND_INVITE": {},
12+
"REVOKE_INVITE": {},
13+
"ADD_COLLABORATOR": {},
14+
"REMOVE_COLLABORATOR": {},
15+
"CREATE_TOKEN": {},
16+
"REVOKE_TOKEN": {},
17+
"LOGIN": {},
18+
"LOGIN2FA": {},
19+
"UNSUCCESSFUL_LOGIN": {},
20+
"ADD_MEMBERSHIP": {},
21+
"REMOVE_MEMBERSHIP": {},
22+
"ERROR_BUDGET_RESET": {},
23+
"START": {},
24+
"END": {},
25+
"STAGE_START": {},
26+
"STAGE_END": {},
27+
"PAUSE": {},
28+
"RESUME": {},
29+
"ABORT": {},
30+
"TIMEOUT": {},
31+
"SIGNED_EULA": {},
32+
"ROLE_ASSIGNMENT_CREATED": {},
33+
"ROLE_ASSIGNMENT_UPDATED": {},
34+
"ROLE_ASSIGNMENT_DELETED": {},
35+
"MOVE": {},
36+
"ENABLED": {},
37+
"DISABLED": {},
38+
"DISMISS_ANOMALY": {},
39+
"RERUN": {},
40+
"BYPASS": {},
41+
"STABLE_VERSION_CHANGED": {},
42+
"SYNC_START": {},
43+
"START_IMPERSONATION": {},
44+
"END_IMPERSONATION": {},
45+
"MOVE_TO_GIT": {},
46+
"FREEZE_BYPASS": {},
47+
"EXPIRED": {},
48+
"FORCE_PUSH": {},
49+
}
50+
351
type AuditPrincipal struct {
452
Type string `json:"type"`
553
Identifier string `json:"identifier"`

pkg/harness/audit.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,9 @@ func previousWeek() string {
5555
// ListAuditsOfUser creates a tool for listing the audit trail.
5656
func ListUserAuditTrailTool(config *config.Config, auditClient *client.AuditService) (tool mcp.Tool, handler server.ToolHandlerFunc) {
5757
return mcp.NewTool("list_user_audits",
58-
mcp.WithDescription("List the audit trail of the user."),
59-
mcp.WithString("user_id",
60-
mcp.Required(),
61-
mcp.Description("The user id(emailId) used to retrieve the audit trail."),
58+
mcp.WithDescription("List the audit trail."),
59+
mcp.WithString("user_id_list",
60+
mcp.Description("Enter one or more user email IDs (comma-separated) to filter the audit trail; leave blank to include all users."),
6261
),
6362
mcp.WithString("start_time",
6463
mcp.Description("Optional start time in ISO 8601 format (e.g., '2025-07-10T08:00:00Z')"),
@@ -68,11 +67,24 @@ func ListUserAuditTrailTool(config *config.Config, auditClient *client.AuditServ
6867
mcp.Description("Optional end time in ISO 8601 format (e.g., '2025-07-10T08:00:00Z')"),
6968
mcp.DefaultString(getCurrentTime()),
7069
),
70+
mcp.WithString("actions",
71+
mcp.Description("Optional actions to filter by. For multiple actions, use comma-separated values."),
72+
mcp.Enum("CREATE", "UPDATE", "RESTORE", "DELETE", "FORCE_DELETE", "UPSERT", "INVITE", "RESEND_INVITE", "REVOKE_INVITE",
73+
"ADD_COLLABORATOR", "REMOVE_COLLABORATOR", "CREATE_TOKEN", "REVOKE_TOKEN", "LOGIN", "LOGIN2FA", "UNSUCCESSFUL_LOGIN",
74+
"ADD_MEMBERSHIP", "REMOVE_MEMBERSHIP", "ERROR_BUDGET_RESET", "START", "END", "STAGE_START", "STAGE_END", "PAUSE", "RESUME", "ABORT", "TIMEOUT", "SIGNED_EULA",
75+
"ROLE_ASSIGNMENT_CREATED", "ROLE_ASSIGNMENT_UPDATED", "ROLE_ASSIGNMENT_DELETED", "MOVE", "ENABLED", "DISABLED", "DISMISS_ANOMALY", "RERUN", "BYPASS", "STABLE_VERSION_CHANGED",
76+
"SYNC_START", "START_IMPERSONATION", "END_IMPERSONATION", "MOVE_TO_GIT", "FREEZE_BYPASS", "EXPIRED", "FORCE_PUSH"),
77+
),
7178
WithScope(config, false),
7279
WithPagination(),
7380
),
7481
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
75-
userID, err := requiredParam[string](request, "user_id")
82+
userIDList, err := OptionalParam[string](request, "user_id_list")
83+
if err != nil {
84+
return mcp.NewToolResultError(err.Error()), nil
85+
}
86+
87+
actionsList, err := OptionalParam[string](request, "actions")
7688
if err != nil {
7789
return mcp.NewToolResultError(err.Error()), nil
7890
}
@@ -96,7 +108,7 @@ func ListUserAuditTrailTool(config *config.Config, auditClient *client.AuditServ
96108
startTimeMilliseconds := convertDateToMilliseconds(startTime)
97109
endTimeMilliseconds := convertDateToMilliseconds(endTime)
98110

99-
data, err := auditClient.ListUserAuditTrail(ctx, scope, userID, page, size, startTimeMilliseconds, endTimeMilliseconds, nil)
111+
data, err := auditClient.ListUserAuditTrail(ctx, scope, userIDList, actionsList, page, size, startTimeMilliseconds, endTimeMilliseconds, nil)
100112
if err != nil {
101113
return nil, fmt.Errorf("failed to list the audit logs: %w", err)
102114
}

prompts.txt

Lines changed: 0 additions & 18 deletions
This file was deleted.

0 commit comments

Comments
 (0)