Skip to content

Commit

Permalink
Apply Commented Changes
Browse files Browse the repository at this point in the history
  • Loading branch information
TheLydonKing committed Oct 23, 2023
1 parent 9e15257 commit 5ebdead
Show file tree
Hide file tree
Showing 7 changed files with 39 additions and 26 deletions.
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,17 +127,20 @@ loginsvc:
poll-time: 30min
alg-name: "RS256"
```
You AWS Secret must have the following values:
`PrivateKey`
`PublicKey`

You will provide the keys for these values in the config.
Your AWS Secret must have at least 2 fields which correspond to the above properties:
```
private-key-field-name: "privateKey"
public-key-field-name: "publicKey"
```
with `"privateKey"` and `"publicKey"` indicating the field-name of those secrets.
Replace the above example values with the field-names you used in AWS Secrets Manager.

There are a few important configuration values to be provided:
- `access-exp-time` which indicates how long a token is valid for,
- Optional property:`poll-time` which indicates how often key pairs (`private-key-field-name` and `public-key-field-name`) are polled and fetched from AWS Secrets Manager. Polling will be disabled if missing.
- `alg-name` which indicates which algorithm is used to encode your keys.
Please note that only one configuration option (`loginsvc.rest.jwt.{aws-secrets-manager|generate-in-memory}`) can be used at a time.

Please note that only one configuration option (`loginsvc.rest.jwt.{aws-secrets-manager|generate-in-memory}`) can be used at a time.

## How to generate Code coverage report
```
Expand Down
2 changes: 1 addition & 1 deletion service/src/main/resources/example.application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ loginsvc:
#Configuration to generate the key in memory
generate-in-memory:
access-exp-time: 15min
rotation-time: 5min
rotation-time: 9h
alg-name: "RS256"
#Instead of generating the key in memory
#The Below Config allows for the application to fetch keys from AWS Secrets Manager.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,6 @@ case class AwsSecretsManagerKeyConfig (secretName: String,

override def validate(): ConfigValidationResult = {

val defaultResults = defaultValidation

val awsSecretsResults = Seq(
Option(secretName)
.map(_ => ConfigValidationSuccess)
Expand All @@ -101,15 +99,15 @@ case class AwsSecretsManagerKeyConfig (secretName: String,

Option(privateKeyFieldName)
.map(_ => ConfigValidationSuccess)
.getOrElse(ConfigValidationError(ConfigValidationException("privateAwsKey is empty"))),
.getOrElse(ConfigValidationError(ConfigValidationException("privateKeyFieldName is empty"))),

Option(publicKeyFieldName)
.map(_ => ConfigValidationSuccess)
.getOrElse(ConfigValidationError(ConfigValidationException("publicAwsKey is empty"))),
.getOrElse(ConfigValidationError(ConfigValidationException("publicKeyFieldName is empty"))),
)

val awsSecretsResultsMerge = awsSecretsResults.foldLeft[ConfigValidationResult](ConfigValidationSuccess)(ConfigValidationResult.merge)

awsSecretsResultsMerge.merge(defaultResults)
super.validate().merge(awsSecretsResultsMerge)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ trait KeyConfig extends ConfigValidatable {
})
}

