Skip to content

Commit 0c2c53d

Browse files
authored
Add buildpack audit events (#4741)
1 parent 4b06b54 commit 0c2c53d

File tree

11 files changed

+215
-26
lines changed

11 files changed

+215
-26
lines changed

app/actions/buildpack_create.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
require 'repositories/buildpack_event_repository'
2+
13
module VCAP::CloudController
24
class BuildpackCreate
35
class Error < ::StandardError
@@ -7,6 +9,10 @@ class Error < ::StandardError
79
DEFAULT_ENABLED = true
810
DEFAULT_LOCKED = false
911

12+
def initialize(user_audit_info)
13+
@user_audit_info = user_audit_info
14+
end
15+
1016
def create(message)
1117
Buildpack.db.transaction do
1218
Locking[name: 'buildpacks'].lock!
@@ -22,6 +28,10 @@ def create(message)
2228
MetadataUpdate.update(buildpack, message)
2329

2430
buildpack.move_to(message.position || DEFAULT_POSITION)
31+
32+
Repositories::BuildpackEventRepository.new.record_buildpack_create(buildpack, @user_audit_info, message.audit_hash)
33+
34+
buildpack
2535
end
2636
rescue Sequel::ValidationFailed => e
2737
validation_error!(e, message)

app/actions/buildpack_delete.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1+
require 'repositories/buildpack_event_repository'
2+
13
module VCAP::CloudController
24
class BuildpackDelete
5+
def initialize(user_audit_info)
6+
@user_audit_info = user_audit_info
7+
end
8+
39
def delete(buildpacks)
410
buildpacks.each do |buildpack|
511
Buildpack.db.transaction do
612
Locking[name: 'buildpacks'].lock!
13+
Repositories::BuildpackEventRepository.new.record_buildpack_delete(buildpack, @user_audit_info)
714
buildpack.destroy
815
end
916
if buildpack.key

app/actions/buildpack_update.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1+
require 'repositories/buildpack_event_repository'
2+
13
module VCAP::CloudController
24
class BuildpackUpdate
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 update(buildpack, message)
713
Buildpack.db.transaction do
814
Locking[name: 'buildpacks'].lock!
@@ -15,6 +21,8 @@ def update(buildpack, message)
1521
buildpack.locked = message.locked if message.requested?(:locked)
1622
buildpack.name = message.name if message.requested?(:name)
1723
buildpack.save
24+
25+
Repositories::BuildpackEventRepository.new.record_buildpack_update(buildpack, @user_audit_info, message.audit_hash)
1826
end
1927
buildpack
2028
rescue Sequel::ValidationFailed => e

app/controllers/v3/buildpacks_controller.rb

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

38-
buildpack = BuildpackCreate.new.create(message)
38+
buildpack = BuildpackCreate.new(user_audit_info).create(message)
3939

4040
render status: :created, json: Presenters::V3::BuildpackPresenter.new(buildpack)
4141
rescue BuildpackCreate::Error => e
@@ -51,7 +51,7 @@ def update
5151
message = BuildpackUpdateMessage.new(hashed_params[:body])
5252
unprocessable!(message.errors.full_messages) unless message.valid?
5353

54-
buildpack = VCAP::CloudController::BuildpackUpdate.new.update(buildpack, message)
54+
buildpack = VCAP::CloudController::BuildpackUpdate.new(user_audit_info).update(buildpack, message)
5555

5656
render status: :ok, json: Presenters::V3::BuildpackPresenter.new(buildpack)
5757
rescue BuildpackUpdate::Error => e
@@ -64,7 +64,7 @@ def destroy
6464

6565
unauthorized! unless permission_queryer.can_write_globally?
6666

67-
delete_action = BuildpackDelete.new
67+
delete_action = BuildpackDelete.new(user_audit_info)
6868
deletion_job = VCAP::CloudController::Jobs::DeleteActionJob.new(Buildpack, buildpack.guid, delete_action)
6969
pollable_job = Jobs::Enqueuer.new(queue: Jobs::Queues.generic).enqueue_pollable(deletion_job)
7070

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 BuildpackEventRepository
6+
def record_buildpack_create(buildpack, user_audit_info, request_attrs)
7+
Event.create(
8+
type: EventTypes::BUILDPACK_CREATE,
9+
actee: buildpack.guid,
10+
actee_type: 'buildpack',
11+
actee_name: buildpack.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_buildpack_update(buildpack, user_audit_info, request_attrs)
26+
Event.create(
27+
type: EventTypes::BUILDPACK_UPDATE,
28+
actee: buildpack.guid,
29+
actee_type: 'buildpack',
30+
actee_name: buildpack.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_buildpack_delete(buildpack, user_audit_info)
45+
Event.create(
46+
type: EventTypes::BUILDPACK_DELETE,
47+
actee: buildpack.guid,
48+
actee_type: 'buildpack',
49+
actee_name: buildpack.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

app/repositories/event_types.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ class EventTypesError < StandardError
5454
APP_SSH_AUTHORIZED = 'audit.app.ssh-authorized'.freeze,
5555
APP_SSH_UNAUTHORIZED = 'audit.app.ssh-unauthorized'.freeze,
5656

57+
BUILDPACK_CREATE = 'audit.buildpack.create'.freeze,
58+
BUILDPACK_UPDATE = 'audit.buildpack.update'.freeze,
59+
BUILDPACK_DELETE = 'audit.buildpack.delete'.freeze,
60+
5761
SERVICE_CREATE = 'audit.service.create'.freeze,
5862
SERVICE_UPDATE = 'audit.service.update'.freeze,
5963
SERVICE_DELETE = 'audit.service.delete'.freeze,

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ For more information, see the [Cloud Foundry docs](https://docs.cloudfoundry.org
5050
- `audit.app.update`
5151
- `audit.app.upload-bits`
5252

53+
##### Buildpack lifecycle
54+
- `audit.buildpack.create`
55+
- `audit.buildpack.delete`
56+
- `audit.buildpack.update`
57+
5358
##### Organization lifecycle
5459
- `audit.organization.create`
5560
- `audit.organization.delete-request`

spec/unit/actions/buildpack_create_spec.rb

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
module VCAP::CloudController
66
RSpec.describe BuildpackCreate do
77
describe 'create' do
8+
let(:user) { User.make }
9+
let(:user_email) { '[email protected]' }
10+
let(:user_name) { 'user-name' }
11+
let(:user_audit_info) { UserAuditInfo.new(user_guid: user.guid, user_email: user_email, user_name: user_name) }
12+
813
let!(:buildpack1) { Buildpack.create(name: 'take-up-position-1', position: 1) }
914
let!(:buildpack2) { Buildpack.create(name: 'take-up-position-2', position: 2) }
1015
let!(:buildpack3) { Buildpack.create(name: 'take-up-position-3', position: 3) }
@@ -22,7 +27,7 @@ module VCAP::CloudController
2227
locked: true,
2328
lifecycle: Lifecycles::BUILDPACK
2429
)
25-
buildpack = BuildpackCreate.new.create(message)
30+
buildpack = BuildpackCreate.new(user_audit_info).create(message)
2631

2732
expect(buildpack.name).to eq('the-name')
2833
expect(buildpack.stack).to eq('the-stack')
@@ -31,6 +36,33 @@ module VCAP::CloudController
3136
expect(buildpack.locked).to be(true)
3237
expect(buildpack.lifecycle).to eq(Lifecycles::BUILDPACK)
3338
end
39+
40+
it 'creates an audit event' do
41+
message = BuildpackCreateMessage.new(
42+
name: 'the-name',
43+
stack: 'the-stack',
44+
enabled: false,
45+
locked: true
46+
)
47+
buildpack = BuildpackCreate.new(user_audit_info).create(message)
48+
49+
event = VCAP::CloudController::Event.last
50+
51+
expect(event.values).to include(
52+
type: 'audit.buildpack.create',
53+
actee: buildpack.guid,
54+
actee_type: 'buildpack',
55+
actee_name: buildpack.name,
56+
actor: user_audit_info.user_guid,
57+
actor_type: 'user',
58+
actor_name: user_audit_info.user_email,
59+
actor_username: user_audit_info.user_name,
60+
space_guid: '',
61+
organization_guid: ''
62+
)
63+
expect(event.metadata).to eq({ 'request' => message.audit_hash })
64+
expect(event.timestamp).to be
65+
end
3466
end
3567

3668
context 'when metadata is provided' do
@@ -49,7 +81,7 @@ module VCAP::CloudController
4981
}
5082
}
5183
)
52-
buildpack = BuildpackCreate.new.create(message)
84+
buildpack = BuildpackCreate.new(user_audit_info).create(message)
5385

5486
expect(buildpack.name).to eq('the-name')
5587
expect(buildpack.stack).to eq('the-stack')
@@ -68,7 +100,7 @@ module VCAP::CloudController
68100
name: 'the-name',
69101
position: 2
70102
)
71-
buildpack = BuildpackCreate.new.create(message)
103+
buildpack = BuildpackCreate.new(user_audit_info).create(message)
72104

