diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/BackendForCosvService.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/BackendForCosvService.kt index ff068ecc40..bf9ef49eab 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/BackendForCosvService.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/BackendForCosvService.kt @@ -3,6 +3,7 @@ package com.saveourtool.save.backend.service import com.saveourtool.save.backend.configs.ConfigProperties import com.saveourtool.save.backend.security.OrganizationPermissionEvaluator import com.saveourtool.save.backend.security.UserPermissionEvaluator +import com.saveourtool.save.domain.Role import com.saveourtool.save.entities.Organization import com.saveourtool.save.entities.User import com.saveourtool.save.entities.cosv.LnkVulnerabilityMetadataTag @@ -15,12 +16,14 @@ import java.nio.file.Path * Service for [IBackendService] to get required info for COSV from backend */ @Service +@Suppress("LongParameterList") class BackendForCosvService( private val organizationService: OrganizationService, private val userDetailsService: UserDetailsService, private val userPermissionEvaluator: UserPermissionEvaluator, private val organizationPermissionEvaluator: OrganizationPermissionEvaluator, private val tagService: TagService, + private val lnkUserOrganizationService: LnkUserOrganizationService, configProperties: ConfigProperties, ) : IBackendService { override val workingDir: Path = configProperties.workingDir @@ -48,4 +51,19 @@ class BackendForCosvService( identifier: String, tagName: Set ): List? = tagService.addVulnerabilityTags(identifier, tagName) + + override fun addVulnerabilityTag( + identifier: String, + tagName: String + ): LnkVulnerabilityMetadataTag = tagService.addVulnerabilityTag(identifier, tagName) + + override fun deleteVulnerabilityTag( + identifier: String, + tagName: String + ) = tagService.deleteVulnerabilityTag(identifier, tagName) + + override fun getGlobalRoleOrOrganizationRole( + authentication: Authentication, + organizationName: String, + ): Role = lnkUserOrganizationService.getGlobalRoleOrOrganizationRole(authentication, organizationName) } 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 e84c842af3..3447aec2e4 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,6 +4,8 @@ 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.repository.OrganizationRepository +import com.saveourtool.save.backend.repository.UserRepository import com.saveourtool.save.backend.security.OrganizationPermissionEvaluator import com.saveourtool.save.backend.security.ProjectPermissionEvaluator import com.saveourtool.save.backend.service.* diff --git a/save-cosv/build.gradle.kts b/save-cosv/build.gradle.kts index 49d80077bb..b8aa036599 100644 --- a/save-cosv/build.gradle.kts +++ b/save-cosv/build.gradle.kts @@ -6,6 +6,7 @@ plugins { } dependencies { + implementation(projects.authenticationService) api(projects.saveCloudCommon) api(libs.cosv4k) implementation(libs.spring.security.core) diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/backend/service/IBackendService.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/backend/service/IBackendService.kt index c65124bf8d..a83eae8305 100644 --- a/save-cosv/src/main/kotlin/com/saveourtool/save/backend/service/IBackendService.kt +++ b/save-cosv/src/main/kotlin/com/saveourtool/save/backend/service/IBackendService.kt @@ -2,6 +2,7 @@ package com.saveourtool.save.backend.service +import com.saveourtool.save.domain.Role import com.saveourtool.save.entities.Organization import com.saveourtool.save.entities.User import com.saveourtool.save.entities.cosv.LnkVulnerabilityMetadataTag @@ -67,11 +68,40 @@ interface IBackendService { /** * @param identifier [com.saveourtool.save.entities.cosv.VulnerabilityMetadata.identifier] - * @param tagName tag to add + * @param tagName tags to add * @return new [LnkVulnerabilityMetadataTag] */ fun addVulnerabilityTags( identifier: String, tagName: Set ): List? + + /** + * @param identifier [com.saveourtool.save.entities.cosv.VulnerabilityMetadata.identifier] + * @param tagName tag to add + * @return new [LnkVulnerabilityMetadataTag] + */ + fun addVulnerabilityTag( + identifier: String, + tagName: String + ): LnkVulnerabilityMetadataTag + + /** + * @param identifier [com.saveourtool.save.entities.cosv.VulnerabilityMetadata.identifier] + * @param tagName tag to delete + */ + fun deleteVulnerabilityTag( + identifier: String, + tagName: String + ) + + /** + * @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 } diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/vulnerability/VulnerabilityController.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/VulnerabilityController.kt similarity index 95% rename from save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/vulnerability/VulnerabilityController.kt rename to save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/VulnerabilityController.kt index f82d463713..8ede1f1d37 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/vulnerability/VulnerabilityController.kt +++ b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/controllers/VulnerabilityController.kt @@ -1,17 +1,20 @@ -package com.saveourtool.save.backend.controllers.vulnerability +package com.saveourtool.save.cosv.controllers import com.saveourtool.save.authservice.utils.username -import com.saveourtool.save.backend.security.VulnerabilityPermissionEvaluator -import com.saveourtool.save.backend.service.LnkUserOrganizationService -import com.saveourtool.save.backend.service.vulnerability.VulnerabilityService -import com.saveourtool.save.backend.utils.hasRole +import com.saveourtool.save.backend.service.IBackendService 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.VulnerabilityMetadataDtoList +import com.saveourtool.save.cosv.service.VulnerabilityService +import com.saveourtool.save.cosv.utils.hasRole import com.saveourtool.save.domain.Role import com.saveourtool.save.entities.cosv.VulnerabilityExt import com.saveourtool.save.entities.cosv.VulnerabilityMetadataDto -import com.saveourtool.save.entities.vulnerability.* +import com.saveourtool.save.entities.vulnerability.VulnerabilityDateDto +import com.saveourtool.save.entities.vulnerability.VulnerabilityProjectDto +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 @@ -33,8 +36,6 @@ import reactor.core.publisher.Flux import reactor.core.publisher.Mono import reactor.kotlin.core.publisher.toMono -typealias VulnerabilityMetadataDtoList = List - /** * Controller for working with vulnerabilities. */ @@ -48,7 +49,7 @@ class VulnerabilityController( private val vulnerabilityService: VulnerabilityService, private val vulnerabilityPermissionEvaluator: VulnerabilityPermissionEvaluator, private val cosvService: CosvService, - private val lnkUserOrganizationService: LnkUserOrganizationService, + private val backendService: IBackendService, ) { @PostMapping("/by-filter") @Operation( @@ -96,7 +97,7 @@ class VulnerabilityController( if (!isPublicVulnerabilities && authentication != null) { val isSuperAdmin = authentication.hasRole(Role.SUPER_ADMIN) val isOwner = filter.authorName?.let { it == authentication.username() } ?: false - val roleInOrganization = filter.organizationName?.let { lnkUserOrganizationService.getGlobalRoleOrOrganizationRole(authentication, it) } + val roleInOrganization = filter.organizationName?.let { backendService.getGlobalRoleOrOrganizationRole(authentication, it) } val isAdminInOrganization = roleInOrganization?.isHigherOrEqualThan(Role.ADMIN) ?: false val isHasAdditionalRights = isSuperAdmin || isOwner || isAdminInOrganization @@ -386,5 +387,5 @@ class VulnerabilityController( .switchIfEmptyToResponseException(HttpStatus.FORBIDDEN) { "Permissions required for comment deletion were not granted." } .flatMap { blockingToMono { vulnerabilityService.deleteUser(userName, it) } } .switchIfEmptyToNotFound { "Could not find user." } - .map { StringResponse.ok("Successfully deleted user from vulnerability.") } + .map { ResponseEntity.ok("Successfully deleted user from vulnerability.") } } diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/repository/OrganizationRepository.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/repository/OrganizationRepository.kt new file mode 100644 index 0000000000..83f939a55d --- /dev/null +++ b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/repository/OrganizationRepository.kt @@ -0,0 +1,36 @@ +package com.saveourtool.save.cosv.repository + +import com.saveourtool.save.entities.Organization +import org.springframework.data.jpa.repository.Query +import org.springframework.data.repository.query.Param +import org.springframework.stereotype.Repository + +/** + * The repository of organization entities + */ +@Repository +interface OrganizationRepository { + /** + * @param organizationName organization name for update + * @param rating new organization rating + * @return updated organization + */ + @Query( + value = "update save_cloud.organization o set o.rating = :rating where o.name = :organization_name", + nativeQuery = true, + ) + fun updateOrganization( + @Param("organization_name") organizationName: String, + @Param("rating") rating: Long, + ) + + /** + * @param name name of organization + * @return found [Organization] by name + */ + @Query( + value = "select * from save_cloud.organization where name = :name", + nativeQuery = true, + ) + fun getOrganizationByName(@Param("name") name: String): Organization +} diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/repository/TagRepository.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/repository/TagRepository.kt new file mode 100644 index 0000000000..e3d872664e --- /dev/null +++ b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/repository/TagRepository.kt @@ -0,0 +1,40 @@ +package com.saveourtool.save.cosv.repository + +import com.saveourtool.save.entities.Tag +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 + +/** + * The repository of tag entities. + */ +@Repository +interface TagRepository { + /** + * Find [Tag] by its [Tag.name] + * + * @param name tag name + * @return [Tag] if found, null otherwise + */ + @Query( + value = "select * from save_cloud.tag t where t.name = :name", + nativeQuery = true, + ) + fun findByName(@Param("name") name: String): Tag? + + /** + * @param name name of tag + * @return save tag + */ + @Transactional + @Modifying + @Query( + value = "insert into save_cloud.tag (name) values (:name)", + nativeQuery = true, + ) + fun saveTag( + @Param("name") name: String, + ) +} diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/repository/UserRepository.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/repository/UserRepository.kt new file mode 100644 index 0000000000..5e67225b9f --- /dev/null +++ b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/repository/UserRepository.kt @@ -0,0 +1,36 @@ +package com.saveourtool.save.cosv.repository + +import com.saveourtool.save.entities.User +import org.springframework.data.jpa.repository.Query +import org.springframework.data.repository.query.Param +import org.springframework.stereotype.Repository + +/** + * Repository to access data about users + */ +@Repository +interface UserRepository { + /** + * @param userName user name for update + * @param rating new user rating + * @return updated user + */ + @Query( + value = "update save_cloud.user u set u.rating = :rating where u.name = :user_name", + nativeQuery = true, + ) + fun updateUser( + @Param("user_name") userName: String, + @Param("rating") rating: Long, + ) + + /** + * @param name name of organization + * @return found [User] by name + */ + @Query( + value = "select * from save_cloud.user where name = :name", + nativeQuery = true, + ) + fun getUserByName(@Param("name") name: String): User +} diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/security/VulnerabilityPermissionEvaluator.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/security/VulnerabilityPermissionEvaluator.kt similarity index 92% rename from save-backend/src/main/kotlin/com/saveourtool/save/backend/security/VulnerabilityPermissionEvaluator.kt rename to save-cosv/src/main/kotlin/com/saveourtool/save/cosv/security/VulnerabilityPermissionEvaluator.kt index 527f7b93d1..88158369fd 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/security/VulnerabilityPermissionEvaluator.kt +++ b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/security/VulnerabilityPermissionEvaluator.kt @@ -1,7 +1,7 @@ -package com.saveourtool.save.backend.security +package com.saveourtool.save.cosv.security -import com.saveourtool.save.backend.service.vulnerability.VulnerabilityService -import com.saveourtool.save.backend.utils.hasRole +import com.saveourtool.save.cosv.service.VulnerabilityService +import com.saveourtool.save.cosv.utils.hasRole import com.saveourtool.save.domain.Role import com.saveourtool.save.entities.cosv.VulnerabilityMetadataDto import com.saveourtool.save.entities.vulnerability.VulnerabilityStatus diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/OrganizationService.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/OrganizationService.kt new file mode 100644 index 0000000000..7075eb75ec --- /dev/null +++ b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/OrganizationService.kt @@ -0,0 +1,23 @@ +package com.saveourtool.save.cosv.service + +import com.saveourtool.save.cosv.repository.OrganizationRepository +import com.saveourtool.save.entities.Organization + +/** + * Service for organization + */ +class OrganizationService( + private val organizationRepository: OrganizationRepository, +) { + /** + * @param organization organization for update + * @return updated organization + */ + fun saveUser(organization: Organization) = organizationRepository.updateOrganization(organization.name, organization.rating) + + /** + * @param name + * @return organization with [name] + */ + fun getOrganizationByName(name: String): Organization = organizationRepository.getOrganizationByName(name) +} diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/TagService.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/TagService.kt new file mode 100644 index 0000000000..75d47655ef --- /dev/null +++ b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/TagService.kt @@ -0,0 +1,22 @@ +package com.saveourtool.save.cosv.service + +import com.saveourtool.save.cosv.repository.TagRepository +import com.saveourtool.save.entities.Tag + +/** + * Service for tag + */ +class TagService( + private val tagRepository: TagRepository, +) { + /** + * @param name name of tag + */ + fun saveTag(name: String) = tagRepository.saveTag(name) + + /** + * @param name + * @return tag with [name] + */ + fun findTagByName(name: String): Tag? = tagRepository.findByName(name) +} 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 new file mode 100644 index 0000000000..175597b4de --- /dev/null +++ b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/UserService.kt @@ -0,0 +1,23 @@ +package com.saveourtool.save.cosv.service + +import com.saveourtool.save.cosv.repository.UserRepository +import com.saveourtool.save.entities.User + +/** + * Service for user + */ +class UserService( + private val userRepository: UserRepository, +) { + /** + * @param user user for update + * @return updated user + */ + fun saveUser(user: User) = userRepository.updateUser(user.name, user.rating) + + /** + * @param name + * @return user with [name] + */ + fun getUserByName(name: String): User = userRepository.getUserByName(name) +} diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/vulnerability/VulnerabilityService.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/VulnerabilityService.kt similarity index 95% rename from save-backend/src/main/kotlin/com/saveourtool/save/backend/service/vulnerability/VulnerabilityService.kt rename to save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/VulnerabilityService.kt index 4357a17a0b..5e79eb8d38 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/vulnerability/VulnerabilityService.kt +++ b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/service/VulnerabilityService.kt @@ -1,12 +1,9 @@ -package com.saveourtool.save.backend.service.vulnerability +package com.saveourtool.save.cosv.service import com.saveourtool.save.authservice.utils.userId -import com.saveourtool.save.backend.repository.UserRepository -import com.saveourtool.save.backend.service.TagService -import com.saveourtool.save.backend.utils.hasRole +import com.saveourtool.save.backend.service.IBackendService import com.saveourtool.save.cosv.repository.* -import com.saveourtool.save.cosv.service.CosvService -import com.saveourtool.save.cosv.service.VulnerabilityRatingService +import com.saveourtool.save.cosv.utils.hasRole import com.saveourtool.save.domain.Role import com.saveourtool.save.entities.Organization import com.saveourtool.save.entities.Tag @@ -40,13 +37,12 @@ import kotlinx.datetime.LocalDateTime class VulnerabilityService( private val vulnerabilityMetadataProjectRepository: VulnerabilityMetadataProjectRepository, private val lnkVulnerabilityMetadataUserRepository: LnkVulnerabilityMetadataUserRepository, - private val userRepository: UserRepository, private val cosvService: CosvService, private val cosvRepository: CosvRepository, private val vulnerabilityMetadataRepository: VulnerabilityMetadataRepository, private val lnkVulnerabilityMetadataTagRepository: LnkVulnerabilityMetadataTagRepository, - private val tagService: TagService, private val vulnerabilityRatingService: VulnerabilityRatingService, + private val backendService: IBackendService, ) { private fun List.toTagMap() = lnkVulnerabilityMetadataTagRepository.findByVulnerabilityMetadataIdIn(this.map { it.requiredId() }) .map { link -> link.vulnerabilityMetadata to link.tag } @@ -65,7 +61,7 @@ class VulnerabilityService( * @return count of vulnerabilities */ fun countByUserNameAndStatus(userName: String, status: VulnerabilityStatus): Int { - val user = userRepository.findByName(userName).orNotFound { + val user = backendService.getUserByName(userName).orNotFound { "Not found user by name = $userName" } return vulnerabilityMetadataRepository.countByUserIdAndStatus(user.requiredId(), status) @@ -203,10 +199,10 @@ class VulnerabilityService( // performance issues are not possible here as tags number is very limited, but need to FixMe it (updatedTags - existingTags).forEach { - tagService.addVulnerabilityTag(vulnerabilityId, it) + backendService.addVulnerabilityTag(vulnerabilityId, it) } (existingTags - updatedTags).forEach { - tagService.deleteVulnerabilityTag(vulnerabilityId, it) + backendService.deleteVulnerabilityTag(vulnerabilityId, it) } } else { throw ResponseStatusException(HttpStatus.FORBIDDEN) @@ -346,7 +342,7 @@ class VulnerabilityService( userName: String, identifier: String, ): Mono = blockingToMono { - val user = userRepository.findByName(userName).orNotFound { "Not found user by name $userName" } + val user = backendService.getUserByName(userName).orNotFound { "Not found user by name $userName" } vulnerabilityMetadataRepository.findByIdentifier(identifier)?.let { metadata -> lnkVulnerabilityMetadataUserRepository.save( LnkVulnerabilityMetadataUser( @@ -358,7 +354,7 @@ class VulnerabilityService( }.flatMap { cosvService.update(identifier) { cosv -> blockingToMono { - userRepository.findByName(userName).orNotFound { "Not found user by name $userName" } + backendService.getUserByName(userName).orNotFound { "Not found user by name $userName" } } .map { user -> cosv.copy( @@ -383,7 +379,7 @@ class VulnerabilityService( }.flatMap { cosvService.update(identifier) { cosv -> blockingToMono { - userRepository.findByName(userName).orNotFound { "Not found user by name $userName" } + backendService.getUserByName(userName).orNotFound { "Not found user by name $userName" } } .map { user -> cosv.copy( diff --git a/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/utils/AuthenticationUtils.kt b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/utils/AuthenticationUtils.kt new file mode 100644 index 0000000000..231523271c --- /dev/null +++ b/save-cosv/src/main/kotlin/com/saveourtool/save/cosv/utils/AuthenticationUtils.kt @@ -0,0 +1,14 @@ +@file:Suppress("HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE") + +package com.saveourtool.save.cosv.utils + +import com.saveourtool.save.domain.Role +import org.springframework.security.core.Authentication + +/** + * 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() }