From a90a2341332b69f93ce50b963d7e79d57acde4ff Mon Sep 17 00:00:00 2001 From: Vladislav Frolov <50615459+Cheshiriks@users.noreply.github.com> Date: Tue, 13 Feb 2024 15:05:30 +0300 Subject: [PATCH] Move services from Save and COSV into a common module (#2920) * Move services from Save and COSV into a common module --- .../save/backend/SaveApplication.kt | 9 +- .../configs/ApplicationConfiguration.kt | 7 - .../backend/controllers/AvatarController.kt | 8 +- .../backend/controllers/CommentController.kt | 4 +- .../backend/controllers/ContestController.kt | 3 +- .../controllers/DemoManagerController.kt | 4 +- .../controllers/ExecutionController.kt | 6 +- .../backend/controllers/FileController.kt | 2 +- .../LnkContestProjectController.kt | 2 + .../LnkOrganizationTestSuiteController.kt | 4 +- .../LnkUserOrganizationController.kt | 6 +- .../controllers/LnkUserProjectController.kt | 6 +- .../controllers/OrganizationController.kt | 5 +- .../controllers/PermissionController.kt | 8 +- .../backend/controllers/ProjectController.kt | 8 +- .../controllers/RunExecutionController.kt | 1 + .../controllers/TestExecutionController.kt | 2 +- .../controllers/TestSuitesSourceController.kt | 2 + .../controllers/UsersDetailsController.kt | 4 +- .../controllers/internal/UsersController.kt | 4 +- .../event/VulnerabilityMetadataListener.kt | 4 +- .../save/backend/event/UserListener.kt | 4 +- .../security/CommentPermissionEvaluator.kt | 42 --- .../security/ProjectPermissionEvaluator.kt | 178 ------------- .../save/backend/service/CommentService.kt | 74 ------ .../save/backend/service/ExecutionService.kt | 1 + .../save/backend/service/GitService.kt | 57 ---- .../service/LnkContestProjectService.kt | 1 + .../service/LnkUserOrganizationService.kt | 244 ------------------ .../backend/service/LnkUserProjectService.kt | 137 ---------- .../backend/service/OrganizationService.kt | 205 --------------- .../save/backend/service/PermissionService.kt | 2 + .../backend/service/ProjectProblemService.kt | 2 +- .../save/backend/service/ProjectService.kt | 232 ----------------- .../backend/service/TestAnalysisService.kt | 2 + .../service/TestSuitesSourceService.kt | 1 + .../save/backend/storage/AvatarStorage.kt | 37 --- .../save/backend/storage/FileS3KeyManager.kt | 2 +- .../saveourtool/save/backend/DatabaseTest.kt | 2 +- .../save/backend/DownloadFilesTest.kt | 7 +- .../controller/AgentsControllerTest.kt | 2 +- .../backend/controller/DeleteEntitiesTest.kt | 4 +- .../controller/ExecutionControllerTest.kt | 1 + .../LnkUserOrganizationControllerTest.kt | 4 +- .../controller/OrganizationControllerTest.kt | 9 +- .../controller/PermissionControllerTest.kt | 8 +- .../controller/ProjectControllerTest.kt | 23 +- .../controllers/RunExecutionControllerTest.kt | 1 + .../OrganizationPermissionEvaluatorTest.kt | 8 +- .../ProjectPermissionEvaluatorTest.kt | 12 +- .../service/LnkContestProjectServiceTest.kt | 1 + .../backend/service/PermissionServiceTest.kt | 2 + save-cloud-common/build.gradle.kts | 1 + .../LnkUserOrganizationRepository.kt | 16 +- .../repository/LnkUserProjectRepository.kt | 9 +- .../save}/repository/ProjectRepository.kt | 2 +- .../security/CommentPermissionEvaluator.kt | 5 +- .../OrganizationPermissionEvaluator.kt | 23 +- .../security/ProjectPermissionEvaluator.kt | 26 +- .../save}/service/CommentService.kt | 10 +- .../saveourtool/save}/service/GitService.kt | 2 +- .../service/LnkUserOrganizationService.kt | 30 ++- .../save}/service/LnkUserProjectService.kt | 20 +- .../save}/service/OrganizationService.kt | 58 ++++- .../save}/service/ProjectService.kt | 6 +- .../saveourtool/save/service/UserService.kt | 213 ++++++++------- .../save}/storage/AvatarStorage.kt | 10 +- .../save/utils/AuthenticationUtils.kt | 29 +++ .../PersistenceSaveAutoConfiguration.kt | 4 +- .../cosv/controllers/CommentController.kt | 4 +- .../save/cosv/controllers/CosvController.kt | 4 +- .../controllers/OrganizationController.kt | 8 +- .../cosv/controllers/RawCosvFileController.kt | 4 +- .../controllers/UsersDetailsController.kt | 2 +- .../controllers/VulnerabilityController.kt | 2 +- .../save/cosv/event/CommentListener.kt | 2 +- .../LnkUserProjectRepository.kt | 68 ----- .../cosv/repositorysave/ProjectRepository.kt | 71 ----- .../OrganizationPermissionEvaluator.kt | 153 ----------- .../save/cosv/service/CosvService.kt | 2 + .../save/cosv/service/UserService.kt | 212 --------------- .../service/VulnerabilityRatingService.kt | 2 + .../save/cosv/service/VulnerabilityService.kt | 2 + .../cosv/storage/RawCosvFileS3KeyManager.kt | 4 +- 84 files changed, 450 insertions(+), 1958 deletions(-) delete mode 100644 save-backend/src/main/kotlin/com/saveourtool/save/backend/security/CommentPermissionEvaluator.kt delete mode 100644 save-backend/src/main/kotlin/com/saveourtool/save/backend/security/ProjectPermissionEvaluator.kt delete mode 100644 save-backend/src/main/kotlin/com/saveourtool/save/backend/service/CommentService.kt delete mode 100644 save-backend/src/main/kotlin/com/saveourtool/save/backend/service/GitService.kt delete mode 100644 save-backend/src/main/kotlin/com/saveourtool/save/backend/service/LnkUserOrganizationService.kt delete mode 100644 save-backend/src/main/kotlin/com/saveourtool/save/backend/service/LnkUserProjectService.kt delete mode 100644 save-backend/src/main/kotlin/com/saveourtool/save/backend/service/OrganizationService.kt delete mode 100644 save-backend/src/main/kotlin/com/saveourtool/save/backend/service/ProjectService.kt delete mode 100644 save-backend/src/main/kotlin/com/saveourtool/save/backend/storage/AvatarStorage.kt rename {save-backend/src/main/kotlin/com/saveourtool/save/backend => save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save}/repository/LnkUserProjectRepository.kt (89%) rename {save-backend/src/main/kotlin/com/saveourtool/save/backend => save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save}/repository/ProjectRepository.kt (98%) rename {save-cosv/src/main/kotlin/com/saveourtool/save/cosv => save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save}/security/CommentPermissionEvaluator.kt (92%) rename {save-backend/src/main/kotlin/com/saveourtool/save/backend => save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save}/security/OrganizationPermissionEvaluator.kt (90%) rename {save-cosv/src/main/kotlin/com/saveourtool/save/cosv => save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save}/security/ProjectPermissionEvaluator.kt (91%) rename {save-cosv/src/main/kotlin/com/saveourtool/save/cosv => save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save}/service/CommentService.kt (87%) rename {save-cosv/src/main/kotlin/com/saveourtool/save/cosv => save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save}/service/GitService.kt (97%) rename {save-cosv/src/main/kotlin/com/saveourtool/save/cosv => save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save}/service/LnkUserOrganizationService.kt (90%) rename {save-cosv/src/main/kotlin/com/saveourtool/save/cosv => save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save}/service/LnkUserProjectService.kt (89%) rename {save-cosv/src/main/kotlin/com/saveourtool/save/cosv => save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save}/service/OrganizationService.kt (82%) rename {save-cosv/src/main/kotlin/com/saveourtool/save/cosv => save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save}/service/ProjectService.kt (97%) rename save-backend/src/main/kotlin/com/saveourtool/save/backend/service/UserDetailsService.kt => save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/service/UserService.kt (82%) rename {save-cosv/src/main/kotlin/com/saveourtool/save/cosv => save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save}/storage/AvatarStorage.kt (73%) create mode 100644 save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/utils/AuthenticationUtils.kt delete mode 100644 save-cosv/src/main/kotlin/com/saveourtool/save/cosv/repositorysave/LnkUserProjectRepository.kt delete mode 100644 save-cosv/src/main/kotlin/com/saveourtool/save/cosv/repositorysave/ProjectRepository.kt delete mode 100644 save-cosv/src/main/kotlin/com/saveourtool/save/cosv/security/OrganizationPermissionEvaluator.kt delete mode 100644 save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/UserService.kt diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/SaveApplication.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/SaveApplication.kt index dbc5f6ccce..83eb779bc8 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/SaveApplication.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/SaveApplication.kt @@ -10,7 +10,14 @@ import org.springframework.context.annotation.Import /** * An entrypoint for spring for save-backend */ -@SpringBootApplication(scanBasePackages = ["com.saveourtool.save.backend", "com.saveourtool.save.repository"]) +@SpringBootApplication(scanBasePackages = [ + "com.saveourtool.save.backend", + "com.saveourtool.save.service", + "com.saveourtool.save.storage", + "com.saveourtool.save.security", + "com.saveourtool.save.utils", + "com.saveourtool.save.repository", +]) @EnableConfigurationProperties(ConfigProperties::class) @Import( DefaultS3Configuration::class, diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/configs/ApplicationConfiguration.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/configs/ApplicationConfiguration.kt index 9711fd59ea..81ac26750c 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/configs/ApplicationConfiguration.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/configs/ApplicationConfiguration.kt @@ -2,7 +2,6 @@ package com.saveourtool.save.backend.configs import com.saveourtool.save.service.LogService import com.saveourtool.save.service.LokiLogService -import com.saveourtool.save.utils.BlockingBridge import org.springframework.boot.actuate.autoconfigure.metrics.orm.jpa.HibernateMetricsAutoConfiguration import org.springframework.boot.autoconfigure.ImportAutoConfiguration import org.springframework.boot.autoconfigure.domain.EntityScan @@ -24,10 +23,4 @@ class ApplicationConfiguration { */ @Bean fun logService(configProperties: ConfigProperties): LogService = LokiLogService.createOrStub(configProperties.loki) - - /** - * @return [BlockingBridge] - */ - @Bean - fun blockingBridge(): BlockingBridge = BlockingBridge.default } diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/AvatarController.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/AvatarController.kt index 8b41e68f9c..2960ba98bb 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/AvatarController.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/AvatarController.kt @@ -1,11 +1,11 @@ package com.saveourtool.save.backend.controllers import com.saveourtool.save.authservice.utils.username -import com.saveourtool.save.backend.service.OrganizationService -import com.saveourtool.save.backend.service.UserDetailsService -import com.saveourtool.save.backend.storage.AvatarStorage import com.saveourtool.save.configs.ApiSwaggerSupport +import com.saveourtool.save.service.OrganizationService +import com.saveourtool.save.service.UserService import com.saveourtool.save.storage.AvatarKey +import com.saveourtool.save.storage.AvatarStorage import com.saveourtool.save.utils.* import com.saveourtool.save.utils.ByteBufferFluxResponse import com.saveourtool.save.v1 @@ -43,7 +43,7 @@ import kotlin.time.toJavaDuration internal class AvatarController( private val avatarStorage: AvatarStorage, private val organizationService: OrganizationService, - private val userDetailsService: UserDetailsService, + private val userDetailsService: UserService, ) { @Operation( method = "POST", diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/CommentController.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/CommentController.kt index 7646458d45..46fca8ab85 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/CommentController.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/CommentController.kt @@ -1,11 +1,11 @@ package com.saveourtool.save.backend.controllers -import com.saveourtool.save.backend.security.CommentPermissionEvaluator -import com.saveourtool.save.backend.service.CommentService import com.saveourtool.save.configs.ApiSwaggerSupport import com.saveourtool.save.entities.Comment import com.saveourtool.save.entities.CommentDto import com.saveourtool.save.permission.Permission +import com.saveourtool.save.security.CommentPermissionEvaluator +import com.saveourtool.save.service.CommentService import com.saveourtool.save.utils.StringResponse import com.saveourtool.save.utils.blockingToMono import com.saveourtool.save.utils.switchIfEmptyToNotFound diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/ContestController.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/ContestController.kt index 781d870428..dd15ddf0b7 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/ContestController.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/ContestController.kt @@ -1,6 +1,5 @@ package com.saveourtool.save.backend.controllers -import com.saveourtool.save.backend.security.OrganizationPermissionEvaluator import com.saveourtool.save.backend.service.* import com.saveourtool.save.backend.storage.TestsSourceSnapshotStorage import com.saveourtool.save.configs.ApiSwaggerSupport @@ -11,6 +10,8 @@ import com.saveourtool.save.entities.contest.ContestDto import com.saveourtool.save.entities.contest.ContestStatus import com.saveourtool.save.permission.Permission import com.saveourtool.save.request.TestFilesRequest +import com.saveourtool.save.security.OrganizationPermissionEvaluator +import com.saveourtool.save.service.OrganizationService import com.saveourtool.save.test.TestFilesContent import com.saveourtool.save.utils.* import com.saveourtool.save.v1 diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/DemoManagerController.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/DemoManagerController.kt index afd7fca973..ba9a22fb0d 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/DemoManagerController.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/DemoManagerController.kt @@ -1,16 +1,16 @@ package com.saveourtool.save.backend.controllers import com.saveourtool.save.backend.configs.ConfigProperties -import com.saveourtool.save.backend.security.ProjectPermissionEvaluator import com.saveourtool.save.backend.service.LnkProjectGithubService -import com.saveourtool.save.backend.service.ProjectService import com.saveourtool.save.configs.ApiSwaggerSupport import com.saveourtool.save.configs.RequiresAuthorizationSourceHeader import com.saveourtool.save.demo.DemoCreationRequest import com.saveourtool.save.entities.FileDto import com.saveourtool.save.entities.Project import com.saveourtool.save.permission.Permission +import com.saveourtool.save.security.ProjectPermissionEvaluator import com.saveourtool.save.service.LogService +import com.saveourtool.save.service.ProjectService import com.saveourtool.save.spring.utils.applyAll import com.saveourtool.save.utils.* import com.saveourtool.save.v1 diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/ExecutionController.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/ExecutionController.kt index 6a0f9b4696..825b7317ff 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/ExecutionController.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/ExecutionController.kt @@ -1,9 +1,6 @@ package com.saveourtool.save.backend.controllers -import com.saveourtool.save.backend.security.ProjectPermissionEvaluator import com.saveourtool.save.backend.service.ExecutionService -import com.saveourtool.save.backend.service.OrganizationService -import com.saveourtool.save.backend.service.ProjectService import com.saveourtool.save.backend.storage.ExecutionInfoStorage import com.saveourtool.save.backend.utils.toMonoOrNotFound import com.saveourtool.save.core.utils.runIf @@ -14,6 +11,9 @@ import com.saveourtool.save.execution.ExecutionUpdateDto import com.saveourtool.save.execution.TestingType import com.saveourtool.save.filters.ExecutionFilter import com.saveourtool.save.permission.Permission +import com.saveourtool.save.security.ProjectPermissionEvaluator +import com.saveourtool.save.service.OrganizationService +import com.saveourtool.save.service.ProjectService import com.saveourtool.save.utils.blockingMap import com.saveourtool.save.utils.orNotFound import com.saveourtool.save.utils.switchIfEmptyToNotFound diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/FileController.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/FileController.kt index 6714b34969..a2b91a962e 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/FileController.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/FileController.kt @@ -1,11 +1,11 @@ package com.saveourtool.save.backend.controllers -import com.saveourtool.save.backend.service.ProjectService import com.saveourtool.save.backend.storage.FileStorage import com.saveourtool.save.configs.ApiSwaggerSupport import com.saveourtool.save.entities.FileDto import com.saveourtool.save.entities.Project import com.saveourtool.save.permission.Permission +import com.saveourtool.save.service.ProjectService import com.saveourtool.save.utils.* import com.saveourtool.save.utils.ByteBufferFluxResponse import com.saveourtool.save.v1 diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/LnkContestProjectController.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/LnkContestProjectController.kt index 433bc2359a..cb38152608 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/LnkContestProjectController.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/LnkContestProjectController.kt @@ -15,6 +15,8 @@ import com.saveourtool.save.entities.LnkContestProject import com.saveourtool.save.entities.contest.ContestResult import com.saveourtool.save.execution.ExecutionDto import com.saveourtool.save.permission.Permission +import com.saveourtool.save.service.LnkUserProjectService +import com.saveourtool.save.service.ProjectService import com.saveourtool.save.utils.* import com.saveourtool.save.v1 diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/LnkOrganizationTestSuiteController.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/LnkOrganizationTestSuiteController.kt index e051b86f78..e8f9c2bff7 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/LnkOrganizationTestSuiteController.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/LnkOrganizationTestSuiteController.kt @@ -7,10 +7,8 @@ package com.saveourtool.save.backend.controllers -import com.saveourtool.save.backend.security.OrganizationPermissionEvaluator import com.saveourtool.save.backend.security.TestSuitePermissionEvaluator import com.saveourtool.save.backend.service.LnkOrganizationTestSuiteService -import com.saveourtool.save.backend.service.OrganizationService import com.saveourtool.save.backend.service.TestSuitesService import com.saveourtool.save.backend.service.TestsSourceVersionService import com.saveourtool.save.configs.ApiSwaggerSupport @@ -23,6 +21,8 @@ import com.saveourtool.save.filters.TestSuiteFilter import com.saveourtool.save.permission.Permission import com.saveourtool.save.permission.Rights import com.saveourtool.save.permission.SetRightsRequest +import com.saveourtool.save.security.OrganizationPermissionEvaluator +import com.saveourtool.save.service.OrganizationService import com.saveourtool.save.testsuite.TestSuiteVersioned import com.saveourtool.save.utils.StringResponse import com.saveourtool.save.utils.switchIfEmptyToNotFound diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/LnkUserOrganizationController.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/LnkUserOrganizationController.kt index 8f24c7a144..fee354dfc1 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/LnkUserOrganizationController.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/LnkUserOrganizationController.kt @@ -9,9 +9,6 @@ package com.saveourtool.save.backend.controllers import com.saveourtool.save.authservice.utils.userId import com.saveourtool.save.authservice.utils.username -import com.saveourtool.save.backend.security.OrganizationPermissionEvaluator -import com.saveourtool.save.backend.service.LnkUserOrganizationService -import com.saveourtool.save.backend.service.OrganizationService import com.saveourtool.save.configs.ApiSwaggerSupport import com.saveourtool.save.configs.RequiresAuthorizationSourceHeader import com.saveourtool.save.domain.Role @@ -21,6 +18,9 @@ import com.saveourtool.save.filters.OrganizationFilter import com.saveourtool.save.info.UserInfo import com.saveourtool.save.permission.Permission import com.saveourtool.save.permission.SetRoleRequest +import com.saveourtool.save.security.OrganizationPermissionEvaluator +import com.saveourtool.save.service.LnkUserOrganizationService +import com.saveourtool.save.service.OrganizationService import com.saveourtool.save.utils.StringResponse import com.saveourtool.save.utils.switchIfEmptyToNotFound import com.saveourtool.save.utils.switchIfEmptyToResponseException diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/LnkUserProjectController.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/LnkUserProjectController.kt index 6e2f9b5609..797590d98f 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/LnkUserProjectController.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/LnkUserProjectController.kt @@ -8,14 +8,14 @@ package com.saveourtool.save.backend.controllers import com.saveourtool.save.authservice.utils.userId -import com.saveourtool.save.backend.security.ProjectPermissionEvaluator -import com.saveourtool.save.backend.service.LnkUserProjectService -import com.saveourtool.save.backend.service.ProjectService import com.saveourtool.save.configs.ApiSwaggerSupport import com.saveourtool.save.configs.RequiresAuthorizationSourceHeader import com.saveourtool.save.entities.ProjectDto import com.saveourtool.save.info.UserInfo import com.saveourtool.save.permission.Permission +import com.saveourtool.save.security.ProjectPermissionEvaluator +import com.saveourtool.save.service.LnkUserProjectService +import com.saveourtool.save.service.ProjectService import com.saveourtool.save.utils.switchIfEmptyToNotFound import com.saveourtool.save.v1 diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/OrganizationController.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/OrganizationController.kt index 7174b37284..566f254525 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/OrganizationController.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/OrganizationController.kt @@ -2,7 +2,6 @@ package com.saveourtool.save.backend.controllers import com.saveourtool.save.authservice.utils.userId import com.saveourtool.save.backend.configs.ConfigProperties -import com.saveourtool.save.backend.security.OrganizationPermissionEvaluator import com.saveourtool.save.backend.service.* import com.saveourtool.save.backend.storage.TestsSourceSnapshotStorage import com.saveourtool.save.configs.ApiSwaggerSupport @@ -12,6 +11,10 @@ import com.saveourtool.save.domain.Role import com.saveourtool.save.entities.* import com.saveourtool.save.filters.OrganizationFilter import com.saveourtool.save.permission.Permission +import com.saveourtool.save.security.OrganizationPermissionEvaluator +import com.saveourtool.save.service.GitService +import com.saveourtool.save.service.LnkUserOrganizationService +import com.saveourtool.save.service.OrganizationService import com.saveourtool.save.utils.* import com.saveourtool.save.v1 diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/PermissionController.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/PermissionController.kt index c8988a894e..5a88445faf 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/PermissionController.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/PermissionController.kt @@ -1,16 +1,16 @@ package com.saveourtool.save.backend.controllers import com.saveourtool.save.authservice.utils.username -import com.saveourtool.save.backend.security.OrganizationPermissionEvaluator -import com.saveourtool.save.backend.security.ProjectPermissionEvaluator -import com.saveourtool.save.backend.service.OrganizationService import com.saveourtool.save.backend.service.PermissionService -import com.saveourtool.save.backend.service.ProjectService import com.saveourtool.save.configs.ApiSwaggerSupport import com.saveourtool.save.configs.RequiresAuthorizationSourceHeader import com.saveourtool.save.domain.Role import com.saveourtool.save.permission.Permission import com.saveourtool.save.permission.SetRoleRequest +import com.saveourtool.save.security.OrganizationPermissionEvaluator +import com.saveourtool.save.security.ProjectPermissionEvaluator +import com.saveourtool.save.service.OrganizationService +import com.saveourtool.save.service.ProjectService import com.saveourtool.save.utils.switchIfEmptyToNotFound import com.saveourtool.save.utils.trace import com.saveourtool.save.v1 diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/ProjectController.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/ProjectController.kt index 95e671509b..68424cc558 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/ProjectController.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/ProjectController.kt @@ -1,11 +1,7 @@ package com.saveourtool.save.backend.controllers import com.saveourtool.save.authservice.utils.userId -import com.saveourtool.save.backend.security.ProjectPermissionEvaluator -import com.saveourtool.save.backend.service.LnkUserProjectService -import com.saveourtool.save.backend.service.OrganizationService import com.saveourtool.save.backend.service.ProjectProblemService -import com.saveourtool.save.backend.service.ProjectService import com.saveourtool.save.configs.ApiSwaggerSupport import com.saveourtool.save.configs.RequiresAuthorizationSourceHeader import com.saveourtool.save.domain.ProjectSaveStatus @@ -14,6 +10,10 @@ import com.saveourtool.save.entities.* import com.saveourtool.save.filters.ProjectFilter import com.saveourtool.save.filters.ProjectProblemFilter import com.saveourtool.save.permission.Permission +import com.saveourtool.save.security.ProjectPermissionEvaluator +import com.saveourtool.save.service.LnkUserProjectService +import com.saveourtool.save.service.OrganizationService +import com.saveourtool.save.service.ProjectService import com.saveourtool.save.utils.* import com.saveourtool.save.v1 import com.saveourtool.save.validation.NAMING_MAX_LENGTH diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/RunExecutionController.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/RunExecutionController.kt index 5281097046..7f99b4fafc 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/RunExecutionController.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/RunExecutionController.kt @@ -12,6 +12,7 @@ import com.saveourtool.save.execution.ExecutionUpdateDto import com.saveourtool.save.execution.TestingType import com.saveourtool.save.permission.Permission import com.saveourtool.save.request.CreateExecutionRequest +import com.saveourtool.save.service.ProjectService import com.saveourtool.save.spring.utils.applyAll import com.saveourtool.save.storage.impl.InternalFileKey import com.saveourtool.save.utils.* diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/TestExecutionController.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/TestExecutionController.kt index d1f11995c8..2c2ba52b84 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/TestExecutionController.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/TestExecutionController.kt @@ -4,7 +4,6 @@ import com.saveourtool.save.agent.TestExecutionDto import com.saveourtool.save.agent.TestExecutionExtDto import com.saveourtool.save.agent.TestExecutionResult import com.saveourtool.save.agent.TestSuiteExecutionStatisticDto -import com.saveourtool.save.backend.security.ProjectPermissionEvaluator import com.saveourtool.save.backend.service.ExecutionService import com.saveourtool.save.backend.service.TestAnalysisService import com.saveourtool.save.backend.service.TestExecutionService @@ -18,6 +17,7 @@ import com.saveourtool.save.domain.TestResultStatus import com.saveourtool.save.entities.TestExecution import com.saveourtool.save.filters.TestExecutionFilter import com.saveourtool.save.permission.Permission +import com.saveourtool.save.security.ProjectPermissionEvaluator import com.saveourtool.save.test.analysis.api.TestIdGenerator import com.saveourtool.save.test.analysis.api.testId import com.saveourtool.save.test.analysis.entities.metadata diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/TestSuitesSourceController.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/TestSuitesSourceController.kt index c7ff960b38..bf1d5fbd08 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/TestSuitesSourceController.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/TestSuitesSourceController.kt @@ -8,6 +8,8 @@ import com.saveourtool.save.configs.RequiresAuthorizationSourceHeader import com.saveourtool.save.domain.EntitySaveStatus import com.saveourtool.save.entities.* import com.saveourtool.save.entities.TestSuitesSource.Companion.toTestSuiteSource +import com.saveourtool.save.service.GitService +import com.saveourtool.save.service.OrganizationService import com.saveourtool.save.test.TestsSourceVersionInfoList import com.saveourtool.save.testsuite.* import com.saveourtool.save.utils.* diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/UsersDetailsController.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/UsersDetailsController.kt index f52c1075f9..b6d71f6ea1 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/UsersDetailsController.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/UsersDetailsController.kt @@ -3,13 +3,13 @@ package com.saveourtool.save.backend.controllers import com.saveourtool.save.authservice.utils.SaveUserDetails import com.saveourtool.save.authservice.utils.userId import com.saveourtool.save.backend.configs.ConfigProperties -import com.saveourtool.save.backend.service.UserDetailsService import com.saveourtool.save.configs.RequiresAuthorizationSourceHeader import com.saveourtool.save.domain.UserSaveStatus import com.saveourtool.save.entities.User import com.saveourtool.save.info.UserInfo import com.saveourtool.save.info.UserStatus import com.saveourtool.save.repository.UserRepository +import com.saveourtool.save.service.UserService import com.saveourtool.save.utils.* import com.saveourtool.save.v1 import com.saveourtool.save.validation.isValidLengthName @@ -40,7 +40,7 @@ import reactor.kotlin.core.publisher.toMono @RequestMapping(path = ["/api/$v1/users"]) class UsersDetailsController( private val userRepository: UserRepository, - private val userDetailsService: UserDetailsService, + private val userDetailsService: UserService, configProperties: ConfigProperties, jackson2WebClientCustomizer: WebClientCustomizer, ) { diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/internal/UsersController.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/internal/UsersController.kt index b18e42688d..2e7e148981 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/internal/UsersController.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/internal/UsersController.kt @@ -1,7 +1,7 @@ package com.saveourtool.save.backend.controllers.internal import com.saveourtool.save.authservice.utils.SaveUserDetails -import com.saveourtool.save.backend.service.UserDetailsService +import com.saveourtool.save.service.UserService import com.saveourtool.save.utils.blockingToMono import org.springframework.http.ResponseEntity @@ -20,7 +20,7 @@ typealias SaveUserDetailsResponse = ResponseEntity @RestController @RequestMapping("/internal/users") class UsersController( - private val userService: UserDetailsService, + private val userService: UserService, ) { /** * Stores user in the DB with provided [name] with default role. diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/vulnerability/event/VulnerabilityMetadataListener.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/vulnerability/event/VulnerabilityMetadataListener.kt index 5a6ae07e71..87aefc62da 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/vulnerability/event/VulnerabilityMetadataListener.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/vulnerability/event/VulnerabilityMetadataListener.kt @@ -1,12 +1,12 @@ package com.saveourtool.save.backend.controllers.vulnerability.event import com.saveourtool.save.backend.service.NotificationService -import com.saveourtool.save.backend.service.UserDetailsService import com.saveourtool.save.domain.Role import com.saveourtool.save.entities.Notification import com.saveourtool.save.entities.vulnerability.VulnerabilityStatus import com.saveourtool.save.entitiescosv.VulnerabilityMetadata import com.saveourtool.save.entitiescosv.evententities.VulnerabilityMetadataEvent +import com.saveourtool.save.service.UserService import org.springframework.context.event.EventListener import org.springframework.stereotype.Component @@ -16,7 +16,7 @@ import org.springframework.stereotype.Component */ @Component class VulnerabilityMetadataListener( - private val userDetailsService: UserDetailsService, + private val userDetailsService: UserService, private val notificationService: NotificationService, ) { /** diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/event/UserListener.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/event/UserListener.kt index dce1cdc571..13a3718d34 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/event/UserListener.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/event/UserListener.kt @@ -1,12 +1,12 @@ package com.saveourtool.save.backend.event import com.saveourtool.save.backend.service.NotificationService -import com.saveourtool.save.backend.service.UserDetailsService import com.saveourtool.save.domain.Role import com.saveourtool.save.entities.Notification import com.saveourtool.save.entities.User import com.saveourtool.save.evententities.UserEvent import com.saveourtool.save.info.UserStatus +import com.saveourtool.save.service.UserService import org.springframework.context.event.EventListener import org.springframework.stereotype.Component @@ -15,7 +15,7 @@ import org.springframework.stereotype.Component */ @Component class UserListener( - private val userDetailsService: UserDetailsService, + private val userDetailsService: UserService, private val notificationService: NotificationService, ) { /** diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/security/CommentPermissionEvaluator.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/security/CommentPermissionEvaluator.kt deleted file mode 100644 index 03bc1e463a..0000000000 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/security/CommentPermissionEvaluator.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.saveourtool.save.backend.security - -import com.saveourtool.save.backend.utils.hasRole -import com.saveourtool.save.domain.Role -import com.saveourtool.save.entities.Comment -import com.saveourtool.save.entities.CommentDto -import com.saveourtool.save.permission.Permission -import org.springframework.security.core.Authentication -import org.springframework.stereotype.Component - -/** - * Class that is capable of assessing user permissions. - * - * TODO: move all the hasPermission methods here - */ -@Component -class CommentPermissionEvaluator { - /** - * Check permission for user to read, write and delete [Comment]s by its [CommentDto] - * - * @param authentication - * @param comment - * @param permission - * @return true if user with [authentication] has [permission] for [comment] - */ - fun hasPermission( - authentication: Authentication?, - comment: CommentDto, - permission: Permission, - ): Boolean { - authentication ?: return false - - if (authentication.hasRole(Role.SUPER_ADMIN)) { - return true - } - - return when (permission) { - Permission.READ -> true - else -> comment.userName == authentication.name - } - } -} diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/security/ProjectPermissionEvaluator.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/security/ProjectPermissionEvaluator.kt deleted file mode 100644 index e85ad7b8b8..0000000000 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/security/ProjectPermissionEvaluator.kt +++ /dev/null @@ -1,178 +0,0 @@ -package com.saveourtool.save.backend.security - -import com.saveourtool.save.authservice.utils.userId -import com.saveourtool.save.backend.repository.LnkUserProjectRepository -import com.saveourtool.save.backend.service.LnkUserOrganizationService -import com.saveourtool.save.backend.service.LnkUserProjectService -import com.saveourtool.save.backend.utils.hasRole -import com.saveourtool.save.domain.Role -import com.saveourtool.save.entities.* -import com.saveourtool.save.permission.Permission -import com.saveourtool.save.utils.getHighestRole - -import org.springframework.http.HttpStatus -import org.springframework.security.core.Authentication -import org.springframework.stereotype.Component -import org.springframework.web.server.ResponseStatusException -import reactor.core.publisher.Mono -import reactor.kotlin.core.publisher.cast -import reactor.kotlin.core.publisher.switchIfEmpty - -/** - * Class that is capable of assessing user's permissions regarding projects. - */ -@Component -class ProjectPermissionEvaluator( - private var lnkUserProjectService: LnkUserProjectService, - private var lnkUserProjectRepository: LnkUserProjectRepository, - private var lnkUserOrganizationService: LnkUserOrganizationService -) { - /** - * @param authentication [Authentication] describing an authenticated request - * @param project is organization in which we want to change the status - * @param newStatus is new status in [project] - * @return whether user described by [authentication] can have permission on change [project] status on [newStatus] - * @throws IllegalStateException - */ - fun hasPermissionToChangeStatus(authentication: Authentication?, project: Project, newStatus: ProjectStatus): Boolean { - val oldStatus = project.status - - return when { - oldStatus == newStatus -> throw IllegalStateException("invalid status") - oldStatus.isBan() || newStatus.isBan() -> hasPermission(authentication, project, Permission.BAN) - else -> hasPermission(authentication, project, Permission.DELETE) - } - } - - /** - * @param authentication [Authentication] describing an authenticated request - * @param project - * @param permission - * @return whether user described by [authentication] can have [permission] on project [project] - */ - fun hasPermission(authentication: Authentication?, project: Project, permission: Permission): Boolean { - authentication ?: return when (permission) { - Permission.READ -> project.public - Permission.BAN, Permission.DELETE, Permission.WRITE -> false - } - if (authentication.hasRole(Role.SUPER_ADMIN)) { - return true - } - - val userId = authentication.userId() - val organizationRole = lnkUserOrganizationService.findRoleByUserIdAndOrganization(userId, project.organization) - val projectRole = lnkUserProjectService.findRoleByUserIdAndProject(userId, project) - - return when (permission) { - Permission.READ -> project.public || hasReadAccess(userId, projectRole, organizationRole) - Permission.WRITE -> hasWriteAccess(userId, projectRole, organizationRole) - Permission.DELETE -> hasDeleteAccess(userId, projectRole, organizationRole) - Permission.BAN -> hasBanAccess(userId, projectRole, organizationRole) - } - } - - /** - * @param authentication - * @param permission - * @param statusIfForbidden - * @return a [Mono] containing the project or `Mono.error` if project can't or shouldn't be accessed by the current user - */ - internal fun Mono.filterByPermission( - authentication: Authentication?, - permission: Permission, - statusIfForbidden: HttpStatus, - ) = switchIfEmpty { Mono.error(ResponseStatusException(HttpStatus.NOT_FOUND)) } - .cast() - .map { actualProject -> - actualProject to hasPermission(authentication, actualProject, permission) - } - .filter { (project, isPermissionGranted) -> project.public || isPermissionGranted } - .flatMap { (project, isPermissionGranted) -> - if (isPermissionGranted) { - Mono.just(project) - } else { - // project is public, but current user lacks permissions - Mono.error(ResponseStatusException(statusIfForbidden)) - } - } - .switchIfEmpty { - // We get here if `!project.public && !isPermissionGranted`, i.e. - // if project either is not found or shouldn't be visible for current user. - Mono.error(ResponseStatusException(HttpStatus.NOT_FOUND)) - } - - private fun hasReadAccess(userId: Long?, projectRole: Role, organizationRole: Role): Boolean = hasWriteAccess(userId, projectRole, organizationRole) || - userId?.let { projectRole == Role.VIEWER } ?: false - - private fun hasWriteAccess(userId: Long?, projectRole: Role, organizationRole: Role): Boolean = hasDeleteAccess(userId, projectRole, organizationRole) || - userId?.let { projectRole == Role.ADMIN } ?: false - - private fun hasDeleteAccess(userId: Long?, projectRole: Role, organizationRole: Role): Boolean = - hasBanAccess(userId, projectRole, organizationRole) || userId?.let { - getHighestRole(organizationRole, projectRole) == Role.OWNER - } ?: false - - /** - * Only [SUPER_ADMIN] can ban the project. And a user with such a global role has permissions for all actions. - * Since we have all the rights issued depending on the following, you need to set [false] here - */ - @Suppress("FunctionOnlyReturningConstant", "UnusedParameter") - private fun hasBanAccess(userId: Long?, projectRole: Role, organizationRole: Role): Boolean = false - - /** - * @param authentication - * @param execution - * @param permission - * @return [Mono] containing `true` if the current user is granted [permission] on the project for this [execution] or Mono with `false` otherwise - */ - internal fun checkPermissions(authentication: Authentication, execution: Execution, permission: Permission): Mono = - Mono.justOrEmpty(execution.project) - .filterByPermission(authentication, permission, HttpStatus.FORBIDDEN) - .map { true } - .defaultIfEmpty(false) - - /** - * @param project in which the role is going to be changed - * @param authentication auth info of a current user - * @param otherUser user whose role is going to be changed - * @param requestedRole role that is going to be set - * @return true if user can change roles in project and false otherwise - */ - @Suppress("UnsafeCallOnNullableType") - fun canChangeRoles( - project: Project, - authentication: Authentication, - otherUser: User, - requestedRole: Role = Role.NONE - ): Boolean { - val selfRole = lnkUserProjectService.getGlobalRoleOrProjectRole(authentication, project) - val otherUserId = otherUser.id!! - val otherRole = lnkUserProjectRepository.findByUserIdAndProject(otherUserId, project)?.role ?: Role.NONE - return isProjectAdminOrHigher(selfRole) && - hasAnotherUserLessPermissions(selfRole, otherRole) && - isRequestedPermissionsCanBeSetByUser(selfRole, requestedRole) - } - - /** - * @param selfRole - * @param otherRole - * @return true if [otherRole] has less permissions than [selfRole], false otherwise. - */ - fun hasAnotherUserLessPermissions(selfRole: Role, otherRole: Role): Boolean = selfRole.priority > otherRole.priority - - /** - * @param selfRole - * @param requestedRole - * @return true if [requestedRole] can be set by user with role [selfRole], false otherwise. - */ - fun isRequestedPermissionsCanBeSetByUser(selfRole: Role, requestedRole: Role): Boolean = selfRole.priority > requestedRole.priority - - /** - * @param userRole - * @return true if [userRole] is [Role.ADMIN] or higher, false otherwise. - */ - fun isProjectAdminOrHigher(userRole: Role): Boolean = userRole.priority >= Role.ADMIN.priority -} - -private fun ProjectStatus.isBan(): Boolean = - this == ProjectStatus.BANNED diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/CommentService.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/CommentService.kt deleted file mode 100644 index 4b092484a9..0000000000 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/CommentService.kt +++ /dev/null @@ -1,74 +0,0 @@ -package com.saveourtool.save.backend.service - -import com.saveourtool.save.authservice.utils.userId -import com.saveourtool.save.entities.Comment -import com.saveourtool.save.entities.CommentDto -import com.saveourtool.save.evententities.CommentEvent -import com.saveourtool.save.repository.CommentRepository -import com.saveourtool.save.repository.UserRepository -import com.saveourtool.save.utils.getByIdOrNotFound - -import org.springframework.context.ApplicationEventPublisher -import org.springframework.security.core.Authentication -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional - -import kotlinx.datetime.toJavaLocalDateTime - -/** - * Service for comments - */ -@Service -@Transactional(readOnly = true) -class CommentService( - private val commentRepository: CommentRepository, - private val userRepository: UserRepository, - private val applicationEventPublisher: ApplicationEventPublisher, -) { - /** - * @param comment comment of user - * @param authentication - */ - @Transactional - fun saveComment(comment: CommentDto, authentication: Authentication) { - val userId = authentication.userId() - val user = userRepository.getByIdOrNotFound(userId) - - val newComment = Comment( - comment.message, - comment.section, - user, - ) - - applicationEventPublisher.publishEvent(CommentEvent(newComment)) - - commentRepository.save(newComment) - } - - /** - * @param section section with comments - * @return list of messages - */ - fun findAllBySection(section: String) = commentRepository.getAllBySection(section) - - /** - * @param section section with comments - * @return count messages - */ - fun countBySection(section: String) = commentRepository.countAllBySection(section) - - /** - * @param comment [CommentDto] that matches the [Comment] that should be deleted - * @return [Unit] if comment was found, `null` otherwise - */ - @Transactional - fun deleteComment(comment: CommentDto): Unit? { - val requestedComment = commentRepository.findByUserNameAndSectionAndCreateDate( - comment.userName, - comment.section, - comment.createDate?.toJavaLocalDateTime() - ) - - return requestedComment?.let { commentRepository.delete(it) } - } -} diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/ExecutionService.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/ExecutionService.kt index 8d9b3c668f..aa4e766b5a 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/ExecutionService.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/ExecutionService.kt @@ -9,6 +9,7 @@ import com.saveourtool.save.entities.* import com.saveourtool.save.execution.ExecutionStatus import com.saveourtool.save.execution.TestingType import com.saveourtool.save.repository.UserRepository +import com.saveourtool.save.service.ProjectService import com.saveourtool.save.test.TestsSourceSnapshotDto import com.saveourtool.save.utils.* diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/GitService.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/GitService.kt deleted file mode 100644 index 8531066107..0000000000 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/GitService.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.saveourtool.save.backend.service - -import com.saveourtool.save.entities.Git -import com.saveourtool.save.entities.GitDto -import com.saveourtool.save.entities.Organization -import com.saveourtool.save.repository.GitRepository -import org.springframework.data.repository.findByIdOrNull -import org.springframework.stereotype.Service - -/** - * Service of git - */ -@Service -class GitService(private val gitRepository: GitRepository) { - /** - * @param organization - * @return list of gits by organization if exists - */ - fun getAllByOrganization(organization: Organization): List = gitRepository.findAllByOrganizationId(organization.requiredId()) - - /** - * @param organization - * @param url - * @return list of gits by organization if exists or null - */ - fun findByOrganizationAndUrl(organization: Organization, url: String): Git? = gitRepository.findByOrganizationAndUrl(organization, url) - - /** - * @param organization - * @param url - * @return list of gits by organization if exists - * @throws NoSuchElementException - */ - fun getByOrganizationAndUrl(organization: Organization, url: String): Git = findByOrganizationAndUrl(organization, url) - ?: throw NoSuchElementException("There is no git credential with url $url in ${organization.name}") - - /** - * @param git - * @return saved or updated git - */ - fun save(git: Git): Git = gitRepository.save(git) - - /** - * @param organization - * @param url git url - * @throws NoSuchElementException - */ - fun delete(organization: Organization, url: String) { - gitRepository.delete(getByOrganizationAndUrl(organization, url)) - } - - /** - * @param id - * @return [GitDto] found by provided values or null - */ - fun findById(id: Long): GitDto? = gitRepository.findByIdOrNull(id)?.toDto() -} diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/LnkContestProjectService.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/LnkContestProjectService.kt index 12602371ba..534d072f7d 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/LnkContestProjectService.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/LnkContestProjectService.kt @@ -3,6 +3,7 @@ package com.saveourtool.save.backend.service import com.saveourtool.save.backend.repository.LnkContestProjectRepository import com.saveourtool.save.domain.ProjectCoordinates import com.saveourtool.save.entities.* +import com.saveourtool.save.service.ProjectService import com.saveourtool.save.utils.debug import com.saveourtool.save.utils.getLogger import org.springframework.data.domain.PageRequest diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/LnkUserOrganizationService.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/LnkUserOrganizationService.kt deleted file mode 100644 index 43454a44c7..0000000000 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/LnkUserOrganizationService.kt +++ /dev/null @@ -1,244 +0,0 @@ -package com.saveourtool.save.backend.service - -import com.saveourtool.save.authservice.utils.userId -import com.saveourtool.save.domain.Role -import com.saveourtool.save.entities.* -import com.saveourtool.save.filters.OrganizationFilter -import com.saveourtool.save.repository.LnkUserOrganizationRepository -import com.saveourtool.save.repository.UserRepository -import com.saveourtool.save.utils.blockingToFlux -import com.saveourtool.save.utils.getHighestRole - -import org.springframework.data.domain.PageRequest -import org.springframework.security.core.Authentication -import org.springframework.stereotype.Service - -import kotlin.NoSuchElementException - -/** - * Service of lnkUserOrganization - */ -@Service -class LnkUserOrganizationService( - private val lnkUserOrganizationRepository: LnkUserOrganizationRepository, - private val userRepository: UserRepository, - private val userDetailsService: UserDetailsService, -) { - /** - * @param organization - * @return all users with role in [organization] - */ - fun getAllUsersAndRolesByOrganization(organization: Organization) = - lnkUserOrganizationRepository.findByOrganization(organization) - .associate { it.user to it.role } - - /** - * @param userId - * @param organization - * @return role for user in [organization] - */ - fun findRoleByUserIdAndOrganization(userId: Long, organization: Organization) = lnkUserOrganizationRepository - .findByUserIdAndOrganization(userId, organization) - ?.role - ?: Role.NONE - - /** - * Set [role] of [user] in [organization]. - * - * @throws IllegalStateException if [role] is [Role.NONE] - */ - @Suppress("KDOC_WITHOUT_PARAM_TAG", "UnsafeCallOnNullableType") - fun setRole(user: User, organization: Organization, role: Role) { - if (role == Role.NONE) { - throw IllegalStateException("Role NONE should not be present in database!") - } - val lnkUserOrganization = lnkUserOrganizationRepository.findByUserIdAndOrganization(user.id!!, organization) - ?.apply { this.role = role } - ?: LnkUserOrganization(organization, user, role) - lnkUserOrganizationRepository.save(lnkUserOrganization) - } - - /** - * Set [role] of user with [userId] in organization with [organizationId]. - * - * @throws IllegalStateException if [role] is [Role.NONE] - */ - @Suppress("KDOC_WITHOUT_PARAM_TAG", "UnsafeCallOnNullableType") - fun setRoleByIds(userId: Long, organizationId: Long, role: Role) { - if (role == Role.NONE) { - throw IllegalStateException("Role NONE should not be present in database!") - } - lnkUserOrganizationRepository.findByUserIdAndOrganizationId(userId, organizationId) - ?.apply { this.role = role } - ?.let { lnkUserOrganizationRepository.save(it) } - ?: lnkUserOrganizationRepository.save(organizationId, userId, role.toString()) - } - - /** - * @param user - * @param organization - * @return role of the [user] in [organization] - */ - @Suppress("UnsafeCallOnNullableType") - fun getRole(user: User, organization: Organization) = lnkUserOrganizationRepository - .findByUserIdAndOrganization(user.id!!, organization) - ?.role - ?: Role.NONE - - /** - * @param userName - * @return user with [userName] - */ - fun getUserByName(userName: String): User? = userRepository.findByName(userName) - - /** - * @param userId - * @return user with [userId] - */ - fun getUserById(userId: Long) = userRepository.findById(userId) - - /** - * Removes role of [user] in [organization]. - * - * @param user - * @param organization - * @return Unit - */ - @Suppress("UnsafeCallOnNullableType") - fun removeRole(user: User, organization: Organization) = lnkUserOrganizationRepository - .findByUserIdAndOrganization(user.id!!, organization) - ?.id - ?.let { lnkUserOrganizationRepository.deleteById(it) } - ?: throw NoSuchElementException( - "Cannot delete user with name ${user.name} because he is not found in organization ${organization.name}" - ) - - /** - * @param name - * @param organizationUserIds - * @return list of all users who have [name] and whose ids are not in [organizationUserIds] - */ - fun getNonOrganizationUsersByName(name: String, organizationUserIds: Set) = userRepository.findByNameAndIdNotIn(name, organizationUserIds) - - /** - * @param prefix - * @param organizationUserIds - * @param pageSize - * @return list of [pageSize] users whose name starts with [prefix] and whose ids are not in [organizationUserIds] - */ - fun getNonOrganizationUsersByNamePrefix(prefix: String, organizationUserIds: Set, pageSize: Int) = if (pageSize > 0) { - userRepository.findByNameStartingWithAndIdNotIn(prefix, organizationUserIds, PageRequest.of(0, pageSize)).content - } else { - emptyList() - } - - /** - * @param userId - * @param organizationName - * @return role for user in organization by user ID and organization name - */ - fun findRoleByUserIdAndOrganizationName(userId: Long, organizationName: String) = lnkUserOrganizationRepository - .findByUserIdAndOrganizationName(userId, organizationName) - ?.role - ?: Role.NONE - - /** - * @param userId - * @param organizationName - * @return role for user in organization by user ID and organization name - */ - fun findByAuthenticationAndOrganizationName(userId: Long, organizationName: String) = lnkUserOrganizationRepository - .findByUserIdAndOrganizationName(userId, organizationName) - - /** - * @param userId ID of User - * @param statuses is set of [statuses], one of which a projects can have - * @return Flux of [Organization]s in which user is in - */ - fun findAllByAuthenticationAndStatuses(userId: Long, statuses: Set = setOf(OrganizationStatus.CREATED)) = blockingToFlux { - lnkUserOrganizationRepository.findByUserId(userId).filter { it.organization.status in statuses } - } - - /** - * @param userId ID of User - * @param statuses is set of [statuses], one of which a projects can have - * @param canBulkUpload ability to bulk upload cosv files - * @return Flux of [Organization]s in which user is in - */ - fun findAllByAuthenticationAndStatusesAndBulkUpload( - userId: Long, - statuses: Set = setOf(OrganizationStatus.CREATED), - canBulkUpload: Boolean = true, - ) = blockingToFlux { - lnkUserOrganizationRepository.findByUserIdAndOrganizationCanBulkUploadAndOrganizationStatusIn(userId, canBulkUpload, statuses) - } - - /** - * @param userName name of User - * @return list of lnkUserOrganization - */ - fun getOrganizationsByUserNameAndCreatedStatus(userName: String) = lnkUserOrganizationRepository.findByUserNameAndOrganizationStatus(userName, OrganizationStatus.CREATED) - - /** - * @param userName name of User - * @param organizationName name of organization - * @return list of lnkUserOrganization - */ - fun getOrganizationsByUserNameAndCreatedStatusAndOrganizationName(userName: String, organizationName: String) = - lnkUserOrganizationRepository.findByUserNameAndOrganizationStatusAndOrganizationName(userName, OrganizationStatus.CREATED, organizationName) - - /** - * @param authentication - * @param organization - * @return the highest of two roles: the one in [organization] and global one. - */ - fun getGlobalRoleOrOrganizationRole(authentication: Authentication, organization: Organization): Role { - val selfId = authentication.userId() - val selfGlobalRole = userDetailsService.getGlobalRole(authentication) - val selfOrganizationRole = findRoleByUserIdAndOrganization(selfId, organization) - return getHighestRole(selfOrganizationRole, selfGlobalRole) - } - - /** - * @param authentication - * @param organizationName - * @return the highest of two roles: the one in organization with name [organizationName] and global one. - */ - fun getGlobalRoleOrOrganizationRole(authentication: Authentication, organizationName: String): Role { - val selfId = authentication.userId() - val selfGlobalRole = userDetailsService.getGlobalRole(authentication) - val selfOrganizationRole = findRoleByUserIdAndOrganizationName(selfId, organizationName) - return getHighestRole(selfOrganizationRole, selfGlobalRole) - } - - /** - * @param userId - * @param requestedRole role that user with id [userId] should have in organization - * @return list of organizations that can create contests - */ - fun getSuperOrganizationsWithRole(userId: Long, requestedRole: Role = Role.OWNER): List = Role.values() - .filter { - it.isHigherOrEqualThan(requestedRole) - } - .let { - lnkUserOrganizationRepository.findByUserIdAndOrganizationCanCreateContestsAndRoleIn(userId, true, it) - } - .map { it.organization } - - /** - * @param user - * @param filters - * @return [Organization]s that are connected to the [user] anf matching filters - */ - fun getOrganizationsAndRolesByUserAndFilters(user: User, filters: OrganizationFilter): List = - lnkUserOrganizationRepository.findByUserId(user.requiredId()).filter { lnkUserOrganization -> - lnkUserOrganization.organization.let { it.name.startsWith(filters.prefix) && it.status in filters.statuses } - } - - /** - * @param user - * @return [Organization]s that are connected to the [user] - */ - fun getCreatedOrganizationAndRoles(user: User): List = - getOrganizationsAndRolesByUserAndFilters(user, OrganizationFilter.created) -} diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/LnkUserProjectService.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/LnkUserProjectService.kt deleted file mode 100644 index 3d53adc645..0000000000 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/LnkUserProjectService.kt +++ /dev/null @@ -1,137 +0,0 @@ -package com.saveourtool.save.backend.service - -import com.saveourtool.save.authservice.utils.userId -import com.saveourtool.save.backend.repository.LnkUserProjectRepository -import com.saveourtool.save.domain.Role -import com.saveourtool.save.entities.LnkUserProject -import com.saveourtool.save.entities.Project -import com.saveourtool.save.entities.ProjectStatus -import com.saveourtool.save.entities.User -import com.saveourtool.save.repository.UserRepository -import com.saveourtool.save.utils.getHighestRole - -import org.springframework.data.domain.PageRequest -import org.springframework.security.core.Authentication -import org.springframework.stereotype.Service - -/** - * Service of lnkUserProjects - */ -@Service -class LnkUserProjectService( - private val lnkUserProjectRepository: LnkUserProjectRepository, - private val userRepository: UserRepository, - private val userDetailsService: UserDetailsService, -) { - /** - * @param project - * @return all users with role in project - */ - fun getAllUsersAndRolesByProject(project: Project) = - lnkUserProjectRepository.findByProject(project).associate { it.user to (it.role) } - - /** - * @param userId - * @param project - * @return role for user in [project] by user ID - */ - fun findRoleByUserIdAndProject(userId: Long, project: Project) = lnkUserProjectRepository - .findByUserIdAndProject(userId, project) - ?.role - ?: Role.NONE - - /** - * Set role of [user] on a project [project] to [role] - * - * @throws IllegalStateException if [role] is [Role.NONE] - */ - @Suppress("KDOC_WITHOUT_PARAM_TAG", "UnsafeCallOnNullableType") - fun setRole(user: User, project: Project, role: Role) { - if (role == Role.NONE) { - throw IllegalStateException("Role NONE should not be present in database!") - } - val lnkUserProject = lnkUserProjectRepository.findByUserIdAndProject(user.id!!, project) - ?.apply { this.role = role } - ?: LnkUserProject(project, user, role) - lnkUserProjectRepository.save(lnkUserProject) - } - - /** - * Set role of user with [userId] on a project with [projectId] to [role] - * - * @throws IllegalStateException if [role] is [Role.NONE] - */ - @Suppress("KDOC_WITHOUT_PARAM_TAG", "UnsafeCallOnNullableType") - fun setRoleByIds(userId: Long, projectId: Long, role: Role) { - if (role == Role.NONE) { - throw IllegalStateException("Role NONE should not be present in database!") - } - lnkUserProjectRepository.findByUserIdAndProjectId(userId, projectId) - ?.apply { this.role = role } - ?.let { lnkUserProjectRepository.save(it) } - ?: lnkUserProjectRepository.save(projectId, userId, role.toString()) - } - - /** - * @param user that should be deleted from [project] - * @param project - * @return none - * @throws NoSuchElementException - */ - @Suppress("UnsafeCallOnNullableType") - fun removeRole(user: User, project: Project) = lnkUserProjectRepository.findByUserIdAndProject(user.id!!, project) - ?.id - ?.let { - lnkUserProjectRepository.deleteById(it) - } - ?: throw NoSuchElementException( - "Cannot delete user with name ${user.name} because he is not found in project ${project.organization.name}/${project.name}" - ) - - /** - * Get certain [pageSize] of platform users with names that start with [prefix] - * - * @param prefix - * @param projectUserIds - * @param pageSize - * @return list of all save-cloud users - */ - fun getNonProjectUsersByNamePrefix(prefix: String, projectUserIds: Set, pageSize: Int): List = if (pageSize > 0) { - userRepository.findByNameStartingWithAndIdNotIn(prefix, projectUserIds, PageRequest.of(0, pageSize)).content - } else { - emptyList() - } - - /** - * @param name - * @param projectUserIds - * @return list of [User]s not from project with names that exactly match [name] - */ - fun getNonProjectUsersByName(name: String, projectUserIds: Set): List = userRepository.findByNameAndIdNotIn(name, projectUserIds) - - /** - * @param project - * @return list of [User]s that are connected to [project] - */ - fun getAllUsersByProject(project: Project): List = lnkUserProjectRepository.findByProject(project).map { it.user } - - /** - * @param userId - * @param statuses - * @return list of [Project]s that are connected to user with [userId] - */ - fun getProjectsByUserIdAndStatuses(userId: Long, statuses: Set = setOf(ProjectStatus.CREATED)): List = - lnkUserProjectRepository.findByUserIdAndProjectStatusIn(userId, statuses).mapNotNull { it.project } - - /** - * @param authentication - * @param project - * @return the highest of two roles: the one in [project] and global one. - */ - fun getGlobalRoleOrProjectRole(authentication: Authentication, project: Project): Role { - val selfId = authentication.userId() - val selfGlobalRole = userDetailsService.getGlobalRole(authentication) - val selfOrganizationRole = findRoleByUserIdAndProject(selfId, project) - return getHighestRole(selfOrganizationRole, selfGlobalRole) - } -} diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/OrganizationService.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/OrganizationService.kt deleted file mode 100644 index a3a26bf953..0000000000 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/OrganizationService.kt +++ /dev/null @@ -1,205 +0,0 @@ -package com.saveourtool.save.backend.service - -import com.saveourtool.save.domain.OrganizationSaveStatus -import com.saveourtool.save.entities.Organization -import com.saveourtool.save.entities.OrganizationStatus -import com.saveourtool.save.entities.ProjectStatus.* -import com.saveourtool.save.filters.OrganizationFilter -import com.saveourtool.save.repository.OrganizationRepository -import com.saveourtool.save.utils.AvatarType -import com.saveourtool.save.utils.orNotFound -import com.saveourtool.save.validation.isValidLengthName -import org.springframework.data.domain.Pageable -import org.springframework.security.core.Authentication -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional -import java.util.* -import kotlin.NoSuchElementException - -/** - * Service for organization - * - * @param organizationRepository - */ -@Service -class OrganizationService( - private val projectService: ProjectService, - private val organizationRepository: OrganizationRepository, -) { - /** - * Store [organization] in the database - * - * @param organization an [Organization] to store - * @return organization's id, should never return null - */ - @Suppress("UnsafeCallOnNullableType", "TooGenericExceptionCaught") - @Transactional - fun saveOrganization(organization: Organization): Pair { - val (organizationId, organizationSaveStatus) = if (!organization.name.isValidLengthName()) { - Pair(0L, OrganizationSaveStatus.INVALID_NAME) - } else if (organizationRepository.validateName(organization.name) != 0L) { - organizationRepository.saveHighLevelName(organization.name) - Pair(organizationRepository.save(organization).id, OrganizationSaveStatus.NEW) - } else { - Pair(0L, OrganizationSaveStatus.CONFLICT) - } - requireNotNull(organizationId) { "Should have gotten an ID for organization from the database" } - return Pair(organizationId, organizationSaveStatus) - } - - /** - * Mark organization with [organization] as [newProjectStatus]. - * Before performing the function, check for user permissions by the [organization]. - * - * @param newProjectStatus is new status for [organization] - * @param organization is organization in which the status will be changed - * @return organization - */ - @Suppress("UnsafeCallOnNullableType") - private fun changeOrganizationStatus(organization: Organization, newProjectStatus: OrganizationStatus): Organization = organization - .apply { - status = newProjectStatus - } - .let { - organizationRepository.save(it) - } - - /** - * Mark organization [organization] as deleted - * - * @param organization an [Organization] to delete - * @return deleted organization - */ - fun deleteOrganization(organization: Organization): Organization = changeOrganizationStatus(organization, OrganizationStatus.DELETED) - - /** - * Mark organization with [organization] as created. - * If an organization was previously banned, then all its projects become deleted. - * - * @param organization an [Organization] to create - * @return recovered organization - */ - @Transactional - fun recoverOrganization(organization: Organization): Organization { - if (organization.status == OrganizationStatus.BANNED) { - projectService.getAllByOrganizationName(organization.name).forEach { - it.status = DELETED - projectService.updateProject(it) - } - } - return changeOrganizationStatus(organization, OrganizationStatus.CREATED) - } - - /** - * Mark organization with [organization] and all its projects as banned. - * - * @param organization an [Organization] to ban - * @return banned organization - */ - @Transactional - fun banOrganization(organization: Organization): Organization { - projectService.getAllByOrganizationName(organization.name).forEach { - it.status = BANNED - projectService.updateProject(it) - } - return changeOrganizationStatus(organization, OrganizationStatus.BANNED) - } - - /** - * @param organizationName the unique name of the organization. - * @return `true` if this organization has at least one non-deleted project, - * `false` otherwise. - */ - fun hasProjects(organizationName: String): Boolean = - projectService.getAllByOrganizationName(organizationName).any { project -> - project.status == CREATED - } - - /** - * @param organizationId - * @return organization by id - */ - fun getOrganizationById(organizationId: Long) = organizationRepository.getOrganizationById(organizationId) - - /** - * @param name - * @param statuses - * @return organization by name and statuses - */ - fun findByNameAndStatuses(name: String, statuses: Set) = - organizationRepository.findByNameAndStatusIn(name, statuses) - - /** - * @param name - * @return organization by name with [CREATED] status - */ - fun findByNameAndCreatedStatus(name: String) = findByNameAndStatuses(name, EnumSet.of(OrganizationStatus.CREATED)) - - /** - * @param name - * @return organization by name - */ - fun findByName(name: String) = organizationRepository.findByName(name) - - /** - * @param name - * @return organization by name - * @throws NoSuchElementException - */ - fun getByName(name: String) = findByNameAndCreatedStatus(name) - ?: throw NoSuchElementException("There is no organization with name $name.") - - /** - * @param organizationFilter - * @param pageable [Pageable] - * @return list of organizations with that match [organizationFilter] - */ - fun getFiltered(organizationFilter: OrganizationFilter, pageable: Pageable): List = organizationRepository.findByNameStartingWithAndStatusIn( - organizationFilter.prefix, - organizationFilter.statuses, - pageable, - ) - - /** - * @param organization - * @return organization - */ - fun updateOrganization(organization: Organization): Organization = organizationRepository.save(organization) - - /** - * We change the version just to work-around the caching on the frontend - * - * @param name - * @return the id (version) of new avatar - * @throws NoSuchElementException - */ - fun updateAvatarVersion(name: String): String { - val organization = organizationRepository.findByName(name).orNotFound() - var version = organization.avatar?.substringAfterLast("?")?.toInt() ?: 0 - val newAvatar = "${AvatarType.ORGANIZATION.toUrlStr(name)}?${++version}" - - organization.apply { - avatar = newAvatar - }.orNotFound { "Organization with name [$name] was not found." } - organization.let { organizationRepository.save(it) } - - return newAvatar - } - - /** - * @return all organizations that were registered in SAVE - */ - fun findAll(): List = organizationRepository.findAll() - - /** - * @param organizationName - * @param authentication - * @return global rating of organization by name [organizationName] based on ratings of all projects under this organization - */ - fun getGlobalRating(organizationName: String, authentication: Authentication?) = - projectService.getProjectsByOrganizationNameAndCreatedStatus(organizationName, authentication) - .collectList() - .map { projectsList -> - projectsList.sumOf { it.contestRating } - } -} diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/PermissionService.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/PermissionService.kt index b395821133..b563b12de6 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/PermissionService.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/PermissionService.kt @@ -5,6 +5,8 @@ import com.saveourtool.save.entities.Project import com.saveourtool.save.entities.User import com.saveourtool.save.permission.SetRoleRequest import com.saveourtool.save.repository.UserRepository +import com.saveourtool.save.service.LnkUserProjectService +import com.saveourtool.save.service.ProjectService import org.springframework.stereotype.Service import reactor.core.publisher.Mono import reactor.kotlin.core.publisher.toMono diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/ProjectProblemService.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/ProjectProblemService.kt index 7382dc7d7d..e70daa8501 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/ProjectProblemService.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/ProjectProblemService.kt @@ -2,10 +2,10 @@ package com.saveourtool.save.backend.service import com.saveourtool.save.authservice.utils.userId import com.saveourtool.save.backend.repository.ProjectProblemRepository -import com.saveourtool.save.backend.repository.ProjectRepository import com.saveourtool.save.entities.ProjectProblem import com.saveourtool.save.entities.ProjectProblemDto import com.saveourtool.save.filters.ProjectProblemFilter +import com.saveourtool.save.repository.ProjectRepository import com.saveourtool.save.repository.UserRepository import com.saveourtool.save.utils.getByIdOrNotFound import com.saveourtool.save.utils.orNotFound diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/ProjectService.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/ProjectService.kt deleted file mode 100644 index 54f4425b80..0000000000 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/ProjectService.kt +++ /dev/null @@ -1,232 +0,0 @@ -package com.saveourtool.save.backend.service - -import com.saveourtool.save.backend.repository.ProjectRepository -import com.saveourtool.save.backend.security.ProjectPermissionEvaluator -import com.saveourtool.save.domain.ProjectSaveStatus -import com.saveourtool.save.entities.* -import com.saveourtool.save.filters.ProjectFilter -import com.saveourtool.save.permission.Permission -import com.saveourtool.save.repository.UserRepository -import com.saveourtool.save.utils.blockingToMono -import com.saveourtool.save.utils.switchIfEmptyToNotFound - -import org.springframework.http.HttpStatus -import org.springframework.security.core.Authentication -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional -import org.springframework.web.server.ResponseStatusException -import reactor.core.publisher.Flux -import reactor.core.publisher.Mono -import reactor.kotlin.core.publisher.switchIfEmpty -import java.util.* - -/** - * Service for project - * - * @param projectRepository - */ -@Service -class ProjectService( - private val projectRepository: ProjectRepository, - private val projectPermissionEvaluator: ProjectPermissionEvaluator, - private val userRepository: UserRepository, -) { - /** - * Store [project] in the database - * - * @param project a [Project] to store - * @return project's id, should never return null - */ - @Suppress("UnsafeCallOnNullableType") - fun getOrSaveProject(project: Project): Pair { - val (projectId, projectSaveStatus) = projectRepository.findByNameAndOrganizationName(project.name, project.organization.name)?.let { - Pair(it.id, ProjectSaveStatus.EXIST) - } ?: run { - val savedProject = projectRepository.save(project) - Pair(savedProject.id, ProjectSaveStatus.NEW) - } - requireNotNull(projectId) { "Should have gotten an ID for project from the database" } - return Pair(projectId, projectSaveStatus) - } - - /** - * Mark organization with [project] as [newProjectStatus] - * Before performing the function, check for user permissions by the [project]. - * - * @param newProjectStatus is new status for [project] - * @param project is organization in which the status will be changed - * @return project - */ - @Suppress("UnsafeCallOnNullableType") - fun changeProjectStatus(project: Project, newProjectStatus: ProjectStatus): Project = project - .apply { - status = newProjectStatus - } - .let { - projectRepository.save(it) - } - - /** - * @param project [Project] to be updated - * @return updated [project] - */ - fun updateProject(project: Project): Project = run { - requireNotNull(project.id) { - "Project must be taken from DB so it's id must not be null" - } - projectRepository.save(project) - } - - /** - * @return list of all projects - */ - fun getProjects(): Flux = projectRepository.findAll().let { Flux.fromIterable(it) } - - /** - * @param name - * @param organization - */ - @Suppress("KDOC_WITHOUT_RETURN_TAG") // remove after new release of diktat - fun findByNameAndOrganization(name: String, organization: Organization) = projectRepository.findByNameAndOrganization(name, organization) - - /** - * @param name - * @param organizationName - * @param statuses - * @return project by [name], [organizationName] and [statuses] - */ - fun findByNameAndOrganizationNameAndStatusIn(name: String, organizationName: String, statuses: Set) = - projectRepository.findByNameAndOrganizationNameAndStatusIn(name, organizationName, statuses) - - /** - * @param name - * @param organizationName - * @return project by [name], [organizationName] and [ProjectStatus.CREATED] status - */ - fun findByNameAndOrganizationNameAndCreatedStatus(name: String, organizationName: String) = - findByNameAndOrganizationNameAndStatusIn(name, organizationName, EnumSet.of(ProjectStatus.CREATED)) - - /** - * @param organizationName - * @return List of the Organization projects - */ - fun getAllByOrganizationName(organizationName: String) = projectRepository.findByOrganizationName(organizationName) - - /** - * @param organizationName - * @return Flux of the Organization projects - */ - fun getAllAsFluxByOrganizationName(organizationName: String) = getAllByOrganizationName(organizationName).let { Flux.fromIterable(it) } - - /** - * @param organizationName is [organization] name - * @param authentication - * @param statuses is status`s - * @return list of not deleted projects - */ - fun getProjectsByOrganizationNameAndStatusIn( - organizationName: String, - authentication: Authentication?, - statuses: Set - ): Flux = getAllAsFluxByOrganizationName(organizationName) - .filter { - it.status in statuses - } - .filter { - projectPermissionEvaluator.hasPermission(authentication, it, Permission.READ) - } - - /** - * @param organizationName - * @param authentication - * @return projects by organizationName and [CREATED] status - */ - fun getProjectsByOrganizationNameAndCreatedStatus(organizationName: String, authentication: Authentication?) = - getProjectsByOrganizationNameAndStatusIn(organizationName, authentication, EnumSet.of(ProjectStatus.CREATED)) - - /** - * @param projectFilter is filter for [projects] - * @return project's with filter - */ - fun getFiltered(projectFilter: ProjectFilter): List = projectRepository.findAll { root, _, cb -> - val publicPredicate = projectFilter.public?.let { cb.equal(root.get("public"), it) } ?: cb.and() - val orgNamePredicate = if (projectFilter.organizationName.isBlank()) { - cb.and() - } else { - cb.equal(root.get("organization").get("name"), projectFilter.organizationName) - } - val namePredicate = if (projectFilter.name.isBlank()) { - cb.and() - } else { - cb.equal(root.get("name"), projectFilter.name) - } - - cb.and( - root.get("status").`in`(projectFilter.statuses), - publicPredicate, - orgNamePredicate, - namePredicate, - ) - } - - /** - * @param value is a string for a wrapper to search by match on a string in the database - * @return string by match on a string in the database - */ - private fun wrapValue(value: String) = value.let { - "%$value%" - } - - /** - * @param authentication [Authentication] of the user who wants to access the project - * @param projectName name of the project - * @param organizationName organization that owns the project - * @param permission requested [Permission] - * @param messageIfNotFound if project is not found, include this into 404 response body - * @param statusIfForbidden return this status if permission is not allowed fot the current user - * @return `Mono` with project; `Mono.error` if project cannot be accessed by the current user. - */ - @Transactional(readOnly = true) - @Suppress("LongParameterList", "TOO_MANY_PARAMETERS") - fun findWithPermissionByNameAndOrganization( - authentication: Authentication, - projectName: String, - organizationName: String, - permission: Permission, - messageIfNotFound: String? = null, - statusIfForbidden: HttpStatus = HttpStatus.FORBIDDEN, - ): Mono = with(projectPermissionEvaluator) { - Mono.fromCallable { findByNameAndOrganizationNameAndCreatedStatus(projectName, organizationName) } - .switchIfEmpty { - Mono.error(ResponseStatusException(HttpStatus.NOT_FOUND, messageIfNotFound)) - } - .filterByPermission(authentication, permission, statusIfForbidden) - } - - /** - * @param userName - * @return optional of user - */ - fun findUserByName(userName: String): User? = userRepository.findByName(userName) - - /** - * @param id - * @return [Project] with given [id] - */ - fun findById(id: Long): Optional = projectRepository.findById(id) - - /** - * @param name - * @param organizationName - * @param lazyMessage - * @return [Mono] of [Project] or [Mono.error] if [Project] is not found - */ - fun projectByCoordinatesOrNotFound( - name: String, - organizationName: String, - lazyMessage: () -> String, - ): Mono = blockingToMono { - findByNameAndOrganizationNameAndCreatedStatus(name, organizationName) - } - .switchIfEmptyToNotFound(lazyMessage) -} diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/TestAnalysisService.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/TestAnalysisService.kt index 8185fcaa6d..a4227aeb52 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/TestAnalysisService.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/TestAnalysisService.kt @@ -8,6 +8,8 @@ import com.saveourtool.save.backend.repository.TestExecutionRepository import com.saveourtool.save.entities.Execution import com.saveourtool.save.entities.Project import com.saveourtool.save.entities.TestExecution +import com.saveourtool.save.service.OrganizationService +import com.saveourtool.save.service.ProjectService import com.saveourtool.save.test.analysis.api.TestId import com.saveourtool.save.test.analysis.api.TestIdGenerator import com.saveourtool.save.test.analysis.api.TestRun diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/TestSuitesSourceService.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/TestSuitesSourceService.kt index 65f27244a4..497f3f9228 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/TestSuitesSourceService.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/TestSuitesSourceService.kt @@ -7,6 +7,7 @@ import com.saveourtool.save.entities.Git import com.saveourtool.save.entities.Organization import com.saveourtool.save.entities.TestSuitesSource import com.saveourtool.save.request.TestsSourceFetchRequest +import com.saveourtool.save.service.OrganizationService import com.saveourtool.save.testsuite.TestSuitesSourceDto import com.saveourtool.save.testsuite.TestSuitesSourceFetchMode import com.saveourtool.save.utils.* diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/storage/AvatarStorage.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/storage/AvatarStorage.kt deleted file mode 100644 index a077d31558..0000000000 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/storage/AvatarStorage.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.saveourtool.save.backend.storage - -import com.saveourtool.save.backend.configs.ConfigProperties -import com.saveourtool.save.s3.S3Operations -import com.saveourtool.save.storage.AbstractSimpleReactiveStorage -import com.saveourtool.save.storage.AvatarKey -import com.saveourtool.save.storage.concatS3Key -import com.saveourtool.save.storage.s3KeyToParts -import com.saveourtool.save.utils.AvatarType -import com.saveourtool.save.utils.orNotFound -import org.springframework.stereotype.Service - -/** - * Storage for Avatars - * Currently, key is (AvatarType, ObjectName) - */ -@Service -class AvatarStorage( - configProperties: ConfigProperties, - s3Operations: S3Operations, -) : AbstractSimpleReactiveStorage( - s3Operations, - concatS3Key(configProperties.s3Storage.prefix, "images", "avatars") -) { - override fun doBuildKeyFromSuffix(s3KeySuffix: String): AvatarKey { - val (typeStr, objectName) = s3KeySuffix.s3KeyToParts() - return AvatarKey( - type = AvatarType.findByUrlPath(typeStr) - .orNotFound { - "Not supported type for path: $typeStr" - }, - objectName = objectName, - ) - } - - override fun doBuildS3KeySuffix(key: AvatarKey): String = concatS3Key(key.type.urlPath, key.objectName) -} diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/storage/FileS3KeyManager.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/storage/FileS3KeyManager.kt index ebf9a5a84c..ba83dcb55e 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/storage/FileS3KeyManager.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/storage/FileS3KeyManager.kt @@ -3,11 +3,11 @@ package com.saveourtool.save.backend.storage import com.saveourtool.save.backend.configs.ConfigProperties import com.saveourtool.save.backend.repository.FileRepository import com.saveourtool.save.backend.service.ExecutionService -import com.saveourtool.save.backend.service.ProjectService import com.saveourtool.save.entities.File import com.saveourtool.save.entities.FileDto import com.saveourtool.save.entities.Project import com.saveourtool.save.entities.toEntity +import com.saveourtool.save.service.ProjectService import com.saveourtool.save.storage.concatS3Key import com.saveourtool.save.storage.key.AbstractS3KeyDtoManager import com.saveourtool.save.utils.BlockingBridge diff --git a/save-backend/src/test/kotlin/com/saveourtool/save/backend/DatabaseTest.kt b/save-backend/src/test/kotlin/com/saveourtool/save/backend/DatabaseTest.kt index 90ce4ed752..1587b2366c 100644 --- a/save-backend/src/test/kotlin/com/saveourtool/save/backend/DatabaseTest.kt +++ b/save-backend/src/test/kotlin/com/saveourtool/save/backend/DatabaseTest.kt @@ -3,10 +3,10 @@ package com.saveourtool.save.backend import com.saveourtool.save.agent.AgentState import com.saveourtool.save.backend.configs.ApplicationConfiguration import com.saveourtool.save.backend.repository.AgentStatusRepository -import com.saveourtool.save.backend.repository.ProjectRepository import com.saveourtool.save.backend.repository.TestExecutionRepository import com.saveourtool.save.backend.utils.InfraExtension import com.saveourtool.save.domain.TestResultStatus +import com.saveourtool.save.repository.ProjectRepository import com.saveourtool.save.utils.BlockingBridge import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test diff --git a/save-backend/src/test/kotlin/com/saveourtool/save/backend/DownloadFilesTest.kt b/save-backend/src/test/kotlin/com/saveourtool/save/backend/DownloadFilesTest.kt index ca7986db44..2d9a68a113 100644 --- a/save-backend/src/test/kotlin/com/saveourtool/save/backend/DownloadFilesTest.kt +++ b/save-backend/src/test/kotlin/com/saveourtool/save/backend/DownloadFilesTest.kt @@ -6,7 +6,6 @@ import com.saveourtool.save.backend.configs.WebConfig import com.saveourtool.save.backend.controllers.DownloadFilesController import com.saveourtool.save.backend.controllers.FileController import com.saveourtool.save.backend.controllers.internal.FileInternalController -import com.saveourtool.save.backend.security.ProjectPermissionEvaluator import com.saveourtool.save.backend.service.* import com.saveourtool.save.backend.storage.* import com.saveourtool.save.backend.utils.mutateMockedUser @@ -15,6 +14,10 @@ import com.saveourtool.save.core.result.Pass import com.saveourtool.save.domain.* import com.saveourtool.save.entities.* import com.saveourtool.save.permission.Permission +import com.saveourtool.save.security.ProjectPermissionEvaluator +import com.saveourtool.save.service.OrganizationService +import com.saveourtool.save.service.ProjectService +import com.saveourtool.save.service.UserService import com.saveourtool.save.utils.BlockingBridge import com.saveourtool.save.utils.CONTENT_LENGTH_CUSTOM import com.saveourtool.save.utils.collectToInputStream @@ -63,7 +66,7 @@ import kotlin.io.path.* @EnableConfigurationProperties(ConfigProperties::class) @MockBeans( MockBean(OrganizationService::class), - MockBean(UserDetailsService::class), + MockBean(UserService::class), MockBean(ExecutionService::class), MockBean(AgentService::class), MockBean(ProjectPermissionEvaluator::class), diff --git a/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/AgentsControllerTest.kt b/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/AgentsControllerTest.kt index 7ac00a81c4..b3b62801e1 100644 --- a/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/AgentsControllerTest.kt +++ b/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/AgentsControllerTest.kt @@ -5,11 +5,11 @@ import com.saveourtool.save.backend.SaveApplication import com.saveourtool.save.backend.controllers.ProjectController import com.saveourtool.save.backend.repository.AgentRepository import com.saveourtool.save.backend.repository.AgentStatusRepository -import com.saveourtool.save.backend.security.ProjectPermissionEvaluator import com.saveourtool.save.backend.utils.InfraExtension import com.saveourtool.save.entities.AgentStatus import com.saveourtool.save.entities.AgentStatusDto import com.saveourtool.save.entities.AgentStatusDtoList +import com.saveourtool.save.security.ProjectPermissionEvaluator import kotlinx.datetime.LocalDateTime import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Assertions.assertTrue diff --git a/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/DeleteEntitiesTest.kt b/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/DeleteEntitiesTest.kt index d6e130335d..f3a0651b96 100644 --- a/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/DeleteEntitiesTest.kt +++ b/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/DeleteEntitiesTest.kt @@ -4,9 +4,7 @@ import com.saveourtool.save.backend.SaveApplication import com.saveourtool.save.backend.repository.AgentRepository import com.saveourtool.save.backend.repository.AgentStatusRepository import com.saveourtool.save.backend.repository.ExecutionRepository -import com.saveourtool.save.backend.repository.ProjectRepository import com.saveourtool.save.backend.repository.TestExecutionRepository -import com.saveourtool.save.backend.security.ProjectPermissionEvaluator import com.saveourtool.save.backend.utils.InfraExtension import com.saveourtool.save.backend.utils.postJsonAndAssert import com.saveourtool.save.entities.Execution @@ -14,6 +12,8 @@ import com.saveourtool.save.entities.Organization import com.saveourtool.save.entities.OrganizationStatus import com.saveourtool.save.entities.Project import com.saveourtool.save.repository.OrganizationRepository +import com.saveourtool.save.repository.ProjectRepository +import com.saveourtool.save.security.ProjectPermissionEvaluator import com.saveourtool.save.utils.DATABASE_DELIMITER import com.saveourtool.save.v1 import org.junit.jupiter.api.BeforeEach diff --git a/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/ExecutionControllerTest.kt b/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/ExecutionControllerTest.kt index 7cdf30ebda..a776b39b12 100644 --- a/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/ExecutionControllerTest.kt +++ b/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/ExecutionControllerTest.kt @@ -10,6 +10,7 @@ import com.saveourtool.save.execution.ExecutionDto import com.saveourtool.save.execution.ExecutionStatus import com.saveourtool.save.execution.ExecutionUpdateDto import com.saveourtool.save.execution.TestingType +import com.saveourtool.save.repository.ProjectRepository import com.saveourtool.save.utils.debug import com.saveourtool.save.utils.getLogger import com.saveourtool.save.v1 diff --git a/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/LnkUserOrganizationControllerTest.kt b/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/LnkUserOrganizationControllerTest.kt index a89aaf9cab..4b09a92237 100644 --- a/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/LnkUserOrganizationControllerTest.kt +++ b/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/LnkUserOrganizationControllerTest.kt @@ -2,7 +2,6 @@ package com.saveourtool.save.backend.controller import com.saveourtool.save.authservice.config.WebSecurityConfig import com.saveourtool.save.backend.controllers.LnkUserOrganizationController -import com.saveourtool.save.backend.security.OrganizationPermissionEvaluator import com.saveourtool.save.backend.service.* import com.saveourtool.save.backend.utils.mutateMockedUser import com.saveourtool.save.domain.Role @@ -11,6 +10,9 @@ import com.saveourtool.save.permission.Permission import com.saveourtool.save.permission.SetRoleRequest import com.saveourtool.save.repository.OriginalLoginRepository import com.saveourtool.save.repository.UserRepository +import com.saveourtool.save.security.OrganizationPermissionEvaluator +import com.saveourtool.save.service.LnkUserOrganizationService +import com.saveourtool.save.service.OrganizationService import com.saveourtool.save.utils.BlockingBridge import com.saveourtool.save.v1 import org.junit.jupiter.api.Test diff --git a/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/OrganizationControllerTest.kt b/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/OrganizationControllerTest.kt index cba3b6edf2..90b90c4e88 100644 --- a/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/OrganizationControllerTest.kt +++ b/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/OrganizationControllerTest.kt @@ -4,16 +4,17 @@ import com.saveourtool.save.authservice.config.NoopWebSecurityConfig import com.saveourtool.save.backend.configs.WebConfig import com.saveourtool.save.backend.controllers.OrganizationController import com.saveourtool.save.backend.repository.* -import com.saveourtool.save.backend.security.OrganizationPermissionEvaluator -import com.saveourtool.save.backend.security.ProjectPermissionEvaluator import com.saveourtool.save.backend.service.* import com.saveourtool.save.backend.S11nTestConfig -import com.saveourtool.save.backend.storage.AvatarStorage import com.saveourtool.save.backend.storage.TestsSourceSnapshotStorage import com.saveourtool.save.backend.utils.mutateMockedUser import com.saveourtool.save.domain.Role import com.saveourtool.save.entities.* import com.saveourtool.save.repository.* +import com.saveourtool.save.security.OrganizationPermissionEvaluator +import com.saveourtool.save.security.ProjectPermissionEvaluator +import com.saveourtool.save.service.* +import com.saveourtool.save.storage.AvatarStorage import com.saveourtool.save.testutils.checkQueues import com.saveourtool.save.testutils.cleanup import com.saveourtool.save.testutils.createMockWebServer @@ -62,7 +63,7 @@ import java.util.concurrent.TimeUnit WebConfig::class, ProjectPermissionEvaluator::class, LnkUserProjectService::class, - UserDetailsService::class, + UserService::class, S11nTestConfig::class, ) @MockBeans( diff --git a/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/PermissionControllerTest.kt b/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/PermissionControllerTest.kt index cadef3bd70..e35e1291c2 100644 --- a/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/PermissionControllerTest.kt +++ b/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/PermissionControllerTest.kt @@ -2,8 +2,6 @@ package com.saveourtool.save.backend.controller import com.saveourtool.save.authservice.config.WebSecurityConfig import com.saveourtool.save.backend.controllers.PermissionController -import com.saveourtool.save.backend.security.OrganizationPermissionEvaluator -import com.saveourtool.save.backend.security.ProjectPermissionEvaluator import com.saveourtool.save.backend.service.* import com.saveourtool.save.backend.utils.mutateMockedUser import com.saveourtool.save.domain.Role @@ -13,6 +11,12 @@ import com.saveourtool.save.permission.SetRoleRequest import com.saveourtool.save.repository.OrganizationRepository import com.saveourtool.save.repository.OriginalLoginRepository import com.saveourtool.save.repository.UserRepository +import com.saveourtool.save.security.OrganizationPermissionEvaluator +import com.saveourtool.save.security.ProjectPermissionEvaluator +import com.saveourtool.save.service.LnkUserOrganizationService +import com.saveourtool.save.service.LnkUserProjectService +import com.saveourtool.save.service.OrganizationService +import com.saveourtool.save.service.ProjectService import com.saveourtool.save.utils.BlockingBridge import com.saveourtool.save.v1 import org.junit.jupiter.api.Test diff --git a/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/ProjectControllerTest.kt b/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/ProjectControllerTest.kt index 75695ee9fd..a20442c89c 100644 --- a/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/ProjectControllerTest.kt +++ b/save-backend/src/test/kotlin/com/saveourtool/save/backend/controller/ProjectControllerTest.kt @@ -1,18 +1,21 @@ package com.saveourtool.save.backend.controller import com.saveourtool.save.backend.SaveApplication -import com.saveourtool.save.backend.repository.ProjectRepository -import com.saveourtool.save.backend.service.LnkUserProjectService import com.saveourtool.save.backend.utils.InfraExtension import com.saveourtool.save.backend.utils.mutateMockedUser import com.saveourtool.save.entities.* import com.saveourtool.save.filters.ProjectFilter import com.saveourtool.save.repository.OrganizationRepository +import com.saveourtool.save.repository.ProjectRepository +import com.saveourtool.save.service.LnkUserProjectService +import com.saveourtool.save.service.UserService import com.saveourtool.save.v1 import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.kotlin.any +import org.mockito.kotlin.given import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient import org.springframework.boot.test.context.SpringBootTest @@ -41,10 +44,12 @@ class ProjectControllerTest { @Autowired lateinit var webClient: WebTestClient + @MockBean private lateinit var userDetailsService: UserService + @Test @WithMockUser fun `should return all public projects`() { - mutateMockedUser(id = 99) + given(userDetailsService.getUserByName(any())).willReturn(mockUser(99)) webClient .post() @@ -81,8 +86,7 @@ class ProjectControllerTest { @Test @WithMockUser(username = "MrBruh", roles = ["VIEWER"]) fun `should return 200 if project is public`() { - mutateMockedUser(id = 99) - + given(userDetailsService.getUserByName(any())).willReturn(mockUser(99)) getProjectAndAssert("huaweiName", "Huawei") { expectStatus().isOk } @@ -91,7 +95,7 @@ class ProjectControllerTest { @Test @WithMockUser(username = "MrBruh", roles = ["VIEWER"]) fun `should return 404 if user doesn't have access to a private project`() { - mutateMockedUser(id = 99) + given(userDetailsService.getUserByName(any())).willReturn(mockUser(99)) getProjectAndAssert("TheProject", "Example.com") { expectStatus().isNotFound @@ -155,7 +159,7 @@ class ProjectControllerTest { @Test @WithMockUser(value = "JohnDoe", roles = ["VIEWER"]) fun `delete project without owner permission`() { - mutateMockedUser(id = 3) + given(userDetailsService.getUserByName(any())).willReturn(mockUser(3)) val organization: Organization = organizationRepository.getOrganizationById(2) val project = Project( "ToDelete1", @@ -182,6 +186,7 @@ class ProjectControllerTest { @Test @WithMockUser(username = "JohnDoe", roles = ["VIEWER"]) fun `check save new project`() { + given(userDetailsService.getUserByName(any())).willReturn(mockUser(2)) mutateMockedUser(id = 2) // `project` references an existing user from test data @@ -214,7 +219,7 @@ class ProjectControllerTest { organization = organizationRepository.findById(1).get() } projectRepository.save(project) - mutateMockedUser(id = 3) + given(userDetailsService.getUserByName(any())).willReturn(mockUser(3)) webClient.post() .uri("/api/$v1/projects/update") @@ -255,4 +260,6 @@ class ProjectControllerTest { .exchange() .let { getAssertion(it) } } + + private fun mockUser(id: Long) = User("mocked", null, null, "").apply { this.id = id } } diff --git a/save-backend/src/test/kotlin/com/saveourtool/save/backend/controllers/RunExecutionControllerTest.kt b/save-backend/src/test/kotlin/com/saveourtool/save/backend/controllers/RunExecutionControllerTest.kt index 3f33281caa..b80f9789e0 100644 --- a/save-backend/src/test/kotlin/com/saveourtool/save/backend/controllers/RunExecutionControllerTest.kt +++ b/save-backend/src/test/kotlin/com/saveourtool/save/backend/controllers/RunExecutionControllerTest.kt @@ -8,6 +8,7 @@ import com.saveourtool.save.backend.utils.mutateMockedUser import com.saveourtool.save.domain.Jdk import com.saveourtool.save.request.CreateExecutionRequest import com.saveourtool.save.execution.TestingType +import com.saveourtool.save.repository.ProjectRepository import com.saveourtool.save.testutils.checkQueues import com.saveourtool.save.testutils.cleanup import com.saveourtool.save.testutils.createMockWebServer diff --git a/save-backend/src/test/kotlin/com/saveourtool/save/backend/security/OrganizationPermissionEvaluatorTest.kt b/save-backend/src/test/kotlin/com/saveourtool/save/backend/security/OrganizationPermissionEvaluatorTest.kt index 3bd6e47d70..6bd1a4f302 100644 --- a/save-backend/src/test/kotlin/com/saveourtool/save/backend/security/OrganizationPermissionEvaluatorTest.kt +++ b/save-backend/src/test/kotlin/com/saveourtool/save/backend/security/OrganizationPermissionEvaluatorTest.kt @@ -1,7 +1,5 @@ package com.saveourtool.save.backend.security -import com.saveourtool.save.backend.service.LnkUserOrganizationService -import com.saveourtool.save.backend.service.UserDetailsService import com.saveourtool.save.authservice.utils.SaveUserDetails import com.saveourtool.save.domain.Role import com.saveourtool.save.entities.* @@ -9,6 +7,9 @@ import com.saveourtool.save.info.UserStatus import com.saveourtool.save.permission.Permission import com.saveourtool.save.repository.LnkUserOrganizationRepository import com.saveourtool.save.repository.UserRepository +import com.saveourtool.save.security.OrganizationPermissionEvaluator +import com.saveourtool.save.service.LnkUserOrganizationService +import com.saveourtool.save.service.UserService import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach @@ -32,7 +33,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension class OrganizationPermissionEvaluatorTest { @Autowired private lateinit var organizationPermissionEvaluator: OrganizationPermissionEvaluator @MockBean private lateinit var lnkUserOrganizationRepository: LnkUserOrganizationRepository - @MockBean private lateinit var userDetailsService: UserDetailsService + @MockBean private lateinit var userDetailsService: UserService private lateinit var mockOrganization: Organization private val ownerPermissions = Permission.values().filterNot { it == Permission.BAN }.toTypedArray() @@ -99,6 +100,7 @@ class OrganizationPermissionEvaluatorTest { ) { val authentication = mockAuth(username, role.asSpringSecurityRole(), id = userId) given(userDetailsService.getGlobalRole(any())).willReturn(Role.VIEWER) + given(userDetailsService.getUserByName(any())).willReturn(mockUser(userId)) whenever(lnkUserOrganizationRepository.findByUserIdAndOrganization(any(), any())).thenAnswer { invocation -> LnkUserOrganization( invocation.arguments[1] as Organization, diff --git a/save-backend/src/test/kotlin/com/saveourtool/save/backend/security/ProjectPermissionEvaluatorTest.kt b/save-backend/src/test/kotlin/com/saveourtool/save/backend/security/ProjectPermissionEvaluatorTest.kt index 7f41940cde..9aaae87189 100644 --- a/save-backend/src/test/kotlin/com/saveourtool/save/backend/security/ProjectPermissionEvaluatorTest.kt +++ b/save-backend/src/test/kotlin/com/saveourtool/save/backend/security/ProjectPermissionEvaluatorTest.kt @@ -1,17 +1,18 @@ package com.saveourtool.save.backend.security -import com.saveourtool.save.backend.repository.LnkUserProjectRepository -import com.saveourtool.save.backend.service.LnkUserProjectService -import com.saveourtool.save.backend.service.UserDetailsService import com.saveourtool.save.authservice.utils.SaveUserDetails -import com.saveourtool.save.backend.service.LnkUserOrganizationService import com.saveourtool.save.domain.Role import com.saveourtool.save.entities.LnkUserProject import com.saveourtool.save.entities.Project import com.saveourtool.save.entities.User import com.saveourtool.save.info.UserStatus import com.saveourtool.save.permission.Permission +import com.saveourtool.save.repository.LnkUserProjectRepository import com.saveourtool.save.repository.UserRepository +import com.saveourtool.save.security.ProjectPermissionEvaluator +import com.saveourtool.save.service.LnkUserOrganizationService +import com.saveourtool.save.service.LnkUserProjectService +import com.saveourtool.save.service.UserService import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach @@ -36,7 +37,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension class ProjectPermissionEvaluatorTest { @Autowired private lateinit var projectPermissionEvaluator: ProjectPermissionEvaluator @MockBean private lateinit var lnkUserProjectRepository: LnkUserProjectRepository - @MockBean private lateinit var userDetailsService: UserDetailsService + @MockBean private lateinit var userDetailsService: UserService private lateinit var mockProject: Project private val ownerPermissions = Permission.values().filterNot { it == Permission.BAN }.toTypedArray() @@ -136,6 +137,7 @@ class ProjectPermissionEvaluatorTest { ) { val authentication = mockAuth(username, role.asSpringSecurityRole(), id = userId) given(userDetailsService.getGlobalRole(any())).willReturn(Role.VIEWER) + given(userDetailsService.getUserByName(any())).willReturn(mockUser(userId)) whenever(lnkUserProjectRepository.findByUserIdAndProject(any(), any())).thenAnswer { invocation -> LnkUserProject( invocation.arguments[1] as Project, diff --git a/save-backend/src/test/kotlin/com/saveourtool/save/backend/service/LnkContestProjectServiceTest.kt b/save-backend/src/test/kotlin/com/saveourtool/save/backend/service/LnkContestProjectServiceTest.kt index 315b087c2e..471bf6a91b 100644 --- a/save-backend/src/test/kotlin/com/saveourtool/save/backend/service/LnkContestProjectServiceTest.kt +++ b/save-backend/src/test/kotlin/com/saveourtool/save/backend/service/LnkContestProjectServiceTest.kt @@ -5,6 +5,7 @@ import com.saveourtool.save.entities.Contest import com.saveourtool.save.entities.Execution import com.saveourtool.save.entities.LnkContestProject import com.saveourtool.save.entities.Project +import com.saveourtool.save.service.ProjectService import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.mockito.kotlin.* diff --git a/save-backend/src/test/kotlin/com/saveourtool/save/backend/service/PermissionServiceTest.kt b/save-backend/src/test/kotlin/com/saveourtool/save/backend/service/PermissionServiceTest.kt index 7f8c5d2739..b4a579b3bc 100644 --- a/save-backend/src/test/kotlin/com/saveourtool/save/backend/service/PermissionServiceTest.kt +++ b/save-backend/src/test/kotlin/com/saveourtool/save/backend/service/PermissionServiceTest.kt @@ -5,6 +5,8 @@ import com.saveourtool.save.entities.Project import com.saveourtool.save.entities.User import com.saveourtool.save.permission.SetRoleRequest import com.saveourtool.save.repository.UserRepository +import com.saveourtool.save.service.LnkUserProjectService +import com.saveourtool.save.service.ProjectService import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith diff --git a/save-cloud-common/build.gradle.kts b/save-cloud-common/build.gradle.kts index fd14c18f4a..5aa7db2d44 100644 --- a/save-cloud-common/build.gradle.kts +++ b/save-cloud-common/build.gradle.kts @@ -70,6 +70,7 @@ kotlin { val jvmMain by getting { dependencies { implementation(project.dependencies.platform(libs.spring.boot.dependencies)) + implementation(libs.spring.security.core) implementation(libs.spring.web) implementation(libs.spring.webflux) implementation(libs.spring.boot) diff --git a/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/repository/LnkUserOrganizationRepository.kt b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/repository/LnkUserOrganizationRepository.kt index 42f3b116a1..d9126b6a6a 100644 --- a/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/repository/LnkUserOrganizationRepository.kt +++ b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/repository/LnkUserOrganizationRepository.kt @@ -25,10 +25,17 @@ interface LnkUserOrganizationRepository : BaseEntityRepository { */ fun findByUserIdAndProject(userId: Long, project: Project): LnkUserProject? + /** + * @param userName + * @param project + * @return [LnkUserProject] by [userName] and [Project] + */ + fun findByUserNameAndProject(userName: String, project: Project): LnkUserProject? + /** * @param userId * @param projectId diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/repository/ProjectRepository.kt b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/repository/ProjectRepository.kt similarity index 98% rename from save-backend/src/main/kotlin/com/saveourtool/save/backend/repository/ProjectRepository.kt rename to save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/repository/ProjectRepository.kt index d3972ec00e..cd708aa344 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/repository/ProjectRepository.kt +++ b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/repository/ProjectRepository.kt @@ -1,4 +1,4 @@ -package com.saveourtool.save.backend.repository +package com.saveourtool.save.repository import com.saveourtool.save.entities.Organization import com.saveourtool.save.entities.Project diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/security/CommentPermissionEvaluator.kt b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/security/CommentPermissionEvaluator.kt similarity index 92% rename from save-cosv/src/main/kotlin/com/saveourtool/save/cosv/security/CommentPermissionEvaluator.kt rename to save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/security/CommentPermissionEvaluator.kt index 778eb36526..a676b9c179 100644 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/security/CommentPermissionEvaluator.kt +++ b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/security/CommentPermissionEvaluator.kt @@ -1,10 +1,11 @@ -package com.saveourtool.save.cosv.security +package com.saveourtool.save.security -import com.saveourtool.save.cosv.utils.hasRole import com.saveourtool.save.domain.Role import com.saveourtool.save.entities.Comment import com.saveourtool.save.entities.CommentDto import com.saveourtool.save.permission.Permission +import com.saveourtool.save.utils.hasRole + import org.springframework.security.core.Authentication import org.springframework.stereotype.Component diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/security/OrganizationPermissionEvaluator.kt b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/security/OrganizationPermissionEvaluator.kt similarity index 90% rename from save-backend/src/main/kotlin/com/saveourtool/save/backend/security/OrganizationPermissionEvaluator.kt rename to save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/security/OrganizationPermissionEvaluator.kt index dd5a937af5..cf728827e5 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/security/OrganizationPermissionEvaluator.kt +++ b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/security/OrganizationPermissionEvaluator.kt @@ -1,13 +1,14 @@ -package com.saveourtool.save.backend.security +package com.saveourtool.save.security -import com.saveourtool.save.authservice.utils.userId -import com.saveourtool.save.backend.service.LnkUserOrganizationService -import com.saveourtool.save.backend.utils.hasRole import com.saveourtool.save.domain.Role import com.saveourtool.save.entities.Organization import com.saveourtool.save.entities.OrganizationStatus import com.saveourtool.save.entities.User import com.saveourtool.save.permission.Permission +import com.saveourtool.save.service.LnkUserOrganizationService +import com.saveourtool.save.service.UserService +import com.saveourtool.save.utils.hasRole +import com.saveourtool.save.utils.username import org.springframework.security.core.Authentication import org.springframework.stereotype.Component @@ -17,7 +18,8 @@ import org.springframework.stereotype.Component */ @Component class OrganizationPermissionEvaluator( - private var lnkUserOrganizationService: LnkUserOrganizationService + private var lnkUserOrganizationService: LnkUserOrganizationService, + private var userDetailsService: UserService, ) { /** * @param authentication @@ -27,11 +29,11 @@ class OrganizationPermissionEvaluator( */ fun hasOrganizationRole(authentication: Authentication?, organization: Organization, role: Role): Boolean { authentication ?: return false - val userId = authentication.userId() + val userName = authentication.username() if (authentication.hasRole(Role.SUPER_ADMIN)) { return true } - return lnkUserOrganizationService.findRoleByUserIdAndOrganization(userId, organization).isHigherOrEqualThan(role) + return lnkUserOrganizationService.findRoleByUserNameAndOrganization(userName, organization).isHigherOrEqualThan(role) } /** @@ -59,7 +61,9 @@ class OrganizationPermissionEvaluator( */ fun hasPermission(authentication: Authentication?, organization: Organization, permission: Permission): Boolean { authentication ?: return permission == Permission.READ - val userId = authentication.userId() + val userName = authentication.username() + val user = userDetailsService.getUserByName(userName) + val userId = user.requiredId() if (authentication.hasRole(Role.SUPER_ADMIN)) { return true } @@ -116,7 +120,7 @@ class OrganizationPermissionEvaluator( requestedRole: Role = Role.NONE ): Boolean { val selfRole = lnkUserOrganizationService.getGlobalRoleOrOrganizationRole(authentication, organization) - val otherRole = lnkUserOrganizationService.findRoleByUserIdAndOrganization(otherUser.id!!, organization) + val otherRole = lnkUserOrganizationService.findRoleByUserNameAndOrganization(otherUser.name, organization) return selfRole.isHigherOrEqualThan(Role.OWNER) || selfRole.isHigherOrEqualThan(Role.ADMIN) && hasAnotherUserLessPermissions(selfRole, otherRole) && isRequestedPermissionsCanBeSetByUser(selfRole, requestedRole) } @@ -145,6 +149,7 @@ class OrganizationPermissionEvaluator( * @return true if [selfRole] is higher than [requestedRole], false otherwise */ fun isRequestedPermissionsCanBeSetByUser(selfRole: Role, requestedRole: Role): Boolean = selfRole.priority > requestedRole.priority + companion object { val contestCreatorMinimalRole = Role.ADMIN } diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/security/ProjectPermissionEvaluator.kt b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/security/ProjectPermissionEvaluator.kt similarity index 91% rename from save-cosv/src/main/kotlin/com/saveourtool/save/cosv/security/ProjectPermissionEvaluator.kt rename to save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/security/ProjectPermissionEvaluator.kt index f37d051043..444ad9ec13 100644 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/security/ProjectPermissionEvaluator.kt +++ b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/security/ProjectPermissionEvaluator.kt @@ -1,18 +1,19 @@ -package com.saveourtool.save.cosv.security +package com.saveourtool.save.security -import com.saveourtool.save.authservice.utils.userId - -import com.saveourtool.save.cosv.repositorysave.LnkUserProjectRepository -import com.saveourtool.save.cosv.service.LnkUserOrganizationService -import com.saveourtool.save.cosv.service.LnkUserProjectService -import com.saveourtool.save.cosv.utils.hasRole import com.saveourtool.save.domain.Role import com.saveourtool.save.entities.Execution import com.saveourtool.save.entities.Project import com.saveourtool.save.entities.ProjectStatus import com.saveourtool.save.entities.User import com.saveourtool.save.permission.Permission +import com.saveourtool.save.repository.LnkUserProjectRepository +import com.saveourtool.save.service.LnkUserOrganizationService +import com.saveourtool.save.service.LnkUserProjectService +import com.saveourtool.save.service.UserService import com.saveourtool.save.utils.getHighestRole +import com.saveourtool.save.utils.hasRole +import com.saveourtool.save.utils.username + import org.springframework.http.HttpStatus import org.springframework.security.core.Authentication import org.springframework.stereotype.Component @@ -28,7 +29,8 @@ import reactor.kotlin.core.publisher.switchIfEmpty class ProjectPermissionEvaluator( private var lnkUserProjectService: LnkUserProjectService, private var lnkUserProjectRepository: LnkUserProjectRepository, - private var lnkUserOrganizationService: LnkUserOrganizationService + private var lnkUserOrganizationService: LnkUserOrganizationService, + private var userDetailsService: UserService, ) { /** * @param authentication [Authentication] describing an authenticated request @@ -62,7 +64,9 @@ class ProjectPermissionEvaluator( return true } - val userId = authentication.userId() + val userName = authentication.username() + val user = userDetailsService.getUserByName(userName) + val userId = user.requiredId() val organizationRole = lnkUserOrganizationService.findRoleByUserIdAndOrganization(userId, project.organization) val projectRole = lnkUserProjectService.findRoleByUserIdAndProject(userId, project) @@ -80,7 +84,7 @@ class ProjectPermissionEvaluator( * @param statusIfForbidden * @return a [Mono] containing the project or `Mono.error` if project can't or shouldn't be accessed by the current user */ - internal fun Mono.filterByPermission( + fun Mono.filterByPermission( authentication: Authentication?, permission: Permission, statusIfForbidden: HttpStatus, @@ -128,7 +132,7 @@ class ProjectPermissionEvaluator( * @param permission * @return [Mono] containing `true` if the current user is granted [permission] on the project for this [execution] or Mono with `false` otherwise */ - internal fun checkPermissions(authentication: Authentication, execution: Execution, permission: Permission): Mono = + fun checkPermissions(authentication: Authentication, execution: Execution, permission: Permission): Mono = Mono.justOrEmpty(execution.project) .filterByPermission(authentication, permission, HttpStatus.FORBIDDEN) .map { true } diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/CommentService.kt b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/service/CommentService.kt similarity index 87% rename from save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/CommentService.kt rename to save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/service/CommentService.kt index d1d0209da3..be76f29475 100644 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/CommentService.kt +++ b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/service/CommentService.kt @@ -1,12 +1,12 @@ -package com.saveourtool.save.cosv.service +package com.saveourtool.save.service -import com.saveourtool.save.authservice.utils.userId import com.saveourtool.save.entities.Comment import com.saveourtool.save.entities.CommentDto import com.saveourtool.save.evententities.CommentEvent import com.saveourtool.save.repository.CommentRepository import com.saveourtool.save.repository.UserRepository -import com.saveourtool.save.utils.getByIdOrNotFound +import com.saveourtool.save.utils.orNotFound +import com.saveourtool.save.utils.username import org.springframework.context.ApplicationEventPublisher import org.springframework.security.core.Authentication @@ -31,8 +31,8 @@ class CommentService( */ @Transactional fun saveComment(comment: CommentDto, authentication: Authentication) { - val userId = authentication.userId() - val user = userRepository.getByIdOrNotFound(userId) + val userName = authentication.username() + val user = userRepository.findByName(userName).orNotFound { "User with name [$userName] was not found." } val newComment = Comment( comment.message, diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/GitService.kt b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/service/GitService.kt similarity index 97% rename from save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/GitService.kt rename to save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/service/GitService.kt index 8d1a062d36..545bb09b37 100644 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/GitService.kt +++ b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/service/GitService.kt @@ -1,4 +1,4 @@ -package com.saveourtool.save.cosv.service +package com.saveourtool.save.service import com.saveourtool.save.entities.Git import com.saveourtool.save.entities.GitDto diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/LnkUserOrganizationService.kt b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/service/LnkUserOrganizationService.kt similarity index 90% rename from save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/LnkUserOrganizationService.kt rename to save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/service/LnkUserOrganizationService.kt index 0c582cd7ac..daf3b67fdc 100644 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/LnkUserOrganizationService.kt +++ b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/service/LnkUserOrganizationService.kt @@ -1,6 +1,5 @@ -package com.saveourtool.save.cosv.service +package com.saveourtool.save.service -import com.saveourtool.save.authservice.utils.userId import com.saveourtool.save.domain.Role import com.saveourtool.save.entities.* import com.saveourtool.save.filters.OrganizationFilter @@ -8,6 +7,7 @@ import com.saveourtool.save.repository.LnkUserOrganizationRepository import com.saveourtool.save.repository.UserRepository import com.saveourtool.save.utils.blockingToFlux import com.saveourtool.save.utils.getHighestRole +import com.saveourtool.save.utils.username import org.springframework.data.domain.PageRequest import org.springframework.security.core.Authentication @@ -32,6 +32,16 @@ class LnkUserOrganizationService( lnkUserOrganizationRepository.findByOrganization(organization) .associate { it.user to it.role } + /** + * @param userName + * @param organization + * @return role for user in [organization] + */ + fun findRoleByUserNameAndOrganization(userName: String, organization: Organization) = lnkUserOrganizationRepository + .findByUserNameAndOrganization(userName, organization) + ?.role + ?: Role.NONE + /** * @param userId * @param organization @@ -133,12 +143,12 @@ class LnkUserOrganizationService( } /** - * @param userId + * @param userName * @param organizationName - * @return role for user in organization by user ID and organization name + * @return role for user in organization by user name and organization name */ - fun findRoleByUserIdAndOrganizationName(userId: Long, organizationName: String) = lnkUserOrganizationRepository - .findByUserIdAndOrganizationName(userId, organizationName) + fun findRoleByUserNameAndOrganizationName(userName: String, organizationName: String) = lnkUserOrganizationRepository + .findByUserNameAndOrganizationName(userName, organizationName) ?.role ?: Role.NONE @@ -193,9 +203,9 @@ class LnkUserOrganizationService( * @return the highest of two roles: the one in [organization] and global one. */ fun getGlobalRoleOrOrganizationRole(authentication: Authentication, organization: Organization): Role { - val selfId = authentication.userId() + val selfName = authentication.username() val selfGlobalRole = userDetailsService.getGlobalRole(authentication) - val selfOrganizationRole = findRoleByUserIdAndOrganization(selfId, organization) + val selfOrganizationRole = findRoleByUserNameAndOrganization(selfName, organization) return getHighestRole(selfOrganizationRole, selfGlobalRole) } @@ -205,9 +215,9 @@ class LnkUserOrganizationService( * @return the highest of two roles: the one in organization with name [organizationName] and global one. */ fun getGlobalRoleOrOrganizationRole(authentication: Authentication, organizationName: String): Role { - val selfId = authentication.userId() + val selfName = authentication.username() val selfGlobalRole = userDetailsService.getGlobalRole(authentication) - val selfOrganizationRole = findRoleByUserIdAndOrganizationName(selfId, organizationName) + val selfOrganizationRole = findRoleByUserNameAndOrganizationName(selfName, organizationName) return getHighestRole(selfOrganizationRole, selfGlobalRole) } diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/LnkUserProjectService.kt b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/service/LnkUserProjectService.kt similarity index 89% rename from save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/LnkUserProjectService.kt rename to save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/service/LnkUserProjectService.kt index 71da7685f0..a2d29a8e9f 100644 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/LnkUserProjectService.kt +++ b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/service/LnkUserProjectService.kt @@ -1,14 +1,14 @@ -package com.saveourtool.save.cosv.service +package com.saveourtool.save.service -import com.saveourtool.save.authservice.utils.userId -import com.saveourtool.save.cosv.repositorysave.LnkUserProjectRepository import com.saveourtool.save.domain.Role import com.saveourtool.save.entities.LnkUserProject import com.saveourtool.save.entities.Project import com.saveourtool.save.entities.ProjectStatus import com.saveourtool.save.entities.User +import com.saveourtool.save.repository.LnkUserProjectRepository import com.saveourtool.save.repository.UserRepository import com.saveourtool.save.utils.getHighestRole +import com.saveourtool.save.utils.username import org.springframework.data.domain.PageRequest import org.springframework.security.core.Authentication @@ -40,6 +40,16 @@ class LnkUserProjectService( ?.role ?: Role.NONE + /** + * @param userName + * @param project + * @return role for user in [project] by user name + */ + fun findRoleByUserNameAndProject(userName: String, project: Project) = lnkUserProjectRepository + .findByUserNameAndProject(userName, project) + ?.role + ?: Role.NONE + /** * Set role of [user] on a project [project] to [role] * @@ -129,9 +139,9 @@ class LnkUserProjectService( * @return the highest of two roles: the one in [project] and global one. */ fun getGlobalRoleOrProjectRole(authentication: Authentication, project: Project): Role { - val selfId = authentication.userId() + val selfName = authentication.username() val selfGlobalRole = userDetailsService.getGlobalRole(authentication) - val selfOrganizationRole = findRoleByUserIdAndProject(selfId, project) + val selfOrganizationRole = findRoleByUserNameAndProject(selfName, project) return getHighestRole(selfOrganizationRole, selfGlobalRole) } } diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/OrganizationService.kt b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/service/OrganizationService.kt similarity index 82% rename from save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/OrganizationService.kt rename to save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/service/OrganizationService.kt index bfdc4dbb54..14a19cedc0 100644 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/OrganizationService.kt +++ b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/service/OrganizationService.kt @@ -1,8 +1,5 @@ -package com.saveourtool.save.cosv.service +package com.saveourtool.save.service -import com.saveourtool.save.authservice.utils.userId -import com.saveourtool.save.authservice.utils.username -import com.saveourtool.save.cosv.utils.hasRole import com.saveourtool.save.domain.OrganizationSaveStatus import com.saveourtool.save.domain.Role import com.saveourtool.save.entities.Organization @@ -15,7 +12,10 @@ import com.saveourtool.save.info.UserPermissionsInOrganization import com.saveourtool.save.permission.Permission import com.saveourtool.save.repository.LnkUserOrganizationRepository import com.saveourtool.save.repository.OrganizationRepository +import com.saveourtool.save.utils.AvatarType +import com.saveourtool.save.utils.hasRole import com.saveourtool.save.utils.orNotFound +import com.saveourtool.save.utils.username import com.saveourtool.save.validation.isValidLengthName import org.jetbrains.annotations.Blocking import org.springframework.data.domain.Pageable @@ -33,6 +33,7 @@ class OrganizationService( private val organizationRepository: OrganizationRepository, private val lnkUserOrganizationRepository: LnkUserOrganizationRepository, private val projectService: ProjectService, + private val userDetailsService: UserService, ) { /** * Store [organization] in the database @@ -80,7 +81,9 @@ class OrganizationService( permission: Permission, ): Boolean { authentication ?: return permission == Permission.READ - val userId = authentication.userId() + val userName = authentication.username() + val user = userDetailsService.getUserByName(userName) + val userId = user.requiredId() if (authentication.hasRole(Role.SUPER_ADMIN)) { return true } @@ -155,6 +158,18 @@ class OrganizationService( */ fun deleteOrganization(organization: Organization): Organization = changeOrganizationStatus(organization, OrganizationStatus.DELETED) + /** + * @param organizationName + * @param authentication + * @return global rating of organization by name [organizationName] based on ratings of all projects under this organization + */ + fun getGlobalRating(organizationName: String, authentication: Authentication?) = + projectService.getProjectsByOrganizationNameAndCreatedStatus(organizationName, authentication) + .collectList() + .map { projectsList -> + projectsList.sumOf { it.contestRating } + } + /** * Mark organization with [organization] as created. * If an organization was previously banned, then all its projects become deleted. @@ -173,6 +188,19 @@ class OrganizationService( return changeOrganizationStatus(organization, OrganizationStatus.CREATED) } + /** + * @param name + * @return organization by name + * @throws NoSuchElementException + */ + fun getByName(name: String) = findByNameAndCreatedStatus(name) + ?: throw NoSuchElementException("There is no organization with name $name.") + + /** + * @return all organizations that were registered in SAVE + */ + fun findAll(): List = organizationRepository.findAll() + /** * Mark organization with [organization] and all its projects as banned. * @@ -215,6 +243,26 @@ class OrganizationService( organizationRepository.save(it) } + /** + * We change the version just to work-around the caching on the frontend + * + * @param name + * @return the id (version) of new avatar + * @throws NoSuchElementException + */ + fun updateAvatarVersion(name: String): String { + val organization = organizationRepository.findByName(name).orNotFound() + var version = organization.avatar?.substringAfterLast("?")?.toInt() ?: 0 + val newAvatar = "${AvatarType.ORGANIZATION.toUrlStr(name)}?${++version}" + + organization.apply { + avatar = newAvatar + }.orNotFound { "Organization with name [$name] was not found." } + organization.let { organizationRepository.save(it) } + + return newAvatar + } + @Suppress("FunctionOnlyReturningConstant", "UNUSED_PARAMETER") private fun hasReadAccess(userId: Long?, organizationRole: Role): Boolean = true diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/ProjectService.kt b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/service/ProjectService.kt similarity index 97% rename from save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/ProjectService.kt rename to save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/service/ProjectService.kt index 32afe844ce..f666ff5ab2 100644 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/ProjectService.kt +++ b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/service/ProjectService.kt @@ -1,12 +1,12 @@ -package com.saveourtool.save.cosv.service +package com.saveourtool.save.service -import com.saveourtool.save.cosv.repositorysave.ProjectRepository -import com.saveourtool.save.cosv.security.ProjectPermissionEvaluator import com.saveourtool.save.domain.ProjectSaveStatus import com.saveourtool.save.entities.* import com.saveourtool.save.filters.ProjectFilter import com.saveourtool.save.permission.Permission +import com.saveourtool.save.repository.ProjectRepository import com.saveourtool.save.repository.UserRepository +import com.saveourtool.save.security.ProjectPermissionEvaluator import com.saveourtool.save.utils.blockingToMono import com.saveourtool.save.utils.switchIfEmptyToNotFound diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/UserDetailsService.kt b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/service/UserService.kt similarity index 82% rename from save-backend/src/main/kotlin/com/saveourtool/save/backend/service/UserDetailsService.kt rename to save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/service/UserService.kt index 6d5f2b17c8..42ab04bab4 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/UserDetailsService.kt +++ b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/service/UserService.kt @@ -1,8 +1,5 @@ -package com.saveourtool.save.backend.service +package com.saveourtool.save.service -import com.saveourtool.save.authservice.utils.userId -import com.saveourtool.save.backend.repository.LnkUserProjectRepository -import com.saveourtool.save.backend.storage.AvatarStorage import com.saveourtool.save.domain.Role import com.saveourtool.save.domain.UserSaveStatus import com.saveourtool.save.entities.OriginalLogin @@ -10,39 +7,41 @@ import com.saveourtool.save.entities.User import com.saveourtool.save.evententities.UserEvent import com.saveourtool.save.info.UserStatus import com.saveourtool.save.repository.LnkUserOrganizationRepository +import com.saveourtool.save.repository.LnkUserProjectRepository import com.saveourtool.save.repository.OriginalLoginRepository import com.saveourtool.save.repository.UserRepository import com.saveourtool.save.storage.AvatarKey +import com.saveourtool.save.storage.AvatarStorage import com.saveourtool.save.utils.* + import org.slf4j.Logger import org.springframework.context.ApplicationEventPublisher import org.springframework.data.repository.findByIdOrNull - import org.springframework.security.core.Authentication import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import reactor.core.scheduler.Schedulers import java.lang.IllegalArgumentException - -import java.util.* +import java.util.NoSuchElementException /** - * A service that provides access to [UserRepository] and [OriginalLoginRepository] + * Service for user */ @Service -class UserDetailsService( +@Suppress("LongParameterList") +class UserService( private val userRepository: UserRepository, - private val originalLoginRepository: OriginalLoginRepository, + private val applicationEventPublisher: ApplicationEventPublisher, private val lnkUserOrganizationRepository: LnkUserOrganizationRepository, + private val originalLoginRepository: OriginalLoginRepository, private val lnkUserProjectRepository: LnkUserProjectRepository, private val avatarStorage: AvatarStorage, - private val applicationEventPublisher: ApplicationEventPublisher, ) { /** * @param user user for update * @return updated user */ - fun saveUser(user: User): User = userRepository.save(user) + fun saveUser(user: User) = userRepository.save(user) /** * @param username @@ -56,18 +55,6 @@ class UserDetailsService( */ fun findByRole(role: String) = userRepository.findByRole(role) - /** - * @param name - * @return found [User] or exception - */ - fun getByName(name: String): User = userRepository.findByName(name).orNotFound { "Not found user by name $name" } - - /** - * @param userId [User.id] - * @return found [User] by provided [userId] or null - */ - fun findById(userId: Long): User? = userRepository.findByIdOrNull(userId) - /** * @param username * @param source source (where the user identity is coming from) @@ -77,51 +64,54 @@ class UserDetailsService( originalLoginRepository.findByNameAndSource(username, source)?.user /** - * We change the version just to work-around the caching on the frontend - * * @param name - * @return the id (version) of new avatar - * @throws NoSuchElementException + * @return user with [name] */ - fun updateAvatarVersion(name: String): String { - val user = userRepository.findByName(name).orNotFound() - var version = user.avatar?.find { it == '?' }?.let { - user.avatar?.substringAfterLast("?")?.toInt() ?: 0 - } ?: 0 - val newAvatar = "${AvatarType.USER.toUrlStr(name)}?${++version}" - user.apply { avatar = newAvatar } - .let { userRepository.save(it) } - - return newAvatar - } + fun getUserByName(name: String): User = userRepository.findByName(name).orNotFound { "Not found user by name $name" } /** - * Only for static resources! - * - * @param name - * @param resource - * @throws IllegalArgumentException + * @param id + * @return user with [id] */ - fun setAvatarFromResource(name: String, resource: String) { - if (!resource.startsWith(AVATARS_PACKS_DIR)) { - throw IllegalArgumentException("Only avatars from $AVATARS_PACKS_DIR can be set for user $name") - } + fun findById(id: Long): User = userRepository.findByIdOrNull(id).orNotFound { "Not found user by id $id" } - userRepository.findByName(name) - .orNotFound() - .apply { avatar = resource } - .let { userRepository.save(it) } - } + /** + * @param ids + * @return users with [ids] + */ + fun findAllByIdIn(ids: List): List = userRepository.findAllByIdIn(ids) /** * @param authentication * @return global [Role] of authenticated user */ - fun getGlobalRole(authentication: Authentication): Role = findById(authentication.userId()) - ?.role + fun getGlobalRole(authentication: Authentication): Role = getUserByName(authentication.username()) + .role ?.let { Role.fromSpringSecurityRole(it) } ?: Role.VIEWER + /** + * @param organizationName + * @param userName + * @return role for user in organization by user ID and organization name + */ + fun findRoleByUserNameAndOrganizationName(userName: String, organizationName: String) = lnkUserOrganizationRepository + .findByUserNameAndOrganizationName(userName, organizationName) + ?.role + ?: Role.NONE + + /** + * @param authentication + * @param organizationName + * @return the highest of two roles: the one in organization with name [organizationName] and global one. + */ + fun getGlobalRoleOrOrganizationRole(authentication: Authentication, organizationName: String): Role { + val selfName = authentication.username() + val selfGlobalRole = getGlobalRole(authentication) + val selfOrganizationRole = findRoleByUserNameAndOrganizationName(selfName, organizationName) + return getHighestRole(selfOrganizationRole, selfGlobalRole) + } + /** * @param newUser * @param oldName @@ -163,6 +153,41 @@ class UserDetailsService( } } + /** + * @param name name of user + * @return UserSaveStatus + */ + @Transactional + fun approveUser( + name: String, + ): UserSaveStatus { + val user: User = userRepository.findByName(name).orNotFound() + + userRepository.save(user.apply { + this.status = UserStatus.ACTIVE + }) + + return UserSaveStatus.APPROVED + } + + /** + * @param name name of user + * @return UserSaveStatus + */ + @Transactional + fun banUser( + name: String, + ): UserSaveStatus { + val user: User = userRepository.findByName(name).orNotFound().apply { + this.status = UserStatus.BANNED + } + applicationEventPublisher.publishEvent(UserEvent(user)) + + userRepository.save(user) + + return UserSaveStatus.BANNED + } + /** * @param source * @param name @@ -184,6 +209,16 @@ class UserDetailsService( } } + /** + * @param user + * @param nameInSource + * @param source + */ + @Transactional + fun addSource(user: User, nameInSource: String, source: String) { + originalLoginRepository.save(OriginalLogin(nameInSource, user, source)) + } + /** * @param userNameCandidate * @return created [User] @@ -212,16 +247,6 @@ class UserDetailsService( ) } - /** - * @param user - * @param nameInSource - * @param source - */ - @Transactional - fun addSource(user: User, nameInSource: String, source: String) { - originalLoginRepository.save(OriginalLogin(nameInSource, user, source)) - } - /** * @param name name of user * @param authentication @@ -232,8 +257,8 @@ class UserDetailsService( name: String, authentication: Authentication, ): UserSaveStatus { - val user: User = userRepository.findByIdOrNull(authentication.userId()).orNotFound { - "User with id ${authentication.userId()} not found in database" + val user: User = userRepository.findByName(authentication.username()).orNotFound { + "User with name ${authentication.username()} not found in database" } val newName = "Deleted-${user.id}" userRepository.deleteHighLevelName(user.name) @@ -266,42 +291,44 @@ class UserDetailsService( } /** - * @param name name of user - * @return UserSaveStatus + * We change the version just to work-around the caching on the frontend + * + * @param name + * @return the id (version) of new avatar + * @throws NoSuchElementException */ - @Transactional - fun banUser( - name: String, - ): UserSaveStatus { - val user: User = userRepository.findByName(name).orNotFound().apply { - this.status = UserStatus.BANNED - } - applicationEventPublisher.publishEvent(UserEvent(user)) - - userRepository.save(user) + fun updateAvatarVersion(name: String): String { + val user = userRepository.findByName(name).orNotFound() + var version = user.avatar?.find { it == '?' }?.let { + user.avatar?.substringAfterLast("?")?.toInt() ?: 0 + } ?: 0 + val newAvatar = "${AvatarType.USER.toUrlStr(name)}?${++version}" + user.apply { avatar = newAvatar } + .let { userRepository.save(it) } - return UserSaveStatus.BANNED + return newAvatar } /** - * @param name name of user - * @return UserSaveStatus + * Only for static resources! + * + * @param name + * @param resource + * @throws IllegalArgumentException */ - @Transactional - fun approveUser( - name: String, - ): UserSaveStatus { - val user: User = userRepository.findByName(name).orNotFound() - - userRepository.save(user.apply { - this.status = UserStatus.ACTIVE - }) + fun setAvatarFromResource(name: String, resource: String) { + if (!resource.startsWith(AVATARS_PACKS_DIR)) { + throw IllegalArgumentException("Only avatars from $AVATARS_PACKS_DIR can be set for user $name") + } - return UserSaveStatus.APPROVED + userRepository.findByName(name) + .orNotFound() + .apply { avatar = resource } + .let { userRepository.save(it) } } companion object { - private val log: Logger = getLogger() + private val log: Logger = getLogger() private const val UNIQUE_NAME_SEPARATOR = "_" private val roleForNewUser = Role.VIEWER.asSpringSecurityRole() } diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/storage/AvatarStorage.kt b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/storage/AvatarStorage.kt similarity index 73% rename from save-cosv/src/main/kotlin/com/saveourtool/save/cosv/storage/AvatarStorage.kt rename to save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/storage/AvatarStorage.kt index 4512ede7a1..875b44b97c 100644 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/storage/AvatarStorage.kt +++ b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/storage/AvatarStorage.kt @@ -1,11 +1,7 @@ -package com.saveourtool.save.cosv.storage +package com.saveourtool.save.storage -import com.saveourtool.save.cosv.configs.ConfigProperties import com.saveourtool.save.s3.S3Operations -import com.saveourtool.save.storage.AbstractSimpleReactiveStorage -import com.saveourtool.save.storage.AvatarKey -import com.saveourtool.save.storage.concatS3Key -import com.saveourtool.save.storage.s3KeyToParts +import com.saveourtool.save.s3.S3OperationsProperties import com.saveourtool.save.utils.AvatarType import com.saveourtool.save.utils.orNotFound import org.springframework.stereotype.Service @@ -16,7 +12,7 @@ import org.springframework.stereotype.Service */ @Service class AvatarStorage( - configProperties: ConfigProperties, + configProperties: S3OperationsProperties.Provider, s3Operations: S3Operations, ) : AbstractSimpleReactiveStorage( s3Operations, diff --git a/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/utils/AuthenticationUtils.kt b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/utils/AuthenticationUtils.kt new file mode 100644 index 0000000000..9375cc867b --- /dev/null +++ b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/utils/AuthenticationUtils.kt @@ -0,0 +1,29 @@ +@file:Suppress("FILE_NAME_MATCH_CLASS", "HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE") + +package com.saveourtool.save.utils + +import com.saveourtool.save.domain.Role +import org.springframework.security.core.Authentication +import org.springframework.security.core.userdetails.UserDetails + +/** + * Check role out of [Authentication] + * + * @param role + * @return true if user with [Authentication] has [role], false otherwise + */ +fun Authentication.hasRole(role: Role): Boolean = authorities.any { it.authority == role.asSpringSecurityRole() } + +/** + * Extract username from this [Authentication]. + * We assume here that most of the authentications are created by [ConvertingAuthenticationManager], + * so `principal` is a String, containing identity source. + * + * @return username + */ +fun Authentication.username(): String = when (principal) { + // this should be the most common branch, as requests are authenticated by `ConvertingAuthenticationManager` + is String -> (principal as String).split(':').last() + is UserDetails -> (principal as UserDetails).username + else -> error("Authentication instance $this has unsupported type of principal: $principal of type ${principal::class}") +} diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/configs/PersistenceSaveAutoConfiguration.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/configs/PersistenceSaveAutoConfiguration.kt index 572add5dc4..b70849ce4f 100644 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/configs/PersistenceSaveAutoConfiguration.kt +++ b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/configs/PersistenceSaveAutoConfiguration.kt @@ -10,6 +10,7 @@ import org.springframework.data.jpa.repository.config.EnableJpaRepositories import org.springframework.orm.jpa.JpaTransactionManager import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean import org.springframework.transaction.PlatformTransactionManager +import org.springframework.transaction.annotation.EnableTransactionManagement import javax.persistence.EntityManagerFactory import javax.sql.DataSource @@ -17,8 +18,9 @@ import javax.sql.DataSource * Configuration for Save database */ @Configuration +@EnableTransactionManagement @EnableJpaRepositories( - basePackages = ["com.saveourtool.save.cosv.repositorysave", "com.saveourtool.save.repository"], + basePackages = ["com.saveourtool.save.repository"], entityManagerFactoryRef = "saveEntityManagerFactory", transactionManagerRef = "saveTransactionManager") class PersistenceSaveAutoConfiguration { diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/CommentController.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/CommentController.kt index e17a577b98..a78b03497b 100644 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/CommentController.kt +++ b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/CommentController.kt @@ -1,11 +1,11 @@ package com.saveourtool.save.cosv.controllers import com.saveourtool.save.configs.ApiSwaggerSupport -import com.saveourtool.save.cosv.security.CommentPermissionEvaluator -import com.saveourtool.save.cosv.service.CommentService import com.saveourtool.save.entities.Comment import com.saveourtool.save.entities.CommentDto import com.saveourtool.save.permission.Permission +import com.saveourtool.save.security.CommentPermissionEvaluator +import com.saveourtool.save.service.CommentService import com.saveourtool.save.utils.StringResponse import com.saveourtool.save.utils.blockingToMono import com.saveourtool.save.utils.switchIfEmptyToNotFound diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/CosvController.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/CosvController.kt index 3368bfec69..6eed5a064c 100644 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/CosvController.kt +++ b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/CosvController.kt @@ -3,11 +3,11 @@ package com.saveourtool.save.cosv.controllers import com.saveourtool.save.configs.ApiSwaggerSupport import com.saveourtool.save.configs.RequiresAuthorizationSourceHeader import com.saveourtool.save.cosv.service.CosvService -import com.saveourtool.save.cosv.service.OrganizationService -import com.saveourtool.save.cosv.service.UserService import com.saveourtool.save.entities.cosv.CosvFileDto import com.saveourtool.save.entities.cosv.VulnerabilityMetadataDto import com.saveourtool.save.entities.cosv.VulnerabilityMetadataDtoWithUserAndOrganization +import com.saveourtool.save.service.OrganizationService +import com.saveourtool.save.service.UserService import com.saveourtool.save.utils.* import com.saveourtool.save.v1 import org.springframework.http.HttpStatus diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/OrganizationController.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/OrganizationController.kt index 4ffab0337c..44bbea27db 100644 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/OrganizationController.kt +++ b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/OrganizationController.kt @@ -4,15 +4,15 @@ import com.saveourtool.save.authservice.utils.userId import com.saveourtool.save.configs.ApiSwaggerSupport import com.saveourtool.save.configs.RequiresAuthorizationSourceHeader import com.saveourtool.save.cosv.configs.ConfigProperties -import com.saveourtool.save.cosv.security.OrganizationPermissionEvaluator -import com.saveourtool.save.cosv.service.GitService -import com.saveourtool.save.cosv.service.LnkUserOrganizationService -import com.saveourtool.save.cosv.service.OrganizationService import com.saveourtool.save.domain.OrganizationSaveStatus import com.saveourtool.save.domain.Role import com.saveourtool.save.entities.* import com.saveourtool.save.filters.OrganizationFilter import com.saveourtool.save.permission.Permission +import com.saveourtool.save.security.OrganizationPermissionEvaluator +import com.saveourtool.save.service.GitService +import com.saveourtool.save.service.LnkUserOrganizationService +import com.saveourtool.save.service.OrganizationService import com.saveourtool.save.utils.* import com.saveourtool.save.v1 diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/RawCosvFileController.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/RawCosvFileController.kt index 5c0af1bd93..6be16d40ef 100644 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/RawCosvFileController.kt +++ b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/RawCosvFileController.kt @@ -5,13 +5,13 @@ import com.saveourtool.save.configs.ApiSwaggerSupport import com.saveourtool.save.configs.RequiresAuthorizationSourceHeader import com.saveourtool.save.cosv.configs.ConfigProperties import com.saveourtool.save.cosv.service.CosvService -import com.saveourtool.save.cosv.service.OrganizationService -import com.saveourtool.save.cosv.service.UserService import com.saveourtool.save.cosv.storage.RawCosvFileStorage import com.saveourtool.save.entities.cosv.* import com.saveourtool.save.entities.cosv.RawCosvFileDto.Companion.isDuplicate import com.saveourtool.save.entities.cosv.RawCosvFileDto.Companion.isUploadedJsonFile import com.saveourtool.save.permission.Permission +import com.saveourtool.save.service.OrganizationService +import com.saveourtool.save.service.UserService import com.saveourtool.save.storage.concatS3Key import com.saveourtool.save.utils.* import com.saveourtool.save.v1 diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/UsersDetailsController.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/UsersDetailsController.kt index 5f7f8397c1..0021e11b21 100644 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/UsersDetailsController.kt +++ b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/UsersDetailsController.kt @@ -4,12 +4,12 @@ import com.saveourtool.save.authservice.utils.SaveUserDetails import com.saveourtool.save.authservice.utils.userId import com.saveourtool.save.configs.RequiresAuthorizationSourceHeader import com.saveourtool.save.cosv.configs.ConfigProperties -import com.saveourtool.save.cosv.service.UserService import com.saveourtool.save.domain.UserSaveStatus import com.saveourtool.save.entities.User import com.saveourtool.save.info.UserInfo import com.saveourtool.save.info.UserStatus import com.saveourtool.save.repository.UserRepository +import com.saveourtool.save.service.UserService import com.saveourtool.save.utils.* import com.saveourtool.save.v1 import com.saveourtool.save.validation.isValidLengthName diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/VulnerabilityController.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/VulnerabilityController.kt index 9c1a42b861..f3a3d6f573 100644 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/VulnerabilityController.kt +++ b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/VulnerabilityController.kt @@ -5,7 +5,6 @@ import com.saveourtool.save.configs.ApiSwaggerSupport import com.saveourtool.save.configs.RequiresAuthorizationSourceHeader import com.saveourtool.save.cosv.security.VulnerabilityPermissionEvaluator import com.saveourtool.save.cosv.service.CosvService -import com.saveourtool.save.cosv.service.UserService import com.saveourtool.save.cosv.service.VulnerabilityMetadataDtoList import com.saveourtool.save.cosv.service.VulnerabilityService import com.saveourtool.save.cosv.utils.hasRole @@ -18,6 +17,7 @@ import com.saveourtool.save.entities.vulnerability.VulnerabilityStatus import com.saveourtool.save.filters.VulnerabilityFilter import com.saveourtool.save.info.UserInfo import com.saveourtool.save.permission.Permission +import com.saveourtool.save.service.UserService import com.saveourtool.save.utils.* import com.saveourtool.save.v1 import io.swagger.v3.oas.annotations.Operation diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/event/CommentListener.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/event/CommentListener.kt index 423dd9bce2..ebaa671e62 100644 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/event/CommentListener.kt +++ b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/event/CommentListener.kt @@ -1,12 +1,12 @@ package com.saveourtool.save.cosv.event import com.saveourtool.save.cosv.repository.LnkVulnerabilityMetadataUserRepository -import com.saveourtool.save.cosv.service.UserService import com.saveourtool.save.cosv.service.VulnerabilityMetadataService import com.saveourtool.save.entities.Notification import com.saveourtool.save.entities.User import com.saveourtool.save.evententities.CommentEvent import com.saveourtool.save.repository.NotificationRepository +import com.saveourtool.save.service.UserService import com.saveourtool.save.utils.orNotFound import org.springframework.context.event.EventListener import org.springframework.stereotype.Component diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/repositorysave/LnkUserProjectRepository.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/repositorysave/LnkUserProjectRepository.kt deleted file mode 100644 index 20aa2983e7..0000000000 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/repositorysave/LnkUserProjectRepository.kt +++ /dev/null @@ -1,68 +0,0 @@ -package com.saveourtool.save.cosv.repositorysave - -import com.saveourtool.save.entities.LnkUserProject -import com.saveourtool.save.entities.Project -import com.saveourtool.save.entities.ProjectStatus -import com.saveourtool.save.spring.repository.BaseEntityRepository -import org.springframework.data.jpa.repository.Modifying -import org.springframework.data.jpa.repository.Query -import org.springframework.data.repository.query.Param -import org.springframework.stereotype.Repository -import org.springframework.transaction.annotation.Transactional - -/** - * Repository of [LnkUserProject] - */ -@Repository -interface LnkUserProjectRepository : BaseEntityRepository { - /** - * @param project - * @return [LnkUserProject] by [project] - */ - fun findByProject(project: Project): List - - /** - * @param userId - * @param project - * @return [LnkUserProject] by [userId] and [Project] - */ - fun findByUserIdAndProject(userId: Long, project: Project): LnkUserProject? - - /** - * @param userId - * @param projectId - * @return [LnkUserProject] by [userId] and [projectId] - */ - fun findByUserIdAndProjectId(userId: Long, projectId: Long): LnkUserProject? - - /** - * @param userId - * @param statuses is set of [statuses], one of which a projects can have - * @return List of [LnkUserProject] where user with [userId] is a member - */ - fun findByUserIdAndProjectStatusIn(userId: Long, statuses: Set): List - - /** - * Save [LnkUserProject] using only ids and role string. - * - * @param userId - * @param projectId - * @param role - */ - @Transactional - @Modifying - @Query( - value = "insert into save_cloud.lnk_user_project (project_id, user_id, role) values (:project_id, :user_id, :role)", - nativeQuery = true, - ) - fun save( - @Param("project_id") projectId: Long, - @Param("user_id") userId: Long, - @Param("role") role: String - ) - - /** - * @param userId id of user - */ - fun deleteByUserId(userId: Long) -} diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/repositorysave/ProjectRepository.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/repositorysave/ProjectRepository.kt deleted file mode 100644 index 79aaa140f3..0000000000 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/repositorysave/ProjectRepository.kt +++ /dev/null @@ -1,71 +0,0 @@ -package com.saveourtool.save.cosv.repositorysave - -import com.saveourtool.save.entities.Organization -import com.saveourtool.save.entities.Project -import com.saveourtool.save.entities.ProjectStatus -import org.springframework.data.jpa.repository.JpaRepository -import org.springframework.data.jpa.repository.JpaSpecificationExecutor -import org.springframework.data.repository.query.QueryByExampleExecutor -import org.springframework.stereotype.Repository - -/** - * The repository of project entities - */ -@Repository -interface ProjectRepository : JpaRepository, QueryByExampleExecutor, -JpaSpecificationExecutor { - /** - * @param name - * @param organization - * @return project by name and owner - */ - fun findByNameAndOrganization(name: String, organization: Organization): Project? - - /** - * @param name - * @param organizationName - * @param statuses - * @return project by name and owner - */ - fun findByNameAndOrganizationNameAndStatusIn(name: String, organizationName: String, statuses: Set): Project? - - /** - * @param name - * @param organizationName - * @return project by name and owner - */ - fun findByNameAndOrganizationName(name: String, organizationName: String): Project? - - /** - * @param organizationName is name of organization - * @return list of projects for organization - */ - fun findByOrganizationName(organizationName: String): List - - /** - * @param organizationName is name of organization - * @param statuses is set of [statuses], one of which a projects can have - * @return list of projects for organization - */ - fun findByOrganizationNameAndStatusIn(organizationName: String, statuses: Set): List - - /** - * @param prefix prefix of project name - * @param statuses is set of [statuses], one of which a projects can have - * @return list of organizations with names that start with [prefix] and having the status in [statuses] - */ - fun findByNameStartingWithAndStatusIn(prefix: String, statuses: Set): List - - /** - * @param statuses is set of [statuses], one of which a projects can have - * @return list of organizations with required [statuses] - */ - fun findByStatusIn(statuses: Set): List - - /** - * @param name of part of or project name - * @param statuses is set of [statuses], one of which a projects can have - * @return list of organizations with required [statuses] - */ - fun findByNameLikeAndStatusIn(name: String, statuses: Set): List -} diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/security/OrganizationPermissionEvaluator.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/security/OrganizationPermissionEvaluator.kt deleted file mode 100644 index 13b4b57a8e..0000000000 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/security/OrganizationPermissionEvaluator.kt +++ /dev/null @@ -1,153 +0,0 @@ -package com.saveourtool.save.cosv.security - -import com.saveourtool.save.authservice.utils.userId -import com.saveourtool.save.cosv.service.LnkUserOrganizationService -import com.saveourtool.save.cosv.utils.hasRole -import com.saveourtool.save.domain.Role -import com.saveourtool.save.entities.Organization -import com.saveourtool.save.entities.OrganizationStatus -import com.saveourtool.save.entities.User -import com.saveourtool.save.permission.Permission -import org.springframework.security.core.Authentication -import org.springframework.stereotype.Component - -/** - * Class that is capable of assessing user's permissions regarding organizations. - */ -@Component -class OrganizationPermissionEvaluator( - private var lnkUserOrganizationService: LnkUserOrganizationService -) { - /** - * @param authentication - * @param organization - * @param role required role - * @return true if user with [authentication] has [role] in [organization]. - */ - fun hasOrganizationRole(authentication: Authentication?, organization: Organization, role: Role): Boolean { - authentication ?: return false - val userId = authentication.userId() - if (authentication.hasRole(Role.SUPER_ADMIN)) { - return true - } - return lnkUserOrganizationService.findRoleByUserIdAndOrganization(userId, organization).isHigherOrEqualThan(role) - } - - /** - * @param authentication [Authentication] describing an authenticated request - * @param organization is organization in which we want to change the status - * @param newStatus is new status in [organization] - * @return whether user described by [authentication] can have permission on change [organization] status on [newStatus] - * @throws IllegalStateException - */ - fun hasPermissionToChangeStatus(authentication: Authentication?, organization: Organization, newStatus: OrganizationStatus): Boolean { - val oldStatus = organization.status - - return when { - oldStatus == newStatus -> throw IllegalStateException("invalid status") - oldStatus.isBan() || newStatus.isBan() -> hasPermission(authentication, organization, Permission.BAN) - else -> hasPermission(authentication, organization, Permission.DELETE) - } - } - - /** - * @param authentication [Authentication] describing an authenticated request - * @param organization - * @param permission - * @return whether user described by [authentication] can have [permission] on [organization] - */ - fun hasPermission(authentication: Authentication?, organization: Organization, permission: Permission): Boolean { - authentication ?: return permission == Permission.READ - val userId = authentication.userId() - if (authentication.hasRole(Role.SUPER_ADMIN)) { - return true - } - - val organizationRole = lnkUserOrganizationService.findRoleByUserIdAndOrganization(userId, organization) - return when (permission) { - Permission.READ -> hasReadAccess(userId, organizationRole) - Permission.WRITE -> hasWriteAccess(userId, organizationRole) - Permission.DELETE -> hasDeleteAccess(userId, organizationRole) - Permission.BAN -> hasBanAccess(userId, organizationRole) - } - } - - /** - * @param authentication - * @param organizationName - * @param requiredRole - * @return true if user with [authentication] info has [requiredRole] in organization with name [organizationName] or globally - */ - fun hasGlobalRoleOrOrganizationRole(authentication: Authentication, organizationName: String, requiredRole: Role): Boolean = - lnkUserOrganizationService.getGlobalRoleOrOrganizationRole(authentication, organizationName).priority >= requiredRole.priority - - @Suppress("FunctionOnlyReturningConstant", "UNUSED_PARAMETER") - private fun hasReadAccess(userId: Long?, organizationRole: Role): Boolean = true - - private fun hasWriteAccess(userId: Long?, organizationRole: Role): Boolean = hasDeleteAccess(userId, organizationRole) || - userId?.let { organizationRole == Role.ADMIN } ?: false - - private fun hasDeleteAccess(userId: Long?, organizationRole: Role): Boolean = - hasBanAccess(userId, organizationRole) || userId?.let { organizationRole == Role.OWNER } ?: false - - /** - * Only [Role.SUPER_ADMIN] can ban the project. And a user with such a global role has permissions for all actions. - * Since we have all the rights issued depending on the following, you need to set [false] here - */ - @Suppress("FunctionOnlyReturningConstant", "UnusedParameter") - private fun hasBanAccess(userId: Long?, organizationRole: Role): Boolean = false - - /** - * In case we widen number of users that can manage roles in an organization, there is a separate method. - * Simply delegating now. - * - * @param organization in which the role is going to be changed - * @param authentication auth info of a current user - * @param otherUser user whose role is going to be changed - * @param requestedRole role that is going to be set - * @return whether the user can change roles in organization - */ - @Suppress("UnsafeCallOnNullableType") - fun canChangeRoles( - organization: Organization, - authentication: Authentication, - otherUser: User, - requestedRole: Role = Role.NONE - ): Boolean { - val selfRole = lnkUserOrganizationService.getGlobalRoleOrOrganizationRole(authentication, organization) - val otherRole = lnkUserOrganizationService.findRoleByUserIdAndOrganization(otherUser.id!!, organization) - return selfRole.isHigherOrEqualThan(Role.OWNER) || selfRole.isHigherOrEqualThan(Role.ADMIN) && hasAnotherUserLessPermissions(selfRole, otherRole) && - isRequestedPermissionsCanBeSetByUser(selfRole, requestedRole) - } - - /** - * @param organization - * @param authentication - */ - fun canCreateContests( - organization: Organization, - authentication: Authentication?, - ): Boolean = authentication?.let { - organization.canCreateContests && hasGlobalRoleOrOrganizationRole(it, organization.name, contestCreatorMinimalRole) - } ?: false - - /** - * @param selfRole - * @param otherRole - * @return true if user with [selfRole] has more permissions than user with [otherRole], false otherwise. - */ - fun hasAnotherUserLessPermissions(selfRole: Role, otherRole: Role): Boolean = selfRole.priority > otherRole.priority - - /** - * @param selfRole - * @param requestedRole - * @return true if [selfRole] is higher than [requestedRole], false otherwise - */ - fun isRequestedPermissionsCanBeSetByUser(selfRole: Role, requestedRole: Role): Boolean = selfRole.priority > requestedRole.priority - - private fun OrganizationStatus.isBan(): Boolean = - this == OrganizationStatus.BANNED - companion object { - val contestCreatorMinimalRole = Role.ADMIN - } -} 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 d1b3a8d821..f60085d341 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 @@ -12,6 +12,8 @@ import com.saveourtool.save.entities.Organization import com.saveourtool.save.entities.User import com.saveourtool.save.entities.cosv.* import com.saveourtool.save.entitiescosv.CosvGeneratedId +import com.saveourtool.save.service.OrganizationService +import com.saveourtool.save.service.UserService import com.saveourtool.save.utils.* import org.slf4j.Logger diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/UserService.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/UserService.kt deleted file mode 100644 index 26e48fdc89..0000000000 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/UserService.kt +++ /dev/null @@ -1,212 +0,0 @@ -package com.saveourtool.save.cosv.service - -import com.saveourtool.save.authservice.utils.userId -import com.saveourtool.save.authservice.utils.username -import com.saveourtool.save.cosv.repositorysave.* -import com.saveourtool.save.cosv.storage.AvatarStorage -import com.saveourtool.save.domain.Role -import com.saveourtool.save.domain.UserSaveStatus -import com.saveourtool.save.entities.User -import com.saveourtool.save.evententities.UserEvent -import com.saveourtool.save.info.UserStatus -import com.saveourtool.save.repository.LnkUserOrganizationRepository -import com.saveourtool.save.repository.OriginalLoginRepository -import com.saveourtool.save.repository.UserRepository -import com.saveourtool.save.storage.AvatarKey -import com.saveourtool.save.utils.AvatarType -import com.saveourtool.save.utils.getHighestRole -import com.saveourtool.save.utils.orNotFound -import org.springframework.context.ApplicationEventPublisher -import org.springframework.data.repository.findByIdOrNull -import org.springframework.security.core.Authentication -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional -import reactor.core.scheduler.Schedulers - -/** - * Service for user - */ -@Service -@Suppress("LongParameterList") -class UserService( - private val userRepository: UserRepository, - private val applicationEventPublisher: ApplicationEventPublisher, - private val originalLoginRepository: OriginalLoginRepository, - private val lnkUserOrganizationRepository: LnkUserOrganizationRepository, - private val lnkUserProjectRepository: LnkUserProjectRepository, - private val avatarStorage: AvatarStorage, -) { - /** - * @param user user for update - * @return updated user - */ - fun saveUser(user: User) = userRepository.save(user) - - /** - * @param name - * @return user with [name] - */ - fun getUserByName(name: String): User = userRepository.findByName(name).orNotFound { "Not found user by name $name" } - - /** - * @param id - * @return user with [id] - */ - fun findById(id: Long): User = userRepository.findByIdOrNull(id).orNotFound { "Not found user by id $id" } - - /** - * @param ids - * @return users with [ids] - */ - fun findAllByIdIn(ids: List): List = userRepository.findAllByIdIn(ids) - - /** - * @param authentication - * @return global [Role] of authenticated user - */ - fun getGlobalRole(authentication: Authentication): Role = getUserByName(authentication.username()) - .role - ?.let { Role.fromSpringSecurityRole(it) } - ?: Role.VIEWER - - /** - * @param userId - * @param organizationName - * @return role for user in organization by user ID and organization name - */ - fun findRoleByUserIdAndOrganizationName(userId: Long, organizationName: String) = lnkUserOrganizationRepository - .findByUserIdAndOrganizationName(userId, organizationName) - ?.role - ?: Role.NONE - - /** - * @param authentication - * @param organizationName - * @return the highest of two roles: the one in organization with name [organizationName] and global one. - */ - fun getGlobalRoleOrOrganizationRole(authentication: Authentication, organizationName: String): Role { - val selfId = authentication.userId() - val selfGlobalRole = getGlobalRole(authentication) - val selfOrganizationRole = findRoleByUserIdAndOrganizationName(selfId, organizationName) - return getHighestRole(selfOrganizationRole, selfGlobalRole) - } - - /** - * @param newUser - * @param oldName - * @param oldUserStatus - * @return UserSaveStatus - */ - @Transactional - fun saveUser(newUser: User, oldName: String?, oldUserStatus: UserStatus): UserSaveStatus { - applicationEventPublisher.publishEvent(UserEvent(newUser)) - val isNameFreeAndNotTaken = userRepository.validateName(newUser.name) != 0L - // if we are registering new user (updating just name and status to NOT_APPROVED): - return if (oldUserStatus == UserStatus.CREATED && newUser.status == UserStatus.NOT_APPROVED) { - // checking if the user with new name already exists (it's definitely not our user, so if found -> CONFLICT) - if (isNameFreeAndNotTaken) { - userRepository.save(newUser) - UserSaveStatus.UPDATE - } else { - UserSaveStatus.CONFLICT - } - } else { - // we are trying to change the name of ACTIVE user - oldName?.let { - // but such name is already taken and exists in db - if (isNameFreeAndNotTaken) { - userRepository.deleteHighLevelName(oldName) - userRepository.saveHighLevelName(newUser.name) - userRepository.save(newUser) - UserSaveStatus.UPDATE - } else { - UserSaveStatus.CONFLICT - } - // if we are changing other fields of ACTIVE users, but not changing the name we can just save user - // here we highly depend on the `oldName` field (from client code) - // but we are safe as we have a unique constraint on the database - } ?: run { - userRepository.save(newUser) - UserSaveStatus.UPDATE - } - } - } - - /** - * @param name name of user - * @param authentication - * @return UserSaveStatus - */ - @Transactional - fun deleteUser( - name: String, - authentication: Authentication, - ): UserSaveStatus { - val user: User = userRepository.findByIdOrNull(authentication.userId()).orNotFound { - "User with id ${authentication.userId()} not found in database" - } - val newName = "Deleted-${user.id}" - userRepository.deleteHighLevelName(user.name) - userRepository.saveHighLevelName(newName) - userRepository.save(user.apply { - this.name = newName - this.status = UserStatus.DELETED - this.avatar = null - this.company = null - this.twitter = null - this.email = null - this.gitHub = null - this.linkedin = null - this.location = null - }) - - val avatarKey = AvatarKey( - AvatarType.USER, - name, - ) - avatarStorage.delete(avatarKey) - .subscribeOn(Schedulers.boundedElastic()) - .subscribe() - - originalLoginRepository.deleteByUserId(user.requiredId()) - lnkUserProjectRepository.deleteByUserId(user.requiredId()) - lnkUserOrganizationRepository.deleteByUserId(user.requiredId()) - - return UserSaveStatus.DELETED - } - - /** - * @param name name of user - * @return UserSaveStatus - */ - @Transactional - fun approveUser( - name: String, - ): UserSaveStatus { - val user: User = userRepository.findByName(name).orNotFound() - - userRepository.save(user.apply { - this.status = UserStatus.ACTIVE - }) - - return UserSaveStatus.APPROVED - } - - /** - * @param name name of user - * @return UserSaveStatus - */ - @Transactional - fun banUser( - name: String, - ): UserSaveStatus { - val user: User = userRepository.findByName(name).orNotFound().apply { - this.status = UserStatus.BANNED - } - applicationEventPublisher.publishEvent(UserEvent(user)) - - userRepository.save(user) - - return UserSaveStatus.BANNED - } -} diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/VulnerabilityRatingService.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/VulnerabilityRatingService.kt index 636260aefc..da7afbc053 100644 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/VulnerabilityRatingService.kt +++ b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/VulnerabilityRatingService.kt @@ -3,6 +3,8 @@ package com.saveourtool.save.cosv.service import com.saveourtool.save.entities.Organization import com.saveourtool.save.entities.User import com.saveourtool.save.entitiescosv.VulnerabilityMetadata +import com.saveourtool.save.service.OrganizationService +import com.saveourtool.save.service.UserService import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/VulnerabilityService.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/VulnerabilityService.kt index 22d260d406..4045572cd2 100644 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/VulnerabilityService.kt +++ b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/VulnerabilityService.kt @@ -14,6 +14,8 @@ import com.saveourtool.save.entitiescosv.VulnerabilityMetadata import com.saveourtool.save.entitiescosv.VulnerabilityMetadataProject import com.saveourtool.save.filters.VulnerabilityFilter import com.saveourtool.save.info.UserInfo +import com.saveourtool.save.service.OrganizationService +import com.saveourtool.save.service.UserService import com.saveourtool.save.utils.* import org.springframework.data.domain.PageRequest diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/storage/RawCosvFileS3KeyManager.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/storage/RawCosvFileS3KeyManager.kt index 7deb9ae211..ab4da2274d 100644 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/storage/RawCosvFileS3KeyManager.kt +++ b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/storage/RawCosvFileS3KeyManager.kt @@ -1,13 +1,13 @@ package com.saveourtool.save.cosv.storage import com.saveourtool.save.cosv.repository.RawCosvFileRepository -import com.saveourtool.save.cosv.service.OrganizationService -import com.saveourtool.save.cosv.service.UserService import com.saveourtool.save.entities.cosv.RawCosvFileDto import com.saveourtool.save.entities.cosv.RawCosvFileStatus import com.saveourtool.save.entitiescosv.RawCosvFile import com.saveourtool.save.entitiescosv.RawCosvFile.Companion.toNewEntity import com.saveourtool.save.s3.S3OperationsProperties +import com.saveourtool.save.service.OrganizationService +import com.saveourtool.save.service.UserService import com.saveourtool.save.storage.concatS3Key import com.saveourtool.save.storage.key.AbstractS3KeyDtoManager import com.saveourtool.save.utils.BlockingBridge