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

117 delay option for new set of key signing #118

Open
wants to merge 14 commits into
base: master
Choose a base branch
from

Conversation

TheLydonKing
Copy link
Collaborator

@TheLydonKing TheLydonKing commented Feb 6, 2025

Release Notes:

  • Added new Optional Config setting that allows for a layover period before signing new JWT's with the new keys
  • Fixed RefreshToken to use both available public keys

closes #117

@TheLydonKing TheLydonKing self-assigned this Feb 6, 2025
@TheLydonKing TheLydonKing linked an issue Feb 6, 2025 that may be closed by this pull request
Copy link

github-actions bot commented Feb 6, 2025

JaCoCo code coverage report - scala:2.12.17

File Coverage [83.51%] 🍏
InMemoryKeyConfig.scala 100% 🍏
KeyConfig.scala 95.94% 🍏
AwsSecretsManagerKeyConfig.scala 91.51% 🍏
JWTService.scala 83.11% 🍏
AwsSecretsUtils.scala 0%
Total Project Coverage 67.88% 🍏

Copy link
Collaborator

@dk1844 dk1844 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the comments are details, but adding tests to the AwsSecretsManagerKeyConfig seem necessary

Comment on lines 10 to 11
key-phase-out-time: 30min
key-lay-over-time: 15min
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
key-phase-out-time: 30min
key-lay-over-time: 15min
key-lay-over-time: 15min
key-phase-out-time: 30min

As these are used in a given order (first, after rotation, the layover happens, then phaseout happens), I would list these two config fields in that order, too (better readability/understandability)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, README.md is not up-to-date in regards to both

  • key-phase-out-time
  • key-lay-over-time

These two keys are not mentioned there (both missing from the examples and missing explanation). It would be especially important to note how these to relate to each other and how it is counted (both from rotation time). Eg.

key-lay-over-time: 15min
key-phase-out-time: 30min

will mean:

t=0: keys rotation happens
t=0-14m: layover period: public keys: old key+new key; old key is still used for signing
t=15-29m: layover is over: public keys: old key+new key; new key is used for signing
t=30m+: phase-out happens: public keys: new key; new key is used for signing

Something like that.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other words, do I understand correctly that phaseout is always counted from the rotation, so the actual phaseout's duration is only phaseOut-layover?

If I got it right, is this a good idea? Would it be better if the time windows of phaseOut was not dependent on layover?

So: you could actually have e.g. phaseOut=15min and layover=30min, and phaseOut would only start after layover finished (so after 45min if layover was defined). This would not force phaseOut > layover, anymore, too.

However, this is just an idea -- when documented clearly, it is not a problem if it behaves as now, it is just a matter of how the configuration times are defined (just conditional addition/subtracting)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other words, do I understand correctly that phaseout is always counted from the rotation, so the actual phaseout's duration is only phaseOut-layover?

If I got it right, is this a good idea? Would it be better if the time windows of phaseOut was not dependent on layover?

So: you could actually have e.g. phaseOut=15min and layover=30min, and phaseOut would only start after layover finished (so after 45min if layover was defined). This would not force phaseOut > layover, anymore, too.

However, this is just an idea -- when documented clearly, it is not a problem if it behaves as now, it is just a matter of how the configuration times are defined (just conditional addition/subtracting)

Hi

Yes, actually that makes a lot of sense. So will implement it like this.

@@ -103,7 +104,7 @@ management:
health:
ldap:
# TODO: Enable Ldap check for actuator/health when fully Implemented - issue #34
enabled: "false"
enabled: "true"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see #34 being closed, so why the change?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this was a mistake, it's changed back

Comment on lines 39 to 40
keyPhaseOutTime: Option[FiniteDuration],
keyLayOverTime: Option[FiniteDuration]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
keyPhaseOutTime: Option[FiniteDuration],
keyLayOverTime: Option[FiniteDuration]
keyLayOverTime: Option[FiniteDuration],
keyPhaseOutTime: Option[FiniteDuration]

Same reason as above.

@@ -79,7 +80,15 @@ case class AwsSecretsManagerKeyConfig(
}
}

(currentKeyPair, previousKeyPair)
previousKeyPair.fold {(currentKeyPair, previousKeyPair)} { pk =>
val exp = keyLayOverTime.exists(!isExpired(currentSecrets.createTime, _))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
val exp = keyLayOverTime.exists(!isExpired(currentSecrets.createTime, _))
val layoverIsActive = keyLayOverTime.exists(!isExpired(currentSecrets.createTime, _))

I think better names denoting more closely what is meant would be welcome. Same with

val exp = keyPhaseOutTime.exists(isExpired(currentSecrets.createTime, _))

(perhaps rename to val phaseOutIsActive)?

(currentKeyPair, previousKeyPair)
previousKeyPair.fold {(currentKeyPair, previousKeyPair)} { pk =>
val exp = keyLayOverTime.exists(!isExpired(currentSecrets.createTime, _))
if (!exp) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw your last commit changing this and I believe it truly is correct now like this.
But the fact that this error was possible clearly demonstrates that this part need to be test-covered, too.

That will mean making AwsSecretsUtils a param (of the method perhaps) so that it can be mocked in tests. (to make things easier, perhaps introduce a trait for it). Perhaps MockFactory mixin can be used.

@@ -294,11 +294,11 @@ class JWTServiceTest extends AnyFlatSpec with BeforeAndAfterEach with Matchers {
assert(initPublicKey._1 == jwtService.publicKeys._2.orNull)
}

it should "phase out older keys after 8 seconds" in {
it should "phase out older keys after 30 seconds" in {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These times are barely bearable, is it necessary to have such long-taking tests?

@TheLydonKing TheLydonKing requested a review from dk1844 February 11, 2025 14:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Delay option for new set of key signing
2 participants