Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions app/actions/stack_create.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
require 'repositories/stack_event_repository'

module VCAP::CloudController
class StackCreate
class Error < ::StandardError
end

def initialize(user_audit_info)
@user_audit_info = user_audit_info
end

def create(message)
stack = VCAP::CloudController::Stack.create(
name: message.name,
Expand All @@ -11,6 +17,8 @@ def create(message)

MetadataUpdate.update(stack, message)

Repositories::StackEventRepository.new.record_stack_create(stack, @user_audit_info, message.audit_hash)

stack
rescue Sequel::ValidationFailed => e
validation_error!(e)
Expand Down
7 changes: 7 additions & 0 deletions app/actions/stack_delete.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
require 'repositories/stack_event_repository'

module VCAP::CloudController
class StackDelete
def initialize(user_audit_info)
@user_audit_info = user_audit_info
end

def delete(stack)
stack.db.transaction do
Repositories::StackEventRepository.new.record_stack_delete(stack, @user_audit_info)
stack.destroy
end
end
Expand Down
7 changes: 6 additions & 1 deletion app/actions/stack_update.rb
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
require 'repositories/stack_event_repository'

module VCAP::CloudController
class StackUpdate
class InvalidStack < StandardError
end

def initialize
def initialize(user_audit_info)
@user_audit_info = user_audit_info
@logger = Steno.logger('cc.action.stack_update')
end

def update(stack, message)
stack.db.transaction do
MetadataUpdate.update(stack, message)
Repositories::StackEventRepository.new.record_stack_update(stack, @user_audit_info, message.audit_hash)
end
@logger.info("Finished updating metadata on stack #{stack.guid}")

stack
rescue Sequel::ValidationFailed => e
raise InvalidStack.new(e.message)
Expand Down
6 changes: 3 additions & 3 deletions app/controllers/v3/stacks_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def create
message = StackCreateMessage.new(hashed_params[:body])
unprocessable!(message.errors.full_messages) unless message.valid?

stack = StackCreate.new.create(message)
stack = StackCreate.new(user_audit_info).create(message)

render status: :created, json: Presenters::V3::StackPresenter.new(stack)
rescue StackCreate::Error => e
Expand All @@ -52,7 +52,7 @@ def update
message = StackUpdateMessage.new(hashed_params[:body])
unprocessable!(message.errors.full_messages) unless message.valid?

stack = StackUpdate.new.update(stack, message)
stack = StackUpdate.new(user_audit_info).update(stack, message)

render status: :ok, json: Presenters::V3::StackPresenter.new(stack)
end
Expand Down Expand Up @@ -84,7 +84,7 @@ def destroy
unauthorized! unless permission_queryer.can_write_globally?

begin
StackDelete.new.delete(stack)
StackDelete.new(user_audit_info).delete(stack)
rescue Stack::AppsStillPresentError
unprocessable! "Cannot delete stack '#{stack.name}' because apps are currently using the stack."
end
Expand Down
4 changes: 4 additions & 0 deletions app/repositories/event_types.rb
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ class EventTypesError < StandardError
SPACE_UPDATE = 'audit.space.update'.freeze,
SPACE_DELETE_REQUEST = 'audit.space.delete-request'.freeze,

STACK_CREATE = 'audit.stack.create'.freeze,
STACK_UPDATE = 'audit.stack.update'.freeze,
STACK_DELETE = 'audit.stack.delete'.freeze,

USER_SPACE_AUDITOR_ADD = 'audit.user.space_auditor_add'.freeze,
USER_SPACE_AUDITOR_REMOVE = 'audit.user.space_auditor_remove'.freeze,
USER_SPACE_SUPPORTER_ADD = 'audit.user.space_supporter_add'.freeze,
Expand Down
62 changes: 62 additions & 0 deletions app/repositories/stack_event_repository.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
require 'repositories/event_types'

module VCAP::CloudController
module Repositories
class StackEventRepository
def record_stack_create(stack, user_audit_info, request_attrs)
Event.create(
type: EventTypes::STACK_CREATE,
actee: stack.guid,
actee_type: 'stack',
actee_name: stack.name,
actor: user_audit_info.user_guid,
actor_type: 'user',
actor_name: user_audit_info.user_email,
actor_username: user_audit_info.user_name,
timestamp: Sequel::CURRENT_TIMESTAMP,
space_guid: '',
organization_guid: '',
metadata: {
request: request_attrs
}
)
end

def record_stack_update(stack, user_audit_info, request_attrs)
Event.create(
type: EventTypes::STACK_UPDATE,
actee: stack.guid,
actee_type: 'stack',
actee_name: stack.name,
actor: user_audit_info.user_guid,
actor_type: 'user',
actor_name: user_audit_info.user_email,
actor_username: user_audit_info.user_name,
timestamp: Sequel::CURRENT_TIMESTAMP,
space_guid: '',
organization_guid: '',
metadata: {
request: request_attrs
}
)
end

def record_stack_delete(stack, user_audit_info)
Event.create(
type: EventTypes::STACK_DELETE,
actee: stack.guid,
actee_type: 'stack',
actee_name: stack.name,
actor: user_audit_info.user_guid,
actor_type: 'user',
actor_name: user_audit_info.user_email,
actor_username: user_audit_info.user_name,
timestamp: Sequel::CURRENT_TIMESTAMP,
space_guid: '',
organization_guid: '',
metadata: {}
)
end
end
end
end
5 changes: 5 additions & 0 deletions docs/v3/source/includes/resources/audit_events/_header.md.erb
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ For more information, see the [Cloud Foundry docs](https://docs.cloudfoundry.org
- `audit.space.delete-request`
- `audit.space.update`

##### Stack lifecycle
- `audit.stack.create`
- `audit.stack.delete`
- `audit.stack.update`

##### User lifecycle
- `audit.user.organization_auditor_add`
- `audit.user.organization_auditor_remove`
Expand Down
41 changes: 36 additions & 5 deletions spec/unit/actions/stack_create_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
module VCAP::CloudController
RSpec.describe StackCreate do
describe 'create' do
let(:user) { User.make }
let(:user_email) { '[email protected]' }
let(:user_audit_info) { UserAuditInfo.new(user_guid: user.guid, user_email: user_email) }

subject(:stack_create) { StackCreate.new(user_audit_info) }

it 'creates a stack' do
message = VCAP::CloudController::StackCreateMessage.new(
name: 'the-name',
Expand All @@ -20,7 +26,7 @@ module VCAP::CloudController
}
}
)
stack = StackCreate.new.create(message)
stack = stack_create.create(message)

