From 14ee230376e34cbe7790f8d4c1f8c7363b1dcd8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20S=C3=B8rensen?= Date: Tue, 12 Nov 2024 16:17:09 +0100 Subject: [PATCH] Add endpoint for initializing risc and sops config by calling init-risc-api --- .../no/risc/config/InitRiScServiceConfig.kt | 10 ++ .../risc/exception/GlobalExceptionHandler.kt | 13 ++ .../GenerateInitialRiScException.kt | 6 + .../kotlin/no/risc/github/GithubConnector.kt | 82 ++++++++++- .../kotlin/no/risc/github/GithubHelper.kt | 20 +++ .../no/risc/github/models/GithubResponse.kt | 10 ++ .../risc/github/models/SopsConfigOnGitHub.kt | 6 + .../connector/InitRiScServiceConnector.kt | 9 ++ .../initRiSc/InitRiScServiceIntegration.kt | 30 ++++ .../kotlin/no/risc/initRiSc/model/DTOs.kt | 15 ++ .../kotlin/no/risc/risc/RiScController.kt | 21 +++ src/main/kotlin/no/risc/risc/RiScService.kt | 128 +++++++++++++++--- src/main/kotlin/no/risc/risc/models/DTOs.kt | 13 ++ src/main/kotlin/no/risc/utils/Utils.kt | 10 ++ .../resources/application-local.properties | 2 +- src/main/resources/application.properties | 3 +- 16 files changed, 358 insertions(+), 20 deletions(-) create mode 100644 src/main/kotlin/no/risc/config/InitRiScServiceConfig.kt create mode 100644 src/main/kotlin/no/risc/exception/exceptions/GenerateInitialRiScException.kt create mode 100644 src/main/kotlin/no/risc/github/models/SopsConfigOnGitHub.kt create mode 100644 src/main/kotlin/no/risc/infra/connector/InitRiScServiceConnector.kt create mode 100644 src/main/kotlin/no/risc/initRiSc/InitRiScServiceIntegration.kt create mode 100644 src/main/kotlin/no/risc/initRiSc/model/DTOs.kt diff --git a/src/main/kotlin/no/risc/config/InitRiScServiceConfig.kt b/src/main/kotlin/no/risc/config/InitRiScServiceConfig.kt new file mode 100644 index 00000000..0a885d06 --- /dev/null +++ b/src/main/kotlin/no/risc/config/InitRiScServiceConfig.kt @@ -0,0 +1,10 @@ +package no.risc.config + +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.stereotype.Component + +@Component +@ConfigurationProperties(prefix = "init-risc") +class InitRiScServiceConfig { + lateinit var baseUrl: String +} diff --git a/src/main/kotlin/no/risc/exception/GlobalExceptionHandler.kt b/src/main/kotlin/no/risc/exception/GlobalExceptionHandler.kt index 4054be3d..cd41c571 100644 --- a/src/main/kotlin/no/risc/exception/GlobalExceptionHandler.kt +++ b/src/main/kotlin/no/risc/exception/GlobalExceptionHandler.kt @@ -3,6 +3,7 @@ package no.risc.exception import no.risc.exception.exceptions.AccessTokenValidationFailedException import no.risc.exception.exceptions.CreatePullRequestException import no.risc.exception.exceptions.CreatingRiScException +import no.risc.exception.exceptions.GenerateInitialRiScException import no.risc.exception.exceptions.InvalidAccessTokensException import no.risc.exception.exceptions.JSONSchemaFetchException import no.risc.exception.exceptions.PermissionDeniedOnGitHubException @@ -156,6 +157,18 @@ internal class GlobalExceptionHandler { ) } + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + @ResponseBody + @ExceptionHandler(GenerateInitialRiScException::class) + fun handleGenerateInitialRiScException(ex: GenerateInitialRiScException): ProcessRiScResultDTO { + logger.error(ex.message, ex) + return ProcessRiScResultDTO( + ex.riScId, + ProcessingStatus.ErrorWhenGeneratingInitialRiSc, + ex.message, + ) + } + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ResponseBody @ExceptionHandler(UnableToParseResponseBodyException::class) diff --git a/src/main/kotlin/no/risc/exception/exceptions/GenerateInitialRiScException.kt b/src/main/kotlin/no/risc/exception/exceptions/GenerateInitialRiScException.kt new file mode 100644 index 00000000..a7faa655 --- /dev/null +++ b/src/main/kotlin/no/risc/exception/exceptions/GenerateInitialRiScException.kt @@ -0,0 +1,6 @@ +package no.risc.exception.exceptions + +data class GenerateInitialRiScException( + override val message: String, + val riScId: String, +) : Exception() diff --git a/src/main/kotlin/no/risc/github/GithubConnector.kt b/src/main/kotlin/no/risc/github/GithubConnector.kt index 9231a362..3f0cafa3 100644 --- a/src/main/kotlin/no/risc/github/GithubConnector.kt +++ b/src/main/kotlin/no/risc/github/GithubConnector.kt @@ -9,6 +9,7 @@ import no.risc.exception.exceptions.PermissionDeniedOnGitHubException import no.risc.exception.exceptions.SopsConfigFetchException import no.risc.github.models.FileContentDTO import no.risc.github.models.FileNameDTO +import no.risc.github.models.PutFileContentsDTO import no.risc.github.models.RepositoryDTO import no.risc.github.models.ShaResponseDTO import no.risc.infra.connector.WebClientConnector @@ -287,11 +288,12 @@ class GithubConnector( requiresNewApproval: Boolean, accessTokens: AccessTokens, userInfo: UserInfo, + commitSha: String?, ): RiScApprovalPRStatus { val accessToken = accessTokens.githubAccessToken.value val githubAuthor = Author(userInfo.name, userInfo.email, Date.from(Instant.now())) // Attempt to get SHA for the existing draft - var latestShaForDraft = getSHAForExistingRiScDraftOrNull(owner, repository, riScId, accessToken) + var latestShaForDraft = commitSha ?: getSHAForExistingRiScDraftOrNull(owner, repository, riScId, accessToken) var latestShaForPublished: String? = "" // Determine if a new branch is needed. "requires new approval" is used to determine if new PR can be created @@ -310,7 +312,11 @@ class GithubConnector( "Create new RiSc with id: $riScId" + if (requiresNewApproval) " requires new approval" else "" } } else { - "Update RiSc with id: $riScId" + if (requiresNewApproval) " requires new approval" else "" + if (commitSha != null) { + "Initialize generated RiSc with id: $riScId" + if (requiresNewApproval) " requires new approval" else "" + } else { + "Update RiSc with id: $riScId" + if (requiresNewApproval) " requires new approval" else "" + } } putFileRequestToGithub( @@ -393,6 +399,18 @@ class GithubConnector( } } + private fun getSHAForExistingSopsConfigDraftOrNull( + owner: String, + repository: String, + riScId: String, + accessToken: String, + ) = try { + getGithubResponse(githubHelper.uriToFindSopsConfigOnDraftBranch(owner, repository, riScId), accessToken) + .shaResponseDTO() + } catch (e: Exception) { + null + } + private fun getSHAForExistingRiScDraftOrNull( owner: String, repository: String, @@ -405,6 +423,18 @@ class GithubConnector( null } + private fun getSHAForPublishedSopsConfigOrNull( + owner: String, + repository: String, + riScId: String, + accessToken: String, + ) = try { + getGithubResponse(githubHelper.uriToFindSopsConfig(owner, repository, riScId), accessToken) + .shaResponseDTO() + } catch (e: Exception) { + null + } + private fun getSHAForPublishedRiScOrNull( owner: String, repository: String, @@ -576,6 +606,54 @@ class GithubConnector( .body(Mono.just(writePayload.toContentBody()), String::class.java) .retrieve() + fun writeSopsConfig( + sopsConfig: String, + repositoryOwner: String, + repositoryName: String, + riScId: String, + accessTokens: AccessTokens, + userInfo: UserInfo, + defaultBranch: String, + requiresNewApproval: Boolean, + ): PutFileContentsDTO { + val accessToken = accessTokens.githubAccessToken.value + val githubAuthor = Author(userInfo.name, userInfo.email, Date.from(Instant.now())) + // Attempt to get SHA for the existing draft + var latestShaForDraft = getSHAForExistingSopsConfigDraftOrNull(repositoryOwner, repositoryName, riScId, accessToken) + var latestShaForPublished: String? = "" + + // Determine if a new branch is needed. "requires new approval" is used to determine if new PR can be created + // through updating. + val commitMessage = + if (latestShaForDraft == null) { + createNewBranch(repositoryOwner, repositoryName, riScId, accessToken, defaultBranch) + // Fetch again after creating branch as it will return null if no branch exists + latestShaForDraft = getSHAForExistingSopsConfigDraftOrNull(repositoryOwner, repositoryName, riScId, accessToken) + + // Fetch to determine if update or create + latestShaForPublished = getSHAForPublishedSopsConfigOrNull(repositoryOwner, repositoryName, riScId, accessToken) + if (latestShaForPublished != null) { + "Update SOPS config with id: $riScId" + if (requiresNewApproval) " requires new approval" else "" + } else { + "Create new SOPS config with id: $riScId" + if (requiresNewApproval) " requires new approval" else "" + } + } else { + "Update SOPS config with id: $riScId" + if (requiresNewApproval) " requires new approval" else "" + } + return putFileRequestToGithub( + githubHelper.uriToPutSopsConfigOnDraftBranch(repositoryOwner, repositoryName, riScId), + accessToken, + GithubWriteToFilePayload( + message = commitMessage, + content = sopsConfig.encodeBase64(), + sha = latestShaForDraft, + branchName = riScId, + author = githubAuthor, + ), + ).bodyToMono().block() + ?: throw IllegalStateException("Failed to de-serialize response from writing SOPS config to GitHub") + } + private suspend fun getGithubResponseSuspend( uri: String, accessToken: String, diff --git a/src/main/kotlin/no/risc/github/GithubHelper.kt b/src/main/kotlin/no/risc/github/GithubHelper.kt index d1de3d7b..c70633f1 100644 --- a/src/main/kotlin/no/risc/github/GithubHelper.kt +++ b/src/main/kotlin/no/risc/github/GithubHelper.kt @@ -109,6 +109,12 @@ class GithubHelper( repository: String, ): String = "/$owner/$repository/contents/$riScFolderPath" + fun uriToFindSopsConfig( + owner: String, + repository: String, + id: String, + ): String = "/$owner/$repository/contents/$riScFolderPath/.sops.yaml" + fun uriToFindRiSc( owner: String, repository: String, @@ -138,6 +144,13 @@ class GithubHelper( draftBranch: String = riScId, ): String = "/$owner/$repository/contents/$riScFolderPath/$riScId.$filenamePostfix.yaml?ref=$draftBranch" + fun uriToFindSopsConfigOnDraftBranch( + owner: String, + repository: String, + riScId: String, + draftBranch: String = riScId, + ): String = "/$owner/$repository/contents/$riScFolderPath/.sops.yaml?ref=$draftBranch" + fun uriToPutRiScOnDraftBranch( owner: String, repository: String, @@ -145,6 +158,13 @@ class GithubHelper( draftBranch: String = riScId, ): String = "/$owner/$repository/contents/$riScFolderPath/$riScId.$filenamePostfix.yaml?ref=$draftBranch" + fun uriToPutSopsConfigOnDraftBranch( + owner: String, + repository: String, + riScId: String, + draftBranch: String = riScId, + ): String = "/$owner/$repository/contents/$riScFolderPath/.sops.yaml?ref=$draftBranch" + fun uriToGetCommitStatus( owner: String, repository: String, diff --git a/src/main/kotlin/no/risc/github/models/GithubResponse.kt b/src/main/kotlin/no/risc/github/models/GithubResponse.kt index a0d03aaf..29927524 100644 --- a/src/main/kotlin/no/risc/github/models/GithubResponse.kt +++ b/src/main/kotlin/no/risc/github/models/GithubResponse.kt @@ -34,3 +34,13 @@ data class RepositoryPermissions( val triage: Boolean, val pull: Boolean, ) + +@JsonIgnoreProperties(ignoreUnknown = true) +data class PutFileContentsDTO( + val commit: GitCommit, +) + +@JsonIgnoreProperties(ignoreUnknown = true) +data class GitCommit( + val sha: String, +) diff --git a/src/main/kotlin/no/risc/github/models/SopsConfigOnGitHub.kt b/src/main/kotlin/no/risc/github/models/SopsConfigOnGitHub.kt new file mode 100644 index 00000000..95c74fde --- /dev/null +++ b/src/main/kotlin/no/risc/github/models/SopsConfigOnGitHub.kt @@ -0,0 +1,6 @@ +package no.risc.github.models + +data class SopsConfigOnGitHub( + val config: String, + val commitSha: String? = null, +) diff --git a/src/main/kotlin/no/risc/infra/connector/InitRiScServiceConnector.kt b/src/main/kotlin/no/risc/infra/connector/InitRiScServiceConnector.kt new file mode 100644 index 00000000..06b7d760 --- /dev/null +++ b/src/main/kotlin/no/risc/infra/connector/InitRiScServiceConnector.kt @@ -0,0 +1,9 @@ +package no.risc.infra.connector + +import no.risc.config.InitRiScServiceConfig +import org.springframework.stereotype.Component + +@Component +class InitRiScServiceConnector( + val initRiScServiceConfig: InitRiScServiceConfig, +) : WebClientConnector(baseURL = initRiScServiceConfig.baseUrl) diff --git a/src/main/kotlin/no/risc/initRiSc/InitRiScServiceIntegration.kt b/src/main/kotlin/no/risc/initRiSc/InitRiScServiceIntegration.kt new file mode 100644 index 00000000..24646143 --- /dev/null +++ b/src/main/kotlin/no/risc/initRiSc/InitRiScServiceIntegration.kt @@ -0,0 +1,30 @@ +package no.risc.initRiSc + +import no.risc.infra.connector.InitRiScServiceConnector +import no.risc.initRiSc.model.GenerateRiScRequestBody +import no.risc.initRiSc.model.GenerateRiScResponseBody +import org.springframework.stereotype.Component +import org.springframework.web.reactive.function.BodyInserters +import org.springframework.web.reactive.function.client.bodyToMono + +@Component +class InitRiScServiceIntegration( + val initRiScServiceConnector: InitRiScServiceConnector, +) { + fun getInitialRiScAndSopsConfig( + repositoryName: String, + publicAgeKey: String?, + gcpProjectId: String, + ): GenerateRiScResponseBody? { + val r = + initRiScServiceConnector.webClient + .post() + .uri("/generate/$repositoryName") + .body(BodyInserters.fromValue(GenerateRiScRequestBody(publicAgeKey, gcpProjectId))) + .retrieve() + .bodyToMono() + + return r + .block() + } +} diff --git a/src/main/kotlin/no/risc/initRiSc/model/DTOs.kt b/src/main/kotlin/no/risc/initRiSc/model/DTOs.kt new file mode 100644 index 00000000..e50b81d1 --- /dev/null +++ b/src/main/kotlin/no/risc/initRiSc/model/DTOs.kt @@ -0,0 +1,15 @@ +package no.risc.initRiSc.model + +import no.risc.risc.models.UserInfo + +data class GenerateRiScResponseBody( + val sopsConfig: String, + val schemaVersion: String, + val initialRiScContent: String, + val userInfo: UserInfo, +) + +data class GenerateRiScRequestBody( + val publicAgeKey: String? = null, + val gcpProjectId: String, +) diff --git a/src/main/kotlin/no/risc/risc/RiScController.kt b/src/main/kotlin/no/risc/risc/RiScController.kt index 445e6e91..25fec7f1 100644 --- a/src/main/kotlin/no/risc/risc/RiScController.kt +++ b/src/main/kotlin/no/risc/risc/RiScController.kt @@ -6,6 +6,7 @@ import no.risc.infra.connector.models.GCPAccessToken import no.risc.infra.connector.models.GithubAccessToken import no.risc.risc.models.DifferenceDTO import no.risc.risc.models.DifferenceRequestBody +import no.risc.risc.models.GenerateInitialRiScRequestBody import no.risc.risc.models.RiScWrapperObject import no.risc.risc.models.UserInfo import org.springframework.http.ResponseEntity @@ -127,6 +128,26 @@ class RiScController( } } + @PostMapping("/{repositoryOwner}/{repositoryName}/initialize") + suspend fun generateInitialRiSc( + @RequestHeader("GCP-Access-Token") gcpAccessToken: String, + @RequestHeader("GitHub-Access-Token") gitHubAccessToken: String, + @PathVariable repositoryOwner: String, + @PathVariable repositoryName: String, + @RequestBody generateInitialRiScRequestBody: GenerateInitialRiScRequestBody, + ): RiScContentResultDTO = + riScService.initializeGeneratedRiSc( + repositoryOwner, + repositoryName, + generateInitialRiScRequestBody.publicAgeKey, + generateInitialRiScRequestBody.gcpProjectId, + AccessTokens( + gcpAccessToken = GCPAccessToken(gcpAccessToken), + githubAccessToken = GithubAccessToken(gitHubAccessToken), + ), + defaultBranch = githubConnector.fetchDefaultBranch(repositoryOwner, repositoryName, gitHubAccessToken), + ) + @PostMapping("/{repositoryOwner}/{repositoryName}/{riscId}/difference", produces = ["application/json"]) suspend fun getDifferenceBetweenTwoRiScs( @RequestHeader("GCP-Access-Token") gcpAccessToken: String, diff --git a/src/main/kotlin/no/risc/risc/RiScService.kt b/src/main/kotlin/no/risc/risc/RiScService.kt index 308e4c7a..51fa6f3a 100644 --- a/src/main/kotlin/no/risc/risc/RiScService.kt +++ b/src/main/kotlin/no/risc/risc/RiScService.kt @@ -7,6 +7,7 @@ import kotlinx.coroutines.coroutineScope import kotlinx.serialization.Serializable import no.risc.encryption.CryptoServiceIntegration import no.risc.exception.exceptions.CreatingRiScException +import no.risc.exception.exceptions.GenerateInitialRiScException import no.risc.exception.exceptions.RiScNotValidOnUpdateException import no.risc.exception.exceptions.SOPSDecryptionException import no.risc.exception.exceptions.SopsConfigFetchException @@ -15,21 +16,25 @@ import no.risc.github.GithubConnector import no.risc.github.GithubContentResponse import no.risc.github.GithubPullRequestObject import no.risc.github.GithubStatus +import no.risc.github.models.SopsConfigOnGitHub import no.risc.infra.connector.models.AccessTokens import no.risc.infra.connector.models.GCPAccessToken +import no.risc.initRiSc.InitRiScServiceIntegration import no.risc.risc.models.DifferenceDTO import no.risc.risc.models.RiScWrapperObject import no.risc.risc.models.UserInfo import no.risc.utils.Difference import no.risc.utils.DifferenceException import no.risc.utils.diff +import no.risc.utils.generateRiScId import no.risc.utils.migrate +import no.risc.utils.modifySopsConfigForGitHub import no.risc.utils.removePathRegex import no.risc.validation.JSONValidator -import org.apache.commons.lang3.RandomStringUtils import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Value +import org.springframework.boot.actuate.health.HttpCodeStatusMapper import org.springframework.stereotype.Service class ProcessRiScResultDTO( @@ -123,6 +128,7 @@ enum class ProcessingStatus( ) { ErrorWhenUpdatingRiSc("Error when updating risk scorecard"), CreatedRiSc("Created new risk scorecard successfully"), + InitializedGeneratedRiSc("Created new auto-generated risk scorecard successfully"), UpdatedRiSc("Updated risk scorecard successfully"), UpdatedRiScAndCreatedPullRequest("Updated risk scorecard and created pull request"), CreatedPullRequest("Created pull request for risk scorecard"), @@ -132,6 +138,7 @@ enum class ProcessingStatus( UpdatedRiScRequiresNewApproval("Updated risk scorecard and requires new approval"), ErrorWhenCreatingRiSc("Error when creating risk scorecard"), AccessTokensValidationFailure("Failure when validating access tokens"), + ErrorWhenGeneratingInitialRiSc("Error when generating initial risk scorecard"), } data class RiScIdentifier( @@ -170,6 +177,8 @@ class RiScService( private val githubConnector: GithubConnector, @Value("\${filename.prefix}") val filenamePrefix: String, private val cryptoService: CryptoServiceIntegration, + private val initRiScService: InitRiScServiceIntegration, + private val healthHttpCodeStatusMapper: HttpCodeStatusMapper, ) { companion object { val LOGGER: Logger = LoggerFactory.getLogger(RiScService::class.java) @@ -444,7 +453,15 @@ class RiScService( content: RiScWrapperObject, accessTokens: AccessTokens, defaultBranch: String, - ): RiScResult = updateOrCreateRiSc(owner, repository, riScId, content, accessTokens, defaultBranch) + ): RiScResult = + updateOrCreateRiSc( + owner = owner, + repository = repository, + riScId = riScId, + content = content, + accessTokens = accessTokens, + defaultBranch = defaultBranch, + ) suspend fun createRiSc( owner: String, @@ -452,10 +469,20 @@ class RiScService( content: RiScWrapperObject, accessTokens: AccessTokens, defaultBranch: String, + sopsConfiguration: String? = null, ): ProcessRiScResultDTO { - val uniqueRiScId = "$filenamePrefix-${RandomStringUtils.randomAlphanumeric(5)}" + val uniqueRiScId = generateRiScId(filenamePrefix) try { - val result = updateOrCreateRiSc(owner, repository, uniqueRiScId, content, accessTokens, defaultBranch) + val result = + updateOrCreateRiSc( + owner, + repository, + uniqueRiScId, + content, + sopsConfiguration, + accessTokens, + defaultBranch, + ) if (result.status == ProcessingStatus.UpdatedRiSc) { return ProcessRiScResultDTO( @@ -478,10 +505,10 @@ class RiScService( repository: String, riScId: String, content: RiScWrapperObject, + sopsConfiguration: String? = null, accessTokens: AccessTokens, defaultBranch: String, ): RiScResult { - println(defaultBranch) val validationStatus = JSONValidator.validateAgainstSchema( riScId, @@ -497,19 +524,45 @@ class RiScService( ) } - val sopsConfig = githubConnector.fetchSopsConfig(owner, repository, accessTokens.githubAccessToken, riScId) - if (sopsConfig.status != GithubStatus.Success) { - throw SopsConfigFetchException( - message = "Failed when fetching SopsConfig from Github with status: ${sopsConfig.status}", - riScId = riScId, - responseMessage = "Could not fetch SOPS config", - ) - } - - val config = removePathRegex(sopsConfig.data()) + val sopsConfigOnGitHub: SopsConfigOnGitHub = + if (sopsConfiguration != null) { + val providedSopsConfig = removePathRegex(sopsConfiguration) + // Need to write generated SOPS config to GitHub + LOGGER.info("Writing SOPS config to GitHub") + val commitSha = + githubConnector + .writeSopsConfig( + modifySopsConfigForGitHub(sopsConfiguration), + owner, + repository, + riScId, + accessTokens, + content.userInfo, + defaultBranch, + true, + ).commit.sha + LOGGER.info("Successfully written SOPS config to GitHub") + SopsConfigOnGitHub(providedSopsConfig, commitSha) + } else { + val gitHubFetchSopsConfigResponse = + githubConnector.fetchSopsConfig( + owner, + repository, + accessTokens.githubAccessToken, + riScId, + ) + if (gitHubFetchSopsConfigResponse.status != GithubStatus.Success) { + throw SopsConfigFetchException( + message = "Failed when fetching SopsConfig from Github with status: ${gitHubFetchSopsConfigResponse.status}", + riScId = riScId, + responseMessage = "Could not fetch SOPS config", + ) + } + SopsConfigOnGitHub(removePathRegex(gitHubFetchSopsConfigResponse.data())) + } val encryptedData: String = - cryptoService.encrypt(content.riSc, config, accessTokens.gcpAccessToken, riScId) + cryptoService.encrypt(content.riSc, sopsConfigOnGitHub.config, accessTokens.gcpAccessToken, riScId) try { val riScApprovalPRStatus = @@ -522,6 +575,7 @@ class RiScService( accessTokens = accessTokens, userInfo = content.userInfo, defaultBranch = defaultBranch, + commitSha = sopsConfigOnGitHub.commitSha, ) return when (riScApprovalPRStatus.pullRequest) { @@ -606,4 +660,46 @@ class RiScService( pullRequestUrl = this.url, pullRequestName = this.head.ref, ) + + suspend fun initializeGeneratedRiSc( + repositoryOwner: String, + repositoryName: String, + publicAgeKey: String?, + gcpProjectId: String, + accessTokens: AccessTokens, + defaultBranch: String, + ): RiScContentResultDTO { + val initialRiScAndSopsConfig = + initRiScService.getInitialRiScAndSopsConfig(repositoryName, publicAgeKey, gcpProjectId) + ?: throw GenerateInitialRiScException( + "Error occurred fetching generated RiSc from Initialize RiSc Service", + "", + ) + LOGGER.info("Successfully fetched initial RiSc") + val result = + createRiSc( + repositoryOwner, + repositoryName, + RiScWrapperObject( + initialRiScAndSopsConfig.initialRiScContent, + true, + initialRiScAndSopsConfig.schemaVersion, + initialRiScAndSopsConfig.userInfo, + ), + accessTokens, + defaultBranch, + initialRiScAndSopsConfig.sopsConfig, + ) + if (result.status == ProcessingStatus.CreatedRiSc) { + return RiScContentResultDTO( + result.riScId, + ContentStatus.Success, + RiScStatus.Draft, + initialRiScAndSopsConfig.initialRiScContent, + null, + ) + } else { + throw GenerateInitialRiScException("Error occurred when encrypting and writing initial RiSc to GitHub", "") + } + } } diff --git a/src/main/kotlin/no/risc/risc/models/DTOs.kt b/src/main/kotlin/no/risc/risc/models/DTOs.kt index eba435eb..92253588 100644 --- a/src/main/kotlin/no/risc/risc/models/DTOs.kt +++ b/src/main/kotlin/no/risc/risc/models/DTOs.kt @@ -1,6 +1,8 @@ package no.risc.risc.models import no.risc.risc.DifferenceStatus +import no.risc.risc.ProcessingStatus +import no.risc.risc.RiScContentResultDTO import no.risc.utils.Difference data class DifferenceDTO( @@ -13,3 +15,14 @@ data class DifferenceDTO( data class DifferenceRequestBody( val riSc: String, ) + +data class GenerateInitialRiScRequestBody( + val publicAgeKey: String? = null, + val gcpProjectId: String, +) + +data class GenerateInitialRiScResponse( + val riSc: RiScContentResultDTO, + val status: ProcessingStatus, + val statusMessage: String, +) diff --git a/src/main/kotlin/no/risc/utils/Utils.kt b/src/main/kotlin/no/risc/utils/Utils.kt index a87586d3..314407a2 100644 --- a/src/main/kotlin/no/risc/utils/Utils.kt +++ b/src/main/kotlin/no/risc/utils/Utils.kt @@ -1,6 +1,7 @@ package no.risc.utils import no.risc.github.models.FileNameDTO +import org.apache.commons.lang3.RandomStringUtils import java.util.Base64 fun getFileNameWithHighestVersion(files: List): String? = @@ -32,6 +33,8 @@ fun String.encodeBase64(): String = Base64.getEncoder().encodeToString(toByteArr fun String.decodeBase64(): String = Base64.getMimeDecoder().decode(toByteArray()).decodeToString() +fun generateRiScId(filenamePrefix: String) = "$filenamePrefix-${RandomStringUtils.randomAlphanumeric(5)}" + fun removePathRegex(config: String): String { val regex = "(?path_regex:.*)".toRegex() @@ -44,3 +47,10 @@ fun removePathRegex(config: String): String { config } } + +fun modifySopsConfigForGitHub(config: String) = + config + .lines() + .drop(1) // Remove the first line (---) + .joinToString("\n") + .replace("\"\\\\.risc\\\\.yaml$\"", "\\.risc\\.yaml$") diff --git a/src/main/resources/application-local.properties b/src/main/resources/application-local.properties index fb53df2b..c95b07ff 100644 --- a/src/main/resources/application-local.properties +++ b/src/main/resources/application-local.properties @@ -1,3 +1,3 @@ cryptoService.baseUrl=http://localhost:8084 - +initRisc.baseUrl=http://localhost:8085 diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index dace1602..a8dc93b1 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -7,4 +7,5 @@ management.endpoints.enabled-by-default=false management.endpoint.health.enabled=true management.endpoint.prometheus.enabled=true management.endpoints.web.exposure.include=prometheus,health -cryptoService.baseUrl=${CRYPTO_SERVICE_URL} \ No newline at end of file +cryptoService.baseUrl=${CRYPTO_SERVICE_URL} +initRisc.baseUrl=${INIT_RISC_URL} \ No newline at end of file