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
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,12 @@ interface DatasetApiServiceInterface : DatasetApiService {
identity: String,
role: String
): DatasetAccessControl

/**
* Update the default security of the dataset passed in parameter
* @param organizationId an organization id
* @param dataset a dataset to update
* @param role new dataset role
*/
fun updateDefaultSecurity(organizationId: String, dataset: Dataset, role: String)
}
Original file line number Diff line number Diff line change
Expand Up @@ -1064,6 +1064,12 @@ class DatasetServiceImpl(
return dataset.security as DatasetSecurity
}

override fun updateDefaultSecurity(organizationId: String, dataset: Dataset, role: String) {
val rbacSecurity = csmRbac.setDefault(dataset.getRbac(), role)
dataset.setRbac(rbacSecurity)
datasetRepository.save(dataset)
}

override fun addDatasetAccessControl(
organizationId: String,
datasetId: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import com.cosmotech.api.utils.getCurrentAuthenticatedUserName
import com.cosmotech.connector.api.ConnectorApiService
import com.cosmotech.connector.domain.Connector
import com.cosmotech.connector.domain.IoTypesEnum
import com.cosmotech.dataset.api.DatasetApiService
import com.cosmotech.dataset.DatasetApiServiceInterface
import com.cosmotech.dataset.domain.*
import com.cosmotech.dataset.repository.DatasetRepository
import com.cosmotech.organization.api.OrganizationApiService
Expand All @@ -39,9 +39,11 @@ import com.cosmotech.workspace.domain.WorkspaceSolution
import com.ninjasquad.springmockk.SpykBean
import com.redis.om.spring.RediSearchIndexer
import com.redis.testcontainers.RedisStackContainer
import io.mockk.clearAllMocks
import io.mockk.every
import io.mockk.junit5.MockKExtension
import io.mockk.mockkStatic
import io.mockk.verify
import java.time.Instant
import java.util.*
import kotlin.test.*
Expand Down Expand Up @@ -79,7 +81,7 @@ class RunnerServiceIntegrationTest : CsmRedisTestBase() {
@Autowired lateinit var rediSearchIndexer: RediSearchIndexer
@Autowired lateinit var connectorApiService: ConnectorApiService
@Autowired lateinit var organizationApiService: OrganizationApiService
@SpykBean @Autowired lateinit var datasetApiService: DatasetApiService
@SpykBean @Autowired lateinit var datasetApiService: DatasetApiServiceInterface
@Autowired lateinit var datasetRepository: DatasetRepository
@Autowired lateinit var solutionApiService: SolutionApiService
@Autowired lateinit var workspaceApiService: WorkspaceApiService
Expand Down Expand Up @@ -127,6 +129,7 @@ class RunnerServiceIntegrationTest : CsmRedisTestBase() {

@BeforeEach
fun setUp() {
clearAllMocks()
mockkStatic("com.cosmotech.api.utils.SecurityUtilsKt")
every { getCurrentAccountIdentifier(any()) } returns CONNECTED_ADMIN_USER
every { getCurrentAuthenticatedUserName(csmPlatformProperties) } returns "test.user"
Expand Down Expand Up @@ -1095,6 +1098,60 @@ class RunnerServiceIntegrationTest : CsmRedisTestBase() {
}
}

@Test
fun `when sharing a runner, the linked dataset default security should be set to at least viewer if the dataset isn't main`() {
runnerSaved.datasetList!!.removeLast()
val linkedDatasets = mutableListOf<Dataset>()
listOf(ROLE_NONE, ROLE_VIEWER, ROLE_USER, ROLE_EDITOR, ROLE_ADMIN).forEach { role ->
linkedDatasets.add(
datasetApiService.createDataset(
organizationSaved.id!!, makeDataset(isMain = false, default = role)))
linkedDatasets.add(
datasetApiService.createDataset(
organizationSaved.id!!, makeDataset(isMain = true, default = role)))
}
linkedDatasets.forEach { runnerSaved.datasetList!!.add(it.id!!) }
runnerSaved =
runnerApiService.updateRunner(
organizationSaved.id!!, workspaceSaved.id!!, runnerSaved.id!!, runnerSaved)
every { datasetApiService.updateDefaultSecurity(any(), any(), any()) } returns Unit
every { datasetApiService.findByOrganizationIdAndDatasetId(any(), any()) } returnsMany
linkedDatasets

runnerApiService.setRunnerDefaultSecurity(
organizationSaved.id!!, workspaceSaved.id!!, runnerSaved.id!!, RunnerRole(ROLE_EDITOR))

// Only one result correspond, the dataset with default security none and main=false
verify(exactly = 1) { datasetApiService.updateDefaultSecurity(any(), any(), ROLE_VIEWER) }
}

@Test
fun `when stopping sharing a scenario, the dataset default security should be set to none it's not higher than viewer and the dataset isn't main`() {
runnerSaved.datasetList!!.removeLast()
val linkedDatasets = mutableListOf<Dataset>()
listOf(ROLE_NONE, ROLE_VIEWER, ROLE_USER, ROLE_EDITOR, ROLE_ADMIN).forEach { role ->
linkedDatasets.add(
datasetApiService.createDataset(
organizationSaved.id!!, makeDataset(isMain = false, default = role)))
linkedDatasets.add(
datasetApiService.createDataset(
organizationSaved.id!!, makeDataset(isMain = true, default = role)))
}
linkedDatasets.forEach { runnerSaved.datasetList!!.add(it.id!!) }
runnerSaved =
runnerApiService.updateRunner(
organizationSaved.id!!, workspaceSaved.id!!, runnerSaved.id!!, runnerSaved)
every { datasetApiService.updateDefaultSecurity(any(), any(), any()) } returns Unit
every { datasetApiService.findByOrganizationIdAndDatasetId(any(), any()) } returnsMany
linkedDatasets

runnerApiService.setRunnerDefaultSecurity(
organizationSaved.id!!, workspaceSaved.id!!, runnerSaved.id!!, RunnerRole(ROLE_NONE))

// Only one result correspond, the dataset with default security viewer and main=false
verify(exactly = 1) { datasetApiService.updateDefaultSecurity(any(), any(), ROLE_NONE) }
}

private fun makeConnector(name: String = "name"): Connector {
return Connector(
key = UUID.randomUUID().toString(),
Expand All @@ -1107,7 +1164,9 @@ class RunnerServiceIntegrationTest : CsmRedisTestBase() {
fun makeDataset(
organizationId: String = organizationSaved.id!!,
name: String = "name",
connector: Connector = connectorSaved
connector: Connector = connectorSaved,
isMain: Boolean = true,
default: String = ROLE_NONE
): Dataset {
return Dataset(
name = name,
Expand All @@ -1120,9 +1179,10 @@ class RunnerServiceIntegrationTest : CsmRedisTestBase() {
name = connector.name,
version = connector.version,
),
main = isMain,
security =
DatasetSecurity(
default = ROLE_NONE,
default = default,
accessControlList =
mutableListOf(
DatasetAccessControl(id = CONNECTED_ADMIN_USER, role = ROLE_ADMIN),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import com.cosmotech.api.utils.getCurrentAuthenticatedUserName
import com.cosmotech.connector.api.ConnectorApiService
import com.cosmotech.connector.domain.Connector
import com.cosmotech.connector.domain.IoTypesEnum
import com.cosmotech.dataset.api.DatasetApiService
import com.cosmotech.dataset.DatasetApiServiceInterface
import com.cosmotech.dataset.domain.Dataset
import com.cosmotech.dataset.domain.DatasetAccessControl
import com.cosmotech.dataset.domain.DatasetConnector
Expand Down Expand Up @@ -56,6 +56,7 @@ import com.cosmotech.workspace.domain.WorkspaceSolution
import com.ninjasquad.springmockk.SpykBean
import com.redis.om.spring.RediSearchIndexer
import com.redis.testcontainers.RedisStackContainer
import io.mockk.clearAllMocks
import io.mockk.every
import io.mockk.junit5.MockKExtension
import io.mockk.mockk
Expand Down Expand Up @@ -96,7 +97,7 @@ class RunnerServiceRBACTest : CsmRedisTestBase() {
@Autowired lateinit var rediSearchIndexer: RediSearchIndexer
@Autowired lateinit var connectorApiService: ConnectorApiService
@Autowired lateinit var organizationApiService: OrganizationApiService
@SpykBean @Autowired lateinit var datasetApiService: DatasetApiService
@SpykBean @Autowired lateinit var datasetApiService: DatasetApiServiceInterface
@Autowired lateinit var solutionApiService: SolutionApiService
@Autowired lateinit var workspaceApiService: WorkspaceApiService
@Autowired lateinit var runnerApiService: RunnerApiService
Expand All @@ -106,6 +107,7 @@ class RunnerServiceRBACTest : CsmRedisTestBase() {

@BeforeEach
fun setUp() {
clearAllMocks()
mockkStatic("com.cosmotech.api.utils.SecurityUtilsKt")
every { getCurrentAccountIdentifier(any()) } returns CONNECTED_ADMIN_USER
every { getCurrentAuthenticatedUserName(csmPlatformProperties) } returns "test.user"
Expand Down Expand Up @@ -2369,6 +2371,9 @@ class RunnerServiceRBACTest : CsmRedisTestBase() {
datasetRepository.save(
datasetSaved.apply { ingestionStatus = IngestionStatusEnum.SUCCESS })
materializeTwingraph(datasetSaved)
every { datasetApiService.updateDefaultSecurity(any(), any(), any()) } returns Unit
every { datasetApiService.findByOrganizationIdAndDatasetId(any(), any()) } returns
datasetSaved
val solution = makeSolution(organizationSaved.id!!, TEST_USER_MAIL, ROLE_ADMIN)
val solutionSaved =
solutionApiService.createSolution(organizationSaved.id!!, solution)
Expand Down Expand Up @@ -2440,6 +2445,9 @@ class RunnerServiceRBACTest : CsmRedisTestBase() {
datasetRepository.save(
datasetSaved.apply { ingestionStatus = IngestionStatusEnum.SUCCESS })
materializeTwingraph(datasetSaved)
every { datasetApiService.updateDefaultSecurity(any(), any(), any()) } returns Unit
every { datasetApiService.findByOrganizationIdAndDatasetId(any(), any()) } returns
datasetSaved
val solution = makeSolution(organizationSaved.id!!, TEST_USER_MAIL, ROLE_ADMIN)
val solutionSaved =
solutionApiService.createSolution(organizationSaved.id!!, solution)
Expand Down Expand Up @@ -2511,6 +2519,9 @@ class RunnerServiceRBACTest : CsmRedisTestBase() {
datasetRepository.save(
datasetSaved.apply { ingestionStatus = IngestionStatusEnum.SUCCESS })
materializeTwingraph(datasetSaved)
every { datasetApiService.updateDefaultSecurity(any(), any(), any()) } returns Unit
every { datasetApiService.findByOrganizationIdAndDatasetId(any(), any()) } returns
datasetSaved
val solution = makeSolution(organizationSaved.id!!, TEST_USER_MAIL, role)
val solutionSaved =
solutionApiService.createSolution(organizationSaved.id!!, solution)
Expand Down Expand Up @@ -2581,6 +2592,9 @@ class RunnerServiceRBACTest : CsmRedisTestBase() {
datasetSaved =
datasetRepository.save(
datasetSaved.apply { ingestionStatus = IngestionStatusEnum.SUCCESS })
every { datasetApiService.updateDefaultSecurity(any(), any(), any()) } returns Unit
every { datasetApiService.findByOrganizationIdAndDatasetId(any(), any()) } returns
datasetSaved
val solution = makeSolution(organizationSaved.id!!, TEST_USER_MAIL, ROLE_ADMIN)
val solutionSaved =
solutionApiService.createSolution(organizationSaved.id!!, solution)
Expand Down Expand Up @@ -2647,6 +2661,9 @@ class RunnerServiceRBACTest : CsmRedisTestBase() {
datasetSaved =
datasetRepository.save(
datasetSaved.apply { ingestionStatus = IngestionStatusEnum.SUCCESS })
every { datasetApiService.updateDefaultSecurity(any(), any(), any()) } returns Unit
every { datasetApiService.findByOrganizationIdAndDatasetId(any(), any()) } returns
datasetSaved
val solution = makeSolution(organizationSaved.id!!, TEST_USER_MAIL, ROLE_ADMIN)
val solutionSaved =
solutionApiService.createSolution(organizationSaved.id!!, solution)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import com.cosmotech.api.rbac.PERMISSION_READ_SECURITY
import com.cosmotech.api.rbac.ROLE_NONE
import com.cosmotech.api.rbac.ROLE_USER
import com.cosmotech.api.rbac.ROLE_VALIDATOR
import com.cosmotech.api.rbac.ROLE_VIEWER
import com.cosmotech.api.rbac.RolesDefinition
import com.cosmotech.api.rbac.getScenarioRolesDefinition
import com.cosmotech.api.rbac.model.RbacAccessControl
Expand Down Expand Up @@ -506,6 +507,30 @@ class RunnerService(
// create a rbacSecurity object from runner Rbac by changing default value
val rbacSecurity = csmRbac.setDefault(this.getRbacSecurity(), role, this.roleDefinition)
this.setRbacSecurity(rbacSecurity)
updateLinkedDatasetDefaultSecurity(role)
}

private fun updateLinkedDatasetDefaultSecurity(role: String) {
var datasetRole = ROLE_VIEWER
if (role == ROLE_NONE) datasetRole = ROLE_NONE
this.runner.datasetList!!.forEach { datasetId ->
val linkedDataset =
datasetApiService.findByOrganizationIdAndDatasetId(
this.runner.organizationId!!, datasetId)
if (linkedDataset!!.main == true) return@forEach
// If the dataset default security is different from none,
// Then it's already viewer or higher and doesn't need to change
if (datasetRole == ROLE_VIEWER && linkedDataset.security!!.default != ROLE_NONE)
return@forEach
// If the dataset default security is different from viewer,
// Then it's either already none or higher than viewer and doesn't need to change
if (datasetRole == ROLE_NONE && linkedDataset.security!!.default != ROLE_VIEWER)
return@forEach
// Filter on dataset copy (because we do not want to update main dataset as it can be shared
// between runners)
datasetApiService.updateDefaultSecurity(
this.runner.organizationId!!, linkedDataset, datasetRole)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,12 @@ import com.ninjasquad.springmockk.MockkBean
import com.ninjasquad.springmockk.SpykBean
import com.redis.om.spring.RediSearchIndexer
import com.redis.testcontainers.RedisStackContainer
import io.mockk.clearAllMocks
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.junit5.MockKExtension
import io.mockk.mockkStatic
import io.mockk.verify
import java.time.Instant
import java.util.*
import kotlin.test.assertEquals
Expand Down Expand Up @@ -142,6 +144,7 @@ class ScenarioServiceIntegrationTest : CsmRedisTestBase() {

@BeforeEach
fun setUp() {
clearAllMocks()
mockkStatic("com.cosmotech.api.utils.SecurityUtilsKt")
every { getCurrentAccountIdentifier(any()) } returns CONNECTED_ADMIN_USER
every { getCurrentAuthenticatedUserName(csmPlatformProperties) } returns "test.user"
Expand Down Expand Up @@ -1257,6 +1260,60 @@ class ScenarioServiceIntegrationTest : CsmRedisTestBase() {
}
}

@Test
fun `when sharing a scenario, the linked dataset default security should be set to at least viewer if the dataset isn't main`() {
scenarioSaved.datasetList!!.removeLast()
val linkedDatasets = mutableListOf<Dataset>()
listOf(ROLE_NONE, ROLE_VIEWER, ROLE_USER, ROLE_EDITOR, ROLE_ADMIN).forEach { role ->
linkedDatasets.add(
datasetApiService.createDataset(
organizationSaved.id!!, makeDataset(isMain = false, default = role)))
linkedDatasets.add(
datasetApiService.createDataset(
organizationSaved.id!!, makeDataset(isMain = true, default = role)))
}
linkedDatasets.forEach { scenarioSaved.datasetList!!.add(it.id!!) }
scenarioSaved =
scenarioApiService.updateScenario(
organizationSaved.id!!, workspaceSaved.id!!, scenarioSaved.id!!, scenarioSaved)
every { datasetApiService.updateDefaultSecurity(any(), any(), any()) } returns Unit
every { datasetApiService.findByOrganizationIdAndDatasetId(any(), any()) } returnsMany
linkedDatasets

scenarioApiService.setScenarioDefaultSecurity(
organizationSaved.id!!, workspaceSaved.id!!, scenarioSaved.id!!, ScenarioRole(ROLE_EDITOR))

// Only one result correspond, the dataset with default security none and main=false
verify(exactly = 1) { datasetApiService.updateDefaultSecurity(any(), any(), ROLE_VIEWER) }
}

@Test
fun `when stopping sharing a scenario, the dataset default security should be set to none it's not higher than viewer and the dataset isn't main`() {
scenarioSaved.datasetList!!.removeLast()
val linkedDatasets = mutableListOf<Dataset>()
listOf(ROLE_NONE, ROLE_VIEWER, ROLE_USER, ROLE_EDITOR, ROLE_ADMIN).forEach { role ->
linkedDatasets.add(
datasetApiService.createDataset(
organizationSaved.id!!, makeDataset(isMain = false, default = role)))
linkedDatasets.add(
datasetApiService.createDataset(
organizationSaved.id!!, makeDataset(isMain = true, default = role)))
}
linkedDatasets.forEach { scenarioSaved.datasetList!!.add(it.id!!) }
scenarioSaved =
scenarioApiService.updateScenario(
organizationSaved.id!!, workspaceSaved.id!!, scenarioSaved.id!!, scenarioSaved)
every { datasetApiService.updateDefaultSecurity(any(), any(), any()) } returns Unit
every { datasetApiService.findByOrganizationIdAndDatasetId(any(), any()) } returnsMany
linkedDatasets

scenarioApiService.setScenarioDefaultSecurity(
organizationSaved.id!!, workspaceSaved.id!!, scenarioSaved.id!!, ScenarioRole(ROLE_NONE))

// Only one result correspond, the dataset with default security viewer and main=false
verify(exactly = 1) { datasetApiService.updateDefaultSecurity(any(), any(), ROLE_NONE) }
}

private fun makeWorkspaceEventHubInfo(eventHubAvailable: Boolean): WorkspaceEventHubInfo {
return WorkspaceEventHubInfo(
eventHubNamespace = "eventHubNamespace",
Expand Down Expand Up @@ -1284,7 +1341,8 @@ class ScenarioServiceIntegrationTest : CsmRedisTestBase() {
name: String = "name",
connector: Connector = connectorSaved,
sourceType: DatasetSourceType = DatasetSourceType.Twincache,
isMain: Boolean = true
isMain: Boolean = true,
default: String = ROLE_NONE
): Dataset {
return Dataset(
name = name,
Expand All @@ -1301,7 +1359,7 @@ class ScenarioServiceIntegrationTest : CsmRedisTestBase() {
main = isMain,
security =
DatasetSecurity(
default = ROLE_NONE,
default = default,
accessControlList =
mutableListOf(
DatasetAccessControl(id = CONNECTED_ADMIN_USER, role = ROLE_ADMIN))))
Expand Down Expand Up @@ -1397,6 +1455,7 @@ class ScenarioServiceIntegrationTest : CsmRedisTestBase() {
name: String = "name",
datasetList: MutableList<String>? = mutableListOf(),
parentId: String? = null,
defaultSecurity: String = ROLE_NONE,
userName: String = "roleName",
role: String = ROLE_USER,
validationStatus: ScenarioValidationStatus = ScenarioValidationStatus.Draft,
Expand All @@ -1416,7 +1475,7 @@ class ScenarioServiceIntegrationTest : CsmRedisTestBase() {
parametersValues = parametersValues,
security =
ScenarioSecurity(
ROLE_NONE,
defaultSecurity,
mutableListOf(
ScenarioAccessControl(CONNECTED_ADMIN_USER, ROLE_ADMIN),
ScenarioAccessControl(userName, role))))
Expand Down
Loading
Loading