From eba0a70b34caedab93487a32f0b1f4471b05d37c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Membr=C3=A9?= Date: Mon, 22 Apr 2013 19:21:14 +0200 Subject: [PATCH] Fixes #3501 : Prevent Self Validation --- .../services/workflows/WorkflowService.scala | 55 +++++++++++++++---- .../resources/configuration.properties.sample | 20 +++++++ .../scala/bootstrap/liftweb/AppConfig.scala | 4 ++ .../administration/ChangeRequestDetails.scala | 5 +- 4 files changed, 71 insertions(+), 13 deletions(-) diff --git a/rudder-core/src/main/scala/com/normation/rudder/services/workflows/WorkflowService.scala b/rudder-core/src/main/scala/com/normation/rudder/services/workflows/WorkflowService.scala index 8bc9c494d7d..df3070ebd98 100644 --- a/rudder-core/src/main/scala/com/normation/rudder/services/workflows/WorkflowService.scala +++ b/rudder-core/src/main/scala/com/normation/rudder/services/workflows/WorkflowService.scala @@ -83,8 +83,18 @@ trait WorkflowService { val stepsValue :List[WorkflowNodeId] - def findNextSteps(currentUserRights:Seq[String],currentStep:WorkflowNodeId) : WorkflowAction - def findBackSteps(currentUserRights:Seq[String],currentStep:WorkflowNodeId) : Seq[(WorkflowNodeId,(ChangeRequestId,EventActor, Option[String]) => Box[WorkflowNodeId])] + def findNextSteps( + currentUserRights : Seq[String] + , currentStep : WorkflowNodeId + , isCreator : Boolean + ) : WorkflowAction + + def findBackSteps( + currentUserRights : Seq[String] + , currentStep : WorkflowNodeId + , isCreator : Boolean + ) : Seq[(WorkflowNodeId,(ChangeRequestId,EventActor, Option[String]) => Box[WorkflowNodeId])] + def findStep(changeRequestId: ChangeRequestId) : Box[WorkflowNodeId] } @@ -111,9 +121,17 @@ class NoWorkflowServiceImpl( val noWorfkflow = WorkflowNodeId("No Workflow") - def findNextSteps(currentUserRights:Seq[String],currentStep:WorkflowNodeId) : WorkflowAction = NoWorkflowAction + def findNextSteps( + currentUserRights : Seq[String] + , currentStep : WorkflowNodeId + , isCreator : Boolean + ) : WorkflowAction = NoWorkflowAction - def findBackSteps(currentUserRights:Seq[String],currentStep:WorkflowNodeId) = Seq() + def findBackSteps( + currentUserRights : Seq[String] + , currentStep : WorkflowNodeId + , isCreator : Boolean + ) : Seq[(WorkflowNodeId,(ChangeRequestId,EventActor, Option[String]) => Box[WorkflowNodeId])] = Seq() def findStep(changeRequestId: ChangeRequestId) : Box[WorkflowNodeId] = Failure("No state when no workflow") @@ -152,6 +170,8 @@ class TwoValidationStepsWorkflowServiceImpl( , roWorkflowRepo : RoWorkflowRepository , woWorkflowRepo : WoWorkflowRepository , workflowComet : AsyncWorkflowInfo + , selfValidation : Boolean + , selfDeployment : Boolean ) extends WorkflowService with Loggable { case object Validation extends WorkflowNode { @@ -176,14 +196,20 @@ class TwoValidationStepsWorkflowServiceImpl( val stepsValue = steps.map(_.id) - def findNextSteps(currentUserRights:Seq[String],currentStep:WorkflowNodeId) = { + def findNextSteps( + currentUserRights : Seq[String] + , currentStep : WorkflowNodeId + , isCreator : Boolean + ) : WorkflowAction = { val authorizedRoles = currentUserRights.filter(role => (role == "validator" || role == "deployer")) + val canValid = selfValidation || !isCreator + val canDeploy = selfDeployment || !isCreator currentStep match { case Validation.id => val validatorActions = - if (authorizedRoles.contains("validator")) + if (authorizedRoles.contains("validator") && canValid) Seq((Deployment.id,stepValidationToDeployment _)) ++ { - if(authorizedRoles.contains("deployer")) + if(authorizedRoles.contains("deployer") && canDeploy) Seq((Deployed.id,stepValidationToDeployed _)) else Seq() } @@ -193,7 +219,7 @@ class TwoValidationStepsWorkflowServiceImpl( case Deployment.id => val actions = - if(authorizedRoles.contains("deployer")) + if(authorizedRoles.contains("deployer") && canDeploy) Seq((Deployed.id,stepDeploymentToDeployed _)) else Seq() WorkflowAction("Deploy",actions) @@ -202,11 +228,18 @@ class TwoValidationStepsWorkflowServiceImpl( } } - def findBackSteps(currentUserRights:Seq[String],currentStep:WorkflowNodeId): Seq[(WorkflowNodeId,(ChangeRequestId,EventActor, Option[String]) => Box[WorkflowNodeId])] = { + def findBackSteps( + currentUserRights : Seq[String] + , currentStep : WorkflowNodeId + , isCreator : Boolean + ) : Seq[(WorkflowNodeId,(ChangeRequestId,EventActor, Option[String]) => Box[WorkflowNodeId])] = { val authorizedRoles = currentUserRights.filter(role => (role == "validator" || role == "deployer")) + val canValid = selfValidation || !isCreator + val canDeploy = selfDeployment || !isCreator currentStep match { - case Validation.id => if (authorizedRoles.contains("validator")) Seq((Cancelled.id,stepValidationToCancelled _)) else Seq() - case Deployment.id => if (authorizedRoles.contains("deployer")) Seq((Cancelled.id,stepDeploymentToCancelled _)) else Seq() + case Validation.id => + if (authorizedRoles.contains("validator") && canValid) Seq((Cancelled.id,stepValidationToCancelled _)) else Seq() + case Deployment.id => if (authorizedRoles.contains("deployer") && canDeploy) Seq((Cancelled.id,stepDeploymentToCancelled _)) else Seq() case Deployed.id => Seq() case Cancelled.id => Seq() } diff --git a/rudder-web/src/main/resources/configuration.properties.sample b/rudder-web/src/main/resources/configuration.properties.sample index 6b73ce456ac..6d2bd0721a3 100644 --- a/rudder-web/src/main/resources/configuration.properties.sample +++ b/rudder-web/src/main/resources/configuration.properties.sample @@ -390,6 +390,26 @@ rudder.batch.reports.logInterval=1 rudder.workflow.enabled=false +# Enable self validation (default false) +# +# If false, no change request can be validated by it's creator +# So every change request will need to be reviewed by someone different from +# who requested this changed +# +# Boolean, defaults to false. + +rudder.workflow.self.validation=false + +# Enable self deployment (default true) +# +# If false, no change request can be deployed by it's creator +# So every change request will need to be reviewed by someone different from +# who requested this changed +# +# Boolean, defaults to true. + +rudder.workflow.self.deployment=true + ######################### # DEPRECATED properties ############################################################# ######################### diff --git a/rudder-web/src/main/scala/bootstrap/liftweb/AppConfig.scala b/rudder-web/src/main/scala/bootstrap/liftweb/AppConfig.scala index d7cbb93b5d6..285bc6f088f 100644 --- a/rudder-web/src/main/scala/bootstrap/liftweb/AppConfig.scala +++ b/rudder-web/src/main/scala/bootstrap/liftweb/AppConfig.scala @@ -236,6 +236,8 @@ object RudderConfig extends Loggable { //workflows configuration val RUDDER_ENABLE_APPROVAL_WORKFLOWS = config.getBoolean("rudder.workflow.enabled") // false + val RUDDER_ENABLE_SELF_VALIDATION = config.getBoolean("rudder.workflow.self.validation") // false + val RUDDER_ENABLE_SELF_DEPLOYMENT = config.getBoolean("rudder.workflow.self.deployment") // true val licensesConfiguration = "licenses.xml" val logentries = "logentries.xml" @@ -363,6 +365,8 @@ object RudderConfig extends Loggable { , roWorkflowRepository , woWorkflowRepository , asyncWorkflowInfo + , RUDDER_ENABLE_SELF_VALIDATION + , RUDDER_ENABLE_SELF_DEPLOYMENT ) case false => new NoWorkflowServiceImpl( commitAndDeployChangeRequest diff --git a/rudder-web/src/main/scala/com/normation/rudder/web/snippet/administration/ChangeRequestDetails.scala b/rudder-web/src/main/scala/com/normation/rudder/web/snippet/administration/ChangeRequestDetails.scala index b181b8dc873..bbd1e53b75b 100644 --- a/rudder-web/src/main/scala/com/normation/rudder/web/snippet/administration/ChangeRequestDetails.scala +++ b/rudder-web/src/main/scala/com/normation/rudder/web/snippet/administration/ChangeRequestDetails.scala @@ -183,8 +183,9 @@ class ChangeRequestDetails extends DispatchSnippet with Loggable { def displayActionButton(cr:ChangeRequest,step:WorkflowNodeId):NodeSeq = { val authz = CurrentUser.getRights.authorizationTypes.toSeq.collect{case Edit(right) => right} + val isOwner = cr.owner == CurrentUser.getActor.name ( "#backStep" #> { - workflowService.findBackSteps(authz, step) match { + workflowService.findBackSteps(authz, step,isOwner) match { case Nil => NodeSeq.Empty case steps => SHtml.ajaxButton( @@ -193,7 +194,7 @@ class ChangeRequestDetails extends DispatchSnippet with Loggable { ) } } & "#nextStep" #> { if(commitAndDeployChangeRequest.isMergeable(cr.id)) { - workflowService.findNextSteps(authz,step) match { + workflowService.findNextSteps(authz,step,isOwner) match { case NoWorkflowAction => NodeSeq.Empty case WorkflowAction(actionName,emptyList) if emptyList.size == 0 => NodeSeq.Empty case WorkflowAction(actionName,steps) =>