Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
200 commits
Select commit Hold shift + click to select a range
a0501f5
Test space-owned service accounts and same-space assignment (red)
rkoster Oct 5, 2026
6d14ea8
Implement space-owned service accounts and same-space assignment
rkoster Oct 5, 2026
d7d6353
Test same-space app account relationship endpoint (red)
rkoster Oct 5, 2026
668c4f0
Expose same-space app service account relationship
rkoster Oct 5, 2026
9008ffd
Test account client and OAuth principal provisioning (red)
rkoster Oct 5, 2026
d78294c
Reconcile account clients and roleless OAuth principals
rkoster Oct 5, 2026
c8a5c2d
Test provisioning HTTP retries and altered TLS policies (red)
rkoster Oct 5, 2026
6aa4ca0
Normalize managed client sets and reject extra TLS policies
rkoster Oct 5, 2026
91fff36
Test retryable account provisioning worker job (red)
rkoster Oct 5, 2026
4428266
Add credential-free retryable account provisioning job
rkoster Oct 5, 2026
1f22e71
Test configured provisioning client and token refresh (red)
rkoster Oct 5, 2026
ed54722
Configure dedicated account provisioning client in workers
rkoster Oct 5, 2026
36d439c
Test asynchronous first-bind provisioning and job reuse (red)
rkoster Oct 5, 2026
0bc48a6
Queue pollable provisioning on authorized first bind
rkoster Oct 5, 2026
9c4a101
Test account listing description and metadata updates (red)
rkoster Oct 5, 2026
98dbf8d
Add scoped account listing description and metadata APIs
rkoster Oct 5, 2026
fee96b0
Test account disable enable and safe deletion lifecycle (red)
rkoster Oct 5, 2026
19167e5
Reconcile account disable enable and unused-account deletion
rkoster Oct 5, 2026
88182e4
Test service account and assignment audit events (red)
rkoster Oct 5, 2026
01a0cf9
Audit service account lifecycle and assignment changes
rkoster Oct 5, 2026
6698b22
Test explicit account roles without human UAA lookup (red)
rkoster Oct 5, 2026
3e1e289
Resolve managed account names locally for explicit role APIs
rkoster Oct 5, 2026
c885c1f
Test typed runtime identity and fail-closed account gates (red)
rkoster Oct 5, 2026
8554429
Build typed ready-account runtime identity and discovery metadata
rkoster Oct 5, 2026
9b190e0
Test runtime and task account propagation boundaries (red)
rkoster Oct 5, 2026
ae7c301
Propagate account identity to runtime recipes and discovery
rkoster Oct 5, 2026
ce5df05
Test task account identity snapshot at creation (red)
rkoster Oct 5, 2026
9b74065
Snapshot task account identity before asynchronous submission
rkoster Oct 5, 2026
d103ed9
Test process account launch snapshot on restart (red)
rkoster Oct 5, 2026
3e3a3d6
Preserve process account launch identity until explicit restart
rkoster Oct 5, 2026
37b08b8
Test staging exclusion of injected account discovery (red)
rkoster Oct 5, 2026
f9c0c9d
Exclude service account discovery from all staging inputs
rkoster Oct 5, 2026
9396e4f
Test active identity deletion and secretless discovery guards (red)
rkoster Oct 5, 2026
2773e73
Protect active launch identities and non-secret endpoint discovery
rkoster Oct 5, 2026
db812b5
Verify pollable account provisioning retries through completion
rkoster Oct 5, 2026
48d64c8
Verify concurrent apps share one first-bind provisioning job
rkoster Oct 5, 2026
bd5c54d
Test provisioning validation for API local workers (red)
rkoster Oct 5, 2026
201ab95
Validate optional provisioning inputs for API local workers
rkoster Oct 5, 2026
b576bf8
Test pre-feature launches cannot acquire new account identity (red)
rkoster Oct 5, 2026
2198d8c
Keep pre-feature launches account-free until explicit launch
rkoster Oct 5, 2026
46eb624
Test account app listing applies requested filters (red)
rkoster Oct 5, 2026
9bc0cc1
Apply normal app filters to account app listing
rkoster Oct 5, 2026
e4bf7ac
Test bind exclusion during retrying lifecycle operations (red)
rkoster Oct 5, 2026
b4cea54
Block binding during retrying disable or delete operations
rkoster Oct 5, 2026
1726f83
Test owned account guard before recursive space deletion (red)
rkoster Oct 5, 2026
817c5a6
Report owned service accounts before recursive space deletion
rkoster Oct 5, 2026
19adbae
Verify rebuilt LRPs preserve launch account after unbind
rkoster Oct 5, 2026
4bccc9b
Document experimental space-owned service account API and rollout
rkoster Oct 5, 2026
f014a3b
Test MySQL account relationship collation on migration retry (red)
rkoster Oct 6, 2026
f7786f5
Match parent MySQL collation and retry partial account assignment mig…
rkoster Oct 6, 2026
7a16b72
Test UAA normalized inert scope during account reconciliation (red)
rkoster Oct 6, 2026
d972bc3
Accept UAA inert scope normalization for managed client reconciliation
rkoster Oct 6, 2026
b6a3634
Test rolling service account creation budget and admin exemption (red)
rkoster Oct 7, 2026
9fe3e79
Enforce atomic weekly service account creation budget for non-admins
rkoster Oct 7, 2026
9642e1c
Test cascading account cleanup on space and org deletion (red)
rkoster Oct 7, 2026
1943c86
Correct task fixture and confirm assigned workload cascade failure (red)
rkoster Oct 7, 2026
fa942e9
Cascade owned service account cleanup during space and org deletion
rkoster Oct 7, 2026
ae39ff0
Test registering account clients by UAA trust bundle reference (red)
rkoster Oct 16, 2026
dc0ce61
Register account clients by UAA trust bundle reference when configured
rkoster Oct 16, 2026
5074283
Test reconciling trust-anchor drift on owned account clients (red)
rkoster Oct 16, 2026
3958f06
Update owned account clients on trust-anchor drift instead of rejecti…
rkoster Oct 16, 2026
e059a98
Test updating owned account clients through the management client (red)
rkoster Oct 16, 2026
6a0864e
Expose client updates on the service account management client
rkoster Oct 16, 2026
d962886
Test requiring UAA trust bundle references for account clients (red)
rkoster Oct 16, 2026
47c6bd5
Require UAA trust bundle references for account clients and drop inli…
rkoster Oct 16, 2026
a4247e6
Test immutable organization service account prefix allocation (red)
rkoster Nov 9, 2026
d54e075
Allocate immutable organization service account prefixes
rkoster Nov 9, 2026
8299885
Test account organization ownership and initial space entitlement (red)
rkoster Nov 9, 2026
c7de546
Persist account organization owners and initial space entitlements
rkoster Nov 9, 2026
6581773
Test retaining org accounts across space deletion (red)
rkoster Nov 9, 2026
8d68274
Retain service accounts on space deletion and clean up with their org
rkoster Nov 9, 2026
a446b3c
Test account space entitlement APIs and developer permissions (red)
rkoster Nov 9, 2026
cc5efc5
Authorize account use and management through org space entitlements
rkoster Nov 9, 2026
d121064
Test qualified org-local account names and explicit tombstone reuse (…
rkoster Nov 9, 2026
637527f
Scope account identities and tombstones to immutable org namespaces
rkoster Nov 9, 2026
c9016a0
Test org account quota API and capacity enforcement (red)
rkoster Nov 9, 2026
e46228c
Enforce organization service account capacity quotas
rkoster Nov 9, 2026
3a7a37b
Test bearer account registration with required org attribution (red)
rkoster Nov 9, 2026
08845a5
Register bearer account clients with owning org and workload attribution
rkoster Nov 9, 2026
3bde1d3
Test prefix-qualified runtime identity across entitled spaces (red)
rkoster Nov 9, 2026
3cdf836
Send qualified account identity for apps in entitled spaces
rkoster Nov 9, 2026
7bcd1a4
Test platform service account context on broker app bindings (red)
rkoster Nov 9, 2026
20efc9b
Correct broker context fixture and confirm missing identity context (…
rkoster Nov 9, 2026
047ca8f
Include platform-controlled account identity in broker binding context
rkoster Nov 9, 2026
76275d8
Test qualified service account route sources and grant cleanup (red)
rkoster Nov 9, 2026
6a86ce7
Resolve qualified account route sources and remove their grants on de…
rkoster Nov 9, 2026
f4d20d2
Test account reassignment guard for broker bindings (red)
rkoster Nov 9, 2026
30b6a5f
Block account reassignment while broker bindings remain
rkoster Nov 9, 2026
5055bb1
Test prefix and writable-space entitlement authorization guards (red)
rkoster Nov 9, 2026
c999e95
Require allocated prefixes and active developer spaces for account ma…
rkoster Nov 9, 2026
e4042d0
Test readable account route sources and qualified filtering (red)
rkoster Nov 9, 2026
a44abb9
Authorize account route grants and support qualified source filters
rkoster Nov 9, 2026
6fe38b1
Test qualified runtime contract gate fails closed by default (red)
rkoster Nov 9, 2026
25292b7
Gate qualified runtime identities on explicit contract version two
rkoster Nov 9, 2026
8fdaf64
Test durable deletion history across explicit account name reuse (red)
rkoster Nov 9, 2026
2be0f15
Retain account deletion timestamps independently of reusable reservat…
rkoster Nov 9, 2026
7498075
Test qualified route account rules require an enforcing router contra…
rkoster Nov 9, 2026
db39623
Gate service account route rules on qualified router support
rkoster Nov 9, 2026
d02758d
Test concurrent account quota enforcement in the registry (red)
rkoster Nov 9, 2026
cd03ff6
Serialize registry quota checks under the owning organization lock
rkoster Nov 9, 2026
6f3060e
Test rejecting new assignments without an organization prefix (red)
rkoster Nov 9, 2026
e89fc24
Require allocated organization prefixes for new account assignments
rkoster Nov 9, 2026
471ca8e
Test named UAA resource policy registration (red)
rkoster Nov 9, 2026
04947ab
Register operator named UAA resource policies when configured
rkoster Nov 9, 2026
5ea1178
Test rejecting new entitlements during account deletion (red)
rkoster Nov 9, 2026
513191d
Reject entitlement additions once account deletion begins
rkoster Nov 9, 2026
d80a64b
Test account relationships and included sources on route policies (red)
rkoster Nov 9, 2026
b2f04d9
Present readable account relationships and included route sources
rkoster Nov 9, 2026
d06c5c8
Test safe rollback of an empty account namespace transition (red)
rkoster Nov 9, 2026
0914c0e
Allow empty namespace rollback while rejecting lossy identity downgrades
rkoster Nov 9, 2026
78fae23
Test same-org enforcement on direct entitlement association writes (red)
rkoster Nov 9, 2026
9e0dfcd
Validate same-organization ownership for every space entitlement
rkoster Nov 9, 2026
044330d
Test rechecking account management rights under the account lock (red)
rkoster Nov 9, 2026
0b452e7
Recheck account lifecycle authorization after locking current entitle…
rkoster Nov 9, 2026
c700b7d
Test legacy unqualified identities retain global collision protection…
rkoster Nov 9, 2026
37d9ad8
Preserve global collision reservations for retiring unqualified ident…
rkoster Nov 9, 2026
60fe0f5
Test preserving pre-identity broker binding semantics (red)
rkoster Nov 9, 2026
496080a
Snapshot account identity on service bindings and retain legacy bindi…
rkoster Nov 9, 2026
c6d854d
Test identity-bearing bindings protect account lifecycle references (…
rkoster Nov 9, 2026
3837232
Protect account deletion and entitlement revocation with binding refe…
rkoster Nov 9, 2026
e049719
Test binding snapshots revalidate account entitlement at persistence …
rkoster Nov 9, 2026
aea3efb
Lock app and account before capturing broker binding identity
rkoster Nov 9, 2026
159287d
Test launch snapshot rechecks the account space entitlement (red)
rkoster Nov 9, 2026
49165a4
Lock and revalidate account entitlement before process launch snapshots
rkoster Nov 9, 2026
b724da3
Test task launch serializes account entitlement validation (red)
rkoster Nov 9, 2026
a358230
Lock account entitlement when creating runtime task snapshots
rkoster Nov 9, 2026
35a434e
Test broker binding fails closed for a missing captured account (red)
rkoster Nov 9, 2026
f1c001d
Refuse broker requests when a captured account identity is missing
rkoster Nov 9, 2026
60a8b9a
Test rejecting entitlements to deleting spaces (red)
rkoster Nov 9, 2026
7f056fb
Reject granting account entitlements to deleting spaces
rkoster Nov 9, 2026
3c7b0de
Test failed org account cleanup leaves a deleting parent (red)
rkoster Nov 9, 2026
8549600
Mark orgs deleting before retryable service account cleanup
rkoster Nov 9, 2026
0fe6772
Test reservation deletion counts and age observability (red)
rkoster Nov 9, 2026
7f93c66
Expose account reservation counts and deletion age statistics
rkoster Nov 9, 2026
6dd9039
Test reservation observability uses the organization namespace (red)
rkoster Nov 9, 2026
9a1c688
Scope unknown reservation age statistics to organization and name
rkoster Nov 9, 2026
6e75d80
Test rejecting route grants while account deletion is active (red)
rkoster Nov 9, 2026
6a11a0e
Serialize account route grants against account deletion
rkoster Nov 9, 2026
bc73646
Test revocation response after the caller loses account visibility (red)
rkoster Nov 9, 2026
c1bc21a
Return successful entitlement revocation without reauthorizing the re…
rkoster Nov 9, 2026
a5ed905
Test account name reuse warnings and prior identity audit linkage (red)
rkoster Nov 9, 2026
1b5bbca
Warn on account name reuse and link the prior identity in audit history
rkoster Nov 9, 2026
eb18c0b
Read prior account deletion from the timestamped history row
rkoster Nov 9, 2026
f6c9262
Test account creation rechecks deleting org after locking (red)
rkoster Nov 9, 2026
60f7001
Reject new accounts when org deletion wins the organization lock
rkoster Nov 9, 2026
b5c0dfa
Test role management for qualified account principals (red)
rkoster Nov 9, 2026
73d6b88
Resolve qualified account principals without human UAA username lookup
rkoster Nov 9, 2026
de04b44
Test shared-domain safeguards remain during org account cleanup (red)
rkoster Nov 9, 2026
1213f2f
Check shared domains before marking account-owning organizations dele…
rkoster Nov 9, 2026
9b4e7b6
Refresh owned domains when guarding account cleanup and preserve exis…
rkoster Nov 9, 2026
ead0dd1
Document org-owned account APIs and align acceptance fixtures
rkoster Nov 9, 2026
a98f8a0
Cover quota lowering with cached organization associations
rkoster Nov 9, 2026
7713454
Test suspended source spaces cannot authorize entitlement expansion (…
rkoster Nov 9, 2026
52068ab
Require current active source entitlements for developer sharing
rkoster Nov 9, 2026
fd9af84
Test account route policies need no router-version setting (red)
rkoster Nov 10, 2026
f9d5880
Remove router contract gate and require routing-first release sequencing
rkoster Nov 10, 2026
ba95284
Align regression expectations with account prefixes and entitlements
rkoster Nov 11, 2026
98bab7d
Test concurrent account provisioning after permission snapshots (red)
rkoster Nov 11, 2026
3c52afd
Use current locking read when reusing account provisioning jobs
rkoster Nov 11, 2026
949f62e
Test MySQL budget snapshots and namespace rollback indexes (red)
rkoster Nov 11, 2026
ef91b92
Test full empty account-assignment rollback on MySQL (red)
rkoster Nov 11, 2026
498d40b
Fix MySQL account creation snapshots and rollback index dependencies
rkoster Nov 11, 2026
7f69c1d
Test account assignment after concurrent entitlement revocation (red)
rkoster Nov 11, 2026
436df9d
Read current space entitlement after locking account for assignment
rkoster Nov 11, 2026
f285ecc
Test independent CAPI user lifecycle for service accounts (red)
rkoster Nov 11, 2026
4eaaf6d
Decouple CAPI users and roles from service account lifecycle
rkoster Nov 11, 2026
25923ee
Test org cleanup of unprovisioned account reservations (red)
rkoster Nov 11, 2026
f9f707f
Clean unused account reservations without UAA provisioning dependency
rkoster Nov 11, 2026
a21dd5e
Remove unpublished POC compatibility and reservation statistics
rkoster Nov 12, 2026
14427ac
Test model-owned lookup of qualified account client IDs (red)
rkoster Nov 12, 2026
1b10bdf
Resolve qualified client identities through service account model
rkoster Nov 12, 2026
3b42219
Test bounded association queries for service account lists (red)
rkoster Nov 12, 2026
ac242dc
Eager load associations for service account list presentation
rkoster Nov 12, 2026
e0bf2ab
Test entitlement revocation in nonwritable spaces (red)
rkoster Nov 12, 2026
523bcac
Require writable spaces for account entitlement revocation
rkoster Nov 12, 2026
1032d9f
Test account-keyed lifecycle reference indexes and rollback (red)
rkoster Nov 12, 2026
32fef5c
Index service account references for lifecycle checks
rkoster Nov 12, 2026
aadbdba
Test user-provided bindings remain independent of account identity (red)
rkoster Nov 12, 2026
8b7dfb6
Capture account identity only for managed service bindings
rkoster Nov 12, 2026
a33e5a4
Test V2 broker binding identity before persistence (red)
rkoster Nov 12, 2026
7c99596
Capture and lock V2 service account identity before broker bind
rkoster Nov 12, 2026
0729130
Test recovery of partially applied app account migration (red)
rkoster Nov 12, 2026
9ddfe25
Resume app account migration after foreign key or index interruption
rkoster Nov 12, 2026
27fe3ee
Preserve V2 orphan cleanup jobs and fix list spec formatting
rkoster Nov 12, 2026
50a88d6
Test invalid concurrent account reference index recovery (red)
rkoster Nov 12, 2026
a47a5cb
Repair invalid PostgreSQL service account reference indexes on retry
rkoster Nov 12, 2026
648f47d
Test recursive V2 org service account cleanup and retry (red)
rkoster Nov 12, 2026
7dc3652
Share retryable service account cleanup with V2 org deletion
rkoster Nov 12, 2026
9f69c86
Test single service account activation and updater worker restart (red)
rkoster Nov 12, 2026
12534ae
Use one service account enable setting across CAPI processes
rkoster Nov 12, 2026
a5eb5e9
Test disabled account reconciliation preserves shared domain freshnes…
rkoster Nov 12, 2026
a35d32b
Pin enabled runtime config in disabled account sync regression (red)
rkoster Nov 12, 2026
405a062
Keep process sync freshness on expected account readiness rejection
rkoster Nov 12, 2026
0aa5d10
Test accepted GUID and timestamp filters on service account lists (red)
rkoster Nov 12, 2026
1bf0e29
Apply common GUID and date filters to service account lists
rkoster Nov 12, 2026
58a04f1
Test space-first entitlement lookup index (red)
rkoster Nov 13, 2026
f2903b3
Index entitlement lookups by space and account
rkoster Nov 13, 2026
2f0f042
Test shared issuer discovery reuse expiry and failure handling (red)
rkoster Nov 13, 2026
e4a087a
Share synchronized UAA issuer discovery across broker binds
rkoster Nov 13, 2026
864cb3b
Parse issuer discovery strictly and fix concurrent spec formatting
rkoster Nov 13, 2026
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
78 changes: 78 additions & 0 deletions app/actions/app_assign_service_account.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
require 'repositories/service_account_event_repository'

module VCAP::CloudController
class AppAssignServiceAccount
class Error < StandardError; end
class Unauthorized < Error; end
class Conflict < Error; end

def initialize(permissions, actor: nil)
@permissions = permissions
@actor = actor
end

def assign(app, account, provision: false)
job = nil
app.db.transaction do
app.lock!
raise Unauthorized.new('not authorized to update the app') unless @permissions.can_write_to_active_space?(app.space.id)

validate_binding_change!(app, account)

if account
account.lock!
raise Conflict.new('organization service account prefix is unset') unless account.organization.service_account_prefix
raise Conflict.new('account must be entitled to the app space in the same organization') unless currently_entitled?(account, app.space)

validate_ready!(account, provision)
raise Conflict.new('another service account is already assigned; unbind first') if app.service_account_guid && app.service_account_guid != account.guid

job = provision_account(account) unless account.status == 'ready'
end

unless app.service_account_guid == account&.guid
previous_guid = app.service_account_guid
app.update(service_account: account)
record_assignment(app, account, previous_guid) if @actor
end
end
provision ? job : app
end

private

def currently_entitled?(account, space)
account.organization_id == space.organization_id && account.db[:service_account_space_entitlements].
where(service_account_guid: account.guid, space_guid: space.guid).for_update.first
end

def validate_binding_change!(app, account)
return unless app.service_account_guid && app.service_account_guid != account&.guid && app.service_bindings_dataset.exclude(service_account_guid: nil).any?

raise Conflict.new('remove service bindings before changing the service account')
end

def record_assignment(app, account, previous_guid)
Repositories::ServiceAccountEventRepository.record(
app, account ? 'assign' : 'unassign', @actor,
service_account_guid: account&.guid || previous_guid
)
end

def validate_ready!(account, provision)
raise Conflict.new('service account is not ready or enabled') unless account.enabled && (account.status == 'ready' || provision)

if PollableJobModel.where(resource_guid: account.guid, resource_type: 'service_account', state: %w[PROCESSING POLLING]).exclude(operation: 'service_account.provision').any?
raise Conflict.new('service account lifecycle operation is in progress')
end
end

def provision_account(account)
job = PollableJobModel.where(resource_guid: account.guid, operation: 'service_account.provision', state: %w[PROCESSING POLLING]).for_update.first
return job if job

account.update(status: 'reconciling')
Jobs::Enqueuer.new(queue: Jobs::Queues.generic).enqueue_pollable(Jobs::V3::ServiceAccountProvision.new(account.guid))
end
end
end
2 changes: 2 additions & 0 deletions app/actions/organization_create.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def create(message)
Organization.db.transaction do
org = VCAP::CloudController::Organization.create(
name: message.name,
service_account_prefix: message.service_account_prefix,
status: message.suspended ? Organization::SUSPENDED : Organization::ACTIVE
)

Expand All @@ -29,6 +30,7 @@ def create(message)
private

def validation_error!(error, message)
error!("Service account prefix '#{message.service_account_prefix}' is already reserved.") if error.errors.on(:service_account_prefix)&.include?(:unique)
error!("Organization '#{message.name}' already exists.") if error.errors.on(:name)&.include?(:unique)
error!(error.message)
end
Expand Down
10 changes: 10 additions & 0 deletions app/actions/organization_delete.rb
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
require 'actions/space_delete'
require 'actions/label_delete'
require 'actions/annotation_delete'
require 'actions/organization_service_account_cleanup'

module VCAP::CloudController
class OrganizationDelete
include OrganizationServiceAccountCleanup

def initialize(space_deleter, user_audit_info)
@space_deleter = space_deleter
@user_audit_info = user_audit_info
end

def delete(org_dataset)
org_dataset.each do |org|
shared_errors = shared_domain_errors(org)
return shared_errors unless shared_errors.empty?

mark_deleting_for_account_cleanup(org)
errs = @space_deleter.delete(org.spaces_dataset)
unless errs.empty?
error_message = errs.map(&:message).join("\n\n")
return [CloudController::Errors::ApiError.new_from_details('OrganizationDeletionFailed', org.name, error_message)]
end

account_errors = delete_service_accounts(org)
return [CloudController::Errors::ApiError.new_from_details('OrganizationDeletionFailed', org.name, account_errors.map(&:message).join("\n\n"))] unless account_errors.empty?
Comment thread
rkoster marked this conversation as resolved.
Resolved

domains_to_unshare = []
org.private_domains.each do |private_domain|
if private_domain.owning_organization == org
Expand Down
1 change: 1 addition & 0 deletions app/actions/organization_quotas_create.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def create(message)
QuotaDefinition.db.transaction do
org_quota = VCAP::CloudController::QuotaDefinition.create(
name: message.name,
total_service_accounts: message.total_service_accounts || QuotaDefinition::UNLIMITED,

# Apps
memory_limit: message.total_memory_in_mb || QuotaDefinition::DEFAULT_MEMORY_LIMIT,
Expand Down
1 change: 1 addition & 0 deletions app/actions/organization_quotas_update.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def self.update(quota, message, user_audit_info)
quota.lock!

quota.name = message.name if message.requested? :name
quota.total_service_accounts = message.total_service_accounts || QuotaDefinition::UNLIMITED if message.service_accounts_limits_message.requested?(:total_service_accounts)

quota.memory_limit = memory_limit(message) if message.apps_limits_message.requested? :total_memory_in_mb
quota.instance_memory_limit = instance_memory_limit(message) if message.apps_limits_message.requested? :per_process_memory_in_mb
Expand Down
56 changes: 56 additions & 0 deletions app/actions/organization_service_account_cleanup.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
require 'actions/service_account_provision'
require 'repositories/service_account_event_repository'

module VCAP::CloudController
module OrganizationServiceAccountCleanup
private

def shared_domain_errors(org)
return [] unless ServiceAccountModel.where(organization_id: org.id).any?

domains = org.owned_private_domains_dataset.all.select(&:shared_with_any_orgs?)
return [] if domains.empty?

details = domains.map { |domain| "Domain '#{domain.name}' is shared with other organizations. Unshare before deleting." }.join("\n")
[CloudController::Errors::ApiError.new_from_details('OrganizationDeletionFailed', org.name, details)]
end

def mark_deleting_for_account_cleanup(org)
return unless ServiceAccountModel.where(organization_id: org.id).any?

org.db.transaction do
org.lock!
org.update(status: Organization::DELETING)
end
end

def delete_service_accounts(org)
accounts = ServiceAccountModel.where(organization_id: org.id).order(:guid).all
return [] if accounts.empty?

accounts.each_with_object([]) do |account, errors|
locally_deleted = false
account.db.transaction do
account.lock!
if PollableJobModel.where(resource_guid: account.guid, resource_type: 'service_account', state: %w[PROCESSING POLLING]).any?
raise ServiceAccountProvision::Conflict.new('service account operation is already in progress')
end

if account.status == 'reserved' && !account.in_use?
Repositories::ServiceAccountEventRepository.record(account, 'delete', @user_audit_info, { recursive: true })
account.destroy
locally_deleted = true
else
account.update(enabled: false, status: 'deleting')
end
end
next if locally_deleted

Repositories::ServiceAccountEventRepository.record(account, 'delete', @user_audit_info, { recursive: true })
CloudController::DependencyLocator.instance.service_account_provisioner.deprovision(account, delete: true)
rescue StandardError => e
errors << e
end
end
end
end
4 changes: 4 additions & 0 deletions app/actions/organization_update.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ def initialize(user_audit_info)
def update(org, message)
org.db.transaction do
org.lock!
org.service_account_prefix = message.service_account_prefix if message.requested?(:service_account_prefix)
org.name = message.name if message.requested?(:name)
LabelsUpdate.update(org, message.labels, OrganizationLabelModel)
AnnotationsUpdate.update(org, message.annotations, OrganizationAnnotationModel)
Expand All @@ -29,6 +30,9 @@ def update(org, message)
end

def validation_error!(error, message)
prefix_errors = error.errors.on(:service_account_prefix)
error!("Service account prefix '#{message.service_account_prefix}' is already reserved.") if prefix_errors&.include?(:unique)
error!('Service account prefix is immutable once allocated.') if prefix_errors&.include?(:immutable)
error!("Organization name '#{message.name}' is already taken.") if error.errors.on(:name)&.include?(:unique)
error!(error.message)
end
Expand Down
2 changes: 1 addition & 1 deletion app/actions/process_restart.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def restart(process:, config:, stop_in_runtime:, revision: nil)
runners(config).runner_for_process(process).stop
end

process.update(state: ProcessModel::STARTED, revision: revision_to_set)
process.update(state: ProcessModel::STARTED, revision: revision_to_set, service_account_guid: process.app.service_account_guid, service_account_snapshot: true)
runners(config).runner_for_process(process).start
end
end
Expand Down
12 changes: 12 additions & 0 deletions app/actions/route_policy_create.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def create(route:, message:)
validate_scope!(route.domain, message.source)

route_policy = RoutePolicy.db.transaction do
lock_account_source!(message.source)
# Lock the parent route row to serialize concurrent creates.
# SELECT ... FOR UPDATE on an empty policies table acquires no row locks,
# so two concurrent transactions can both read [] and both pass cf:any
Expand Down Expand Up @@ -45,6 +46,17 @@ def create(route:, message:)

private

def lock_account_source!(source)
match = source.match(RoutePolicy::SERVICE_ACCOUNT_SOURCE_REGEX)
return unless match

account = ServiceAccountModel.first(identity_prefix: match[1], name: match[2])
raise Error.new('Service account not found') unless account

account.lock!
raise Error.new('Service account is being deleted') if account.status == 'deleting'
end

def validate_scope!(domain, source)
return unless domain.route_policies_scope == 'space'
return unless source.start_with?('cf:org:')
Expand Down
27 changes: 27 additions & 0 deletions app/actions/service_account_creation_budget.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module VCAP::CloudController
class ServiceAccountCreationBudget
WINDOW = 7.days

def self.consume(principal, exempt:)
return yield if exempt

# Lock the authenticated principal, not a space: requests on different API
# instances and in different spaces must share one atomic budget.
principal.lock!
now = Time.now.utc
creations = ServiceAccountModel.db[:service_account_creations]
limit = Config.config.get(:service_account_creation_limit) || -1
# MySQL permission/authentication reads may have established an older
# repeatable-read snapshot before the principal lock was acquired.
used = creations.where(principal_guid: principal.guid).where { created_at > now - WINDOW }.select(:principal_guid).for_update.all.size
raise CloudController::Errors::V3::ApiError.new_from_details('ServiceAccountCreationLimitExceeded') if limit.between?(0, used)

result = yield
# Independent of accounts, tombstones and audit-event retention. Failed
# creations roll this back; successful deletions never refund the budget.
creations.insert(principal_guid: principal.guid, created_at: now) # rubocop:disable Rails/SkipsModelValidations -- Transactional creation ledger, not an ActiveRecord model.
creations.where(principal_guid: principal.guid).where { created_at <= now - WINDOW }.delete
result
end
end
end
Loading
Loading