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

MAIN-T-5 Add roles for users #99

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
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
2 changes: 2 additions & 0 deletions server/src/apiTest/resources/sql/DocumentSpec/cleanup.sql
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
delete d from document d inner join user u on d.creator_id = u.id where u.uid = "[email protected]";

delete a from user_authorities a inner join user u on a.user_id = u.id where u.uid = "[email protected]";

delete u from user u where u.uid = "[email protected]";
15 changes: 10 additions & 5 deletions server/src/apiTest/resources/sql/DocumentSpec/setup.sql
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
replace into document (name, description, file_name, created_date, modified_date, creator_id )
INSERT IGNORE INTO document (name, description, file_name, created_date, modified_date, creator_id )
select "Test_1", "That is a test document", "test.txt", '2024-01-15 13:00:00', '2024-01-15 13:00:00', u.id from user u where u.uid = "[email protected]";

replace into document (name, description, created_date, modified_date, creator_id )
INSERT IGNORE INTO document (name, description, created_date, modified_date, creator_id )
select "Test_2", "That is a second test document", '2024-01-15 16:00:00', '2024-01-15 16:00:00', u.id from user u where u.uid = "[email protected]";

replace into user (uid, name, password) values ( "[email protected]", "Hanna", "$2a$10$bdZSuBncZqaM4XwHcjxbpeQuXeOxk6vCEsFrTwa91xh3M3JpvW41m" );
INSERT IGNORE INTO user (uid, name, password) values ( "[email protected]", "Hanna", "$2a$10$bdZSuBncZqaM4XwHcjxbpeQuXeOxk6vCEsFrTwa91xh3M3JpvW41m" );

replace into document (name, description, created_date, modified_date, creator_id )
INSERT IGNORE INTO user_authorities (user_id, authority_id)
SELECT u.id, a.id
FROM user u, authorities a
WHERE a.authority = 'ROLE_USER' AND u.uid = "[email protected]";

INSERT IGNORE INTO document (name, description, created_date, modified_date, creator_id )
select "Test_3", "That is a test document", '2024-02-13 13:00:00', '2024-03-15 13:24:00', u.id from user u where u.uid = "[email protected]";

replace into document (name, description, created_date, modified_date, creator_id )
INSERT IGNORE INTO document (name, description, created_date, modified_date, creator_id )
select "Test_4", "That is a second test document", '2024-01-15 13:00:00', '2024-01-15 13:00:00', u.id from user u where u.uid = "[email protected]";
2 changes: 2 additions & 0 deletions server/src/apiTest/resources/sql/RegistrationSpec/cleanup.sql
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
delete u from user u where u.uid = "[email protected]";

delete a from user_authorities a inner join user u on a.user_id = u.id where u.uid = "[email protected]";
2 changes: 2 additions & 0 deletions server/src/apiTest/resources/sql/SearchSpec/cleanup.sql
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
delete d from document d inner join user u on d.creator_id = u.id where u.uid = "[email protected]";

delete a from user_authorities a inner join user u on a.user_id = u.id where u.uid = "[email protected]";

delete u from user u where u.uid = "[email protected]";
15 changes: 10 additions & 5 deletions server/src/apiTest/resources/sql/SearchSpec/setup.sql
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
replace into document (name, description, file_name, created_date, modified_date, creator_id )
INSERT IGNORE INTO document (name, description, file_name, created_date, modified_date, creator_id )
select "Test note 1", "That is a test document", "test.txt", '2024-01-15 13:00:00', '2024-01-15 13:00:00', u.id from user u where u.uid = "[email protected]";

replace into document (name, description, created_date, modified_date, creator_id )
INSERT IGNORE INTO document (name, description, created_date, modified_date, creator_id )
select "Lorem", "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed vitae augue et tortor dapibus finibus.", '2024-01-15 16:00:00', '2024-01-15 16:00:00', u.id from user u where u.uid = "[email protected]";

