Skip to content

Commit

Permalink
Merge pull request #168 from MTES-MCT/feature/user-add-roles-token
Browse files Browse the repository at this point in the history
feat(backend): user roles in token
  • Loading branch information
xtiannyeto authored Jun 13, 2024
2 parents a8024a8 + 7821f38 commit d1f1379
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package fr.gouv.dgampa.rapportnav.domain.entities.user

enum class RoleTypeEnum {
ADMIN,
USER_PAM,
USER_ULAM,
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ data class User(
val email: String,
val password: String,
var token: String? = null,
var roles: List<RoleTypeEnum>
) {
constructor(user: User) : this(
id = user.id,
Expand All @@ -17,6 +18,7 @@ data class User(
email = user.email,
password = user.password,
token = user.token,
roles = user.roles
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package fr.gouv.dgampa.rapportnav.domain.use_cases.auth
import fr.gouv.dgampa.rapportnav.domain.entities.user.User
import fr.gouv.dgampa.rapportnav.domain.repositories.user.IUserRepository
import org.springframework.security.oauth2.jwt.*
import org.springframework.stereotype.Component
import org.springframework.stereotype.Service
import java.time.Instant
import java.time.temporal.ChronoUnit
Expand All @@ -19,13 +20,18 @@ class TokenService(
) {
fun createToken(user: User): String {
val jwsHeader = JwsHeader.with { "HS256" }.build()
val claims = JwtClaimsSet.builder()
val claims = this.getClaims(user);
return jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims)).tokenValue
}

fun getClaims(user: User): JwtClaimsSet {
return JwtClaimsSet.builder()
.issuedAt(Instant.now())
.expiresAt(Instant.now().plus(30L, ChronoUnit.DAYS))
.subject(user.email)
.claim("userId", user.id)
.claim("roles", user.roles)
.build()
return jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims)).tokenValue
}

fun parseToken(token: String): User? {
Expand All @@ -37,4 +43,5 @@ class TokenService(
null
}
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package fr.gouv.dgampa.rapportnav.infrastructure.api.auth

import fr.gouv.dgampa.rapportnav.domain.entities.user.RoleTypeEnum
import fr.gouv.dgampa.rapportnav.domain.entities.user.User
import fr.gouv.dgampa.rapportnav.domain.exceptions.BackendUsageErrorCode
import fr.gouv.dgampa.rapportnav.domain.exceptions.BackendUsageException
Expand Down Expand Up @@ -41,7 +42,8 @@ class ApiAuthController(
lastName = body.lastName.lowercase().trim(),
email = body.email.trim(),
password = hashService.hashBcrypt(body.password),
serviceId = body.serviceId
serviceId = body.serviceId,
roles = body.roles ?: listOf(RoleTypeEnum.USER_PAM)
)

if (findByEmail.execute(body.email) != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package fr.gouv.dgampa.rapportnav.infrastructure.api.auth.adapters.inputs

import fr.gouv.dgampa.rapportnav.domain.entities.user.RoleTypeEnum

data class AuthRegisterDataInput(
val id: Int?,
val firstName: String,
val lastName: String,
val email: String,
val password: String,
val serviceId: Int? = null
val serviceId: Int? = null,
val roles: List<RoleTypeEnum>?
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package fr.gouv.dgampa.rapportnav.infrastructure.database.model.user

import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.databind.ObjectMapper
import fr.gouv.dgampa.rapportnav.domain.entities.user.RoleTypeEnum
import fr.gouv.dgampa.rapportnav.domain.entities.user.User
import jakarta.persistence.*

Expand All @@ -28,6 +29,9 @@ class UserModel(

@Column(name = "service_id")
var serviceId: Int? = null,

@Column(name = "roles")
var roles: List<RoleTypeEnum>
) {


Expand All @@ -38,6 +42,7 @@ class UserModel(
firstName = firstName,
lastName = lastName,
password = password,
roles = roles
)

companion object {
Expand All @@ -48,6 +53,7 @@ class UserModel(
lastName = user.lastName,
email = user.email,
password = user.password,
roles = user.roles
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
DO $$
BEGIN
CREATE TYPE "RoleType" AS ENUM ('ADMIN', 'USER_PAM', 'USER_ULAM');
ALTER TABLE "user"
ADD COLUMN roles "RoleType"[] DEFAULT ARRAY['USER_PAM']::"RoleType"[];
END $$;
7 changes: 7 additions & 0 deletions backend/src/main/resources/graphql/user.graphqls
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
enum RoleTypeEnum {
ADMIN,
USER_PAM,
USER_ULAM
}

type User {
id: ID
name: String!
email: String!
token: String
roles: [RoleTypeEnum]!
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package fr.gouv.gmampa.rapportnav.domain.use_cases.auth

import fr.gouv.dgampa.rapportnav.domain.entities.user.RoleTypeEnum
import fr.gouv.dgampa.rapportnav.domain.entities.user.User
import fr.gouv.dgampa.rapportnav.domain.repositories.user.IUserRepository
import fr.gouv.dgampa.rapportnav.domain.use_cases.auth.TokenService
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.security.oauth2.jwt.JwtDecoder
import org.springframework.security.oauth2.jwt.JwtEncoder
import org.springframework.test.context.junit.jupiter.SpringExtension

@ExtendWith(SpringExtension::class)
@SpringBootTest(classes = [TokenService::class])
class TokenServiceTests {

@MockBean
private lateinit var jwtDecoder: JwtDecoder;

@MockBean
private lateinit var jwtEncoder: JwtEncoder;

@MockBean
private lateinit var userRepository: IUserRepository;


private val user:User = User(
id = 3,
firstName = "Jean",
lastName = "Dupont",
email = "[email protected]",
password = "MyBeautifulPassword",
serviceId = 6,
roles = listOf(RoleTypeEnum.USER_ULAM)
);


@Test
fun `execute should have roles with claim user id and user role`() {
val tokenService = TokenService(jwtDecoder, jwtEncoder, userRepository);
val claims = tokenService.getClaims(user);
assertThat(claims).isNotNull();

assertThat(claims.getClaim<Int>("userId")).isEqualTo(user.id);
assertThat(claims.getClaim<List<RoleTypeEnum>>("roles")).isEqualTo(user.roles);
}

}


0 comments on commit d1f1379

Please sign in to comment.