Skip to content

Commit

Permalink
Revert "feat(pipeline executions/orca) : Added ability to add roles t…
Browse files Browse the repository at this point in the history
…o manual judgment stage. (#3988)" (#4024)

This reverts commit b8c5a7d.
  • Loading branch information
ajordens committed Dec 8, 2020
1 parent b8c5a7d commit 604b1bd
Show file tree
Hide file tree
Showing 7 changed files with 27 additions and 306 deletions.
2 changes: 1 addition & 1 deletion orca-echo/orca-echo.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ dependencies {
implementation("org.springframework.boot:spring-boot-autoconfigure")
implementation("javax.validation:validation-api")
implementation("com.netflix.spinnaker.fiat:fiat-core:$fiatVersion")
implementation("com.netflix.spinnaker.fiat:fiat-api:$fiatVersion")

testImplementation("com.squareup.retrofit:retrofit-mock")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,31 +19,23 @@ package com.netflix.spinnaker.orca.echo.pipeline
import com.fasterxml.jackson.annotation.JsonAnyGetter
import com.fasterxml.jackson.annotation.JsonAnySetter
import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.databind.ObjectMapper
import com.google.common.annotations.VisibleForTesting
import com.google.common.base.Strings
import com.netflix.spinnaker.fiat.model.UserPermission
import com.netflix.spinnaker.fiat.shared.FiatPermissionEvaluator
import com.netflix.spinnaker.fiat.shared.FiatStatus
import com.netflix.spinnaker.orca.AuthenticatedStage
import com.netflix.spinnaker.orca.api.pipeline.models.ExecutionStatus
import com.netflix.spinnaker.orca.api.pipeline.OverridableTimeoutRetryableTask
import com.netflix.spinnaker.orca.api.pipeline.models.StageExecution
import com.netflix.spinnaker.orca.api.pipeline.TaskResult

import javax.annotation.Nonnull
import java.util.concurrent.TimeUnit
import com.google.common.annotations.VisibleForTesting
import com.netflix.spinnaker.orca.*
import com.netflix.spinnaker.orca.echo.EchoService
import com.netflix.spinnaker.orca.api.pipeline.graph.StageDefinitionBuilder
import com.netflix.spinnaker.orca.api.pipeline.graph.TaskNode
import com.netflix.spinnaker.orca.api.pipeline.models.ExecutionStatus
import com.netflix.spinnaker.orca.api.pipeline.models.StageExecution
import com.netflix.spinnaker.orca.echo.EchoService
import com.netflix.spinnaker.orca.echo.util.ManualJudgmentAuthzGroupsUtil
import com.netflix.spinnaker.security.AuthenticatedRequest
import com.netflix.spinnaker.security.User
import groovy.util.logging.Slf4j
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component

import javax.annotation.Nonnull
import java.util.concurrent.TimeUnit

@Component
class ManualJudgmentStage implements StageDefinitionBuilder, AuthenticatedStage {

Expand Down Expand Up @@ -80,52 +72,21 @@ class ManualJudgmentStage implements StageDefinitionBuilder, AuthenticatedStage
final long backoffPeriod = 15000
final long timeout = TimeUnit.DAYS.toMillis(3)

private final EchoService echoService

private final FiatPermissionEvaluator fiatPermissionEvaluator

private FiatStatus fiatStatus

private ManualJudgmentAuthzGroupsUtil manualJudgmentAuthzGroupsUtil

private ObjectMapper objectMapper

@Autowired
WaitForManualJudgmentTask(Optional<EchoService> echoService, Optional<FiatPermissionEvaluator> fpe,
Optional<FiatStatus> fiatStatus, Optional<ObjectMapper> objectMapper,
Optional<ManualJudgmentAuthzGroupsUtil> manualJudgmentAuthzGroupsUtil) {
this.echoService = echoService.orElse(null)
this.fiatPermissionEvaluator = fpe.orElse(null)
this.fiatStatus = fiatStatus.orElse(null)
this.objectMapper = objectMapper.orElse(null)
this.manualJudgmentAuthzGroupsUtil = manualJudgmentAuthzGroupsUtil.orElse(null)
}
@Autowired(required = false)
EchoService echoService

@Override
TaskResult execute(StageExecution stage) {
StageData stageData = stage.mapTo(StageData)
def username = AuthenticatedRequest.getSpinnakerUser().orElse(stage.lastModified ? stage.lastModified.user : "")
boolean fiatEnabled = fiatStatus ? fiatStatus.isEnabled() : false
boolean isAuthorized = false
def appPermissions
def stageRoles
if (fiatEnabled) {
stageRoles = stage.context.selectedStageRoles
if (stageRoles) {
appPermissions = getApplicationPermissions(stage)
}
}
String notificationState
ExecutionStatus executionStatus

switch (stageData.state) {
case StageData.State.CONTINUE:
isAuthorized = !fiatEnabled || checkManualJudgmentAuthorizedGroups(stageRoles, appPermissions, username)
notificationState = "manualJudgmentContinue"
executionStatus = ExecutionStatus.SUCCEEDED
break
case StageData.State.STOP:
isAuthorized = !fiatEnabled || checkManualJudgmentAuthorizedGroups(stageRoles, appPermissions, username)
notificationState = "manualJudgmentStop"
executionStatus = ExecutionStatus.TERMINAL
break
Expand All @@ -134,47 +95,12 @@ class ManualJudgmentStage implements StageDefinitionBuilder, AuthenticatedStage
executionStatus = ExecutionStatus.RUNNING
break
}
if (!isAuthorized) {
notificationState = "manualJudgment"
executionStatus = ExecutionStatus.RUNNING
stage.context.put("judgmentStatus", "")
}

Map outputs = processNotifications(stage, stageData, notificationState)

return TaskResult.builder(executionStatus).context(outputs).build()
}

private Map<String, Object> getApplicationPermissions(StageExecution stage) {

def applicationName = stage.execution.application
def permissions
if (applicationName) {
manualJudgmentAuthzGroupsUtil.getApplication(applicationName).ifPresent({ application ->
if (application.getPermission().permissions && application.getPermission().permissions.permissions) {
permissions = objectMapper.convertValue(application.getPermission().permissions.permissions,
new TypeReference<Map<String, Object>>() {})
}
});
}
return permissions
}

boolean checkManualJudgmentAuthorizedGroups(List<String> stageRoles, Map<String, Object> permissions, String username) {

if (!Strings.isNullOrEmpty(username)) {
UserPermission.View permission = fiatPermissionEvaluator.getPermission(username);
if (permission == null) { // Should never happen?
log.warn("Attempted to get user permission for '$username' but none were found.")
return false;
}
// User has to have all the pipeline roles.
def userRoles = permission.getRoles().collect { it.getName().trim() }
return ManualJudgmentAuthzGroupsUtil.checkAuthorizedGroups(userRoles, stageRoles, permissions)
} else {
return false
}
}

Map processNotifications(StageExecution stage, StageData stageData, String notificationState) {
if (echoService) {
// sendNotifications will be true if using the new scheme for configuration notifications.
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,9 @@

package com.netflix.spinnaker.orca.echo.pipeline

import com.fasterxml.jackson.databind.ObjectMapper
import com.netflix.spinnaker.fiat.model.UserPermission
import com.netflix.spinnaker.fiat.model.resources.Role
import com.netflix.spinnaker.fiat.shared.FiatPermissionEvaluator
import com.netflix.spinnaker.fiat.shared.FiatStatus
import com.netflix.spinnaker.orca.api.pipeline.models.ExecutionStatus
import com.netflix.spinnaker.orca.api.pipeline.models.StageExecution
import com.netflix.spinnaker.orca.echo.EchoService
import com.netflix.spinnaker.orca.echo.util.ManualJudgmentAuthzGroupsUtil
import com.netflix.spinnaker.orca.front50.Front50Service
import com.netflix.spinnaker.orca.front50.model.Application
import com.netflix.spinnaker.orca.pipeline.model.PipelineExecutionImpl
import com.netflix.spinnaker.orca.pipeline.model.StageExecutionImpl
import spock.lang.Specification
Expand All @@ -35,41 +27,17 @@ import static com.netflix.spinnaker.orca.echo.pipeline.ManualJudgmentStage.Notif
import static com.netflix.spinnaker.orca.echo.pipeline.ManualJudgmentStage.WaitForManualJudgmentTask

class ManualJudgmentStageSpec extends Specification {

EchoService echoService = Mock(EchoService)

Front50Service front50Service = Mock(Front50Service)

FiatPermissionEvaluator fpe = Mock(FiatPermissionEvaluator)

FiatStatus fiatStatus = Mock() {
_ * isEnabled() >> true
}

ManualJudgmentAuthzGroupsUtil manualJudgmentAuthzGroupsUtil = new ManualJudgmentAuthzGroupsUtil(Optional.of(front50Service))

ObjectMapper objectMapper = new ObjectMapper()

def config = [
application: [
"name" : "orca",
"owner" : "owner",
"permissions" : [WRITE: ["foo"], READ: ["foo","baz"], EXECUTE: ["foo"]]
],
user : "testUser"
]

@Unroll
void "should return execution status based on judgmentStatus"() {
given:
def task = new WaitForManualJudgmentTask(Optional.of(echoService), Optional.of(fpe), Optional.of(fiatStatus),
Optional.of(objectMapper), Optional.of(manualJudgmentAuthzGroupsUtil))
def task = new WaitForManualJudgmentTask()

when:
def result = task.execute(new StageExecutionImpl(PipelineExecutionImpl.newPipeline("orca"), "", context))

then:
1 * fiatStatus.isEnabled() >> { return false }
result.status == expectedStatus
result.context.isEmpty()

where:
context || expectedStatus
Expand All @@ -81,39 +49,9 @@ class ManualJudgmentStageSpec extends Specification {
[judgmentStatus: "unknown"] || ExecutionStatus.RUNNING
}

@Unroll
void "should return execution status based on authorizedGroups"() {
given:
1 * fpe.getPermission('abc@somedomain.io') >> {
new UserPermission().addResources([new Role('foo'), new Role('baz')]).view
}
1 * front50Service.get("orca") >> new Application(config.application)

def task = new WaitForManualJudgmentTask(Optional.of(echoService), Optional.of(fpe), Optional.of(fiatStatus),
Optional.of(objectMapper), Optional.of(manualJudgmentAuthzGroupsUtil))

when:
def stage = new StageExecutionImpl(PipelineExecutionImpl.newPipeline("orca"), "", context)
stage.lastModified = new StageExecution.LastModifiedDetails(user: "abc@somedomain.io", allowedAccounts: ["group1"])
def result = task.execute(stage)

then:
result.status == expectedStatus

where:
context || expectedStatus
[judgmentStatus: "continue", selectedStageRoles: ['foo']] || ExecutionStatus.SUCCEEDED
[judgmentStatus: "Continue", selectedStageRoles: ['foo']] || ExecutionStatus.SUCCEEDED
[judgmentStatus: "stop", selectedStageRoles: ['foo']] || ExecutionStatus.TERMINAL
[judgmentStatus: "STOP", selectedStageRoles: ['foo']] || ExecutionStatus.TERMINAL
[judgmentStatus: "Continue", selectedStageRoles: ['baz']] || ExecutionStatus.RUNNING
[judgmentStatus: "Stop", selectedStageRoles: ['baz']] || ExecutionStatus.RUNNING
}

void "should only send notifications for supported types"() {
given:
def task = new WaitForManualJudgmentTask(Optional.of(echoService), Optional.of(fpe), Optional.of(fiatStatus),
Optional.of(objectMapper), Optional.of(manualJudgmentAuthzGroupsUtil))
def task = new WaitForManualJudgmentTask(echoService: Mock(EchoService))

when:
def result = task.execute(new StageExecutionImpl(PipelineExecutionImpl.newPipeline("orca"), "", [notifications: [
Expand All @@ -134,8 +72,7 @@ class ManualJudgmentStageSpec extends Specification {
@Unroll
void "if deprecated notification configuration is in use, only send notifications for awaiting judgment state"() {
given:
def task = new WaitForManualJudgmentTask(Optional.of(echoService), Optional.of(fpe), Optional.of(fiatStatus),
Optional.of(objectMapper), Optional.of(manualJudgmentAuthzGroupsUtil))
def task = new WaitForManualJudgmentTask(echoService: Mock(EchoService))

when:
def result = task.execute(new StageExecutionImpl(PipelineExecutionImpl.newPipeline("orca"), "", [
Expand All @@ -147,7 +84,6 @@ class ManualJudgmentStageSpec extends Specification {
]))

then:
1 * fiatStatus.isEnabled() >> { return false }
result.status == executionStatus
if (sent) result.context.notifications?.getAt(0)?.lastNotifiedByNotificationState?.containsKey(notificationState)

Expand Down Expand Up @@ -217,8 +153,7 @@ class ManualJudgmentStageSpec extends Specification {
@Unroll
void "should retain unknown fields in the notification context"() {
given:
def task = new WaitForManualJudgmentTask(Optional.of(echoService), Optional.of(fpe), Optional.of(fiatStatus),
Optional.of(objectMapper), Optional.of(manualJudgmentAuthzGroupsUtil))
def task = new WaitForManualJudgmentTask(echoService: Mock(EchoService))

def slackNotification = new Notification(type: "slack")
slackNotification.setOther("customMessage", "hello slack")
Expand Down
Loading

0 comments on commit 604b1bd

Please sign in to comment.