replace into user (uid, name, password) values ( "[email protected]", "Hanna", "$2a$10$bdZSuBncZqaM4XwHcjxbpeQuXeOxk6vCEsFrTwa91xh3M3JpvW41m" );
INSERT IGNORE INTO user (uid, name, password) values ( "[email protected]", "Hanna", "$2a$10$bdZSuBncZqaM4XwHcjxbpeQuXeOxk6vCEsFrTwa91xh3M3JpvW41m" );

replace into document (name, description, created_date, modified_date, creator_id )
INSERT IGNORE INTO user_authorities (user_id, authority_id)
SELECT u.id, a.id
FROM user u, authorities a
WHERE a.authority = 'ROLE_USER' AND u.uid = "[email protected]";

INSERT IGNORE INTO document (name, description, created_date, modified_date, creator_id )
select "Test note 2", "That is a test document", '2024-02-13 13:00:00', '2024-03-15 13:24:00', u.id from user u where u.uid = "[email protected]";

replace into document (name, description, created_date, modified_date, creator_id )
INSERT IGNORE INTO document (name, description, created_date, modified_date, creator_id )
select "Cras at nulla ex", "Phasellus vestibulum nisl augue, a pharetra nunc molestie ut. Integer mollis ex fringilla vulputate facilisis.", '2024-01-15 13:00:00', '2024-01-15 13:00:00', u.id from user u where u.uid = "[email protected]";
2 changes: 2 additions & 0 deletions server/src/apiTest/resources/sql/cleanup_base_test_data.sql
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ delete d from document d inner join user u on d.creator_id = u.id where u.uid =

delete from reset_password_token;

delete a from user_authorities a inner join user u on a.user_id = u.id where u.uid = "[email protected]";

delete u from user u where u.uid = "[email protected]";
7 changes: 6 additions & 1 deletion server/src/apiTest/resources/sql/create_base_test_data.sql
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
replace into user (uid, name, password) values ( "[email protected]", "Hanna", "$2a$10$bdZSuBncZqaM4XwHcjxbpeQuXeOxk6vCEsFrTwa91xh3M3JpvW41m" );
INSERT IGNORE INTO user (uid, name, password) values ( "[email protected]", "Hanna", "$2a$10$bdZSuBncZqaM4XwHcjxbpeQuXeOxk6vCEsFrTwa91xh3M3JpvW41m" );

