Skip to content

Commit 8150e76

Browse files
authored
Add audit events to stacks (#4733)
1 parent f9d9167 commit 8150e76

File tree

12 files changed

+260
-11
lines changed

12 files changed

+260
-11
lines changed

app/actions/stack_create.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1+
require 'repositories/stack_event_repository'
2+
13
module VCAP::CloudController
24
class StackCreate
35
class Error < ::StandardError
46
end
57

8+
def initialize(user_audit_info)
9+
@user_audit_info = user_audit_info
10+
end
11+
612
def create(message)
713
stack = VCAP::CloudController::Stack.create(
814
name: message.name,
@@ -11,6 +17,8 @@ def create(message)
1117

1218
MetadataUpdate.update(stack, message)
1319

20+
Repositories::StackEventRepository.new.record_stack_create(stack, @user_audit_info, message.audit_hash)
21+
1422
stack
1523
rescue Sequel::ValidationFailed => e
1624
validation_error!(e)

app/actions/stack_delete.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1+
require 'repositories/stack_event_repository'
2+
13
module VCAP::CloudController
24
class StackDelete
5+
def initialize(user_audit_info)
6+
@user_audit_info = user_audit_info
7+
end
8+
39
def delete(stack)
410
stack.db.transaction do
11+
Repositories::StackEventRepository.new.record_stack_delete(stack, @user_audit_info)
512
stack.destroy
613
end
714
end

app/actions/stack_update.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
1+
require 'repositories/stack_event_repository'
2+
13
module VCAP::CloudController
24
class StackUpdate
35
class InvalidStack < StandardError
46
end
57

6-
def initialize
8+
def initialize(user_audit_info)
9+
@user_audit_info = user_audit_info
710
@logger = Steno.logger('cc.action.stack_update')
811
end
912

1013
def update(stack, message)
1114
stack.db.transaction do
1215
MetadataUpdate.update(stack, message)
16+
Repositories::StackEventRepository.new.record_stack_update(stack, @user_audit_info, message.audit_hash)
1317
end
1418
@logger.info("Finished updating metadata on stack #{stack.guid}")
19+
1520
stack
1621
rescue Sequel::ValidationFailed => e
1722
raise InvalidStack.new(e.message)

app/controllers/v3/stacks_controller.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def create
3636
message = StackCreateMessage.new(hashed_params[:body])
3737
unprocessable!(message.errors.full_messages) unless message.valid?
3838

39-
stack = StackCreate.new.create(message)
39+
stack = StackCreate.new(user_audit_info).create(message)
4040

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

55-
stack = StackUpdate.new.update(stack, message)
55+
stack = StackUpdate.new(user_audit_info).update(stack, message)
5656

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

8686
begin
87-
StackDelete.new.delete(stack)
87+
StackDelete.new(user_audit_info).delete(stack)
8888
rescue Stack::AppsStillPresentError
8989
unprocessable! "Cannot delete stack '#{stack.name}' because apps are currently using the stack."
9090
end

app/repositories/event_types.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,10 @@ class EventTypesError < StandardError
123123
SPACE_UPDATE = 'audit.space.update'.freeze,
124124
SPACE_DELETE_REQUEST = 'audit.space.delete-request'.freeze,
125125

126+
STACK_CREATE = 'audit.stack.create'.freeze,
127+
STACK_UPDATE = 'audit.stack.update'.freeze,
128+
STACK_DELETE = 'audit.stack.delete'.freeze,
129+
126130
USER_SPACE_AUDITOR_ADD = 'audit.user.space_auditor_add'.freeze,
127131
USER_SPACE_AUDITOR_REMOVE = 'audit.user.space_auditor_remove'.freeze,
128132
USER_SPACE_SUPPORTER_ADD = 'audit.user.space_supporter_add'.freeze,
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
require 'repositories/event_types'
2+
3+
module VCAP::CloudController
4+
module Repositories
5+
class StackEventRepository
6+
def record_stack_create(stack, user_audit_info, request_attrs)
7+
Event.create(
8+
type: EventTypes::STACK_CREATE,
9+
actee: stack.guid,
10+
actee_type: 'stack',
11+
actee_name: stack.name,
12+
actor: user_audit_info.user_guid,
13+
actor_type: 'user',
14+
actor_name: user_audit_info.user_email,
15+
actor_username: user_audit_info.user_name,
16+
timestamp: Sequel::CURRENT_TIMESTAMP,
17+
space_guid: '',
18+
organization_guid: '',
19+
metadata: {
20+
request: request_attrs
21+
}
22+
)
23+
end
24+
25+
def record_stack_update(stack, user_audit_info, request_attrs)
26+
Event.create(
27+
type: EventTypes::STACK_UPDATE,
28+
actee: stack.guid,
29+
actee_type: 'stack',
30+
actee_name: stack.name,
31+
actor: user_audit_info.user_guid,
32+
actor_type: 'user',
33+
actor_name: user_audit_info.user_email,
34+
actor_username: user_audit_info.user_name,
35+
timestamp: Sequel::CURRENT_TIMESTAMP,
36+
space_guid: '',
37+
organization_guid: '',
38+
metadata: {
39+
request: request_attrs
40+
}
41+
)
42+
end
43+
44+
def record_stack_delete(stack, user_audit_info)
45+
Event.create(
46+
type: EventTypes::STACK_DELETE,
47+
actee: stack.guid,
48+
actee_type: 'stack',
49+
actee_name: stack.name,
50+
actor: user_audit_info.user_guid,
51+
actor_type: 'user',
52+
actor_name: user_audit_info.user_email,
53+
actor_username: user_audit_info.user_name,
54+
timestamp: Sequel::CURRENT_TIMESTAMP,
55+
space_guid: '',
56+
organization_guid: '',
57+
metadata: {}
58+
)
59+
end
60+
end
61+
end
62+
end

docs/v3/source/includes/resources/audit_events/_header.md.erb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,11 @@ For more information, see the [Cloud Foundry docs](https://docs.cloudfoundry.org
129129
- `audit.space.delete-request`
130130
- `audit.space.update`
131131

132+
##### Stack lifecycle
133+
- `audit.stack.create`
134+
- `audit.stack.delete`
135+
- `audit.stack.update`
136+
132137
##### User lifecycle
133138
- `audit.user.organization_auditor_add`
134139
- `audit.user.organization_auditor_remove`

spec/unit/actions/stack_create_spec.rb

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
module VCAP::CloudController
66
RSpec.describe StackCreate do
77
describe 'create' do
8+
let(:user) { User.make }
9+
let(:user_email) { '[email protected]' }
10+
let(:user_audit_info) { UserAuditInfo.new(user_guid: user.guid, user_email: user_email) }
11+
12+
subject(:stack_create) { StackCreate.new(user_audit_info) }
13+
814
it 'creates a stack' do
915
message = VCAP::CloudController::StackCreateMessage.new(
1016
name: 'the-name',
@@ -20,7 +26,7 @@ module VCAP::CloudController
2026
}
2127
}
2228
)
23-
stack = StackCreate.new.create(message)
29+
stack = stack_create.create(message)
2430

2531
expect(stack.name).to eq('the-name')
2632
expect(stack.description).to eq('the-description')
@@ -35,6 +41,31 @@ module VCAP::CloudController
3541
)
3642
end
3743

