Skip to content

Commit

Permalink
CID-1829: validate configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
mohamedlajmileanix committed Aug 28, 2023
1 parent 9d75e99 commit b991b93
Show file tree
Hide file tree
Showing 10 changed files with 215 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package net.leanix.vsm.gitlab.broker.connector.adapter.feign

import jakarta.websocket.server.PathParam
import net.leanix.vsm.gitlab.broker.connector.adapter.feign.data.GitlabUser
import net.leanix.vsm.gitlab.broker.shared.auth.adapter.feign.GitlabFeignClientConfiguration
import org.springframework.cloud.openfeign.FeignClient
import org.springframework.web.bind.annotation.GetMapping

@FeignClient(
name = "gitlab",
url = "\${leanix.gitlab.base-url}",
configuration = [GitlabFeignClientConfiguration::class]
)
interface GitlabClient {
@GetMapping("/user")
fun getCurrentUser(): GitlabUser

@GetMapping("/users/{userId}")
fun getUserById(@PathParam("userId") userId: Int): GitlabUser

@GetMapping("/projects/{projectName}")
fun getProjectByNameWithNamespace(@PathParam("projectName") projectName: String)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package net.leanix.vsm.gitlab.broker.connector.adapter.feign

import net.leanix.vsm.gitlab.broker.connector.adapter.feign.data.GitlabUser
import net.leanix.vsm.gitlab.broker.shared.exception.VsmException
import org.slf4j.LoggerFactory

class GitlabClientProvider(
private val gitlabClient: GitlabClient
) {

private val logger = LoggerFactory.getLogger(GitlabClientProvider::class.java)

fun getCurrentUser(): GitlabUser {
val user = kotlin.runCatching { gitlabClient.getCurrentUser() }
.onFailure {
logger.error("Invalid token, could not get current user")
throw VsmException.InvalidToken()
}.getOrThrow()
return runCatching { gitlabClient.getUserById(user.id) }
.onFailure { logger.error("Could not get user with id ${user.id}") }
.getOrThrow()
}

fun getOrg(orgName: String) {
runCatching { gitlabClient.getProjectByNameWithNamespace(orgName) }
.onFailure { logger.error("Could not get org info for $orgName") }
.getOrThrow()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package net.leanix.vsm.gitlab.broker.connector.adapter.feign.data

import com.fasterxml.jackson.annotation.JsonProperty

class GitlabUser(
@JsonProperty("id")
val id: Int,
@JsonProperty("is_admin")
val isAdmin: Boolean?
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package net.leanix.vsm.gitlab.broker.connector.application

import net.leanix.vsm.gitlab.broker.connector.domain.GitLabAssignment
import net.leanix.vsm.gitlab.broker.logs.application.LoggingService
import net.leanix.vsm.gitlab.broker.logs.domain.AdminLog
import net.leanix.vsm.gitlab.broker.logs.domain.LogLevel
import net.leanix.vsm.gitlab.broker.logs.domain.LogStatus
import net.leanix.vsm.gitlab.broker.logs.domain.StatusLog
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.MessageSource
import java.util.*

open class BaseConnectorService {

@Autowired
private lateinit var loggingService: LoggingService

@Autowired
private lateinit var messageSource: MessageSource

private val logger = LoggerFactory.getLogger(BaseConnectorService::class.java)

fun logFailedStatus(message: String? = "empty message", runId: UUID) {
logger.error(message)
loggingService.sendStatusLog(
StatusLog(runId, LogStatus.FAILED, message)
)
}

fun logInfoMessages(code: String, arguments: Array<Any>, assignment: GitLabAssignment) {
val message = messageSource.getMessage(
code,
arguments,
Locale.ENGLISH
)
loggingService.sendAdminLog(
AdminLog(
runId = assignment.runId,
configurationId = assignment.configurationId,
subject = LogLevel.INFO.toString(),
level = LogLevel.INFO,
message = message
)
)
}

fun logFailedMessages(code: String, arguments: Array<Any>, assignment: GitLabAssignment) {
val message = messageSource.getMessage(
code,
arguments,
Locale.ENGLISH
)
loggingService.sendAdminLog(
AdminLog(
runId = assignment.runId,
configurationId = assignment.configurationId,
subject = LogLevel.ERROR.toString(),
level = LogLevel.ERROR,
message = message
)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package net.leanix.vsm.gitlab.broker.connector.application

import feign.FeignException
import net.leanix.vsm.gitlab.broker.connector.adapter.feign.GitlabClientProvider
import net.leanix.vsm.gitlab.broker.connector.domain.GitLabAssignment
import net.leanix.vsm.gitlab.broker.shared.exception.VsmException
import org.springframework.stereotype.Component
import java.net.URLEncoder

@Component
class ValidationService(
private val gitlabClientProvider: GitlabClientProvider
) : BaseConnectorService() {

fun validateConfiguration(gitLabAssignment: GitLabAssignment) {
val orgName = gitLabAssignment.connectorConfiguration.orgName
runCatching {
validateUserAccess()
validateOrgName(gitLabAssignment.connectorConfiguration.orgName)
}.onSuccess {
logInfoMessages("vsm.configuration.validation.successful", arrayOf(orgName), gitLabAssignment)
}.onFailure { exception ->
handleExceptions(exception, orgName, gitLabAssignment)
}
}

private fun validateUserAccess() {
run {
val user = gitlabClientProvider.getCurrentUser()
if (user.isAdmin != true) throw VsmException.AccessLevelValidationFailed()
}
}

private fun validateOrgName(orgName: String) {
runCatching {
gitlabClientProvider.getOrg(URLEncoder.encode(orgName, "UTF-8"))
}.onFailure {
throw VsmException.OrgNameValidationFailed()
}
}

private fun handleExceptions(
exception: Throwable,
orgName: String,
gitLabAssignment: GitLabAssignment
) {
when (exception) {
is VsmException.InvalidToken -> {
logFailedMessages("vsm.configuration.invalid_token", arrayOf(orgName), gitLabAssignment)
}

is VsmException.AccessLevelValidationFailed -> {
logFailedMessages("vsm.configuration.access_level", arrayOf(orgName), gitLabAssignment)
}

is VsmException.OrgNameValidationFailed -> {
logFailedMessages("vsm.configuration.invalid_org_name", arrayOf(orgName), gitLabAssignment)
}

is FeignException -> {
logFailedMessages("vsm.configuration.validation.failed", arrayOf(orgName), gitLabAssignment)
}
}
logFailedStatus(exception.message, gitLabAssignment.runId)
throw exception
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package net.leanix.vsm.gitlab.broker.connector.runner

import net.leanix.vsm.gitlab.broker.connector.application.AssignmentService
import net.leanix.vsm.gitlab.broker.connector.application.ValidationService
import net.leanix.vsm.gitlab.broker.shared.cache.AssignmentsCache
import net.leanix.vsm.gitlab.broker.webhook.domain.WebhookService
import org.slf4j.Logger
Expand All @@ -12,6 +13,7 @@ import org.springframework.stereotype.Component
@Component
class InitialStateRunner(
private val assignmentService: AssignmentService,
private val validationService: ValidationService,
private val webhookService: WebhookService
) : ApplicationRunner {

Expand All @@ -30,6 +32,7 @@ class InitialStateRunner(
"Received assignment for ${assignment.connectorConfiguration.orgName} " +
"with configuration id: ${assignment.configurationId} and with run id: ${assignment.runId}"
)
validationService.validateConfiguration(assignment)
}
}.onSuccess {
logger.info("Cached ${AssignmentsCache.getAll().size} assignments")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package net.leanix.vsm.gitlab.broker.webhook.adapter.feign
package net.leanix.vsm.gitlab.broker.shared.auth.adapter.feign

import feign.RequestInterceptor
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
class GitlabWebhookFeignClientConfiguration(
class GitlabFeignClientConfiguration(

@Value("\${leanix.gitlab.access-token}") private val gitlabAccessToken: String
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package net.leanix.vsm.gitlab.broker.shared.exception

sealed class VsmException(message: String? = null) : RuntimeException(message) {

class InvalidToken : VsmException()

class AccessLevelValidationFailed : VsmException()

class OrgNameValidationFailed : VsmException()
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package net.leanix.vsm.gitlab.broker.webhook.adapter.feign

import net.leanix.vsm.gitlab.broker.shared.auth.adapter.feign.GitlabFeignClientConfiguration
import net.leanix.vsm.gitlab.broker.webhook.domain.GitlabWebhook
import org.springframework.cloud.openfeign.FeignClient
import org.springframework.web.bind.annotation.DeleteMapping
Expand All @@ -11,7 +12,7 @@ import org.springframework.web.bind.annotation.RequestParam
@FeignClient(
name = "gitlabWebhookClient",
url = "\${leanix.gitlab.base-url}",
configuration = [GitlabWebhookFeignClientConfiguration::class]
configuration = [GitlabFeignClientConfiguration::class]
)
interface GitlabWebhookClient {

Expand Down
5 changes: 5 additions & 0 deletions src/main/resources/messages.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
vsm.configuration.validation.successful=Validation successful for configuration with organisation name {0}
vsm.configuration.validation.failed=Validation failed for configuration with organisation name {0}
vsm.configuration.invalid_token=Invalid token in configuration with organisation name {0}
vsm.configuration.access_level=Access level insufficient for configuration with organisation name {0}
vsm.configuration.invalid_org_name=Invalid organisation name in configuration with organisation name {0}

0 comments on commit b991b93

Please sign in to comment.