Skip to content

Commit 4052de4

Browse files
committed
Use cosmotech-api-common in version 0.1.38-hotfixed
- Twingraph endpoints are now secured by platform role (Organization.User,...) - OID can be set in RBAC (to access/manipulate organization/workspace/scenario) - Users with Platform.Admin role can now delete/update ressources which was not created by them
1 parent d92fdcd commit 4052de4

File tree

11 files changed

+64
-40
lines changed

11 files changed

+64
-40
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ group = "com.cosmotech"
3939
version = scmVersion.version
4040

4141
val kotlinJvmTarget = 17
42-
val cosmotechApiCommonVersion = "0.1.37-hotfixed"
42+
val cosmotechApiCommonVersion = "0.1.38-hotfixed"
4343
val cosmotechApiAzureVersion = "0.1.9-SNAPSHOT"
4444
val cosmotechApiCosmosDBVersion = "0.1.0-SNAPSHOT"
4545
val azureSpringBootBomVersion = "3.14.0"

connector/src/main/kotlin/com/cosmotech/connector/azure/ConnectorServiceImpl.kt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import com.cosmotech.api.azure.cosmosdb.ext.findByIdOrThrow
88
import com.cosmotech.api.azure.cosmosdb.service.CsmCosmosDBService
99
import com.cosmotech.api.events.ConnectorRemoved
1010
import com.cosmotech.api.exceptions.CsmAccessForbiddenException
11+
import com.cosmotech.api.security.ROLE_PLATFORM_ADMIN
12+
import com.cosmotech.api.utils.getCurrentAuthenticatedRoles
1113
import com.cosmotech.api.utils.getCurrentAuthenticatedUserName
1214
import com.cosmotech.connector.api.ConnectorApiService
1315
import com.cosmotech.connector.domain.Connector
@@ -46,14 +48,17 @@ internal class ConnectorServiceImpl : CsmCosmosDBService(), ConnectorApiService
4648
return cosmosTemplate.insert(
4749
coreConnectorContainer,
4850
connector.copy(
49-
id = idGenerator.generate("connector"), ownerId = getCurrentAuthenticatedUserName()))
51+
id = idGenerator.generate("connector"),
52+
ownerId = getCurrentAuthenticatedUserName(csmPlatformProperties)))
5053
?: throw IllegalStateException("No connector returned in response: $connector")
5154
}
5255