expect(stack.name).to eq('the-name')
expect(stack.description).to eq('the-description')
Expand All @@ -35,6 +41,31 @@ module VCAP::CloudController
)
end

it 'creates an audit event' do
message = VCAP::CloudController::StackCreateMessage.new(
name: 'my-stack',
description: 'my-description'
)
created_stack = stack_create.create(message)

expect(VCAP::CloudController::Event.count).to eq(1)
stack_create_event = VCAP::CloudController::Event.find(type: 'audit.stack.create')
expect(stack_create_event).to exist
expect(stack_create_event.values).to include(
type: 'audit.stack.create',
actor: user_audit_info.user_guid,
actor_type: 'user',
actor_name: user_audit_info.user_email,
actee: created_stack.guid,
actee_type: 'stack',
actee_name: 'my-stack',
space_guid: '',
organization_guid: ''
)
expect(stack_create_event.metadata).to eq({ 'request' => message.audit_hash })
expect(stack_create_event.timestamp).to be
end

context 'when a model validation fails' do
it 'raises an error' do
errors = Sequel::Model::Errors.new
Expand All @@ -44,7 +75,7 @@ module VCAP::CloudController

message = VCAP::CloudController::StackCreateMessage.new(name: 'foobar')
expect do
StackCreate.new.create(message)
stack_create.create(message)
end.to raise_error(StackCreate::Error, 'blork is busted')
end
end
Expand All @@ -59,7 +90,7 @@ module VCAP::CloudController
it 'raises a human-friendly error' do
message = VCAP::CloudController::StackCreateMessage.new(name:)
expect do
StackCreate.new.create(message)
stack_create.create(message)
end.to raise_error(StackCreate::Error, 'Name must be unique')
end
end
Expand All @@ -71,15 +102,15 @@ module VCAP::CloudController
message = VCAP::CloudController::StackCreateMessage.new(name:)
# First request, should succeed
expect do
StackCreate.new.create(message)
stack_create.create(message)
end.not_to raise_error

