diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/security/UserPermissionEvaluator.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/security/UserPermissionEvaluator.kt index f8cb771b73..5cc0bc0867 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/security/UserPermissionEvaluator.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/security/UserPermissionEvaluator.kt @@ -1,7 +1,8 @@ package com.saveourtool.save.backend.security import com.saveourtool.save.authservice.utils.username -import com.saveourtool.save.backend.service.LnkUserOrganizationService +import com.saveourtool.save.backend.repository.LnkUserOrganizationRepository +import com.saveourtool.save.entities.OrganizationStatus import com.saveourtool.save.info.UserPermissions import com.saveourtool.save.info.UserPermissionsInOrganization import org.springframework.security.core.Authentication @@ -12,7 +13,7 @@ import org.springframework.stereotype.Component */ @Component class UserPermissionEvaluator( - private var lnkUserOrganizationService: LnkUserOrganizationService, + private var lnkUserOrganizationRepository: LnkUserOrganizationRepository, ) { /** * @param authentication @@ -21,7 +22,21 @@ class UserPermissionEvaluator( fun getUserPermissions( authentication: Authentication, ): UserPermissions { - val lnkOrganizations = lnkUserOrganizationService.getOrganizationsByUserNameAndCreatedStatus(authentication.username()) + val lnkOrganizations = lnkUserOrganizationRepository.findByUserNameAndOrganizationStatus(authentication.username(), OrganizationStatus.CREATED) + + return UserPermissions( + lnkOrganizations.associate { it.organization.name to UserPermissionsInOrganization(it.organization.canCreateContests, it.organization.canBulkUpload) }, + ) + } + + /** + * @param userName + * @return UserPermissions + */ + fun getUserPermissionsByName( + userName: String, + ): UserPermissions { + val lnkOrganizations = lnkUserOrganizationRepository.findByUserNameAndOrganizationStatus(userName, OrganizationStatus.CREATED) return UserPermissions( lnkOrganizations.associate { it.organization.name to UserPermissionsInOrganization(it.organization.canCreateContests, it.organization.canBulkUpload) }, @@ -37,7 +52,8 @@ class UserPermissionEvaluator( authentication: Authentication, organizationName: String, ): UserPermissions { - val lnkOrganization = lnkUserOrganizationService.getOrganizationsByUserNameAndCreatedStatusAndOrganizationName(authentication.username(), organizationName) + val lnkOrganization = lnkUserOrganizationRepository.findByUserNameAndOrganizationStatusAndOrganizationName(authentication.username(), OrganizationStatus.CREATED, + organizationName) val isPermittedCreateContest = lnkOrganization?.organization?.canCreateContests ?: false val isPermittedToBulkUpload = lnkOrganization?.organization?.canBulkUpload ?: false diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/BackendForCosvService.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/BackendForCosvService.kt index 9403829cdb..35fa582082 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/BackendForCosvService.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/BackendForCosvService.kt @@ -1,10 +1,12 @@ package com.saveourtool.save.backend.service +import com.saveourtool.save.backend.security.UserPermissionEvaluator import com.saveourtool.save.backend.service.vulnerability.VulnerabilityService import com.saveourtool.save.entities.Organization import com.saveourtool.save.entities.User import com.saveourtool.save.entities.vulnerabilities.Vulnerability import com.saveourtool.save.entities.vulnerability.VulnerabilityDto +import org.springframework.security.core.Authentication import org.springframework.stereotype.Service /** @@ -15,6 +17,7 @@ class BackendForCosvService( private val vulnerabilityService: VulnerabilityService, private val organizationService: OrganizationService, private val userDetailsService: UserDetailsService, + private val userPermissionEvaluator: UserPermissionEvaluator, ) : IBackendService { override fun findVulnerabilityByName(name: String): Vulnerability? = vulnerabilityService.findByName(name) @@ -23,4 +26,8 @@ class BackendForCosvService( override fun getOrganizationByName(name: String): Organization = organizationService.getByName(name) override fun getUserByName(name: String): User = userDetailsService.getByName(name) + + override fun getUserPermissionsByOrganizationName(authentication: Authentication, organizationName: String) = userPermissionEvaluator.getUserPermissionsByOrganizationName( + authentication, organizationName + ) } diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/UserDetailsService.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/UserDetailsService.kt index 980a45c99c..8d3c9172a2 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/UserDetailsService.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/UserDetailsService.kt @@ -1,7 +1,6 @@ package com.saveourtool.save.backend.service import com.saveourtool.save.authservice.utils.userId -import com.saveourtool.save.authservice.utils.username import com.saveourtool.save.backend.repository.LnkUserOrganizationRepository import com.saveourtool.save.backend.repository.LnkUserProjectRepository import com.saveourtool.save.backend.repository.OriginalLoginRepository diff --git a/save-cloud-common/src/commonMain/kotlin/com/saveourtool/save/info/UserPermissions.kt b/save-cloud-common/src/commonMain/kotlin/com/saveourtool/save/info/UserPermissions.kt index a5a50e6ceb..1fea46effa 100644 --- a/save-cloud-common/src/commonMain/kotlin/com/saveourtool/save/info/UserPermissions.kt +++ b/save-cloud-common/src/commonMain/kotlin/com/saveourtool/save/info/UserPermissions.kt @@ -8,4 +8,13 @@ import kotlinx.serialization.Serializable @Serializable data class UserPermissions( val inOrganizations: Map = emptyMap(), -) +) { + companion object { + /** + * Value that represents an empty [UserPermissions] + */ + val empty = UserPermissions( + inOrganizations = emptyMap(), + ) + } +} diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/backend/service/IBackendService.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/backend/service/IBackendService.kt index e6f3cf178a..97ca85aee6 100644 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/backend/service/IBackendService.kt +++ b/save-cosv/src/main/kotlin/com/saveourtool/save/backend/service/IBackendService.kt @@ -6,6 +6,8 @@ import com.saveourtool.save.entities.Organization import com.saveourtool.save.entities.User import com.saveourtool.save.entities.vulnerabilities.Vulnerability import com.saveourtool.save.entities.vulnerability.VulnerabilityDto +import com.saveourtool.save.info.UserPermissions +import org.springframework.security.core.Authentication /** * Interface for service to get required info for COSV from backend @@ -39,4 +41,11 @@ interface IBackendService { * @return found [User] by name */ fun getUserByName(name: String): User + + /** + * @param authentication + * @param organizationName name of organization + * @return found [UserPermissions] by organizationName + */ + fun getUserPermissionsByOrganizationName(authentication: Authentication, organizationName: String): UserPermissions } diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/CosvService.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/CosvService.kt index 459dcdcca0..8e542f155a 100644 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/CosvService.kt +++ b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/CosvService.kt @@ -13,8 +13,10 @@ import com.saveourtool.save.entities.vulnerability.* import com.saveourtool.save.utils.* import com.saveourtool.osv4k.* +import org.springframework.http.HttpStatus import org.springframework.security.core.Authentication import org.springframework.stereotype.Service +import org.springframework.web.server.ResponseStatusException import reactor.core.publisher.Flux import reactor.core.publisher.Mono import reactor.kotlin.core.publisher.toMono @@ -47,6 +49,7 @@ class CosvService( * @param authentication who uploads [inputStream] * @param organizationName to which is uploaded * @return save's vulnerability identifiers + * @throws ResponseStatusException */ @OptIn(ExperimentalSerializationApi::class) fun decodeAndSave( @@ -56,6 +59,12 @@ class CosvService( ): Flux { val user = backendService.getUserByName(authentication.name) val organization = backendService.getOrganizationByName(organizationName) + val userPermissions = backendService.getUserPermissionsByOrganizationName(authentication, organizationName) + + if (userPermissions.inOrganizations[organizationName]?.canDoBulkUpload != true) { + return Flux.error(ResponseStatusException(HttpStatus.FORBIDDEN, "You do not have permission to upload COSV files on behalf of this organization: $organizationName")) + } + return inputStreams.flatMap { inputStream -> decode(json.decodeFromStream(inputStream), user, organization) }.save(user) @@ -68,6 +77,7 @@ class CosvService( * @param authentication who uploads [content] * @param organizationName to which is uploaded * @return save's vulnerability identifiers + * @throws ResponseStatusException */ fun decodeAndSave( content: String, @@ -76,6 +86,12 @@ class CosvService( ): Flux { val user = backendService.getUserByName(authentication.name) val organization = backendService.getOrganizationByName(organizationName) + val userPermissions = backendService.getUserPermissionsByOrganizationName(authentication, organizationName) + + if (userPermissions.inOrganizations[organizationName]?.canDoBulkUpload != true) { + return Flux.error(ResponseStatusException(HttpStatus.FORBIDDEN, "You do not have permission to upload COSV file on behalf of this organization: $organizationName")) + } + return decode(json.parseToJsonElement(content), user, organization).save(user) }