INSERT IGNORE INTO user_authorities (user_id, authority_id)
SELECT u.id, a.id
FROM user u, authorities a
WHERE a.authority = 'ROLE_USER' AND u.uid = "[email protected]";
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import org.apache.solr.client.solrj.SolrClient
import org.apache.solr.common.params.CommonParams
import org.apache.solr.common.params.ModifiableSolrParams
import org.hkurh.doky.DokyIntegrationTest
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
Expand Down Expand Up @@ -47,6 +47,6 @@ class DefaultDocumentIndexServiceIntegrationTest : DokyIntegrationTest() {
// then
val solrParams = ModifiableSolrParams()
solrParams.add(CommonParams.Q, "*:*")
assertTrue(solrClient.query(coreName, solrParams).results.size == 4)
assertEquals(4, solrClient.query(coreName, solrParams).results.size)
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
delete d from document d inner join user u on d.creator_id = u.id where u.uid = "[email protected]";

delete a from user_authorities a inner join user u on a.user_id = u.id where u.uid = "[email protected]";

delete u from user u where u.uid = "[email protected]";
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
insert into document (name, description, created_date, modified_date, creator_id )
INSERT IGNORE INTO document (name, description, created_date, modified_date, creator_id )
select "Test_1", "That is a test document", '2024-01-15 13:00:00', '2024-01-15 13:00:00', u.id from user u where u.uid = "[email protected]";

insert into document (name, description, created_date, modified_date, creator_id )
INSERT IGNORE INTO document (name, description, created_date, modified_date, creator_id )
select "Test_2", "That is a second test document", '2024-01-15 16:00:00', '2024-01-15 16:00:00', u.id from user u where u.uid = "[email protected]";

insert into user (uid, name, password) values ( "[email protected]", "Hanna", "$2a$10$bdZSuBncZqaM4XwHcjxbpeQuXeOxk6vCEsFrTwa91xh3M3JpvW41m" );
INSERT IGNORE INTO user (uid, name, password) values ( "[email protected]", "Hanna", "$2a$10$bdZSuBncZqaM4XwHcjxbpeQuXeOxk6vCEsFrTwa91xh3M3JpvW41m" );

insert into document (name, description, created_date, modified_date, creator_id )
INSERT IGNORE INTO document (name, description, created_date, modified_date, creator_id )
select "Test_3", "That is a test document", '2024-02-13 13:00:00', '2024-03-15 13:24:00', u.id from user u where u.uid = "[email protected]";

insert into document (name, description, created_date, modified_date, creator_id )
INSERT IGNORE INTO document (name, description, created_date, modified_date, creator_id )
select "Test_4", "That is a second test document", '2024-01-15 13:00:00', '2024-01-15 13:00:00', u.id from user u where u.uid = "[email protected]";
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
delete d from document d inner join user u on d.creator_id = u.id where u.uid = "[email protected]";

delete a from user_authorities a inner join user u on a.user_id = u.id where u.uid = "[email protected]";

delete u from user u where u.uid = "[email protected]";
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
insert into user (uid, name, password) values ( "[email protected]", "Hanna", "$2a$10$bdZSuBncZqaM4XwHcjxbpeQuXeOxk6vCEsFrTwa91xh3M3JpvW41m" );
INSERT IGNORE INTO user (uid, name, password) values ( "[email protected]", "Hanna", "$2a$10$bdZSuBncZqaM4XwHcjxbpeQuXeOxk6vCEsFrTwa91xh3M3JpvW41m" );

INSERT IGNORE INTO user_authorities (user_id, authority_id)
SELECT u.id, a.id
FROM user u, authorities a
WHERE a.authority = 'ROLE_USER' AND u.uid = "[email protected]"
9 changes: 5 additions & 4 deletions server/src/main/kotlin/org/hkurh/doky/DokyMapper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ import java.util.*

fun UserEntity.toDto(): UserDto {
val entity = this
return UserDto(
id = entity.id,
uid = entity.uid,
return UserDto().apply {
id = entity.id
uid = entity.uid
name = entity.name
)
roles = entity.authorities.map { it.authority.name }.toMutableSet()
}
}

fun DocumentEntity.toDto(): DocumentResponse {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ class AuthorizationUserController(private val userFacade: UserFacade) : Authoriz
override fun login(@Valid @RequestBody authenticationRequest: AuthenticationRequest): ResponseEntity<AuthenticationResponse> {
val username = authenticationRequest.uid
val password = authenticationRequest.password
userFacade.checkCredentials(username, password)
val token = generateToken(username)
val user = userFacade.checkCredentials(username, password)
val token = generateToken(user.uid, user.roles)
return ResponseEntity.ok(AuthenticationResponse(token))
}

Expand All @@ -30,7 +30,7 @@ class AuthorizationUserController(private val userFacade: UserFacade) : Authoriz
val registeredUser = userFacade.register(registrationRequest.uid, registrationRequest.password)
val resourceLocation = ServletUriComponentsBuilder.fromCurrentRequest()
.path("/{id}").build(registeredUser.id)
val token = generateToken(registeredUser.uid)
val token = generateToken(registeredUser.uid, registeredUser.roles)
return ResponseEntity.created(resourceLocation).body(AuthenticationResponse(token))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ package org.hkurh.doky.documents.api

import jakarta.validation.Valid
import org.hkurh.doky.documents.DocumentFacade
import org.hkurh.doky.security.DokyAuthority
import org.slf4j.LoggerFactory
import org.springframework.http.HttpHeaders
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.security.access.annotation.Secured
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.web.bind.annotation.*
import org.springframework.web.multipart.MultipartFile
import org.springframework.web.servlet.support.ServletUriComponentsBuilder
Expand All @@ -19,7 +18,7 @@ import java.net.MalformedURLException
*/
@RestController
@RequestMapping("/documents")
@Secured(DokyAuthority.Role.ROLE_USER)
@PreAuthorize("hasRole('ROLE_USER')")
class DocumentController(private val documentFacade: DocumentFacade) : DocumentApi {

@PostMapping("/{id}/upload")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class DefaultPasswordFacade(

val user = userService.findUserByUid(email)
val token = resetPasswordService.generateAndSaveResetToken(user!!)
LOG.debug("Generate reset password token for user ${user.id}")
LOG.debug("Generate reset password token for user [${user.id}]")
try {
emailService.sendRestorePasswordEmail(user, token)
} catch (e: Exception) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package org.hkurh.doky.search.api

import org.hkurh.doky.search.DocumentSearchFacade
import org.hkurh.doky.security.DokyAuthority
import org.springframework.http.ResponseEntity
import org.springframework.security.access.annotation.Secured
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
Expand All @@ -16,7 +15,7 @@ import org.springframework.web.bind.annotation.RestController
*/
@RestController
@RequestMapping("/documents")
@Secured(DokyAuthority.Role.ROLE_USER)
@PreAuthorize("hasRole('ROLE_USER')")
class DocumentSearchController(
private val documentSearchFacade: DocumentSearchFacade
) : DocumentSearchApi {
Expand Down
15 changes: 0 additions & 15 deletions server/src/main/kotlin/org/hkurh/doky/security/DokyAuthority.kt

This file was deleted.

28 changes: 16 additions & 12 deletions server/src/main/kotlin/org/hkurh/doky/security/DokyUserDetails.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,29 @@ package org.hkurh.doky.security

import org.hkurh.doky.users.db.UserEntity
import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.userdetails.UserDetails

class DokyUserDetails : UserDetails {
class DokyUserDetails() : UserDetails {
private var userEntity: UserEntity? = null
private var username: String = ""
private var password: String = ""
private var grantedAuthorities: Collection<GrantedAuthority?>? = null


constructor(userEntity: UserEntity) : this() {
setUserEntity(userEntity)
}

private fun setUserEntity(userEntity: UserEntity?) {
this.userEntity = userEntity
userEntity?.let { user ->
this.username = user.uid
this.password = user.password
this.grantedAuthorities = user.authorities.map { SimpleGrantedAuthority(it.authority.name) }
}
}

override fun getAuthorities(): Collection<GrantedAuthority?> = grantedAuthorities!!
override fun getPassword(): String = password
override fun getUsername(): String = username
Expand All @@ -18,15 +33,4 @@ class DokyUserDetails : UserDetails {
override fun isCredentialsNonExpired(): Boolean = true
override fun isEnabled(): Boolean = true
fun getUserEntity(): UserEntity? = userEntity

companion object {
fun createUserDetails(userEntity: UserEntity): DokyUserDetails {
val dokyUserDetails = DokyUserDetails()
dokyUserDetails.userEntity = userEntity
dokyUserDetails.username = userEntity.uid
dokyUserDetails.password = userEntity.password
dokyUserDetails.grantedAuthorities = listOf(DokyAuthority.ROLE_USER)
return dokyUserDetails
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import java.io.IOException
@Component
class JwtAuthorizationFilter(private val userService: UserService) : OncePerRequestFilter() {
private val authorizationHeader = "Authorization"
private val anonymousEndpoints = arrayOf("/register", "/login", "/password/reset")
private val anonymousEndpoints = setOf("/register", "/login", "/password/reset")

@Throws(ServletException::class, IOException::class)
override fun doFilterInternal(
Expand All @@ -48,30 +48,36 @@ class JwtAuthorizationFilter(private val userService: UserService) : OncePerRequ
}

try {
val token = getTokenFromRequest(request)
if (token != null) {
val usernameFromToken = getUsernameFromToken(token)
val currentUser = userService.findUserByUid(usernameFromToken)
val dokyUserDetails: DokyUserDetails = DokyUserDetails.createUserDetails(currentUser!!)
val authenticationToken =
UsernamePasswordAuthenticationToken(dokyUserDetails, null, dokyUserDetails.getAuthorities())
SecurityContextHolder.getContext().authentication = authenticationToken
processAuthorization(request, filterChain, response)
} catch (e: Exception) {
if (e is JwtException || e is DokyNotFoundException) {
response.status = HttpServletResponse.SC_FORBIDDEN
response.sendError(HttpServletResponse.SC_FORBIDDEN, e.message)
} else {
LOG.warn("Token is not presented. Clear authorization context.")
SecurityContextHolder.clearContext()
throw e
}
filterChain.doFilter(request, response)
} catch (e: Exception) {
when (e) {
is JwtException,
is DokyNotFoundException -> {
response.status = HttpServletResponse.SC_FORBIDDEN
response.sendError(HttpServletResponse.SC_FORBIDDEN, e.message)
}
}
}

else -> throw e
private fun processAuthorization(
request: HttpServletRequest,
filterChain: FilterChain,
response: HttpServletResponse
) {
val token = getTokenFromRequest(request)
if (token != null) {
val usernameFromToken = getUsernameFromToken(token)
userService.findUserByUid(usernameFromToken)?.let {
val dokyUserDetails = DokyUserDetails(it)
val authenticationToken =
UsernamePasswordAuthenticationToken(dokyUserDetails, null, dokyUserDetails.getAuthorities())
SecurityContextHolder.getContext().authentication = authenticationToken
}
} else {
LOG.warn("Token is not presented. Clear authorization context.")
SecurityContextHolder.clearContext()
}
filterChain.doFilter(request, response)
}

private fun getTokenFromRequest(request: HttpServletRequest): String? {
Expand Down
20 changes: 12 additions & 8 deletions server/src/main/kotlin/org/hkurh/doky/security/JwtProvider.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,27 @@ import org.springframework.stereotype.Component
@Component
object JwtProvider {
private val jwtParser = Jwts.parserBuilder().setSigningKey(DokyApplication.SECRET_KEY_SPEC).build()
private const val USERNAME_KEY = "username"
private const val ROLES_KEY = "roles"

/**
* Generates a token for the given username.
*
* @param username The username for which to generate the token.
* @param roles The list of roles associated to user.
* @return The generated token.
*/
fun generateToken(username: String): String {
fun generateToken(username: String, roles: Set<Any>): String {
val currentTime = DateTime(DateTimeZone.getDefault())
val expireTokenTime = currentTime.plusDays(1)
val claims = mapOf(USERNAME_KEY to username, ROLES_KEY to roles)
return Jwts.builder()
.setId("dokyToken")
.setSubject(username)
.setIssuedAt(currentTime.toDate())
.setExpiration(expireTokenTime.toDate())
.signWith(DokyApplication.SECRET_KEY_SPEC, SignatureAlgorithm.HS256)
.compact()
.setId("dokyToken")
.setClaims(claims)
.setIssuedAt(currentTime.toDate())
.setExpiration(expireTokenTime.toDate())
.signWith(DokyApplication.SECRET_KEY_SPEC, SignatureAlgorithm.HS256)
.compact()
}

/**
Expand All @@ -39,6 +43,6 @@ object JwtProvider {
* @return The extracted username.
*/
fun getUsernameFromToken(token: String): String {
return jwtParser.parseClaimsJws(token).body.subject
return jwtParser.parseClaimsJws(token).body[USERNAME_KEY].toString()
}
}
Loading
Loading