# Mock the validation for the second request to simulate the race condition and trigger a unique constraint violation
allow_any_instance_of(Stack).to receive(:validate).and_return(true)

# Second request, should fail with correct error
expect do
StackCreate.new.create(message)
stack_create.create(message)
end.to raise_error(StackCreate::Error, 'Name must be unique')
end
end
Expand Down
30 changes: 29 additions & 1 deletion spec/unit/actions/stack_delete_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@

module VCAP::CloudController
RSpec.describe StackDelete do
subject(:stack_delete) { StackDelete.new }
let(:user) { User.make }
let(:user_email) { '[email protected]' }
let(:user_audit_info) { UserAuditInfo.new(user_guid: user.guid, user_email: user_email) }

subject(:stack_delete) { StackDelete.new(user_audit_info) }

describe '#delete' do
context 'when the stack exists' do
Expand All @@ -16,6 +20,30 @@ module VCAP::CloudController
expect { stack.refresh }.to raise_error(Sequel::Error, 'Record not found')
end

it 'creates an audit event' do
stack_guid = stack.guid
stack_name = stack.name

stack_delete.delete(stack)

expect(VCAP::CloudController::Event.count).to eq(1)
stack_delete_event = VCAP::CloudController::Event.find(type: 'audit.stack.delete')
expect(stack_delete_event).to exist
expect(stack_delete_event.values).to include(
type: 'audit.stack.delete',
actor: user_audit_info.user_guid,
actor_type: 'user',
actor_name: user_audit_info.user_email,
actee: stack_guid,
actee_type: 'stack',
actee_name: stack_name,
space_guid: '',
organization_guid: ''
)
expect(stack_delete_event.metadata).to eq({})
expect(stack_delete_event.timestamp).to be
end

it 'deletes associated labels' do
label = StackLabelModel.make(resource_guid: stack.guid, key_name: 'test1', value: 'bommel')
expect do
Expand Down
27 changes: 26 additions & 1 deletion spec/unit/actions/stack_update_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@

module VCAP::CloudController
RSpec.describe StackUpdate do
subject(:stack_update) { StackUpdate.new }
let(:user) { User.make }
let(:user_email) { '[email protected]' }
let(:user_audit_info) { UserAuditInfo.new(user_guid: user.guid, user_email: user_email) }

subject(:stack_update) { StackUpdate.new(user_audit_info) }

describe '#update' do
let(:body) do
Expand All @@ -29,6 +33,27 @@ module VCAP::CloudController
expect(stack).to have_labels({ key_name: 'freaky', value: 'wednesday' })
expect(stack).to have_annotations({ key_name: 'tokyo', value: 'grapes' })
end

it 'creates an audit event' do
stack_update.update(stack, message)

expect(VCAP::CloudController::Event.count).to eq(1)
stack_update_event = VCAP::CloudController::Event.find(type: 'audit.stack.update')
expect(stack_update_event).to exist
expect(stack_update_event.values).to include(
type: 'audit.stack.update',
actor: user_audit_info.user_guid,
actor_type: 'user',
actor_name: user_audit_info.user_email,
actee: stack.guid,
actee_type: 'stack',
actee_name: stack.name,
space_guid: '',
organization_guid: ''
)
expect(stack_update_event.metadata).to eq({ 'request' => message.audit_hash })
expect(stack_update_event.timestamp).to be
end
end
end
end
3 changes: 3 additions & 0 deletions spec/unit/repositories/event_types_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ module Repositories
'audit.space.create',
'audit.space.update',
'audit.space.delete-request',
'audit.stack.create',
'audit.stack.update',
'audit.stack.delete',
'audit.user.space_auditor_add',
'audit.user.space_auditor_remove',
'audit.user.space_supporter_add',
Expand Down
Loading