Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactored permission validation in RolePermissionValidator #51

Merged
merged 1 commit into from
Sep 26, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Refactored permission validation in RolePermissionValidator to make i…
…t 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
  • Loading branch information
RonAzar committed Sep 26, 2024
commit e9d8183dfd03c861ae5abb2732e63e1578dd2685
Original file line number Diff line number Diff line change
@@ -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,31 +84,23 @@ 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()
}

@Path("update/status")
@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()
}
Original file line number Diff line number Diff line change
@@ -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)

Original file line number Diff line number Diff line change
@@ -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<EmployeeRole>,
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<EmployeeRole>): Boolean {
return requiredRoles.contains(role)
}
}
}