44+
it 'creates an audit event' do
45+
message = VCAP::CloudController::StackCreateMessage.new(
46+
name: 'my-stack',
47+
description: 'my-description'
48+
)
49+
created_stack = stack_create.create(message)
50+
51+
expect(VCAP::CloudController::Event.count).to eq(1)
52+
stack_create_event = VCAP::CloudController::Event.find(type: 'audit.stack.create')
53+
expect(stack_create_event).to exist
54+
expect(stack_create_event.values).to include(
55+
type: 'audit.stack.create',
56+
actor: user_audit_info.user_guid,
57+
actor_type: 'user',
58+
actor_name: user_audit_info.user_email,
59+
actee: created_stack.guid,
60+
actee_type: 'stack',
61+
actee_name: 'my-stack',
62+
space_guid: '',
63+
organization_guid: ''
64+
)
65+
expect(stack_create_event.metadata).to eq({ 'request' => message.audit_hash })
66+
expect(stack_create_event.timestamp).to be
67+
end
68+
3869
context 'when a model validation fails' do
3970
it 'raises an error' do
4071
errors = Sequel::Model::Errors.new
@@ -44,7 +75,7 @@ module VCAP::CloudController
4475

4576
message = VCAP::CloudController::StackCreateMessage.new(name: 'foobar')
4677
expect do
47-
StackCreate.new.create(message)
78+
stack_create.create(message)
4879
end.to raise_error(StackCreate::Error, 'blork is busted')
4980
end
5081
end
@@ -59,7 +90,7 @@ module VCAP::CloudController
5990
it 'raises a human-friendly error' do
6091
message = VCAP::CloudController::StackCreateMessage.new(name:)
6192
expect do
62-
StackCreate.new.create(message)
93+
stack_create.create(message)
6394
end.to raise_error(StackCreate::Error, 'Name must be unique')
6495
end
6596
end
@@ -71,15 +102,15 @@ module VCAP::CloudController
71102
message = VCAP::CloudController::StackCreateMessage.new(name:)
72103
# First request, should succeed
73104
expect do
74-
StackCreate.new.create(message)
105+
stack_create.create(message)
75106
end.not_to raise_error
76107

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