5356
override fun unregisterConnector(connectorId: String) {
5457
val connector = this.findConnectorById(connectorId)
55-
if (connector.ownerId != getCurrentAuthenticatedUserName()) {
56-
// TODO Only the owner or an admin should be able to perform this operation
58+
val isPlatformAdmin =
59+
getCurrentAuthenticatedRoles(csmPlatformProperties).contains(ROLE_PLATFORM_ADMIN)
60+
if (connector.ownerId != getCurrentAuthenticatedUserName(csmPlatformProperties) &&
61+
!isPlatformAdmin) {
5762
throw CsmAccessForbiddenException("You are not allowed to delete this Resource")
5863
}
5964
cosmosTemplate.deleteEntity(coreConnectorContainer, connector)

dataset/src/main/kotlin/com/cosmotech/dataset/azure/DatasetServiceImpl.kt

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ import com.cosmotech.api.events.ConnectorRemovedForOrganization
1414
import com.cosmotech.api.events.OrganizationRegistered
1515
import com.cosmotech.api.events.OrganizationUnregistered
1616
import com.cosmotech.api.exceptions.CsmAccessForbiddenException
17+
import com.cosmotech.api.security.ROLE_PLATFORM_ADMIN
1718
import com.cosmotech.api.utils.changed
1819
import com.cosmotech.api.utils.compareToAndMutateIfNeeded
20+
import com.cosmotech.api.utils.getCurrentAuthenticatedRoles
1921
import com.cosmotech.api.utils.getCurrentAuthenticatedUserName
2022
import com.cosmotech.api.utils.toDomain
2123
import com.cosmotech.connector.api.ConnectorApiService
@@ -67,7 +69,8 @@ internal class DatasetServiceImpl(
6769

6870
val datasetCopy =
6971
dataset.copy(
70-
id = idGenerator.generate("dataset"), ownerId = getCurrentAuthenticatedUserName())
72+
id = idGenerator.generate("dataset"),
73+
ownerId = getCurrentAuthenticatedUserName(csmPlatformProperties))
7174
datasetCopy.connector!!.apply {
7275
name = existingConnector.name
7376
version = existingConnector.version
@@ -78,8 +81,10 @@ internal class DatasetServiceImpl(
7881

7982
override fun deleteDataset(organizationId: String, datasetId: String) {
8083
val dataset = findDatasetById(organizationId, datasetId)
81-
if (dataset.ownerId != getCurrentAuthenticatedUserName()) {
82-
// TODO Only the owner or an admin should be able to perform this operation
84+
val isPlatformAdmin =
85+
getCurrentAuthenticatedRoles(csmPlatformProperties).contains(ROLE_PLATFORM_ADMIN)
86+
if (dataset.ownerId != getCurrentAuthenticatedUserName(csmPlatformProperties) &&
87+
!isPlatformAdmin) {
8388
throw CsmAccessForbiddenException("You are not allowed to delete this Resource")
8489
}
8590
cosmosTemplate.deleteEntity("${organizationId}_datasets", dataset)
@@ -94,9 +99,11 @@ internal class DatasetServiceImpl(
9499
.isNotEmpty()
95100

96101
if (dataset.ownerId != null && dataset.changed(existingDataset) { ownerId }) {
102+
val isPlatformAdmin =
103+
getCurrentAuthenticatedRoles(csmPlatformProperties).contains(ROLE_PLATFORM_ADMIN)
97104
// Allow to change the ownerId as well, but only the owner can transfer the ownership
98-
if (existingDataset.ownerId != getCurrentAuthenticatedUserName()) {
99-
// TODO Only the owner or an admin should be able to perform this operation
105+
if (existingDataset.ownerId != getCurrentAuthenticatedUserName(csmPlatformProperties) &&
106+
!isPlatformAdmin) {
100107
throw CsmAccessForbiddenException(
101108
"You are not allowed to change the ownership of this Resource")
102109
}

organization/src/main/kotlin/com/cosmotech/organization/azure/OrganizationServiceImpl.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import com.cosmotech.api.rbac.model.RbacAccessControl
2828
import com.cosmotech.api.rbac.model.RbacSecurity
2929
import com.cosmotech.api.utils.changed
3030
import com.cosmotech.api.utils.compareToAndMutateIfNeeded
31-
import com.cosmotech.api.utils.getCurrentAuthenticatedMail
31+
import com.cosmotech.api.utils.getCurrentAccountIdentifier
3232
import com.cosmotech.api.utils.getCurrentAuthenticatedUserName
3333
import com.cosmotech.api.utils.toDomain
3434
import com.cosmotech.organization.api.OrganizationApiService
@@ -65,7 +65,7 @@ class OrganizationServiceImpl(private val csmRbac: CsmRbac, private val csmAdmin
6565
if (isAdmin || !this.csmPlatformProperties.rbac.enabled) {
6666
return cosmosTemplate.findAll(coreOrganizationContainer)
6767
}
68-
val currentUser = getCurrentAuthenticatedMail(this.csmPlatformProperties)
68+
val currentUser = getCurrentAccountIdentifier(this.csmPlatformProperties)
6969
return cosmosCoreDatabase
7070
.getContainer(this.coreOrganizationContainer)
7171
.queryItems(
@@ -101,15 +101,15 @@ class OrganizationServiceImpl(private val csmRbac: CsmRbac, private val csmAdmin
101101

102102
var organizationSecurity = organization.security
103103
if (organizationSecurity == null) {
104-
organizationSecurity = initSecurity(getCurrentAuthenticatedMail(this.csmPlatformProperties))
104+
organizationSecurity = initSecurity(getCurrentAccountIdentifier(this.csmPlatformProperties))
105105
}
106106

107107
val organizationRegistered =
108108
cosmosTemplate.insert(
109109
coreOrganizationContainer,
110110
organization.copy(
111111
id = newOrganizationId,
112-
ownerId = getCurrentAuthenticatedUserName(),
112+
ownerId = getCurrentAuthenticatedUserName(csmPlatformProperties),
113113
security = organizationSecurity))
114114

115115
val organizationId =

scenario/src/main/kotlin/com/cosmotech/scenario/azure/ScenarioServiceImpl.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import com.cosmotech.api.rbac.getScenarioRolesDefinition
4040
import com.cosmotech.api.scenario.ScenarioMetaData
4141
import com.cosmotech.api.utils.changed
4242
import com.cosmotech.api.utils.compareToAndMutateIfNeeded
43-
import com.cosmotech.api.utils.getCurrentAuthenticatedMail
43+
import com.cosmotech.api.utils.getCurrentAccountIdentifier
4444
import com.cosmotech.api.utils.getCurrentAuthenticatedUserName
4545
import com.cosmotech.api.utils.toDomain
4646
import com.cosmotech.organization.api.OrganizationApiService
@@ -159,14 +159,14 @@ internal class ScenarioServiceImpl(
159159

160160
var scenarioSecurity = scenario.security
161161
if (scenarioSecurity == null) {
162-
scenarioSecurity = initSecurity(getCurrentAuthenticatedMail(this.csmPlatformProperties))
162+
scenarioSecurity = initSecurity(getCurrentAccountIdentifier(this.csmPlatformProperties))
163163
}
164164

165165
val now = OffsetDateTime.now()
166166
val scenarioToSave =
167167
scenario.copy(
168168
id = idGenerator.generate("scenario"),
169-
ownerId = getCurrentAuthenticatedUserName(),
169+
ownerId = getCurrentAuthenticatedUserName(csmPlatformProperties),
170170
solutionId = solution?.id,
171171
solutionName = solution?.name,
172172
runTemplateName = runTemplate?.name,
@@ -401,7 +401,7 @@ internal class ScenarioServiceImpl(
401401
}
402402
val isAdmin = csmRbac.isAdmin(workspace.getRbac(), getCommonRolesDefinition())
403403
if (!isAdmin && this.csmPlatformProperties.rbac.enabled) {
404-
val currentUser = getCurrentAuthenticatedMail(this.csmPlatformProperties)
404+
val currentUser = getCurrentAccountIdentifier(this.csmPlatformProperties)
405405
templateQuery +=
406406
" AND (ARRAY_CONTAINS(c.security.accessControlList, {id: @ACL_USER}, true) " +
407407
"OR c.security.default NOT LIKE 'none')"

scenario/src/test/kotlin/com/cosmotech/scenario/azure/ScenarioServiceImplTests.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import com.cosmotech.api.events.WorkflowStatusRequest
1818
import com.cosmotech.api.id.CsmIdGenerator
1919
import com.cosmotech.api.rbac.CsmRbac
2020
import com.cosmotech.api.rbac.PERMISSION_CREATE_CHILDREN
21-
import com.cosmotech.api.utils.getCurrentAuthenticatedMail
21+
import com.cosmotech.api.utils.getCurrentAccountIdentifier
2222
import com.cosmotech.api.utils.getCurrentAuthenticatedUserName
2323
import com.cosmotech.api.utils.getCurrentAuthentication
2424
import com.cosmotech.organization.api.OrganizationApiService
@@ -139,8 +139,8 @@ class ScenarioServiceImplTests {
139139
every { scenarioServiceImpl getProperty "csmPlatformProperties" } returns csmPlatformProperties
140140

141141
mockkStatic("com.cosmotech.api.utils.SecurityUtilsKt")
142-
every { getCurrentAuthenticatedUserName() } returns AUTHENTICATED_USERNAME
143-
every { getCurrentAuthenticatedMail(csmPlatformProperties) } returns "[email protected]"
142+
every { getCurrentAuthenticatedUserName(csmPlatformProperties) } returns AUTHENTICATED_USERNAME
143+
every { getCurrentAccountIdentifier(csmPlatformProperties) } returns "[email protected]"
144144

145145
scenarioServiceImpl.init()
146146
}

scenariorun/src/main/kotlin/com/cosmotech/scenariorun/azure/ScenarioRunServiceImpl.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ import com.cosmotech.api.events.WorkflowPhaseToStateRequest
2828
import com.cosmotech.api.exceptions.CsmAccessForbiddenException
2929
import com.cosmotech.api.scenario.ScenarioRunMetaData
3030
import com.cosmotech.api.scenariorun.DataIngestionState
31+
import com.cosmotech.api.security.ROLE_PLATFORM_ADMIN
3132
import com.cosmotech.api.security.coroutine.SecurityCoroutineContext
3233
import com.cosmotech.api.utils.convertToMap
34+
import com.cosmotech.api.utils.getCurrentAuthenticatedRoles
3335
import com.cosmotech.api.utils.getCurrentAuthenticatedUserName
3436
import com.cosmotech.api.utils.toDomain
3537
import com.cosmotech.scenario.api.ScenarioApiService
@@ -118,8 +120,10 @@ internal class ScenarioRunServiceImpl(
118120

119121
override fun deleteScenarioRun(organizationId: String, scenariorunId: String) {
120122
val scenarioRun = this.findScenarioRunById(organizationId, scenariorunId)
121-
if (scenarioRun.ownerId != getCurrentAuthenticatedUserName()) {
122-
// TODO Only the owner or an admin should be able to perform this operation
123+
val isPlatformAdmin =
124+
getCurrentAuthenticatedRoles(csmPlatformProperties).contains(ROLE_PLATFORM_ADMIN)
125+
if (scenarioRun.ownerId != getCurrentAuthenticatedUserName(csmPlatformProperties) &&
126+
!isPlatformAdmin) {
123127
throw CsmAccessForbiddenException("You are not allowed to delete this Resource")
124128
}
125129
deleteScenarioRunWithoutAccessEnforcement(scenarioRun)
@@ -618,7 +622,7 @@ internal class ScenarioRunServiceImpl(
618622
val scenarioRun =
619623
scenarioRunRequest.copy(
620624
id = idGenerator.generate("scenariorun", prependPrefix = "sr-"),
621-
ownerId = getCurrentAuthenticatedUserName(),
625+
ownerId = getCurrentAuthenticatedUserName(csmPlatformProperties),
622626
csmSimulationRun = csmSimulationId,
623627
organizationId = organizationId,
624628
workspaceId = workspaceId,

scenariorun/src/test/kotlin/com/cosmotech/scenariorun/azure/ScenarioRunServiceImplTests.kt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import com.cosmotech.api.azure.eventhubs.AzureEventHubsClient
1515
import com.cosmotech.api.config.CsmPlatformProperties
1616
import com.cosmotech.api.events.CsmEventPublisher
1717
import com.cosmotech.api.id.CsmIdGenerator
18+
import com.cosmotech.api.utils.getCurrentAuthenticatedRoles
1819
import com.cosmotech.api.utils.getCurrentAuthenticatedUserName
1920
import com.cosmotech.api.utils.getCurrentAuthentication
2021
import com.cosmotech.api.utils.objectMapper
@@ -49,7 +50,7 @@ import kotlin.test.assertEquals
4950
import kotlin.test.assertNotNull
5051
import kotlin.test.assertNull
5152
import org.junit.jupiter.api.extension.ExtendWith
52-
import org.springframework.security.core.Authentication
53+
import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication
5354

5455
private const val ORGANIZATION_ID = "O-AbCdEf123"
5556
private const val WORKSPACE_ID = "W-AbCdEf123"
@@ -122,8 +123,9 @@ class ScenarioRunServiceImplTests {
122123
csmPlatformProperties
123124

124125
mockkStatic(::getCurrentAuthentication)
125-
val authentication = mockk<Authentication>()
126-
every { authentication.name } returns AUTHENTICATED_USERNAME
126+
val authentication = mockk<BearerTokenAuthentication>()
127+
every { getCurrentAuthenticatedUserName(csmPlatformProperties) } returns AUTHENTICATED_USERNAME
128+
every { getCurrentAuthenticatedRoles(any()) } returns listOf()
127129
every { getCurrentAuthentication() } returns authentication
128130

129131
scenarioRunServiceImpl.init()
@@ -446,8 +448,8 @@ class ScenarioRunServiceImplTests {
446448
every { scenarioRun.workspaceKey } returns "wk"
447449
every { scenarioRun.csmSimulationRun } returns "csmSimulationRun"
448450
every { scenarioRun.scenarioId } returns "scenarioId"
449-
every { getCurrentAuthenticatedUserName() } returns "ownerId"
450-
scenarioRun.ownerId != getCurrentAuthenticatedUserName()
451+
every { getCurrentAuthenticatedUserName(csmPlatformProperties) } returns "ownerId"
452+
scenarioRun.ownerId != getCurrentAuthenticatedUserName(csmPlatformProperties)
451453
scenarioRunServiceImpl.deleteScenarioRun("orgId", "scenariorunId")
452454
verify(exactly = 1) { scenarioRunServiceImpl.deleteScenarioRun("orgId", "scenariorunId") }
453455
}

solution/src/main/kotlin/com/cosmotech/solution/azure/SolutionServiceImpl.kt

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ import com.cosmotech.api.events.OrganizationRegistered
1212
import com.cosmotech.api.events.OrganizationUnregistered
1313
import com.cosmotech.api.exceptions.CsmAccessForbiddenException
1414
import com.cosmotech.api.exceptions.CsmResourceNotFoundException
15+
import com.cosmotech.api.security.ROLE_PLATFORM_ADMIN
1516
import com.cosmotech.api.utils.ResourceScanner
1617
import com.cosmotech.api.utils.changed
1718
import com.cosmotech.api.utils.compareToAndMutateIfNeeded
19+
import com.cosmotech.api.utils.getCurrentAuthenticatedRoles
1820
import com.cosmotech.api.utils.getCurrentAuthenticatedUserName
1921
import com.cosmotech.solution.api.SolutionApiService
2022
import com.cosmotech.solution.domain.RunTemplate
@@ -138,13 +140,15 @@ internal class SolutionServiceImpl(
138140
"${organizationId}_solutions",
139141
solution.copy(
140142
id = idGenerator.generate("solution", prependPrefix = "sol-"),
141-
ownerId = getCurrentAuthenticatedUserName()))
143+
ownerId = getCurrentAuthenticatedUserName(csmPlatformProperties)))
142144
?: throw IllegalArgumentException("No solution returned in response: $solution")
143145

144146
override fun deleteSolution(organizationId: String, solutionId: String) {
145147
val solution = findSolutionById(organizationId, solutionId)
146-
if (solution.ownerId != getCurrentAuthenticatedUserName()) {
147-
// TODO Only the owner or an admin should be able to perform this operation
148+
val isPlatformAdmin =
149+
getCurrentAuthenticatedRoles(csmPlatformProperties).contains(ROLE_PLATFORM_ADMIN)
150+
if (solution.ownerId != getCurrentAuthenticatedUserName(csmPlatformProperties) &&
151+
!isPlatformAdmin) {
148152
throw CsmAccessForbiddenException("You are not allowed to delete this Resource")
149153
}
150154
cosmosTemplate.deleteEntity("${organizationId}_solutions", solution)
@@ -177,8 +181,10 @@ internal class SolutionServiceImpl(
177181

178182
if (solution.ownerId != null && solution.changed(existingSolution) { ownerId }) {
179183
// Allow to change the ownerId as well, but only the owner can transfer the ownership
180-
if (existingSolution.ownerId != getCurrentAuthenticatedUserName()) {
181-
// TODO Only the owner or an admin should be able to perform this operation
184+
val isPlatformAdmin =
185+
getCurrentAuthenticatedRoles(csmPlatformProperties).contains(ROLE_PLATFORM_ADMIN)
186+
if (existingSolution.ownerId != getCurrentAuthenticatedUserName(csmPlatformProperties) &&
187+
!isPlatformAdmin) {
182188
throw CsmAccessForbiddenException(
183189
"You are not allowed to change the ownership of this Resource")
184190
}

workspace/src/main/kotlin/com/cosmotech/workspace/azure/WorkspaceServiceImpl.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import com.cosmotech.api.security.coroutine.SecurityCoroutineContext
3737
import com.cosmotech.api.utils.ResourceScanner
3838
import com.cosmotech.api.utils.SecretManager
3939
import com.cosmotech.api.utils.compareToAndMutateIfNeeded
40-
import com.cosmotech.api.utils.getCurrentAuthenticatedMail
40+
import com.cosmotech.api.utils.getCurrentAccountIdentifier
4141
import com.cosmotech.api.utils.getCurrentAuthenticatedUserName
4242
import com.cosmotech.api.utils.toDomain
4343
import com.cosmotech.organization.api.OrganizationApiService
@@ -89,7 +89,7 @@ internal class WorkspaceServiceImpl(
8989
"OR c.security.default NOT LIKE 'none'"
9090
logger.debug("Template query: $templateQuery")
9191

92-
val currentUser = getCurrentAuthenticatedMail(this.csmPlatformProperties)
92+
val currentUser = getCurrentAccountIdentifier(this.csmPlatformProperties)
9393
logger.debug("Getting workspaces for user $currentUser")
9494
return cosmosCoreDatabase
9595
.getContainer("${organizationId}_workspaces")
@@ -126,14 +126,14 @@ internal class WorkspaceServiceImpl(
126126

127127
var workspaceSecurity = workspace.security
128128
if (workspaceSecurity == null) {
129-
workspaceSecurity = initSecurity(getCurrentAuthenticatedMail(this.csmPlatformProperties))
129+
workspaceSecurity = initSecurity(getCurrentAccountIdentifier(this.csmPlatformProperties))
130130
}
131131

132132
return cosmosTemplate.insert(
133133
"${organizationId}_workspaces",
134134
workspace.copy(
135135
id = idGenerator.generate("workspace"),
136-
ownerId = getCurrentAuthenticatedUserName(),
136+
ownerId = getCurrentAuthenticatedUserName(csmPlatformProperties),
137137
security = workspaceSecurity))
138138
?: throw IllegalArgumentException("No Workspace returned in response: $workspace")
139139
}

0 commit comments

Comments
 (0)