Skip to content

Commit

Permalink
Management: Audit create event. (#68)
Browse files Browse the repository at this point in the history
  • Loading branch information
ami-descope authored Apr 17, 2024
1 parent 22eed7f commit b5af94e
Show file tree
Hide file tree
Showing 9 changed files with 209 additions and 11 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ gemspec

group :development do
gem 'rubocop', '1.63.2', require: false
gem 'rubocop-rails', '2.24.1', require: false
end

group :test do
Expand Down
6 changes: 6 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ GEM
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.31.2)
parser (>= 3.3.0.4)
rubocop-rails (2.24.1)
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 1.33.0, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0)
ruby-progressbar (1.13.0)
rubyzip (2.3.2)
selenium-webdriver (4.19.0)
Expand Down Expand Up @@ -135,6 +140,7 @@ DEPENDENCIES
rotp (= 6.3.0)
rspec (= 3.13.0)
rubocop (= 1.63.2)
rubocop-rails (= 2.24.1)
selenium-webdriver (= 4.19.0)
simplecov (= 0.22.0)
super_diff (= 0.11.0)
Expand Down
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ These sections show how to use the SDK to perform permission and user management
8. [Manage Flows](#manage-flows-and-theme)
9. [Manage JWTs](#manage-jwts)
10. [Embedded links](#embedded-links)
11. [Search Audit](#search-audit)
11. [Audit](#audit)
12. [Manage ReBAC Authz](#manage-rebac-authz)
13. [Manage Project](#manage-project)

Expand Down Expand Up @@ -871,7 +871,7 @@ This token can then be verified using the magic link 'verify' function, either d
token = descope_client.generate_embedded_link(login_id: 'desmond@descope.com', custom_claims: {'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.
Below are some examples. For a full list of available search criteria options, see the function documentation.
Expand All @@ -898,6 +898,21 @@ audits = descope_client.audit_search(
audits = descope_client.audit_search(actions: ['LoginSucceed'])
```

You can also create audit event with data

```ruby
descope_client.audit_create_event(
actor_id: "UXXX", # required, for example a user ID
tenant_id: "tenant-id", # required
action: "pencil.created", # required
type: "info", # either: info/warn/error # required
data: {
pencil_id: "PXXX",
pencil_name: "Pencil Name"
} # optional
)
```

### Manage ReBAC Authz

Descope supports full relation based access control (ReBAC) using a [Google Zanzibar](https://research.google/pubs/pub48190/) like schema and operations.
Expand Down
39 changes: 31 additions & 8 deletions examples/ruby/management/audit_app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,36 @@
@client = Descope::Client.new({ project_id: @project_id, management_key: @management_key })

begin
@logger.info('Going to search audit')
text = nil
text = ARGV[0] if ARGV.length > 1
from_ts = nil
from_ts = DateTime.iso8601(ARGV[1]) if ARGV.length > 2
res = @client.audit_search(text: text, from_ts: from_ts)
@logger.info("Audit search result: #{res}")
@logger.info('Do you want to to create a new audit event? [y/n] ')
create_audit = gets.chomp
if create_audit == 'y'
@logger.info('Enter the action for the audit event: ')
action = gets.chomp
@logger.info('Enter the type for the audit event: [info/warn/error] ')
type = gets.chomp
@logger.info('Enter the actorId for the audit event: ')
actor_id = gets.chomp
@logger.info('Enter the tenantId for the audit event: ')
tenant_id = gets.chomp
res = @client.audit_create_event(
action: action,
type: type,
actor_id: actor_id,
tenant_id: tenant_id
)
@logger.info("Audit event created successfully: #{res}")
end

@logger.info('Do you want to search the audit trail? [y/n] ')
search_audit = gets.chomp
if search_audit == 'y'
@logger.info('Enter the text to search: ')
text = gets.chomp
@logger.info('Enter the from_ts in ISO8601 format (2024-01-01 15:00:00.000) to search: ')
from_ts = gets.chomp
res = @client.audit_search(text: text, from_ts: from_ts)
@logger.info("Audit search result: #{res}")
end
rescue Descope::AuthException => e
@logger.error("Audit search failed #{e}")
@logger.error("Audit action failed #{e}")
end
24 changes: 24 additions & 0 deletions lib/descope/api/v1/management/audit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,30 @@ def audit_search(
{ 'audits' => res['audits'].map { |audit| convert_audit_record(audit) } }
end

def audit_create_event(action: nil, type: nil, data: nil, user_id: nil, actor_id: nil, tenant_id: nil)
# Create an audit event
unless %w[info warn error].include?(type)
raise Descope::AuthException, 'type must be either info, warn or error'
end

# validation
raise Descope::AuthException, 'data must be provided as a key, value Hash' unless data.is_a?(Hash)
raise Descope::AuthException, 'action must be provided' if action.nil?
raise Descope::AuthException, 'actor_id must be provided' if actor_id.nil?
raise Descope::AuthException, 'tenant_id must be provided' if tenant_id.nil?

request_params = {
action:,
tenantId: tenant_id,
type:,
actorId: actor_id,
data:
}
request_params[:userId] = user_id unless user_id.nil?

post(AUDIT_CREATE_EVENT, request_params)
end

private

def convert_audit_record(audit)
Expand Down
1 change: 1 addition & 0 deletions lib/descope/api/v1/management/common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ module Common

# Audit
AUDIT_SEARCH = '/v1/mgmt/audit/search'
AUDIT_CREATE_EVENT = '/v1/mgmt/audit/event'

# Authz ReBAC
AUTHZ_SCHEMA_SAVE = '/v1/mgmt/authz/schema/save'
Expand Down
36 changes: 36 additions & 0 deletions spec/integration/lib.descope/api/v1/management/audit_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,48 @@
describe Descope::Api::V1::Management::Audit do
before(:all) do
@client = DescopeClient.new(Configuration.config)
@client.logger.info('Deleting all tenants for Ruby SDK...')
@client.search_all_tenants(names: ['Ruby-SDK-test'])['tenants'].each do |tenant|
@client.logger.info("Deleting tenant: #{tenant['name']}")
@client.delete_tenant(tenant['id'])
end
@client.logger.info('Cleanup completed. Starting tests...')
end

after(:all) do
all_users = @client.search_all_users
all_users['users'].each do |user|
if user['middleName'] == 'Ruby SDK User'
puts "Deleting ruby spec test user #{user['loginIds'][0]}"
@client.delete_user(user['loginIds'][0])
end
end
end

it 'should search the audit trail for user operations' do
res = @client.audit_search(actions: ['LoginSucceed'])
expect(res).to be_a(Hash)
expect(res['audits']).to be_a(Array)
end

it 'should create a new audit event' do
# Create tenants
@client.logger.info('creating Ruby-SDK-test tenant')
tenant_id = @client.create_tenant(name: 'Ruby-SDK-test')['id']

# Create a user (actor)
user = build(:user)
created_user = @client.create_user(**user)['user']

expect do
res = @client.audit_create_event(
action: 'pencil.created',
type: 'info',
tenant_id:,
actor_id: created_user['loginIds'][0],
data: { 'key' => 'value' }
)
expect(res).to eq({})
end.not_to raise_error
end
end
2 changes: 1 addition & 1 deletion spec/lib.descope/api/v1/auth_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@

expect do
exp_in_seconds = 20
puts "Sleeping for #{exp_in_seconds} seconds to test token expiration. Please wait..."
puts "\nAuthSpec.validate_token::Sleeping for #{exp_in_seconds} seconds to test token expiration. Please wait...\n"
sleep(exp_in_seconds)
@instance.send(:validate_token, token)
end.to raise_error(
Expand Down
92 changes: 92 additions & 0 deletions spec/lib.descope/api/v1/management/audit_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,96 @@
expect(res['audits'][0]['projectId']).to eq('abc')
end
end

context '.create_event' do
it 'should respond to .audit_create_event' do
expect(@instance).to respond_to :audit_create_event
end

it 'should raise an error if type is not info, warn or error' do
expect do
@instance.audit_create_event(
action: 'get',
type: 'debug',
data: { key: 'value' },
user_id: 'user_id',
actor_id: 'actor_id',
tenant_id: 'tenant_id'
)
end.to raise_error(Descope::AuthException, 'type must be either info, warn or error')
end

it 'should raise an error if data is not a hash' do
expect do
@instance.audit_create_event(
action: 'get',
type: 'info',
data: 'data',
user_id: 'user_id',
actor_id: 'actor_id',
tenant_id: 'tenant_id'
)
end.to raise_error(Descope::AuthException, 'data must be provided as a key, value Hash')
end

it 'should raise an error if action is not provided' do
expect do
@instance.audit_create_event(
type: 'info',
data: { key: 'value' },
user_id: 'user_id',
actor_id: 'actor_id',
tenant_id: 'tenant_id'
)
end.to raise_error(Descope::AuthException, 'action must be provided')
end

it 'should raise an error if actor is not provided' do
expect do
@instance.audit_create_event(
action: 'get',
type: 'info',
data: { key: 'value' },
user_id: 'user_id',
tenant_id: 'tenant_id'
)
end.to raise_error(Descope::AuthException, 'actor_id must be provided')
end

it 'should raise an error if tenant_id is not provided' do
expect do
@instance.audit_create_event(
action: 'get',
type: 'info',
data: { key: 'value' },
user_id: 'user_id',
actor_id: 'actor_id'
)
end.to raise_error(Descope::AuthException, 'tenant_id must be provided')
end

it 'is expected to create an audit event' do
expect(@instance).to receive(:post).with(
'/v1/mgmt/audit/event',
{
action: 'get',
type: 'info',
actorId: 'actor_id',
data: { key: 'value' },
tenantId: 'tenant_id',
userId: 'user_id'
}
)
expect do
@instance.audit_create_event(
action: 'get',
type: 'info',
data: { key: 'value' },
user_id: 'user_id',
actor_id: 'actor_id',
tenant_id: 'tenant_id'
)
end.not_to raise_error
end
end
end

0 comments on commit b5af94e

Please sign in to comment.