80111
# Second request, should fail with correct error
81112
expect do
82-
StackCreate.new.create(message)
113+
stack_create.create(message)
83114
end.to raise_error(StackCreate::Error, 'Name must be unique')
84115
end
85116
end

spec/unit/actions/stack_delete_spec.rb

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33

44
module VCAP::CloudController
55
RSpec.describe StackDelete do
6-
subject(:stack_delete) { StackDelete.new }
6+
let(:user) { User.make }
7+
let(:user_email) { '[email protected]' }
8+
let(:user_audit_info) { UserAuditInfo.new(user_guid: user.guid, user_email: user_email) }
9+
10+
subject(:stack_delete) { StackDelete.new(user_audit_info) }
711

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

23+
it 'creates an audit event' do
24+
stack_guid = stack.guid
25+
stack_name = stack.name
26+
27+
stack_delete.delete(stack)
28+
29+
expect(VCAP::CloudController::Event.count).to eq(1)
30+
stack_delete_event = VCAP::CloudController::Event.find(type: 'audit.stack.delete')
31+
expect(stack_delete_event).to exist
32+
expect(stack_delete_event.values).to include(
33+
type: 'audit.stack.delete',
34+
actor: user_audit_info.user_guid,
35+
actor_type: 'user',
36+
actor_name: user_audit_info.user_email,
37+
actee: stack_guid,
38+
actee_type: 'stack',
39+
actee_name: stack_name,
40+
space_guid: '',
41+
organization_guid: ''
42+
)
43+
expect(stack_delete_event.metadata).to eq({})
44+
expect(stack_delete_event.timestamp).to be
45+
end
46+
1947
it 'deletes associated labels' do
2048
label = StackLabelModel.make(resource_guid: stack.guid, key_name: 'test1', value: 'bommel')
2149
expect do

spec/unit/actions/stack_update_spec.rb

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33

44
module VCAP::CloudController
55
RSpec.describe StackUpdate do
6-
subject(:stack_update) { StackUpdate.new }
6+
let(:user) { User.make }
7+
let(:user_email) { '[email protected]' }
8+
let(:user_audit_info) { UserAuditInfo.new(user_guid: user.guid, user_email: user_email) }
9+
10+
subject(:stack_update) { StackUpdate.new(user_audit_info) }
711

812
describe '#update' do
913
let(:body) do
@@ -29,6 +33,27 @@ module VCAP::CloudController
2933
expect(stack).to have_labels({ key_name: 'freaky', value: 'wednesday' })
3034
expect(stack).to have_annotations({ key_name: 'tokyo', value: 'grapes' })
3135
end
36+
37+
it 'creates an audit event' do
38+
stack_update.update(stack, message)
39+
40+
expect(VCAP::CloudController::Event.count).to eq(1)
41+
stack_update_event = VCAP::CloudController::Event.find(type: 'audit.stack.update')
42+
expect(stack_update_event).to exist
43+
expect(stack_update_event.values).to include(
44+
type: 'audit.stack.update',
45+
actor: user_audit_info.user_guid,
46+
actor_type: 'user',
47+
actor_name: user_audit_info.user_email,
48+
actee: stack.guid,
49+
actee_type: 'stack',
50+
actee_name: stack.name,
51+
space_guid: '',
52+
organization_guid: ''
53+
)
54+
expect(stack_update_event.metadata).to eq({ 'request' => message.audit_hash })
55+
expect(stack_update_event.timestamp).to be
56+
end
3257
end
3358
end
3459
end

0 commit comments

Comments
 (0)