Skip to content

Commit

Permalink
Merge pull request #251 from nationalarchives/SPTDR-restart-transfer-…
Browse files Browse the repository at this point in the history
…service

Sptdr restart transfer service
  • Loading branch information
TomJKing authored Nov 20, 2024
2 parents 427406c + 495b4a3 commit 8c21835
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 14 deletions.
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ It is used to rotate the client secrets for each of the confidential clients in

* Rotating the client secret using the Keycloak Admin Client and getting the new value.
* Overwriting the existing value in the parameter store with the new value.
* Restarting the front end ECS service.
* Restarting the relevant ECS services.

It is also used to rotate the realm keys for the TDR realm. These are the keys that are used to sign the access and refresh tokens for the users.
As the master realm doesn't issue tokens, we don't need to update the keys there. The process for rotating the realm keys is:
Expand All @@ -15,7 +15,6 @@ As the master realm doesn't issue tokens, we don't need to update the keys there
* Mark the previous active key as passive. No new tokens will be signed with this key but existing ones will still work. This will then be deleted on the next lambda run.



## Running Locally
There is a `LambdaRunner` class which you can run from IntelliJ or by using `sbt run`.
You will need AWS credentials with permissions to get and put parameters for each of the Keycloak client secret parameters and to update the front end ECS service
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,15 @@ class RotateClientSecrets(keycloakClient: Keycloak,

val logger: Logger = new SimpleLoggerFactory().getLogger(this.getClass.getName)

private def restartFrontEndService(): UpdateServiceResponse = {
private val ecsServices: Set[(String, String)] = Set(
s"frontend_service_$stage" -> s"frontend_$stage",
s"transferservice_$stage" -> s"transferservice_service_$stage"
)

private def restartEcsService(serviceName: String, clusterName: String): UpdateServiceResponse = {
val updateServiceRequest = UpdateServiceRequest.builder
.service(s"frontend_service_$stage")
.cluster(s"frontend_$stage")
.service(serviceName)
.cluster(clusterName)
.forceNewDeployment(true)
.build()
ecsClient.updateService(updateServiceRequest)
Expand Down Expand Up @@ -58,11 +63,11 @@ class RotateClientSecrets(keycloakClient: Keycloak,
}.toList

Try {
restartFrontEndService()
ecsServices.foreach(s => restartEcsService(s._1, s._2))
} match {
case
Failure(exception) => logger.error("Error restarting ECS Frontend", exception)
messages :+ Message(s"ECS Frontend task failed to restart: ${exception.getMessage}")
Failure(exception) => logger.error("Error restarting ECS", exception)
messages :+ Message(s"ECS task failed to restart: ${exception.getMessage}")
case Success(_) => messages
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ class RotateClientSecretsSpec extends AnyFlatSpec with Matchers with MockitoSuga
val results = rotateClientSecrets.rotate()
results.size should be(1)
results.head.message should be("Client a secret has been rotated successfully")
verify(mockEcsClient, times(1)).updateService(any[UpdateServiceRequest])
verify(mockEcsClient, times(2)).updateService(any[UpdateServiceRequest])
}

"The rotate function" should "use the correct secret path" in {
Expand All @@ -104,7 +104,7 @@ class RotateClientSecretsSpec extends AnyFlatSpec with Matchers with MockitoSuga
results.size should be(1)
results.head.message should be("Client a secret has been rotated successfully")
putParameterArgumentCaptor.getValue.name() should equal(parameterPath)
verify(mockEcsClient, times(1)).updateService(any[UpdateServiceRequest])
verify(mockEcsClient, times(2)).updateService(any[UpdateServiceRequest])
}

"The rotate function" should "return an error and success response if only one update fails" in {
Expand All @@ -125,7 +125,7 @@ class RotateClientSecretsSpec extends AnyFlatSpec with Matchers with MockitoSuga
results.size should be(2)
results.exists(_.message == "Client a secret has been rotated successfully") should be(true)
results.exists(_.message == "Client b has failed HTTP 404 Not Found") should be(true)
verify(mockEcsClient, times(1)).updateService(any[UpdateServiceRequest])
verify(mockEcsClient, times(2)).updateService(any[UpdateServiceRequest])
}

"The rotate function" should "return an error if the Keycloak secret update fails" in {
Expand All @@ -140,7 +140,7 @@ class RotateClientSecretsSpec extends AnyFlatSpec with Matchers with MockitoSuga
results.size should be(1)

results.exists(_.message == "Client a has failed HTTP 404 Not Found") should be(true)
verify(mockEcsClient, times(1)).updateService(any[UpdateServiceRequest])
verify(mockEcsClient, times(2)).updateService(any[UpdateServiceRequest])
}

"The rotate function" should "return an error if the systems manager update fails" in {
Expand All @@ -156,7 +156,7 @@ class RotateClientSecretsSpec extends AnyFlatSpec with Matchers with MockitoSuga

results.size should be(1)
results.exists(_.message == s"Client a has failed $errorMessage") should be(true)
verify(mockEcsClient, times(1)).updateService(any[UpdateServiceRequest])
verify(mockEcsClient, times(2)).updateService(any[UpdateServiceRequest])
}

"The rotate function" should "return an error if the ECS service update fails" in {
Expand All @@ -175,7 +175,7 @@ class RotateClientSecretsSpec extends AnyFlatSpec with Matchers with MockitoSuga
val results = rotateClientSecrets.rotate()
results.size should be(2)
results.contains(Message(s"Client a secret has been rotated successfully")) should be(true)
results.contains(Message(s"ECS Frontend task failed to restart: $errorMessage")) should be(true)
results.contains(Message(s"ECS task failed to restart: $errorMessage")) should be(true)
}

"The clients" should "be correct" in {
Expand Down

0 comments on commit 8c21835

Please sign in to comment.