feature/#30-group-authentication #31
|
@ -37,9 +37,9 @@ object Constants {
|
|||
const val RECIPE_IMAGES = "$IMAGES/recipes"
|
||||
}
|
||||
|
||||
object HeaderNames {
|
||||
const val ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers"
|
||||
const val AUTHORIZATION = "Authorization"
|
||||
object JwtType {
|
||||
const val USER = 0
|
||||
const val GROUP = 1
|
||||
}
|
||||
|
||||
object ModelNames {
|
||||
|
|
|
@ -1,30 +1,56 @@
|
|||
package dev.fyloz.colorrecipesexplorer.config.security
|
||||
|
||||
import dev.fyloz.colorrecipesexplorer.dtos.account.GroupTokenDto
|
||||
import dev.fyloz.colorrecipesexplorer.dtos.account.UserDetails
|
||||
import dev.fyloz.colorrecipesexplorer.exception.NotFoundException
|
||||
import dev.fyloz.colorrecipesexplorer.logic.account.GroupTokenLogic
|
||||
import mu.KotlinLogging
|
||||
import org.springframework.security.authentication.AuthenticationProvider
|
||||
import org.springframework.security.authentication.BadCredentialsException
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.AuthenticationException
|
||||
import java.util.*
|
||||
|
||||
class GroupTokenAuthenticationProvider(private val groupTokenLogic: GroupTokenLogic) : AuthenticationProvider {
|
||||
private val logger = KotlinLogging.logger {}
|
||||
|
||||
override fun authenticate(authentication: Authentication): Authentication {
|
||||
val groupAuthenticationToken = authentication as GroupAuthenticationToken
|
||||
val groupToken = retrieveGroupToken(groupAuthenticationToken.id)
|
||||
|
||||
val groupToken = try {
|
||||
retrieveGroupToken(groupAuthenticationToken.id)
|
||||
} catch (e: AuthenticationException) {
|
||||
logger.debug(e.message)
|
||||
throw e
|
||||
}
|
||||
|
||||
val userDetails =
|
||||
UserDetails(groupToken.id.toString(), groupToken.name, "", groupToken.group, groupToken.group.permissions)
|
||||
UserDetails(
|
||||
groupToken.id.toString(),
|
||||
groupToken.name,
|
||||
"",
|
||||
groupToken.group,
|
||||
groupToken.group.permissions,
|
||||
true
|
||||
)
|
||||
return UsernamePasswordAuthenticationToken(userDetails, null, userDetails.authorities)
|
||||
}
|
||||
|
||||
override fun supports(authentication: Class<*>) =
|
||||
authentication.isAssignableFrom(GroupAuthenticationToken::class.java)
|
||||
|
||||
private fun retrieveGroupToken(id: UUID) = try {
|
||||
groupTokenLogic.getById(id)
|
||||
} catch (_: NotFoundException) {
|
||||
throw BadCredentialsException("Failed to find group token with id '$id'")
|
||||
private fun retrieveGroupToken(id: UUID): GroupTokenDto {
|
||||
val groupToken = try {
|
||||
groupTokenLogic.getById(id)
|
||||
} catch (_: NotFoundException) {
|
||||
throw BadCredentialsException("Failed to find group token with id '$id'")
|
||||
}
|
||||
|
||||
if (groupTokenLogic.isDisabled(groupToken.id.toString())) {
|
||||
throw BadCredentialsException("Group token '${groupToken.id}' is disabled")
|
||||
}
|
||||
|
||||
return groupToken
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,7 +101,7 @@ abstract class BaseSecurityConfig(
|
|||
BasicAuthenticationFilter::class.java
|
||||
)
|
||||
.addFilter(
|
||||
JwtAuthorizationFilter(jwtLogic, authenticationManager())
|
||||
JwtAuthorizationFilter(jwtLogic, groupTokenLogic, authManager)
|
||||
)
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
.and()
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
package dev.fyloz.colorrecipesexplorer.config.security.filters
|
||||
|
||||
import dev.fyloz.colorrecipesexplorer.Constants
|
||||
import dev.fyloz.colorrecipesexplorer.dtos.account.UserJwt
|
||||
import dev.fyloz.colorrecipesexplorer.logic.account.GroupTokenLogic
|
||||
import dev.fyloz.colorrecipesexplorer.logic.account.JwtLogic
|
||||
import dev.fyloz.colorrecipesexplorer.logic.account.UserJwt
|
||||
import dev.fyloz.colorrecipesexplorer.utils.parseBearer
|
||||
import io.jsonwebtoken.ExpiredJwtException
|
||||
import mu.KotlinLogging
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.AuthenticationException
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter
|
||||
import org.springframework.web.util.WebUtils
|
||||
|
@ -17,10 +20,11 @@ import javax.servlet.http.HttpServletResponse
|
|||
|
||||
class JwtAuthorizationFilter(
|
||||
private val jwtLogic: JwtLogic,
|
||||
private val groupTokenLogic: GroupTokenLogic,
|
||||
authenticationManager: AuthenticationManager
|
||||
) : BasicAuthenticationFilter(authenticationManager) {
|
||||
override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, chain: FilterChain) {
|
||||
val authorizationCookie = WebUtils.getCookie(request, Constants.HeaderNames.AUTHORIZATION)
|
||||
val authorizationCookie = WebUtils.getCookie(request, Constants.CookieNames.AUTHORIZATION)
|
||||
|
||||
// If there is no authorization cookie, the user is not authenticated
|
||||
if (authorizationCookie == null) {
|
||||
|
@ -30,6 +34,8 @@ class JwtAuthorizationFilter(
|
|||
|
||||
val authorizationToken = authorizationCookie.value
|
||||
if (!isJwtValid(authorizationToken)) {
|
||||
logger.debug("Received request with invalid ${Constants.CookieNames.AUTHORIZATION} cookie")
|
||||
|
||||
chain.doFilter(request, response)
|
||||
return
|
||||
}
|
||||
|
@ -47,8 +53,14 @@ class JwtAuthorizationFilter(
|
|||
val jwt = parseBearer(authorizationToken)
|
||||
val user = jwtLogic.parseUserJwt(jwt)
|
||||
|
||||
if (user.isGroup && groupTokenLogic.isDisabled(user.id)) {
|
||||
logger.debug("Rejected authorization for disabled group token '${user.id}'")
|
||||
return null
|
||||
}
|
||||
|
||||
getAuthentication(user)
|
||||
} catch (_: ExpiredJwtException) {
|
||||
logger.debug("Rejected authorization for expired JWT")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ data class GroupTokenDto(
|
|||
|
||||
val name: String,
|
||||
|
||||
val isValid: Boolean,
|
||||
val enabled: Boolean,
|
||||
|
||||
val group: GroupDto
|
||||
)
|
||||
|
|
|
@ -5,7 +5,7 @@ import dev.fyloz.colorrecipesexplorer.Constants
|
|||
import dev.fyloz.colorrecipesexplorer.SpringUserDetails
|
||||
import dev.fyloz.colorrecipesexplorer.dtos.EntityDto
|
||||
import dev.fyloz.colorrecipesexplorer.model.account.Permission
|
||||
import dev.fyloz.colorrecipesexplorer.model.account.toAuthority
|
||||
import org.springframework.security.core.GrantedAuthority
|
||||
import java.time.LocalDateTime
|
||||
import javax.validation.constraints.NotBlank
|
||||
import javax.validation.constraints.Size
|
||||
|
@ -67,12 +67,15 @@ data class UserUpdateDto(
|
|||
|
||||
data class UserLoginRequestDto(val id: Long, val password: String)
|
||||
|
||||
data class UserJwt(val id: String, val authorities: Collection<GrantedAuthority>, val isGroup: Boolean)
|
||||
|
||||
class UserDetails(
|
||||
val id: String,
|
||||
private val username: String,
|
||||
private val password: String,
|
||||
val group: GroupDto?,
|
||||
val permissions: Collection<Permission>
|
||||
val permissions: Collection<Permission>,
|
||||
val isGroup: Boolean = false
|
||||
) : SpringUserDetails {
|
||||
constructor(user: UserDto) : this(user.id.toString(), user.fullName, user.password, user.group, user.permissions)
|
||||
|
||||
|
@ -94,4 +97,4 @@ data class UserLoginResponse(
|
|||
val groupId: Long?,
|
||||
val groupName: String?,
|
||||
val permissions: Collection<Permission>
|
||||
)
|
||||
)
|
||||
|
|
|
@ -8,13 +8,17 @@ import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException
|
|||
import dev.fyloz.colorrecipesexplorer.exception.NotFoundException
|
||||
import dev.fyloz.colorrecipesexplorer.logic.BaseLogic
|
||||
import dev.fyloz.colorrecipesexplorer.service.account.GroupTokenService
|
||||
import java.util.UUID
|
||||
import java.util.*
|
||||
import javax.annotation.PostConstruct
|
||||
|
||||
interface GroupTokenLogic {
|
||||
fun isDisabled(id: String): Boolean
|
||||
fun getAll(): Collection<GroupTokenDto>
|
||||
fun getById(id: String): GroupTokenDto
|
||||
fun getById(id: UUID): GroupTokenDto
|
||||
fun save(dto: GroupTokenSaveDto): GroupTokenDto
|
||||
fun enable(id: String): GroupTokenDto
|
||||
fun disable(id: String): GroupTokenDto
|
||||
fun deleteById(id: String)
|
||||
}
|
||||
|
||||
|
@ -24,30 +28,50 @@ class DefaultGroupTokenLogic(private val service: GroupTokenService, private val
|
|||
private val typeName = Constants.ModelNames.GROUP_TOKEN
|
||||
private val typeNameLowerCase = typeName.lowercase()
|
||||
|
||||
override fun getAll() = service.getAll()
|
||||
private val enabledTokensCache = hashSetOf<String>()
|
||||
|
||||
@PostConstruct
|
||||
fun initEnabledTokensCache() {
|
||||
val tokensIds = getAll().filter { it.enabled }.map { it.id.toString() }
|
||||
enabledTokensCache.addAll(tokensIds)
|
||||
}
|
||||
|
||||
override fun isDisabled(id: String) = !enabledTokensCache.contains(id)
|
||||
override fun getAll() = service.getAll()
|
||||
override fun getById(id: String) = getById(UUID.fromString(id))
|
||||
|
||||
override fun getById(id: UUID) =
|
||||
service.getById(id) ?: throw notFoundException(value = id)
|
||||
override fun getById(id: UUID) = service.getById(id) ?: throw notFoundException(value = id)
|
||||
|
||||
override fun save(dto: GroupTokenSaveDto): GroupTokenDto {
|
||||
throwIfNameAlreadyExists(dto.name)
|
||||
|
||||
val token = GroupTokenDto(
|
||||
generateUniqueUUIDForName(dto.name),
|
||||
dto.name,
|
||||
true,
|
||||
groupLogic.getById(dto.groupId)
|
||||
generateUniqueUUIDForName(dto.name), dto.name, true, groupLogic.getById(dto.groupId)
|
||||
)
|
||||
|
||||
return service.save(token)
|
||||
val savedToken = service.save(token)
|
||||
enabledTokensCache.add(savedToken.id.toString())
|
||||
|
||||
return savedToken
|
||||
}
|
||||
|
||||
override fun enable(id: String) = setEnabled(id, true).also {
|
||||
enabledTokensCache.add(id)
|
||||
}
|
||||
|
||||
override fun disable(id: String) = setEnabled(id, false).also {
|
||||
enabledTokensCache.remove(id)
|
||||
}
|
||||
|
||||
override fun deleteById(id: String) {
|
||||
enabledTokensCache.remove(id)
|
||||
service.deleteById(UUID.fromString(id))
|
||||
}
|
||||
|
||||
private fun setEnabled(id: String, enabled: Boolean) = with(getById(id)) {
|
||||
service.save(this.copy(enabled = enabled))
|
||||
}
|
||||
|
||||
private fun generateUniqueUUIDForName(name: String): UUID {
|
||||
var id = generateUUIDForName(name)
|
||||
|
||||
|
@ -59,8 +83,7 @@ class DefaultGroupTokenLogic(private val service: GroupTokenService, private val
|
|||
return id
|
||||
}
|
||||
|
||||
private fun generateUUIDForName(name: String) =
|
||||
UUID.nameUUIDFromBytes(name.toByteArray())
|
||||
private fun generateUUIDForName(name: String) = UUID.nameUUIDFromBytes(name.toByteArray())
|
||||
|
||||
private fun throwIfNameAlreadyExists(name: String) {
|
||||
if (service.existsByName(name)) {
|
||||
|
|
|
@ -4,14 +4,13 @@ import com.fasterxml.jackson.databind.ObjectMapper
|
|||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import dev.fyloz.colorrecipesexplorer.config.properties.CreSecurityProperties
|
||||
import dev.fyloz.colorrecipesexplorer.dtos.account.UserDetails
|
||||
import dev.fyloz.colorrecipesexplorer.dtos.account.UserJwt
|
||||
import dev.fyloz.colorrecipesexplorer.model.account.Permission
|
||||
import dev.fyloz.colorrecipesexplorer.model.account.toAuthority
|
||||
import dev.fyloz.colorrecipesexplorer.utils.base64encode
|
||||
import dev.fyloz.colorrecipesexplorer.utils.toDate
|
||||
import io.jsonwebtoken.Jwts
|
||||
import io.jsonwebtoken.jackson.io.JacksonDeserializer
|
||||
import io.jsonwebtoken.jackson.io.JacksonSerializer
|
||||
import org.springframework.security.core.GrantedAuthority
|
||||
import org.springframework.stereotype.Service
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
|
@ -41,11 +40,15 @@ class DefaultJwtLogic(
|
|||
securityProperties.jwtSecret.base64encode()
|
||||
}
|
||||
|
||||
private val permissionsById = Permission.values()
|
||||
.associateBy { it.id }
|
||||
|
||||
// Must be a new instance every time, or data from the last token will still be there
|
||||
private val jwtBuilder get() =
|
||||
Jwts.builder()
|
||||
.serializeToJsonWith(JacksonSerializer<Map<String, *>>(objectMapper))
|
||||
.signWith(secretKey)
|
||||
private val jwtBuilder
|
||||
get() =
|
||||
Jwts.builder()
|
||||
.serializeToJsonWith(JacksonSerializer<Map<String, *>>(objectMapper))
|
||||
.signWith(secretKey)
|
||||
|
||||
private val jwtParser by lazy {
|
||||
Jwts.parserBuilder()
|
||||
|
@ -54,12 +57,17 @@ class DefaultJwtLogic(
|
|||
.build()
|
||||
}
|
||||
|
||||
override fun buildUserJwt(userDetails: UserDetails): String =
|
||||
jwtBuilder
|
||||
override fun buildUserJwt(userDetails: UserDetails): String {
|
||||
val permissionsIds = userDetails.permissions.map { it.id }
|
||||
val type = if (userDetails.isGroup) JWT_TYPE_GROUP else JWT_TYPE_USER
|
||||
|
||||
return jwtBuilder
|
||||
.setSubject(userDetails.id)
|
||||
.setExpiration(getCurrentExpirationDate())
|
||||
.claim(JWT_CLAIM_PERMISSIONS, objectMapper.writeValueAsString(userDetails.permissions))
|
||||
.claim(JWT_CLAIM_PERMISSIONS, objectMapper.writeValueAsString(permissionsIds))
|
||||
.claim(JWT_CLAIM_TYPE, type)
|
||||
.compact()
|
||||
}
|
||||
|
||||
override fun buildGroupTokenIdJwt(groupTokenId: UUID): String =
|
||||
jwtBuilder
|
||||
|
@ -70,13 +78,17 @@ class DefaultJwtLogic(
|
|||
val parsedJwt = jwtParser.parseClaimsJws(jwt)
|
||||
|
||||
val serializedPermissions = parsedJwt.body.get(JWT_CLAIM_PERMISSIONS, String::class.java)
|
||||
val permissions = objectMapper.readValue<Collection<Permission>>(serializedPermissions)
|
||||
val permissionsIds = objectMapper.readValue<Collection<Int>>(serializedPermissions)
|
||||
val permissions = permissionsIds.map { permissionsById[it]!! }
|
||||
|
||||
val type = parsedJwt.body[JWT_CLAIM_TYPE] as Int
|
||||
val isGroup = type == JWT_TYPE_GROUP
|
||||
|
||||
val authorities = permissions
|
||||
.map { it.toAuthority() }
|
||||
.toMutableList()
|
||||
|
||||
return UserJwt(parsedJwt.body.subject, authorities)
|
||||
return UserJwt(parsedJwt.body.subject, authorities, isGroup)
|
||||
}
|
||||
|
||||
override fun parseGroupTokenIdJwt(jwt: String): UUID {
|
||||
|
@ -90,8 +102,10 @@ class DefaultJwtLogic(
|
|||
.toDate()
|
||||
|
||||
companion object {
|
||||
private const val JWT_CLAIM_PERMISSIONS = "permissions"
|
||||
private const val JWT_CLAIM_PERMISSIONS = "perms"
|
||||
private const val JWT_CLAIM_TYPE = "type"
|
||||
|
||||
private const val JWT_TYPE_USER = 0
|
||||
private const val JWT_TYPE_GROUP = 1
|
||||
}
|
||||
}
|
||||
|
||||
data class UserJwt(val id: String, val authorities: Collection<GrantedAuthority>)
|
|
@ -114,7 +114,7 @@ class DefaultUserLogic(
|
|||
}
|
||||
|
||||
override fun logout(request: HttpServletRequest) {
|
||||
val authorizationCookie = WebUtils.getCookie(request, Constants.HeaderNames.AUTHORIZATION)
|
||||
val authorizationCookie = WebUtils.getCookie(request, Constants.CookieNames.AUTHORIZATION)
|
||||
if (authorizationCookie != null) {
|
||||
val authorizationToken = authorizationCookie.value
|
||||
if (authorizationToken != null && authorizationToken.startsWith("Bearer")) {
|
||||
|
|
|
@ -4,32 +4,34 @@ import org.springframework.security.core.GrantedAuthority
|
|||
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
||||
|
||||
enum class Permission(
|
||||
val impliedPermissions: List<Permission> = listOf(),
|
||||
val id: Int,
|
||||
private val impliedPermissions: List<Permission> = listOf(),
|
||||
val deprecated: Boolean = false
|
||||
) {
|
||||
READ_FILE,
|
||||
WRITE_FILE(listOf(READ_FILE)),
|
||||
READ_FILE(0),
|
||||
WRITE_FILE(1, listOf(READ_FILE)),
|
||||
|
||||
VIEW_RECIPES(listOf(READ_FILE)),
|
||||
VIEW_CATALOG(listOf(READ_FILE)),
|
||||
VIEW_USERS,
|
||||
VIEW_RECIPES(2, listOf(READ_FILE)),
|
||||
VIEW_CATALOG(3, listOf(READ_FILE)),
|
||||
VIEW_USERS(4),
|
||||
|
||||
EDIT_RECIPES_PUBLIC_DATA(listOf(VIEW_RECIPES)),
|
||||
EDIT_RECIPES(listOf(EDIT_RECIPES_PUBLIC_DATA, WRITE_FILE)),
|
||||
EDIT_MATERIALS(listOf(VIEW_CATALOG, WRITE_FILE)),
|
||||
EDIT_MATERIAL_TYPES(listOf(VIEW_CATALOG)),
|
||||
EDIT_COMPANIES(listOf(VIEW_CATALOG)),
|
||||
EDIT_USERS(listOf(VIEW_USERS)),
|
||||
EDIT_CATALOG(listOf(EDIT_MATERIALS, EDIT_MATERIAL_TYPES, EDIT_COMPANIES)),
|
||||
EDIT_RECIPES_PUBLIC_DATA(5, listOf(VIEW_RECIPES)),
|
||||
EDIT_RECIPES(6, listOf(EDIT_RECIPES_PUBLIC_DATA, WRITE_FILE)),
|
||||
EDIT_MATERIALS(7, listOf(VIEW_CATALOG, WRITE_FILE)),
|
||||
EDIT_MATERIAL_TYPES(8, listOf(VIEW_CATALOG)),
|
||||
EDIT_COMPANIES(9, listOf(VIEW_CATALOG)),
|
||||
EDIT_USERS(10, listOf(VIEW_USERS)),
|
||||
EDIT_CATALOG(11, listOf(EDIT_MATERIALS, EDIT_MATERIAL_TYPES, EDIT_COMPANIES)),
|
||||
|
||||
VIEW_TOUCH_UP_KITS,
|
||||
EDIT_TOUCH_UP_KITS(listOf(VIEW_TOUCH_UP_KITS)),
|
||||
VIEW_TOUCH_UP_KITS(12),
|
||||
EDIT_TOUCH_UP_KITS(13, listOf(VIEW_TOUCH_UP_KITS)),
|
||||
|
||||
PRINT_MIXES(listOf(VIEW_RECIPES)),
|
||||
ADD_TO_INVENTORY(listOf(VIEW_CATALOG)),
|
||||
DEDUCT_FROM_INVENTORY(listOf(VIEW_RECIPES)),
|
||||
PRINT_MIXES(14, listOf(VIEW_RECIPES)),
|
||||
ADD_TO_INVENTORY(15, listOf(VIEW_CATALOG)),
|
||||
DEDUCT_FROM_INVENTORY(16, listOf(VIEW_RECIPES)),
|
||||
|
||||
ADMIN(
|
||||
17,
|
||||
listOf(
|
||||
EDIT_RECIPES,
|
||||
EDIT_CATALOG,
|
||||
|
@ -44,58 +46,58 @@ enum class Permission(
|
|||
),
|
||||
|
||||
// deprecated permissions
|
||||
VIEW_RECIPE(listOf(VIEW_RECIPES), true),
|
||||
VIEW_MATERIAL(listOf(VIEW_CATALOG), true),
|
||||
VIEW_MATERIAL_TYPE(listOf(VIEW_CATALOG), true),
|
||||
VIEW_COMPANY(listOf(VIEW_CATALOG), true),
|
||||
VIEW(listOf(VIEW_RECIPES, VIEW_CATALOG), true),
|
||||
VIEW_EMPLOYEE(listOf(VIEW_USERS), true),
|
||||
VIEW_EMPLOYEE_GROUP(listOf(VIEW_USERS), true),
|
||||
VIEW_RECIPE(101, listOf(VIEW_RECIPES), true),
|
||||
VIEW_MATERIAL(102, listOf(VIEW_CATALOG), true),
|
||||
VIEW_MATERIAL_TYPE(103, listOf(VIEW_CATALOG), true),
|
||||
VIEW_COMPANY(104, listOf(VIEW_CATALOG), true),
|
||||
VIEW(105, listOf(VIEW_RECIPES, VIEW_CATALOG), true),
|
||||
VIEW_EMPLOYEE(106, listOf(VIEW_USERS), true),
|
||||
VIEW_EMPLOYEE_GROUP(107, listOf(VIEW_USERS), true),
|
||||
|
||||
EDIT_RECIPE(listOf(EDIT_RECIPES), true),
|
||||
EDIT_MATERIAL(listOf(EDIT_MATERIALS), true),
|
||||
EDIT_MATERIAL_TYPE(listOf(EDIT_MATERIAL_TYPES), true),
|
||||
EDIT_COMPANY(listOf(EDIT_COMPANIES), true),
|
||||
EDIT(listOf(EDIT_RECIPES, EDIT_CATALOG), true),
|
||||
EDIT_EMPLOYEE(listOf(EDIT_USERS), true),
|
||||
EDIT_EMPLOYEE_PASSWORD(listOf(EDIT_USERS), true),
|
||||
EDIT_EMPLOYEE_GROUP(listOf(EDIT_USERS), true),
|
||||
EDIT_RECIPE(108, listOf(EDIT_RECIPES), true),
|
||||
EDIT_MATERIAL(109, listOf(EDIT_MATERIALS), true),
|
||||
EDIT_MATERIAL_TYPE(110, listOf(EDIT_MATERIAL_TYPES), true),
|
||||
EDIT_COMPANY(111, listOf(EDIT_COMPANIES), true),
|
||||
EDIT(112, listOf(EDIT_RECIPES, EDIT_CATALOG), true),
|
||||
EDIT_EMPLOYEE(113, listOf(EDIT_USERS), true),
|
||||
EDIT_EMPLOYEE_PASSWORD(114, listOf(EDIT_USERS), true),
|
||||
EDIT_EMPLOYEE_GROUP(115, listOf(EDIT_USERS), true),
|
||||
|
||||
REMOVE_FILE(listOf(WRITE_FILE), true),
|
||||
GENERATE_TOUCH_UP_KIT(listOf(VIEW_TOUCH_UP_KITS), true),
|
||||
REMOVE_FILE(116, listOf(WRITE_FILE), true),
|
||||
GENERATE_TOUCH_UP_KIT(117, listOf(VIEW_TOUCH_UP_KITS), true),
|
||||
|
||||
REMOVE_RECIPES(listOf(EDIT_RECIPES, REMOVE_FILE), true),
|
||||
REMOVE_MATERIALS(listOf(EDIT_MATERIALS, REMOVE_FILE), true),
|
||||
REMOVE_MATERIAL_TYPES(listOf(EDIT_MATERIAL_TYPES), true),
|
||||
REMOVE_COMPANIES(listOf(EDIT_COMPANIES), true),
|
||||
REMOVE_USERS(listOf(EDIT_USERS), true),
|
||||
REMOVE_CATALOG(listOf(REMOVE_MATERIALS, REMOVE_MATERIAL_TYPES, REMOVE_COMPANIES), true),
|
||||
REMOVE_RECIPES(118, listOf(EDIT_RECIPES, REMOVE_FILE), true),
|
||||
REMOVE_MATERIALS(119, listOf(EDIT_MATERIALS, REMOVE_FILE), true),
|
||||
REMOVE_MATERIAL_TYPES(120, listOf(EDIT_MATERIAL_TYPES), true),
|
||||
REMOVE_COMPANIES(121, listOf(EDIT_COMPANIES), true),
|
||||
REMOVE_USERS(122, listOf(EDIT_USERS), true),
|
||||
REMOVE_CATALOG(123, listOf(REMOVE_MATERIALS, REMOVE_MATERIAL_TYPES, REMOVE_COMPANIES), true),
|
||||
|
||||
REMOVE_RECIPE(listOf(REMOVE_RECIPES), true),
|
||||
REMOVE_MATERIAL(listOf(REMOVE_MATERIALS), true),
|
||||
REMOVE_MATERIAL_TYPE(listOf(REMOVE_MATERIAL_TYPES), true),
|
||||
REMOVE_COMPANY(listOf(REMOVE_COMPANIES), true),
|
||||
REMOVE(listOf(REMOVE_RECIPES, REMOVE_CATALOG), true),
|
||||
REMOVE_EMPLOYEE(listOf(REMOVE_USERS), true),
|
||||
REMOVE_EMPLOYEE_GROUP(listOf(REMOVE_USERS), true),
|
||||
REMOVE_RECIPE(124, listOf(REMOVE_RECIPES), true),
|
||||
REMOVE_MATERIAL(125, listOf(REMOVE_MATERIALS), true),
|
||||
REMOVE_MATERIAL_TYPE(126, listOf(REMOVE_MATERIAL_TYPES), true),
|
||||
REMOVE_COMPANY(127, listOf(REMOVE_COMPANIES), true),
|
||||
REMOVE(128, listOf(REMOVE_RECIPES, REMOVE_CATALOG), true),
|
||||
REMOVE_EMPLOYEE(129, listOf(REMOVE_USERS), true),
|
||||
REMOVE_EMPLOYEE_GROUP(130, listOf(REMOVE_USERS), true),
|
||||
|
||||
SET_BROWSER_DEFAULT_GROUP(listOf(VIEW_USERS), true),
|
||||
SET_BROWSER_DEFAULT_GROUP(131, listOf(VIEW_USERS), true),
|
||||
;
|
||||
|
||||
operator fun contains(permission: Permission): Boolean {
|
||||
return permission == this || impliedPermissions.any { permission in it }
|
||||
}
|
||||
}
|
||||
|
||||
fun Permission.flat(): Iterable<Permission> {
|
||||
return mutableSetOf(this).apply {
|
||||
impliedPermissions.forEach {
|
||||
addAll(it.flat())
|
||||
fun flat(): Iterable<Permission> {
|
||||
return mutableSetOf(this).apply {
|
||||
impliedPermissions.forEach {
|
||||
addAll(it.flat())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Converts the given [Permission] to a [GrantedAuthority]. */
|
||||
fun Permission.toAuthority(): GrantedAuthority {
|
||||
return SimpleGrantedAuthority(name)
|
||||
/** Converts the given permission to a [GrantedAuthority]. */
|
||||
fun toAuthority(): GrantedAuthority {
|
||||
return SimpleGrantedAuthority(name)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import dev.fyloz.colorrecipesexplorer.logic.config.ConfigurationLogic
|
|||
import dev.fyloz.colorrecipesexplorer.model.ConfigurationBase
|
||||
import dev.fyloz.colorrecipesexplorer.model.ConfigurationDto
|
||||
import dev.fyloz.colorrecipesexplorer.model.account.Permission
|
||||
import dev.fyloz.colorrecipesexplorer.model.account.toAuthority
|
||||
import dev.fyloz.colorrecipesexplorer.restartApplication
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.security.access.prepost.PreAuthorize
|
||||
|
|
|
@ -20,8 +20,7 @@ import javax.validation.Valid
|
|||
@PreAuthorizeAdmin
|
||||
@Profile("!emergency")
|
||||
class GroupTokenController(
|
||||
private val groupTokenLogic: GroupTokenLogic,
|
||||
private val jwtLogic: JwtLogic
|
||||
private val groupTokenLogic: GroupTokenLogic, private val jwtLogic: JwtLogic
|
||||
) {
|
||||
@GetMapping
|
||||
fun getAll() = ok(groupTokenLogic.getAll())
|
||||
|
@ -29,14 +28,6 @@ class GroupTokenController(
|
|||
@GetMapping("{id}")
|
||||
fun getById(@PathVariable id: String) = ok(groupTokenLogic.getById(id))
|
||||
|
||||
// TODO Remove when group tokens will be fully implemented
|
||||
@Deprecated("Only use for testing purposes")
|
||||
@GetMapping("{id}/cookie")
|
||||
fun addCookieForId(@PathVariable id: String, response: HttpServletResponse) {
|
||||
val groupToken = groupTokenLogic.getById(id)
|
||||
addGroupTokenCookie(response, groupToken)
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
fun save(@RequestBody @Valid dto: GroupTokenSaveDto, response: HttpServletResponse) =
|
||||
with(groupTokenLogic.save(dto)) {
|
||||
|
@ -44,6 +35,16 @@ class GroupTokenController(
|
|||
created(Constants.ControllerPaths.GROUP_TOKEN, this, this.id)
|
||||
}
|
||||
|
||||
@PutMapping("{id}/enable")
|
||||
fun enable(@PathVariable id: String) = noContent {
|
||||
groupTokenLogic.enable(id)
|
||||
}
|
||||
|
||||
@PutMapping("{id}/disable")
|
||||
fun disable(@PathVariable id: String) = noContent {
|
||||
groupTokenLogic.disable(id)
|
||||
}
|
||||
|
||||
@DeleteMapping("{id}")
|
||||
fun deleteById(@PathVariable id: String) = noContent {
|
||||
groupTokenLogic.deleteById(id)
|
||||
|
|
|
@ -4,7 +4,6 @@ import dev.fyloz.colorrecipesexplorer.config.annotations.ServiceComponent
|
|||
import dev.fyloz.colorrecipesexplorer.dtos.account.GroupDto
|
||||
import dev.fyloz.colorrecipesexplorer.model.account.Group
|
||||
import dev.fyloz.colorrecipesexplorer.model.account.Permission
|
||||
import dev.fyloz.colorrecipesexplorer.model.account.flat
|
||||
import dev.fyloz.colorrecipesexplorer.repository.GroupRepository
|
||||
import dev.fyloz.colorrecipesexplorer.service.BaseService
|
||||
import dev.fyloz.colorrecipesexplorer.service.Service
|
||||
|
|
|
@ -44,5 +44,5 @@ class DefaultGroupTokenService(private val repository: GroupTokenRepository, pri
|
|||
GroupTokenDto(entity.id, entity.name, entity.isValid, groupService.toDto(entity.group))
|
||||
|
||||
override fun toEntity(dto: GroupTokenDto) =
|
||||
GroupToken(dto.id, dto.name, dto.isValid, groupService.toEntity(dto.group))
|
||||
GroupToken(dto.id, dto.name, dto.enabled, groupService.toEntity(dto.group))
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import dev.fyloz.colorrecipesexplorer.dtos.account.GroupDto
|
|||
import dev.fyloz.colorrecipesexplorer.dtos.account.UserDto
|
||||
import dev.fyloz.colorrecipesexplorer.model.account.Permission
|
||||
import dev.fyloz.colorrecipesexplorer.model.account.User
|
||||
import dev.fyloz.colorrecipesexplorer.model.account.flat
|
||||
import dev.fyloz.colorrecipesexplorer.repository.UserRepository
|
||||
import dev.fyloz.colorrecipesexplorer.service.BaseService
|
||||
import dev.fyloz.colorrecipesexplorer.service.Service
|
||||
|
|
|
@ -1 +1 @@
|
|||
spring.jpa.show-sql=true
|
||||
spring.jpa.show-sql=false
|
||||
|
|
Loading…
Reference in New Issue