final def defaultValidation : ConfigValidationResult = {
override def validate(): ConfigValidationResult = {

val algValidation = Try {
SignatureAlgorithm.valueOf(algName)
Expand All @@ -55,19 +55,19 @@ trait KeyConfig extends ConfigValidatable {
case Failure(e) => throw e
}

val accessExpTimeResult = if (accessExpTime < minAccessExpTime) {
ConfigValidationError(ConfigValidationException(s"accessExpTime must be at least $minAccessExpTime"))
val accessExpTimeResult = if (accessExpTime < KeyConfig.minAccessExpTime) {
ConfigValidationError(ConfigValidationException(s"accessExpTime must be at least ${KeyConfig.minAccessExpTime}"))
} else ConfigValidationSuccess

val refreshKeyTimeResult = if (refreshKeyTime.nonEmpty && refreshKeyTime.get < minRefreshKeyTime) {
ConfigValidationError(ConfigValidationException(s"refreshKeyTime must be at least $minRefreshKeyTime"))
val refreshKeyTimeResult = if (refreshKeyTime.nonEmpty && refreshKeyTime.get < KeyConfig.minRefreshKeyTime) {
ConfigValidationError(ConfigValidationException(s"refreshKeyTime must be at least ${KeyConfig.minRefreshKeyTime}"))
} else ConfigValidationSuccess

algValidation.merge(accessExpTimeResult).merge(refreshKeyTimeResult)
}
}

override def validate(): ConfigValidationResult = defaultValidation

object KeyConfig {
val minAccessExpTime: FiniteDuration = FiniteDuration(10, TimeUnit.MILLISECONDS)
val minRefreshKeyTime: FiniteDuration = FiniteDuration(5, TimeUnit.MINUTES)
val minRefreshKeyTime: FiniteDuration = FiniteDuration(10, TimeUnit.MILLISECONDS)
}
2 changes: 1 addition & 1 deletion service/src/test/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ loginsvc:
jwt:
generate-in-memory:
access-exp-time: 15min
rotation-time: 9h
rotation-time: 5sec
alg-name: "RS256"
config:
some-key: "BETA"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ package za.co.absa.loginsvc.rest.config

import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import za.co.absa.loginsvc.rest.config.jwt.{AwsSecretsManagerKeyConfig, InMemoryKeyConfig}
import za.co.absa.loginsvc.rest.config.jwt.{AwsSecretsManagerKeyConfig, InMemoryKeyConfig, KeyConfig}
import za.co.absa.loginsvc.rest.config.validation.ConfigValidationException
import za.co.absa.loginsvc.rest.config.validation.ConfigValidationResult.{ConfigValidationError, ConfigValidationSuccess}

Expand Down Expand Up @@ -59,22 +59,22 @@ class JwtConfigTest extends AnyFlatSpec with Matchers {

"inMemoryKeyConfig" should "fail on non-negative accessExpTime" in {
inMemoryKeyConfig.copy(accessExpTime = FiniteDuration(5, TimeUnit.MILLISECONDS)).validate() shouldBe
ConfigValidationError(ConfigValidationException(s"accessExpTime must be at least ${inMemoryKeyConfig.minAccessExpTime}"))
ConfigValidationError(ConfigValidationException(s"accessExpTime must be at least ${KeyConfig.minAccessExpTime}"))
}

"awsSecretsManagerKeyConfig" should "fail on non-negative accessExpTime" in {
awsSecretsManagerKeyConfig.copy(accessExpTime = FiniteDuration(5, TimeUnit.MILLISECONDS)).validate() shouldBe
ConfigValidationError(ConfigValidationException(s"accessExpTime must be at least ${inMemoryKeyConfig.minAccessExpTime}"))
ConfigValidationError(ConfigValidationException(s"accessExpTime must be at least ${KeyConfig.minAccessExpTime}"))
}

"inMemoryKeyConfig" should "fail on non-negative refreshExpTime" in {
inMemoryKeyConfig.copy(rotationTime = Option(FiniteDuration(5, TimeUnit.MILLISECONDS))).validate() shouldBe
ConfigValidationError(ConfigValidationException(s"refreshKeyTime must be at least ${inMemoryKeyConfig.minRefreshKeyTime}"))
ConfigValidationError(ConfigValidationException(s"refreshKeyTime must be at least ${KeyConfig.minRefreshKeyTime}"))
}

"awsSecretsManagerKeyConfig" should "fail on non-negative refreshExpTime" in {
awsSecretsManagerKeyConfig.copy(pollTime = Option(FiniteDuration(5, TimeUnit.MILLISECONDS))).validate() shouldBe
ConfigValidationError(ConfigValidationException(s"refreshKeyTime must be at least ${inMemoryKeyConfig.minRefreshKeyTime}"))
ConfigValidationError(ConfigValidationException(s"refreshKeyTime must be at least ${KeyConfig.minRefreshKeyTime}"))
}

"awsSecretsManagerKeyConfig" should "fail on missing value" in {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,16 @@ class JWTServiceTest extends AnyFlatSpec {
assert(jwk.getAlgorithm == JWSAlgorithm.RS256)
assert(jwk.getKeyUse == KeyUse.SIGNATURE)
}

it should "rotate an public and private keys after 5 seconds" in {
val initToken = jwtService.generateToken(userWithoutGroups)
val initPublicKey = jwtService.publicKey

Thread.sleep(5 * 1000)
val refreshedToken = jwtService.generateToken(userWithoutGroups)

assert(parseJWT(initToken).isFailure)
assert(parseJWT(refreshedToken).isSuccess)
assert(initPublicKey != jwtService.publicKey)
}
}

0 comments on commit 5ebdead

Please sign in to comment.