Skip to content

Commit

Permalink
Merge pull request #2652 from cyberark/audit-list
Browse files Browse the repository at this point in the history
  • Loading branch information
aloncarmel111 authored Sep 19, 2022
2 parents 190feeb + ac0e71d commit dace15f
Show file tree
Hide file tree
Showing 6 changed files with 328 additions and 5 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [1.18.5] - 2022-09-14

### Added
- List resources request (`GET /resources`) now produce audit events.
([cyberark/conjur#2652](https://github.com/cyberark/conjur/pull/2652)

## [1.18.4] - 2022-09-11

### Added
Expand Down
42 changes: 39 additions & 3 deletions app/controllers/resources_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ def index
options[:limit] = conjur_config.api_resource_list_limit_max.to_s unless options[:limit]

unless options[:limit].to_i <= conjur_config.api_resource_list_limit_max
raise ApplicationController::UnprocessableEntity, \
"'Limit' parameter must not exceed #{conjur_config.api_resource_list_limit_max}"
error_message = "'Limit' parameter must not exceed #{conjur_config.api_resource_list_limit_max}"
audit_list_failure(options, error_message)
raise ApplicationController::UnprocessableEntity, error_message

end
end

Expand All @@ -35,8 +37,10 @@ def index
begin
scope = Resource.visible_to(assumed_role(query_role)).search(**options)
rescue ApplicationController::Forbidden
audit_list_failure(options, "The authenticated user lacks the necessary privilege")
raise
rescue ArgumentError => e
audit_list_failure(options, e.message)
raise ApplicationController::UnprocessableEntity, e.message
end

Expand All @@ -51,7 +55,7 @@ def index
eager(:policy_versions).
all
end

audit_list_success(options)
render(json: result)
end

Expand Down Expand Up @@ -120,4 +124,36 @@ def audit_failure(privilege, err)
def conjur_config
Rails.application.config.conjur_config
end

private

def audit_list_success(options)
additional_params = %i[count acting_as role]
additional_options = params.permit(*additional_params)
.slice(*additional_params).to_h.symbolize_keys
Audit.logger.log(
Audit::Event::List.new(
user_id: current_user.role_id,
client_ip: request.ip,
subject: options.clone.merge(additional_options),
success: true
)
)
end

def audit_list_failure(options, error_message)
additional_params = %i[count acting_as role]
additional_options = params.permit(*additional_params)
.slice(*additional_params).to_h.symbolize_keys
Audit.logger.log(
Audit::Event::List.new(
user_id: current_user.role_id,
client_ip: request.ip,
subject: options.clone.merge(additional_options),
success: false,
error_message: error_message
)
)
end

end
90 changes: 90 additions & 0 deletions app/models/audit/event/list.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
module Audit
module Event
# NOTE: Breaking this class up further would harm clarity.
# :reek:TooManyInstanceVariables and :reek:TooManyParameters
class List

def initialize(
user_id:,
client_ip:,
success:,
subject:,
error_message: nil
)
@user_id = user_id
@client_ip = client_ip
@subject = subject
@success = success
@error_message = error_message

# Implements `==` for audit events
@comparable_evt = ComparableEvent.new(self)
end

# NOTE: We want this class to be responsible for providing `progname`.
# At the same time, `progname` is currently always "conjur" and this is
# unlikely to change. Moving `progname` into the constructor now
# feels like premature optimization, so we ignore reek here.
# :reek:UtilityFunction
def progname
Event.progname
end

# action_sd means "action structured data"
def action_sd
attempted_action.action_sd
end

def severity
attempted_action.severity
end

def to_s
message
end

def message
attempted_action.message(
success_msg: "#{@user_id} successfully listed resources with parameters: #{@subject}",
failure_msg: "#{@user_id} failed to list resources with parameters: #{@subject}",
error_msg: @error_message
)
end

def message_id
'list'
end

def structured_data
{
SDID::AUTH => { user: @user_id },
SDID::SUBJECT => @subject,
SDID::CLIENT => { ip: @client_ip }
}.merge(
attempted_action.action_sd
)
end

def facility
# Security or authorization messages which should be kept private. See:
# https://github.com/ruby/ruby/blob/master/ext/syslog/syslog.c#L109
# Note: Changed this to from LOG_AUTH to LOG_AUTHPRIV because the former
# is deprecated.
Syslog::LOG_AUTHPRIV
end

def ==(other)
@comparable_evt == other
end

private

def attempted_action
@attempted_action ||= AttemptedAction.new(
success: @success,
operation: 'list'
)
end
end
end
end
97 changes: 95 additions & 2 deletions cucumber/api/features/resource_list.feature
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,74 @@
Feature: List resources with various types of filtering

Background:
Given I create 3 new resources
Given I am a user named "alice"
And I create 3 new resources

@smoke
Scenario: The resource list includes a new resource.

The most basic resource listing route returns all resources in an account.

Given I save my place in the audit log file for remote
When I successfully GET "/resources/cucumber"
Then the resource list should include the newest resources
And there is an audit record matching:
"""
<86>1 * * conjur * list
[auth@43868 user="cucumber:user:alice"]
[subject@43868 account="cucumber"]
[client@43868 ip="\d+\.\d+\.\d+\.\d+"]
[action@43868 result="success" operation="list"]
cucumber:user:alice successfully listed resources with parameters: {:account=>"cucumber"}
"""

@smoke
Scenario: The resource list can be filtered by resource kind.
Given I create a new "custom" resource
And I save my place in the audit log file for remote
When I successfully GET "/resources/cucumber/custom"
Then the resource list should include the newest resource
And there is an audit record matching:
"""
<86>1 * * conjur * list
[auth@43868 user="cucumber:user:alice"]
[subject@43868 account="cucumber" kind="custom"]
[client@43868 ip="\d+\.\d+\.\d+\.\d+"]
[action@43868 result="success" operation="list"]
cucumber:user:alice successfully listed resources with parameters: {:account=>"cucumber", :kind=>"custom"}
"""

@acceptance
Scenario: The resource list, when filtered by a different resource kind, does not include the newest resource.
Given I create a new "custom" resource
And I save my place in the audit log file for remote
When I successfully GET "/resources/cucumber/uncreated-resource-kind"
Then the resource list should not include the newest resource
And there is an audit record matching:
"""
<86>1 * * conjur * list
[auth@43868 user="cucumber:user:alice"]
[subject@43868 account="cucumber" kind="uncreated-resource-kind"]
[client@43868 ip="\d+\.\d+\.\d+\.\d+"]
[action@43868 result="success" operation="list"]
cucumber:user:alice successfully listed resources with parameters: {:account=>"cucumber", :kind=>"uncreated-resource-kind"}
"""

@smoke
Scenario: The resource list is searched and contains a resource with a matching resource id.
Given I create a new resource called "target"
And I save my place in the audit log file for remote
When I successfully GET "/resources/cucumber/test-resource?search=target"
Then the resource list should only include the searched resource
And there is an audit record matching:
"""
<86>1 * * conjur * list
[auth@43868 user="cucumber:user:alice"]
[subject@43868 account="cucumber" kind="test-resource" search="target"]
[client@43868 ip="\d+\.\d+\.\d+\.\d+"]
[action@43868 result="success" operation="list"]
cucumber:user:alice successfully listed resources with parameters: {:account=>"cucumber", :kind=>"test-resource", :search=>"target"}
"""

@smoke
Scenario: The resource list is searched and contains a resource with a matching annotation.
Expand Down Expand Up @@ -70,17 +111,39 @@ Feature: List resources with various types of filtering

@negative @acceptance
Scenario: The resource list cannot be limited with non numeric value
Given I save my place in the audit log file for remote
When I GET "/resources/cucumber/test-resource?limit=abc"
Then the HTTP response status code is 422
And there is an error
And the error message includes "'limit' contains an invalid value. 'limit' must be a positive integer."
And there is an audit record matching:
"""
<84>1 * * conjur * list
[auth@43868 user="cucumber:user:alice"]
[subject@43868 account="cucumber" kind="test-resource" limit="abc"]
[client@43868 ip="\d+\.\d+\.\d+\.\d+"]
[action@43868 result="failure" operation="list"]
cucumber:user:alice failed to list resources with parameters: {:account=>"cucumber", :kind=>"test-resource", :limit=>"abc"}:
'limit' contains an invalid value. 'limit' must be a positive integer
"""

@negative @acceptance
Scenario: The resource list cannot be limited with zero
Given I save my place in the audit log file for remote
When I GET "/resources/cucumber/test-resource?limit=0"
Then the HTTP response status code is 422
And there is an error
And the error message includes "'limit' contains an invalid value. 'limit' must be a positive integer."
And there is an audit record matching:
"""
<84>1 * * conjur * list
[auth@43868 user="cucumber:user:alice"]
[subject@43868 account="cucumber" kind="test-resource" limit="0"]
[client@43868 ip="\d+\.\d+\.\d+\.\d+"]
[action@43868 result="failure" operation="list"]
cucumber:user:alice failed to list resources with parameters: {:account=>"cucumber", :kind=>"test-resource", :limit=>"0"}:
'limit' contains an invalid value. 'limit' must be a positive integer
"""

@negative @acceptance
Scenario: The resource list cannot be limited with negative integer
Expand All @@ -96,10 +159,21 @@ Feature: List resources with various types of filtering

@negative @acceptance
Scenario: The resource list cannot be retrieved starting from a specific offset with non numeric value
Given I save my place in the audit log file for remote
When I GET "/resources/cucumber/test-resource?offset=abc"
Then the HTTP response status code is 422
And there is an error
And the error message includes "'offset' contains an invalid value. 'offset' must be an integer greater than or equal to 0."
And there is an audit record matching:
"""
<84>1 * * conjur * list
[auth@43868 user="cucumber:user:alice"]
[subject@43868 account="cucumber" kind="test-resource" offset="abc"]
[client@43868 ip="\d+\.\d+\.\d+\.\d+"]
[action@43868 result="failure" operation="list"]
cucumber:user:alice failed to list resources with parameters: {:account=>"cucumber", :kind=>"test-resource", :offset=>"abc"}:
'offset' contains an invalid value. 'offset' must be an integer greater than or equal to 0
"""

@negative @acceptance
Scenario: The resource list cannot be retrieved starting from a specific offset with negative integer
Expand All @@ -110,9 +184,18 @@ Feature: List resources with various types of filtering

@smoke
Scenario: The resource list is retrieved starting from a specific offset and is limited
Given I save my place in the audit log file for remote
When I successfully GET "/resources/cucumber/test-resource?offset=1&limit=1"
Then I receive 1 resources

And there is an audit record matching:
"""
<86>1 * * conjur * list
[auth@43868 user="cucumber:user:alice"]
[subject@43868 account="cucumber" kind="test-resource" limit="1" offset="1"]
[client@43868 ip="\d+\.\d+\.\d+\.\d+"]
[action@43868 result="success" operation="list"]
cucumber:user:alice successfully listed resources with parameters: {:account=>"cucumber", :kind=>"test-resource", :limit=>"1", :offset=>"1"}
"""
@negative @acceptance
Scenario: The resource list cannot be retrieved with non numeric limit delimiter
When I GET "/resources/cucumber/test-resource?offset=1&limit=abc"
Expand All @@ -129,5 +212,15 @@ Feature: List resources with various types of filtering

@smoke
Scenario: The resource list is counted.
Given I save my place in the audit log file for remote
When I successfully GET "/resources/cucumber/test-resource?count=true"
Then I receive a count of 3
And there is an audit record matching:
"""
<86>1 * * conjur * list
[auth@43868 user="cucumber:user:alice"]
[subject@43868 account="cucumber" kind="test-resource" count="true"]
[client@43868 ip="\d+\.\d+\.\d+\.\d+"]
[action@43868 result="success" operation="list"]
cucumber:user:alice successfully listed resources with parameters: {:account=>"cucumber", :kind=>"test-resource", :count=>"true"}
"""
21 changes: 21 additions & 0 deletions cucumber/api/features/resource_list_by_role.feature
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,19 @@ Feature: List resources for another role

@smoke
Scenario: The resource list can be retrieved for a different role, specified the query parameter role
Given I save my place in the audit log file for remote
When I successfully GET "/resources?role=cucumber:user:alice"
Then the resource list should contain "variable" "dev/dev-var"
And the resource list should not contain "variable" "prod/prod-var"
And there is an audit record matching:
"""
<86>1 * * conjur * list
[auth@43868 user="cucumber:user:admin"]
[subject@43868 role="cucumber:user:alice"]
[client@43868 ip="\d+\.\d+\.\d+\.\d+"]
[action@43868 result="success" operation="list"]
cucumber:user:admin successfully listed resources with parameters: {:role=>"cucumber:user:alice"}
"""

@smoke
Scenario: The resource list can be retrieved for a different role, specified the query parameter acting_as
Expand All @@ -33,5 +43,16 @@ Feature: List resources for another role

@negative @acceptance
Scenario: Attempting to retrieve the resource list for a different role but without giving the account in the ID results in a 403
Given I save my place in the audit log file for remote
When I GET "/resources?acting_as=user:alice"
Then the HTTP response status code is 403
And there is an audit record matching:
"""
<84>1 * * conjur * list
[auth@43868 user="cucumber:user:admin"]
[subject@43868 acting_as="user:alice"]
[client@43868 ip="\d+\.\d+\.\d+\.\d+"]
[action@43868 result="failure" operation="list"]
cucumber:user:admin failed to list resources with parameters: {:acting_as=>"user:alice"}:
The authenticated user lacks the necessary privilege
"""
Loading

0 comments on commit dace15f

Please sign in to comment.