73105
expect(buildpack.position).to eq(2)
74106
expect(buildpack1.reload.position).to eq(1)
@@ -83,7 +115,7 @@ module VCAP::CloudController
83115
name: 'the-name',
84116
position: 42
85117
)
86-
buildpack = BuildpackCreate.new.create(message)
118+
buildpack = BuildpackCreate.new(user_audit_info).create(message)
87119

88120
expect(buildpack.position).to eq(4)
89121
end
@@ -97,7 +129,7 @@ module VCAP::CloudController
97129
stack: 'the-stack',
98130
locked: true
99131
)
100-
buildpack = BuildpackCreate.new.create(message)
132+
buildpack = BuildpackCreate.new(user_audit_info).create(message)
101133

102134
expect(buildpack.enabled).to be(true)
103135
end
@@ -110,7 +142,7 @@ module VCAP::CloudController
110142
stack: 'the-stack',
111143
enabled: true
112144
)
113-
buildpack = BuildpackCreate.new.create(message)
145+
buildpack = BuildpackCreate.new(user_audit_info).create(message)
114146

115147
expect(buildpack.locked).to be(false)
116148
end
@@ -123,7 +155,7 @@ module VCAP::CloudController
123155
stack: 'the-stack',
124156
lifecycle: Lifecycles::CNB
125157
)
126-
buildpack = BuildpackCreate.new.create(message)
158+
buildpack = BuildpackCreate.new(user_audit_info).create(message)
127159

