From a335f95e0b4f214e1ff8211c98089cec1a548bf9 Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Thu, 24 Nov 2022 11:44:51 +0000 Subject: [PATCH] Authenticate against the database --- .../up/fe/ni/website/backend/config/AuthConfig.kt | 7 +++++++ .../website/backend/controller/AuthController.kt | 4 ++-- .../pt/up/fe/ni/website/backend/model/Account.kt | 10 ++++++---- .../backend/model/constants/AccountConstants.kt | 5 +++++ .../fe/ni/website/backend/model/dto/AccountDto.kt | 5 +++-- .../ni/website/backend/service/AccountService.kt | 7 ++++++- .../fe/ni/website/backend/service/AuthService.kt | 15 +++++++++++---- 7 files changed, 40 insertions(+), 13 deletions(-) diff --git a/src/main/kotlin/pt/up/fe/ni/website/backend/config/AuthConfig.kt b/src/main/kotlin/pt/up/fe/ni/website/backend/config/AuthConfig.kt index e5220647..9bb418ff 100644 --- a/src/main/kotlin/pt/up/fe/ni/website/backend/config/AuthConfig.kt +++ b/src/main/kotlin/pt/up/fe/ni/website/backend/config/AuthConfig.kt @@ -11,6 +11,8 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer import org.springframework.security.config.http.SessionCreationPolicy +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder +import org.springframework.security.crypto.password.PasswordEncoder import org.springframework.security.oauth2.jwt.JwtDecoder import org.springframework.security.oauth2.jwt.JwtEncoder import org.springframework.security.oauth2.jwt.NimbusJwtDecoder @@ -39,4 +41,9 @@ class AuthConfig(val rsaKeys: RSAKeyProperties) { val jwt = RSAKey.Builder(rsaKeys::publicKey.get()).privateKey(rsaKeys::privateKey.get()).build() return NimbusJwtEncoder(ImmutableJWKSet(JWKSet(jwt))) } + + @Bean + fun passwordEncoder(): PasswordEncoder { + return BCryptPasswordEncoder() + } } diff --git a/src/main/kotlin/pt/up/fe/ni/website/backend/controller/AuthController.kt b/src/main/kotlin/pt/up/fe/ni/website/backend/controller/AuthController.kt index 15f1ec4e..8493f6b6 100644 --- a/src/main/kotlin/pt/up/fe/ni/website/backend/controller/AuthController.kt +++ b/src/main/kotlin/pt/up/fe/ni/website/backend/controller/AuthController.kt @@ -13,8 +13,8 @@ import pt.up.fe.ni.website.backend.service.AuthService @RestController @RequestMapping("/auth") class AuthController(val authService: AuthService) { - @PostMapping - fun getToken(authentication: Authentication?): Map { + @PostMapping("/new_token") + fun getNewToken(authentication: Authentication?): Map { if (authentication == null) { throw ResponseStatusException(HttpStatus.BAD_REQUEST, "No credentials were provided") } diff --git a/src/main/kotlin/pt/up/fe/ni/website/backend/model/Account.kt b/src/main/kotlin/pt/up/fe/ni/website/backend/model/Account.kt index 5833e958..2d5141e3 100644 --- a/src/main/kotlin/pt/up/fe/ni/website/backend/model/Account.kt +++ b/src/main/kotlin/pt/up/fe/ni/website/backend/model/Account.kt @@ -21,16 +21,18 @@ import pt.up.fe.ni.website.backend.model.constants.AccountConstants as Constants @Entity class Account( - @JsonProperty(required = true) - @field:Size(min = Constants.Name.minSize, max = Constants.Name.maxSize) - var name: String, - @JsonProperty(required = true) @Column(unique = true) @field:NotEmpty @field:Email var email: String, + @field:Size(min = Constants.Name.minSize, max = Constants.Name.maxSize) + var name: String, + + @field:Size(min = Constants.Password.minSize, max = Constants.Password.maxSize) + var password: String, + @field:Size(min = Constants.Bio.minSize, max = Constants.Bio.maxSize) var bio: String?, diff --git a/src/main/kotlin/pt/up/fe/ni/website/backend/model/constants/AccountConstants.kt b/src/main/kotlin/pt/up/fe/ni/website/backend/model/constants/AccountConstants.kt index f6b3995d..15c19403 100644 --- a/src/main/kotlin/pt/up/fe/ni/website/backend/model/constants/AccountConstants.kt +++ b/src/main/kotlin/pt/up/fe/ni/website/backend/model/constants/AccountConstants.kt @@ -10,4 +10,9 @@ object AccountConstants { const val minSize = 5 const val maxSize = 500 } + + object Password { + const val minSize = 8 + const val maxSize = 100 + } } diff --git a/src/main/kotlin/pt/up/fe/ni/website/backend/model/dto/AccountDto.kt b/src/main/kotlin/pt/up/fe/ni/website/backend/model/dto/AccountDto.kt index 0d65b6de..a10a9e4a 100644 --- a/src/main/kotlin/pt/up/fe/ni/website/backend/model/dto/AccountDto.kt +++ b/src/main/kotlin/pt/up/fe/ni/website/backend/model/dto/AccountDto.kt @@ -4,12 +4,13 @@ import pt.up.fe.ni.website.backend.model.Account import java.util.Date class AccountDto( - val name: String, val email: String, + val password: String, + val name: String?, val bio: String?, val birthDate: Date?, val photoPath: String?, val linkedin: String?, val github: String?, - val websites: List + val websites: List? ) : Dto() diff --git a/src/main/kotlin/pt/up/fe/ni/website/backend/service/AccountService.kt b/src/main/kotlin/pt/up/fe/ni/website/backend/service/AccountService.kt index 2974e27b..49ed885b 100644 --- a/src/main/kotlin/pt/up/fe/ni/website/backend/service/AccountService.kt +++ b/src/main/kotlin/pt/up/fe/ni/website/backend/service/AccountService.kt @@ -1,13 +1,14 @@ package pt.up.fe.ni.website.backend.service import org.springframework.data.repository.findByIdOrNull +import org.springframework.security.crypto.password.PasswordEncoder import org.springframework.stereotype.Service import pt.up.fe.ni.website.backend.model.Account import pt.up.fe.ni.website.backend.model.dto.AccountDto import pt.up.fe.ni.website.backend.repository.AccountRepository @Service -class AccountService(private val repository: AccountRepository) { +class AccountService(private val repository: AccountRepository, private val encoder: PasswordEncoder) { fun getAllAccounts(): List = repository.findAll().toList() fun createAccount(dto: AccountDto): Account { @@ -16,9 +17,13 @@ class AccountService(private val repository: AccountRepository) { } val account = dto.create() + account.password = encoder.encode(dto.password) return repository.save(account) } fun getAccountById(id: Long): Account = repository.findByIdOrNull(id) ?: throw NoSuchElementException("account not found with id $id") + + fun getAccountByEmail(email: String): Account = repository.findByEmail(email) + ?: throw NoSuchElementException("account not found with email $email") } diff --git a/src/main/kotlin/pt/up/fe/ni/website/backend/service/AuthService.kt b/src/main/kotlin/pt/up/fe/ni/website/backend/service/AuthService.kt index 3e34fca6..e23980d4 100644 --- a/src/main/kotlin/pt/up/fe/ni/website/backend/service/AuthService.kt +++ b/src/main/kotlin/pt/up/fe/ni/website/backend/service/AuthService.kt @@ -3,6 +3,7 @@ package pt.up.fe.ni.website.backend.service import org.springframework.http.HttpStatus import org.springframework.security.core.Authentication import org.springframework.security.core.GrantedAuthority +import org.springframework.security.crypto.password.PasswordEncoder import org.springframework.security.oauth2.jwt.JwtClaimsSet import org.springframework.security.oauth2.jwt.JwtEncoder import org.springframework.security.oauth2.jwt.JwtEncoderParameters @@ -13,7 +14,11 @@ import java.time.Period import java.util.stream.Collectors @Service -class AuthService(val encoder: JwtEncoder) { +class AuthService( + val accountService: AccountService, + val jwtEncoder: JwtEncoder, + private val passwordEncoder: PasswordEncoder +) { fun authenticate(authentication: Authentication): String { if (!okCredentials(authentication)) { throw ResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY, "Invalid credentials") @@ -21,8 +26,10 @@ class AuthService(val encoder: JwtEncoder) { return generateToken(authentication) } - // When accounts are ready, check against the database for the user - private fun okCredentials(authentication: Authentication) = true + private fun okCredentials(authentication: Authentication): Boolean { + val account = accountService.getAccountByEmail(authentication.name) + return passwordEncoder.matches(authentication.credentials.toString(), account.password) + } private fun generateToken(authentication: Authentication): String { val scope = authentication @@ -39,6 +46,6 @@ class AuthService(val encoder: JwtEncoder) { .subject(authentication.name) .claim("scope", scope) .build() - return encoder.encode(JwtEncoderParameters.from(claims)).tokenValue + return jwtEncoder.encode(JwtEncoderParameters.from(claims)).tokenValue } }