diff --git a/README.md b/README.md index 9074aa3b..2e72bbdf 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ These sections show how to use the SDK to perform API management functions. Befo 8. [Manage Flows](#manage-flows) 9. [Manage JWTs](#manage-jwts) 10. [Impersonate](#impersonate) -11. [Search Audit](#search-audit) +11. [Audit](#audit) 12. [Embedded Links](#embedded-links) 13. [Manage ReBAC Authz](#manage-rebac-authz) 14. [Manage Project](#manage-project) @@ -1201,7 +1201,7 @@ if err != nil { token, err := descopeClient.Management.User().GenerateEmbeddedLink(context.Background(), "desmond@descope.com", map[string]any{"key1":"value1"}) ``` -### Search Audit +### Audit You can perform an audit search for either specific values or full-text across the fields. Audit search is limited to the last 30 days. @@ -1219,6 +1219,19 @@ if err == nil { } ``` +You can also create audit event with data + +```go +err := descopeClient.Management.Audit().CreateEvent(context.Background(), &descope.AuditCreateOptions{ + UserID: , + Action: , + Type: , + ActorID: , + Data: , + TenantID: , +}) +``` + ### Manage ReBAC Authz Descope supports full relation based access control (ReBAC) using a zanzibar like schema and operations. diff --git a/descope/api/client.go b/descope/api/client.go index b3dc7038..e77b6886 100644 --- a/descope/api/client.go +++ b/descope/api/client.go @@ -172,6 +172,7 @@ var ( projectImportSnapshot: "mgmt/project/snapshot/import", projectValidateSnapshot: "mgmt/project/snapshot/validate", auditSearch: "mgmt/audit/search", + auditCreate: "mgmt/audit/event", authzSchemaSave: "mgmt/authz/schema/save", authzSchemaDelete: "mgmt/authz/schema/delete", authzSchemaLoad: "mgmt/authz/schema/load", @@ -369,6 +370,7 @@ type mgmtEndpoints struct { projectValidateSnapshot string auditSearch string + auditCreate string authzSchemaSave string authzSchemaDelete string @@ -959,6 +961,10 @@ func (e *endpoints) ManagementAuditSearch() string { return path.Join(e.version, e.mgmt.auditSearch) } +func (e *endpoints) ManagementAuditCreate() string { + return path.Join(e.version, e.mgmt.auditCreate) +} + func (e *endpoints) ManagementAuthzSchemaSave() string { return path.Join(e.version, e.mgmt.authzSchemaSave) } diff --git a/descope/internal/mgmt/audit.go b/descope/internal/mgmt/audit.go index edbdfeeb..935055d3 100644 --- a/descope/internal/mgmt/audit.go +++ b/descope/internal/mgmt/audit.go @@ -37,6 +37,22 @@ func (a *audit) Search(ctx context.Context, options *descope.AuditSearchOptions) return unmarshalAuditRecords(res) } +func (a *audit) CreateEvent(ctx context.Context, options *descope.AuditCreateOptions) error { + body := map[string]any{ + "userId": options.UserID, + "action": options.Action, + "type": options.Type, + "actorId": options.ActorID, + "data": options.Data, + "tenantId": options.TenantID, + } + _, err := a.client.DoPostRequest(ctx, api.Routes.ManagementAuditCreate(), body, nil, a.conf.ManagementKey) + if err != nil { + return err + } + return nil +} + type apiAuditRecord struct { ProjectID string `json:"projectId,omitempty"` UserID string `json:"userId,omitempty"` diff --git a/descope/internal/mgmt/audit_test.go b/descope/internal/mgmt/audit_test.go index c9727f73..6387c7ff 100644 --- a/descope/internal/mgmt/audit_test.go +++ b/descope/internal/mgmt/audit_test.go @@ -89,3 +89,27 @@ func TestAuditSearch(t *testing.T) { assert.EqualValues(t, response.Audits[0].Tenants, res[0].Tenants) assert.EqualValues(t, response.Audits[0].Data["x"], res[0].Data["x"]) } + +func TestAuditCreate(t *testing.T) { + auditCreateOptions := &descope.AuditCreateOptions{ + UserID: "userId", + Action: "action", + Type: "type", + ActorID: "actorId", + Data: map[string]interface{}{"aaa": "bbb"}, + TenantID: "tenantId", + } + mgmt := newTestMgmt(nil, helpers.DoOkWithBody(func(r *http.Request) { + require.Equal(t, r.Header.Get("Authorization"), "Bearer a:key") + req := map[string]any{} + require.NoError(t, helpers.ReadBody(r, &req)) + assert.EqualValues(t, auditCreateOptions.UserID, req["userId"]) + assert.EqualValues(t, auditCreateOptions.Action, req["action"]) + assert.EqualValues(t, auditCreateOptions.Type, req["type"]) + assert.EqualValues(t, auditCreateOptions.ActorID, req["actorId"]) + assert.EqualValues(t, auditCreateOptions.Data, req["data"]) + assert.EqualValues(t, auditCreateOptions.TenantID, req["tenantId"]) + }, nil)) + err := mgmt.Audit().CreateEvent(context.Background(), auditCreateOptions) + require.NoError(t, err) +} diff --git a/descope/sdk/mgmt.go b/descope/sdk/mgmt.go index 5c320701..9939d786 100644 --- a/descope/sdk/mgmt.go +++ b/descope/sdk/mgmt.go @@ -699,6 +699,7 @@ type Project interface { // Provides search project audit trail type Audit interface { Search(ctx context.Context, options *descope.AuditSearchOptions) ([]*descope.AuditRecord, error) + CreateEvent(ctx context.Context, options *descope.AuditCreateOptions) error } // Provides authorization ReBAC capabilities @@ -802,7 +803,7 @@ type Management interface { // Provide functions for managing flows and theme in a project Flow() Flow - // Provides search project audit trail + // Provides functions for managing audit Audit() Audit // Provide functions for managing projects diff --git a/descope/tests/mocks/mgmt/managementmock.go b/descope/tests/mocks/mgmt/managementmock.go index 0b852bd8..259d11c5 100644 --- a/descope/tests/mocks/mgmt/managementmock.go +++ b/descope/tests/mocks/mgmt/managementmock.go @@ -1221,6 +1221,9 @@ type MockAudit struct { SearchAssert func(*descope.AuditSearchOptions) SearchResponse []*descope.AuditRecord SearchError error + + CreateEventAssert func(*descope.AuditCreateOptions) + CreateEventError error } func (m *MockAudit) Search(_ context.Context, options *descope.AuditSearchOptions) ([]*descope.AuditRecord, error) { @@ -1230,6 +1233,13 @@ func (m *MockAudit) Search(_ context.Context, options *descope.AuditSearchOption return m.SearchResponse, m.SearchError } +func (m *MockAudit) CreateEvent(_ context.Context, options *descope.AuditCreateOptions) error { + if m.CreateEventAssert != nil { + m.CreateEventAssert(options) + } + return m.CreateEventError +} + type MockAuthz struct { SaveSchemaAssert func(schema *descope.AuthzSchema, upgrade bool) SaveSchemaError error diff --git a/descope/types.go b/descope/types.go index eaf8a7d9..11dc943e 100644 --- a/descope/types.go +++ b/descope/types.go @@ -806,6 +806,15 @@ type AuditSearchOptions struct { Text string `json:"text"` // Free text search across all fields } +type AuditCreateOptions struct { + UserID string `json:"userId,omitempty"` + Action string `json:"action,omitempty"` + Type string `json:"type,omitempty"` + ActorID string `json:"actorId,omitempty"` + Data map[string]interface{} `json:"data,omitempty"` + TenantID string `json:"tenantId,omitempty"` +} + type ExportSnapshotResponse struct { // All project settings and configurations represented as JSON files Files map[string]any `json:"files"`