128160
expect(buildpack.lifecycle).to eq(Lifecycles::CNB)
129161
end
@@ -138,7 +170,7 @@ module VCAP::CloudController
138170

139171
message = BuildpackCreateMessage.new(name: 'foobar')
140172
expect do
141-
BuildpackCreate.new.create(message)
173+
BuildpackCreate.new(user_audit_info).create(message)
142174
end.to raise_error(BuildpackCreate::Error, 'blork is busted')
143175
end
144176
end
@@ -148,7 +180,7 @@ module VCAP::CloudController
148180
message = BuildpackCreateMessage.new(name: 'the-name', stack: 'does-not-exist')
149181

150182
expect do
151-
BuildpackCreate.new.create(message)
183+
BuildpackCreate.new(user_audit_info).create(message)
152184
end.to raise_error(BuildpackCreate::Error, "Stack 'does-not-exist' does not exist")
153185
end
154186
end
@@ -164,7 +196,7 @@ module VCAP::CloudController
164196
it 'raises a human-friendly error' do
165197
message = BuildpackCreateMessage.new(name:)
166198
expect do
167-
BuildpackCreate.new.create(message)
199+
BuildpackCreate.new(user_audit_info).create(message)
168200
end.to raise_error(BuildpackCreate::Error, "Buildpack with name 'the-name' and an unassigned stack already exists")
169201
end
170202
end
@@ -177,7 +209,7 @@ module VCAP::CloudController
177209
it 'raises a human-friendly error' do
178210
message = BuildpackCreateMessage.new(name: name, stack: 'the-stack')
179211
expect do
180-
BuildpackCreate.new.create(message)
212+
BuildpackCreate.new(user_audit_info).create(message)
181213
end.to raise_error(BuildpackCreate::Error, "Buildpack with name 'the-name', stack 'the-stack' and lifecycle 'buildpack' already exists")
182214
end
183215
end

spec/unit/actions/buildpack_delete_spec.rb

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

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

813
describe '#delete' do
914
let!(:buildpack) { Buildpack.make }
@@ -15,6 +20,30 @@ module VCAP::CloudController
1520
expect { buildpack.refresh }.to raise_error Sequel::Error, 'Record not found'
1621
end
1722

23+
it 'creates an audit event' do
24+
buildpack_guid = buildpack.guid
25+
buildpack_name = buildpack.name
26+
27+
buildpack_delete.delete([buildpack])
28+
29+
event = VCAP::CloudController::Event.last
30+
31+
expect(event.values).to include(
32+
type: 'audit.buildpack.delete',
33+
actee: buildpack_guid,
34+
actee_type: 'buildpack',
35+
actee_name: buildpack_name,
36+
actor: user_audit_info.user_guid,
37+
actor_type: 'user',
38+
actor_name: user_audit_info.user_email,
39+
actor_username: user_audit_info.user_name,
40+
space_guid: '',
41+
organization_guid: ''
42+
)
43+
expect(event.metadata).to eq({})
44+
expect(event.timestamp).to be
45+
end
46+
1847
context 'when the buildpack has associated bits in the blobstore' do
1948
before do
2049
buildpack.update(key: 'the-key')

0 commit comments

Comments
 (0)