From e9d8183dfd03c861ae5abb2732e63e1578dd2685 Mon Sep 17 00:00:00 2001 From: Ron Azar Date: Thu, 26 Sep 2024 12:44:06 +0300 Subject: [PATCH] Refactored permission validation in RolePermissionValidator to make it less dependent on specific API endpoints. Moved role and permission checks closer to where they are used in resource classes, enhancing maintainability and adhering to clean code principles. recognize abortWith doesn't work properly --- .../resource/FeedbackResource.kt | 70 ++++++++----------- .../resource/ResponseResource.kt | 24 +++---- .../validation/RolePermissionValidator.kt | 46 ++++++------ 3 files changed, 62 insertions(+), 78 deletions(-) diff --git a/src/main/kotlin/com/hibob/academy/employee_feedback_feature/resource/FeedbackResource.kt b/src/main/kotlin/com/hibob/academy/employee_feedback_feature/resource/FeedbackResource.kt index df4c838a7..63e6a8186 100644 --- a/src/main/kotlin/com/hibob/academy/employee_feedback_feature/resource/FeedbackResource.kt +++ b/src/main/kotlin/com/hibob/academy/employee_feedback_feature/resource/FeedbackResource.kt @@ -9,9 +9,7 @@ import jakarta.ws.rs.core.MediaType import jakarta.ws.rs.core.Response import org.springframework.stereotype.Controller import com.hibob.academy.employee_feedback_feature.validation.RolePermissionValidator.Companion.extractClaimAsLong -import com.hibob.academy.employee_feedback_feature.validation.RolePermissionValidator.Companion.extractRole -import com.hibob.academy.employee_feedback_feature.validation.RolePermissionValidator.Companion.hasPermission -import com.hibob.academy.employee_feedback_feature.validation.RolePermissionValidator.Companion.Permissions +import com.hibob.academy.employee_feedback_feature.validation.RolePermissionValidator.Companion.validatePermissions @Controller @Path("/api/feedback") @@ -20,9 +18,13 @@ class FeedbackResource(private val feedbackService: FeedbackService) { @POST @Consumes(MediaType.APPLICATION_JSON) fun submitFeedback(@Context requestContext: ContainerRequestContext, newFeedback: FeedbackRequest): Response { - val companyId = extractClaimAsLong(requestContext, "companyId")!! + val companyId = validatePermissions( + requestContext, setOf(EmployeeRole.EMPLOYEE, EmployeeRole.ADMIN, EmployeeRole.HR, EmployeeRole.MANAGER), + "UNAUTHORIZED: You do not have access to submit feedbacks" + ) + val employeeId = if (!newFeedback.isAnonymous) { - extractClaimAsLong(requestContext, "employeeId")!! + extractClaimAsLong(requestContext, "employeeId") } else { null } @@ -42,8 +44,11 @@ class FeedbackResource(private val feedbackService: FeedbackService) { @GET @Produces(MediaType.APPLICATION_JSON) fun getFeedbackHistory(@Context requestContext: ContainerRequestContext): Response { - val companyId = extractClaimAsLong(requestContext, "companyId")!! - val employeeId = extractClaimAsLong(requestContext, "employeeId")!! + val companyId = validatePermissions( + requestContext, setOf(EmployeeRole.EMPLOYEE, EmployeeRole.ADMIN, EmployeeRole.HR, EmployeeRole.MANAGER), + "UNAUTHORIZED: You do not have access to get history" + ) + val employeeId = extractClaimAsLong(requestContext, "employeeId") return Response.ok(feedbackService.getFeedbackHistory(companyId, employeeId)).build() } @@ -52,31 +57,24 @@ class FeedbackResource(private val feedbackService: FeedbackService) { @GET @Produces(MediaType.APPLICATION_JSON) fun getAllFeedbacks(@Context requestContext: ContainerRequestContext): Response { - val companyId = extractClaimAsLong(requestContext, "companyId")!! - val employeeRole = extractRole(requestContext)!! + val companyId = validatePermissions( + requestContext, setOf(EmployeeRole.HR, EmployeeRole.ADMIN), + "UNAUTHORIZED: You do not have access to view feedbacks" + ) - return if (hasPermission(employeeRole, Permissions.VIEW_ALL_FEEDBACKS)) { - Response.ok(feedbackService.getAllFeedbacks(companyId)).build() - } else { - Response.status(Response.Status.UNAUTHORIZED) - .entity("UNAUTHORIZED: You do not have access to view feedbacks") - .build() - } + return Response.ok(feedbackService.getAllFeedbacks(companyId)).build() } @Path("view/filter") @POST @Produces(MediaType.APPLICATION_JSON) fun getFilteredFeedbacks(@Context requestContext: ContainerRequestContext, filter: FeedbackFilter): Response { - val companyId = extractClaimAsLong(requestContext, "companyId")!! - val employeeRole = extractRole(requestContext)!! + val companyId = validatePermissions( + requestContext, setOf(EmployeeRole.HR, EmployeeRole.ADMIN), + "UNAUTHORIZED: You do not have access to view feedbacks" + ) - return if (hasPermission(employeeRole, Permissions.VIEW_ALL_FEEDBACKS)) { - Response.ok(feedbackService.getFeedbacksUsingFilter(filter, companyId)).build() - } else { - Response.status(Response.Status.UNAUTHORIZED) - .entity("UNAUTHORIZED: You do not have access to view feedbacks").build() - } + return Response.ok(feedbackService.getFeedbacksUsingFilter(filter, companyId)).build() } @Path("view/status/{feedbackId}") @@ -86,16 +84,12 @@ class FeedbackResource(private val feedbackService: FeedbackService) { @Context requestContext: ContainerRequestContext, @PathParam("feedbackId") feedbackId: Long ): Response { - val role = extractRole(requestContext)!! - val companyId = extractClaimAsLong(requestContext, "companyId")!! + val companyId = validatePermissions( + requestContext, setOf(EmployeeRole.HR, EmployeeRole.ADMIN), + "UNAUTHORIZED: You do not have access to view feedback status!" + ) val searchedFeedback = SearchedFeedback(companyId, feedbackId) - if (!hasPermission(role, Permissions.VIEW_ALL_FEEDBACKS)) { - - return Response.status(Response.Status.UNAUTHORIZED) - .entity("UNAUTHORIZED: You do not have access to view feedback status!").build() - } - return Response.ok("feedback status: ${feedbackService.getFeedbackStatus(searchedFeedback)}").build() } @@ -103,14 +97,10 @@ class FeedbackResource(private val feedbackService: FeedbackService) { @PUT @Produces(MediaType.APPLICATION_JSON) fun updateStatus(@Context requestContext: ContainerRequestContext, updateFeedback: UpdateFeedbackStatus): Response { - val companyId = extractClaimAsLong(requestContext, "companyId")!! - val role = extractRole(requestContext)!! - - if (!hasPermission(role, Permissions.CHANGE_FEEDBACK_STATUS)) { - - return Response.status(Response.Status.UNAUTHORIZED) - .entity("UNAUTHORIZED: You do not have access to change feedback status!").build() - } + val companyId = validatePermissions( + requestContext, setOf(EmployeeRole.HR), + "UNAUTHORIZED: You do not have access to change feedback status!" + ) return Response.ok(feedbackService.updateFeedbackStatus(updateFeedback, companyId)).build() } diff --git a/src/main/kotlin/com/hibob/academy/employee_feedback_feature/resource/ResponseResource.kt b/src/main/kotlin/com/hibob/academy/employee_feedback_feature/resource/ResponseResource.kt index bfca0c58a..f0090a70c 100644 --- a/src/main/kotlin/com/hibob/academy/employee_feedback_feature/resource/ResponseResource.kt +++ b/src/main/kotlin/com/hibob/academy/employee_feedback_feature/resource/ResponseResource.kt @@ -1,11 +1,10 @@ package com.hibob.academy.employee_feedback_feature.resource +import com.hibob.academy.employee_feedback_feature.dao.EmployeeRole import com.hibob.academy.employee_feedback_feature.dao.ResponseSubmission import com.hibob.academy.employee_feedback_feature.service.ResponseService -import com.hibob.academy.employee_feedback_feature.validation.RolePermissionValidator.Companion.Permissions import com.hibob.academy.employee_feedback_feature.validation.RolePermissionValidator.Companion.extractClaimAsLong -import com.hibob.academy.employee_feedback_feature.validation.RolePermissionValidator.Companion.extractRole -import com.hibob.academy.employee_feedback_feature.validation.RolePermissionValidator.Companion.hasPermission +import com.hibob.academy.employee_feedback_feature.validation.RolePermissionValidator.Companion.validatePermissions import jakarta.ws.rs.Consumes import jakarta.ws.rs.POST import jakarta.ws.rs.Path @@ -21,19 +20,12 @@ class ResponseResource(private val responseService: ResponseService) { @Path("/submit") @POST @Consumes(MediaType.APPLICATION_JSON) - fun submitResponse( - @Context requestContext: ContainerRequestContext, - newFeedbackResponse: ResponseSubmission - ): Response { - val companyId = extractClaimAsLong(requestContext, "companyId")!! - val role = extractRole(requestContext)!! - val employeeId = extractClaimAsLong(requestContext, "employeeId")!! - - if (!hasPermission(role, Permissions.RESPONSE_TO_FEEDBACK)) { - - return Response.status(Response.Status.UNAUTHORIZED) - .entity("UNAUTHORIZED: You do not have permission to respond to feedbacks").build() - } + fun submitResponse(@Context requestContext: ContainerRequestContext, newFeedbackResponse: ResponseSubmission): Response { + val companyId = validatePermissions( + requestContext, setOf(EmployeeRole.HR), + "UNAUTHORIZED: You do not have permission to respond to feedbacks" + ) + val employeeId = extractClaimAsLong(requestContext, "employeeId") val responseId = responseService.submitResponse(newFeedbackResponse, employeeId, companyId) diff --git a/src/main/kotlin/com/hibob/academy/employee_feedback_feature/validation/RolePermissionValidator.kt b/src/main/kotlin/com/hibob/academy/employee_feedback_feature/validation/RolePermissionValidator.kt index 973b96936..5c1fc54a1 100644 --- a/src/main/kotlin/com/hibob/academy/employee_feedback_feature/validation/RolePermissionValidator.kt +++ b/src/main/kotlin/com/hibob/academy/employee_feedback_feature/validation/RolePermissionValidator.kt @@ -1,47 +1,49 @@ package com.hibob.academy.employee_feedback_feature.validation import com.hibob.academy.employee_feedback_feature.dao.EmployeeRole +import jakarta.ws.rs.BadRequestException import jakarta.ws.rs.container.ContainerRequestContext -import jakarta.ws.rs.core.Response class RolePermissionValidator { companion object { - fun extractClaimAsLong(requestContext: ContainerRequestContext, claim: String): Long? { + fun extractClaimAsLong(requestContext: ContainerRequestContext, claim: String): Long { return (requestContext.getProperty(claim) as? Number)?.toLong() ?: run { - requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).entity("UNAUTHORIZED: Missing or invalid $claim").build()) - null + + throw BadRequestException("UNAUTHORIZED: Missing or invalid $claim") } } - fun extractRole(requestContext: ContainerRequestContext): EmployeeRole? { + private fun extractRole(requestContext: ContainerRequestContext): EmployeeRole { return (requestContext.getProperty("role") as? String)?.uppercase()?.let { try { EmployeeRole.valueOf(it) } catch (e: IllegalArgumentException) { - requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).entity("UNAUTHORIZED: Invalid role").build()) - null + + throw BadRequestException("UNAUTHORIZED: Invalid role") } } ?: run { - requestContext.abortWith( - Response.status(Response.Status.UNAUTHORIZED).entity("UNAUTHORIZED: Missing role").build() - ) - null + + throw BadRequestException("UNAUTHORIZED: Missing role") } } - enum class Permissions { - VIEW_ALL_FEEDBACKS, - CHANGE_FEEDBACK_STATUS, - RESPONSE_TO_FEEDBACK - } + fun validatePermissions( + requestContext: ContainerRequestContext, + requiredRoles: Set, + errorMessage: String + ): Long { + val companyId = extractClaimAsLong(requestContext, "companyId") + val role = extractRole(requestContext) - private val rolePermissions = mapOf( - EmployeeRole.HR to setOf(Permissions.VIEW_ALL_FEEDBACKS, Permissions.CHANGE_FEEDBACK_STATUS, Permissions.RESPONSE_TO_FEEDBACK), - EmployeeRole.ADMIN to setOf(Permissions.VIEW_ALL_FEEDBACKS), - ) + if (!hasPermission(role, requiredRoles)) { + throw BadRequestException(errorMessage) + } + + return companyId + } - fun hasPermission(role: EmployeeRole, permission: Permissions): Boolean { - return rolePermissions[role]?.contains(permission) ?: false + private fun hasPermission(role: EmployeeRole, requiredRoles: Set): Boolean { + return requiredRoles.contains(role) } } } \ No newline at end of file