Skip to content

Commit

Permalink
- test coverage
Browse files Browse the repository at this point in the history
- ttl for lambda cached result
- add more data in the lambda context
  • Loading branch information
mrFlick72 committed Feb 20, 2024
1 parent 9005eea commit d0d1763
Show file tree
Hide file tree
Showing 44 changed files with 184 additions and 102 deletions.
3 changes: 2 additions & 1 deletion docs/lambda.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ The expected event body scheme is like below:
```json
{
"general_context_claims" : {
"client_id": "your-client-app-id"
"client_id": "your-client-app-id" ,
"grant_flow": "all supported grant flow for access_token and id_token"
},
"access_token_claims" : {
....
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<io.mockk.version>1.13.9</io.mockk.version>
<aws.sdk.version>2.23.19</aws.sdk.version>
<greenmail.version>2.0.1</greenmail.version>
<oauth2-authorization-server.version>1.2.1</oauth2-authorization-server.version>
<oauth2-authorization-server.version>1.2.2</oauth2-authorization-server.version>
<wiremock.version>3.0.1</wiremock.version>
<two-factor-auth.version>1.3</two-factor-auth.version>
</properties>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ class AuthorizationServerConfig {
val assignedKeys = mutableSetOf<Kid>()
OAuth2TokenEnhancer(assignedKeys, keyRepository, clientApplicationRepository).customize(context)
IdTokenEnhancer(assignedKeys, keyRepository).customize(context)
LambdaTokenEnhancer(enabled, lambdaName, lambdaFunction, AwsLambdaFunctionContextFactory())
LambdaTokenEnhancer(enabled, lambdaName, lambdaFunction, AwsLambdaFunctionContextFactory(accountRepository))
.customize(context)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@ package com.vauthenticator.server.config

import com.fasterxml.jackson.databind.ObjectMapper
import com.vauthenticator.server.lambdas.AwsLambdaFunction
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.data.redis.core.RedisTemplate
import software.amazon.awssdk.services.lambda.LambdaClient
import java.time.Duration

@Configuration(proxyBeanMethods = false)
class LambdaConfig {

@Bean
fun lambdaFunction(
redisTemplate: RedisTemplate<String, String>,
@Value("\${vauthenticator.lambda.aws.function-result-cache-ttl:10s}") ttl: Duration,
objectMapper: ObjectMapper,
client: LambdaClient
) = AwsLambdaFunction(redisTemplate, objectMapper, client)
) = AwsLambdaFunction(redisTemplate,ttl, objectMapper, client)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import org.springframework.web.context.request.RequestContextHolder.currentReque
import software.amazon.awssdk.core.SdkBytes
import software.amazon.awssdk.services.lambda.LambdaClient
import software.amazon.awssdk.services.lambda.model.InvokeRequest
import java.time.Duration
import java.util.*

interface LambdaFunction {
Expand All @@ -30,7 +31,23 @@ value class LambdaFunctionId(val content: String)
typealias LambdaFunctionContent = ByteArray

@JvmInline
value class LambdaFunctionContext(val content: Map<String, Any>)
value class LambdaFunctionContext(val content: Map<String, Any>) {

companion object {
fun empty() = LambdaFunctionContext(
mapOf(
"user" to emptyMap(),
"general_context_claims" to mapOf(
"client_id" to "",
"grant_flow" to "",
"authorized_scope" to emptySet<String>()
),
"access_token_claims" to emptyMap(),
"id_token_claims" to emptyMap()
)
)
}
}

data class LambdaFunctionDependency(val name: String, val version: String)

Expand All @@ -46,6 +63,7 @@ fun interface LambdaFunctionContextFactory<T> {

class AwsLambdaFunction(
private val redisTemplate: RedisTemplate<String, String>,
private val ttl: Duration,
private val objectMapper: ObjectMapper,
private val client: LambdaClient
) : LambdaFunction {
Expand Down Expand Up @@ -78,6 +96,7 @@ class AwsLambdaFunction(
val serializedBody = invoke.payload().asUtf8String()

opsForHash.put(sessionId, sessionId.toSha256(), serializedBody)
opsForHash.operations.expire(sessionId, ttl)
LambdaFunctionContext(objectMapper.readValue(serializedBody, typeRef))
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package com.vauthenticator.server.lambdas

import com.vauthenticator.server.account.repository.AccountRepository
import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer

import java.util.*


class LambdaTokenEnhancer(
private val enabled: Boolean,
private val lambdaName : String,
private val lambdaName: String,
private val lambdaFunction: LambdaFunction,
private val lambdaFunctionContextFactory: LambdaFunctionContextFactory<JwtEncodingContext>
) : OAuth2TokenCustomizer<JwtEncodingContext> {
Expand All @@ -33,18 +34,40 @@ class LambdaTokenEnhancer(
}
}

class AwsLambdaFunctionContextFactory : LambdaFunctionContextFactory<JwtEncodingContext> {
class AwsLambdaFunctionContextFactory(private val accountRepository: AccountRepository) :
LambdaFunctionContextFactory<JwtEncodingContext> {
override fun newLambdaFunctionContext(input: JwtEncodingContext): LambdaFunctionContext {
val clientId = input.registeredClient.clientId
val grantFlow = input.authorizationGrantType.value
val authorizedScope = input.authorizedScopes
val userContext = mutableMapOf<String, Any>()
val generalContext = mutableMapOf<String, Any>()

val generalContext = mutableMapOf<String, String>()
generalContext["client_id"] = clientId
generalContext["grant_flow"] = grantFlow
generalContext["authorized_scope"] = authorizedScope

Optional.ofNullable(input.authorization)
.map {
accountRepository.accountFor(it.principalName)
.map { account ->
userContext["sub"] = account.sub
userContext["email"] = account.email
userContext["first_name"] = account.firstName
userContext["last_name"] = account.lastName
userContext["birth_date"] = account.birthDate.map { it.formattedDate() }.orElseGet { "" }
userContext["phone"] = account.phone.map { it.formattedPhone() }.orElseGet { "" }
userContext["email_verified"] = account.emailVerified
userContext["roles"] = account.authorities
}
}

val accessTokenContext = mutableMapOf<String, String>()
val idTokenContext = mutableMapOf<String, String>()

return LambdaFunctionContext(
mapOf(
"user" to userContext,
"general_context_claims" to generalContext,
"access_token_claims" to accessTokenContext,
"id_token_claims" to idTokenContext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,14 @@ class OAuth2TokenEnhancer(
val clientId = context.registeredClient.clientId
val findOne = clientApplicationRepository.findOne(ClientAppId(clientId))
findOne.ifPresent {
context.claims.claim(
"authorities",
it.authorities.content.stream().map { authority -> authority.content }
.collect(Collectors.toList())
)
context.claims.claim("user_name", it.clientAppId.content)
context.claims.claim("authorities", it.authorities.content.stream().map { authority -> authority.content }.collect(Collectors.toList()))
}
} else {
val attributes = context.authorization!!.attributes
val principal = attributes["java.security.Principal"] as Authentication

context.claims.claim("user_name", principal.name)
context.claims.claim("authorities", principal.authorities
.stream()
.map { obj: GrantedAuthority -> obj.authority }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.vauthenticator.server.account

import com.fasterxml.jackson.databind.ObjectMapper
import com.vauthenticator.server.account.AccountTestFixture.anAccount
import com.vauthenticator.server.support.AccountTestFixture.anAccount
import com.vauthenticator.server.support.JsonUtils.prettifyInOneLineJsonFrom
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package com.vauthenticator.server.account

import com.vauthenticator.server.account.AccountTestFixture.anAccount
import com.vauthenticator.server.account.repository.AccountRepository
import com.vauthenticator.server.support.AccountTestFixture.anAccount
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.junit5.MockKExtension
import io.mockk.just
import io.mockk.runs
import io.mockk.verify
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package com.vauthenticator.server.account

import com.vauthenticator.server.account.AccountTestFixture.anAccount
import com.vauthenticator.server.account.repository.AccountRepository
import com.vauthenticator.server.support.AccountTestFixture.anAccount
import com.vauthenticator.server.support.SecurityFixture
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.junit5.MockKExtension
import io.mockk.just
import io.mockk.runs
import io.mockk.verify
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
Expand All @@ -32,7 +31,7 @@ class SaveAccountTest {
@Test
fun `when account data are updated`() {
val account = anAccount().copy(firstName = "A_NEW_FIRST_NAME")
val principal: JwtAuthenticationToken = SecurityFixture.principalFor("A_CLIENTAPP_ID", anAccount().email)
val principal: JwtAuthenticationToken = SecurityFixture.principalFor("A_CLIENT_APP_ID", anAccount().email)

every { accountRepository.accountFor(account.email) } returns Optional.of(account)
every { accountRepository.save(account) } just runs
Expand All @@ -45,7 +44,7 @@ class SaveAccountTest {
@Test
fun `when account is not found`() {
val account = anAccount()
val principal: JwtAuthenticationToken = SecurityFixture.principalFor("A_CLIENTAPP_ID", account.email)
val principal: JwtAuthenticationToken = SecurityFixture.principalFor("A_CLIENT_APP_ID", account.email)

every { accountRepository.accountFor(account.email) } returns Optional.empty()

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.vauthenticator.server.account.api

import com.vauthenticator.server.account.AccountTestFixture.anAccount
import com.vauthenticator.server.account.api.AccountConverter.fromDomainToAccountApiRepresentation
import com.vauthenticator.server.config.adminRole
import com.vauthenticator.server.role.Role
import com.vauthenticator.server.support.AccountTestFixture.anAccount
import io.mockk.mockk
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.vauthenticator.server.account.api

import com.fasterxml.jackson.databind.ObjectMapper
import com.vauthenticator.server.account.AccountTestFixture.anAccount
import com.vauthenticator.server.account.EMAIL
import com.vauthenticator.server.account.Phone
import com.vauthenticator.server.account.SaveAccount
import com.vauthenticator.server.account.repository.AccountRepository
Expand All @@ -14,6 +12,8 @@ import com.vauthenticator.server.oauth2.clientapp.ClientApplicationRepository
import com.vauthenticator.server.oauth2.clientapp.Scope
import com.vauthenticator.server.oauth2.clientapp.Scopes
import com.vauthenticator.server.role.PermissionValidator
import com.vauthenticator.server.support.AccountTestFixture.anAccount
import com.vauthenticator.server.support.EMAIL
import com.vauthenticator.server.support.SecurityFixture.principalFor
import com.vauthenticator.server.support.VAUTHENTICATOR_ADMIN
import io.mockk.every
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.vauthenticator.server.account.api

import com.fasterxml.jackson.databind.ObjectMapper
import com.vauthenticator.server.account.AccountTestFixture
import com.vauthenticator.server.account.ChangeAccountEnabling
import com.vauthenticator.server.account.repository.AccountRepository
import com.vauthenticator.server.support.AccountTestFixture
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.junit5.MockKExtension
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
package com.vauthenticator.server.account.mailverification

import com.fasterxml.jackson.databind.ObjectMapper
import com.vauthenticator.server.account.EMAIL
import com.vauthenticator.server.clientapp.A_CLIENT_APP_ID
import com.vauthenticator.server.oauth2.clientapp.ClientApplicationRepository
import com.vauthenticator.server.oauth2.clientapp.Scope
import com.vauthenticator.server.role.PermissionValidator
import com.vauthenticator.server.support.EMAIL
import com.vauthenticator.server.support.SecurityFixture
import com.vauthenticator.server.support.SecurityFixture.signedJWTFor
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.junit5.MockKExtension
import io.mockk.just
import io.mockk.runs
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
Expand All @@ -22,7 +21,7 @@ import org.springframework.security.oauth2.jwt.Jwt
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.*
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
import org.springframework.test.web.servlet.setup.MockMvcBuilders
import java.time.Instant

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.vauthenticator.server.account.mailverification

import com.vauthenticator.server.account.AccountNotFoundException
import com.vauthenticator.server.account.AccountTestFixture.anAccount
import com.vauthenticator.server.account.repository.AccountRepository
import com.vauthenticator.server.account.ticket.VerificationTicket
import com.vauthenticator.server.account.ticket.VerificationTicketFactory
Expand All @@ -12,6 +11,7 @@ import com.vauthenticator.server.oauth2.clientapp.ClientAppId
import com.vauthenticator.server.oauth2.clientapp.ClientApplicationRepository
import com.vauthenticator.server.oauth2.clientapp.Scope
import com.vauthenticator.server.oauth2.clientapp.Scopes
import com.vauthenticator.server.support.AccountTestFixture.anAccount
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.junit5.MockKExtension
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.vauthenticator.server.account.mailverification

import com.vauthenticator.server.account.AccountTestFixture.anAccount
import com.vauthenticator.server.account.EMAIL
import com.vauthenticator.server.account.ticket.TicketRepository
import com.vauthenticator.server.account.ticket.VerificationTicket
import com.vauthenticator.server.account.ticket.VerificationTicketFactory
import com.vauthenticator.server.account.ticket.VerificationTicketFeatures
import com.vauthenticator.server.clientapp.A_CLIENT_APP_ID
import com.vauthenticator.server.oauth2.clientapp.ClientAppId
import com.vauthenticator.server.support.AccountTestFixture.anAccount
import com.vauthenticator.server.support.EMAIL
import com.vauthenticator.server.support.TicketFixture.ticketFor
import io.mockk.every
import io.mockk.impl.annotations.MockK
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.vauthenticator.server.account.ticket.VerificationTicket
import com.vauthenticator.server.mfa.MfaMethod
import com.vauthenticator.server.mfa.MfaMethodsEnrolmentAssociation
import com.vauthenticator.server.oauth2.clientapp.ClientAppId
import com.vauthenticator.server.support.AccountTestFixture
import com.vauthenticator.server.support.TicketFixture
import io.mockk.every
import io.mockk.impl.annotations.MockK
Expand Down Expand Up @@ -45,7 +46,7 @@ internal class VerifyMailChallengeSentTest {

@Test
internal fun `happy path`() {
val account = com.vauthenticator.server.account.AccountTestFixture.anAccount()
val account = AccountTestFixture.anAccount()
val enabledAccount = account.copy(accountNonLocked = true, enabled = true, emailVerified = true)
val verificationTicket = VerificationTicket("A_TICKET")

Expand All @@ -67,7 +68,7 @@ internal class VerifyMailChallengeSentTest {

@Test
internal fun `when the account does not exist`() {
val account = com.vauthenticator.server.account.AccountTestFixture.anAccount()
val account = AccountTestFixture.anAccount()
val verificationTicket = VerificationTicket("A_TICKET")

every { ticketRepository.loadFor(verificationTicket) } returns Optional.of(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package com.vauthenticator.server.account.repository

import com.vauthenticator.server.account.AccountCacheContentConverter
import com.vauthenticator.server.account.AccountTestFixture.anAccount
import com.vauthenticator.server.cache.CacheOperation
import com.vauthenticator.server.support.AccountTestFixture.anAccount
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.junit5.MockKExtension
import io.mockk.just
import io.mockk.runs
import io.mockk.verify
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package com.vauthenticator.server.account.repository

import com.vauthenticator.server.account.Account
import com.vauthenticator.server.account.AccountMandatoryAction.RESET_PASSWORD
import com.vauthenticator.server.account.AccountTestFixture.anAccount
import com.vauthenticator.server.role.*
import com.vauthenticator.server.support.AccountTestFixture.anAccount
import com.vauthenticator.server.support.DatabaseUtils.dynamoAccountTableName
import com.vauthenticator.server.support.DatabaseUtils.dynamoDbClient
import com.vauthenticator.server.support.DatabaseUtils.dynamoRoleTableName
Expand Down
Loading

0 comments on commit d0d1763

Please sign in to comment.