From 5929a1d7935b82d7d8676ca686a816d63f810185 Mon Sep 17 00:00:00 2001 From: Filip Chmielewski Date: Fri, 8 Jun 2018 20:18:26 +0200 Subject: [PATCH] Add security parentage validation Change-Id: I325690a56114db03ba2d3ba0cf99e393678a97da Closes-Bug: #1772594 (cherry picked from commit fb2c4b6b58b2e9ffa2be7be999bd145cb9575362) --- .../juniper/contrail/vro/config/Actions.kt | 1 + .../vro/config/constants/Constants.kt | 1 + .../SecurityScopeValidationSpec.groovy | 339 ++++++++++++++++++ .../AddServiceToServiceGroupSpec.groovy | 3 +- .../EditServiceOfServiceGroupSpec.groovy | 2 +- .../RemoveServiceFromServiceGroupSpec.groovy | 2 +- .../vro/tests/workflows/Dependencies.kt | 50 ++- .../main/js/actions/matchesSecurityScope.js | 30 ++ .../vro/workflows/custom/FirewallRule.kt | 58 +-- .../vro/workflows/custom/Validation.kt | 17 +- .../vro/workflows/custom/ValidationActions.kt | 11 + 11 files changed, 476 insertions(+), 38 deletions(-) create mode 100644 o11nplugin-contrail-tests/src/test/groovy/net/juniper/contrail/vro/tests/actions/SecurityScopeValidationSpec.groovy create mode 100644 o11nplugin-contrail-workflows/src/main/js/actions/matchesSecurityScope.js diff --git a/o11nplugin-contrail-config/src/main/kotlin/net/juniper/contrail/vro/config/Actions.kt b/o11nplugin-contrail-config/src/main/kotlin/net/juniper/contrail/vro/config/Actions.kt index 3c139a7d..70d3defc 100644 --- a/o11nplugin-contrail-config/src/main/kotlin/net/juniper/contrail/vro/config/Actions.kt +++ b/o11nplugin-contrail-config/src/main/kotlin/net/juniper/contrail/vro/config/Actions.kt @@ -42,6 +42,7 @@ val ipamHasNotAllocationMode = "ipamHasNotAllocationMode" val networkHasNotAllcationMode = "networkHasNotAllocationMode" val listElementProperty = "listElementProperty" val listTagTypes = "listTagTypes" +val matchesSecurityScope = "matchesSecurityScope" val portOfVCVirtualMachine = "portOfVCVirtualMachine" val networkOfVCPortGroup = "networkOfVCPortGroup" \ No newline at end of file diff --git a/o11nplugin-contrail-config/src/main/kotlin/net/juniper/contrail/vro/config/constants/Constants.kt b/o11nplugin-contrail-config/src/main/kotlin/net/juniper/contrail/vro/config/constants/Constants.kt index 949f9b02..0baaffaa 100644 --- a/o11nplugin-contrail-config/src/main/kotlin/net/juniper/contrail/vro/config/constants/Constants.kt +++ b/o11nplugin-contrail-config/src/main/kotlin/net/juniper/contrail/vro/config/constants/Constants.kt @@ -17,6 +17,7 @@ val VirtualMachine = "VirtualMachine" val Configuration = "Configuration" val Connection = "Connection" val parent = "parent" +val rule = "rule" val child = "child" val item = "item" val name = "name" diff --git a/o11nplugin-contrail-tests/src/test/groovy/net/juniper/contrail/vro/tests/actions/SecurityScopeValidationSpec.groovy b/o11nplugin-contrail-tests/src/test/groovy/net/juniper/contrail/vro/tests/actions/SecurityScopeValidationSpec.groovy new file mode 100644 index 00000000..8865293f --- /dev/null +++ b/o11nplugin-contrail-tests/src/test/groovy/net/juniper/contrail/vro/tests/actions/SecurityScopeValidationSpec.groovy @@ -0,0 +1,339 @@ +/* + * Copyright (c) 2018 Juniper Networks, Inc. All rights reserved. + */ + +package net.juniper.contrail.vro.tests.actions + +import net.juniper.contrail.vro.tests.workflows.WorkflowSpec + +import static net.juniper.contrail.vro.config.Actions.matchesSecurityScope + +// workflowSpec is required for dependencies +class SecurityScopeValidationSpec extends WorkflowSpec implements ValidationAsserts { + def validateSecurityScope = actionFromScript(matchesSecurityScope) + String securityScopeValidationMessage(badObjectName) { + return "$badObjectName comes from an inaccessible project." + } + + def connection = dependencies.connection + def project1 = dependencies.someProject() + def project2 = dependencies.someProject() + def projectFirewallRule = dependencies.someProjectFirewallRule(project1) + def globalFirewallRule = dependencies.someGlobalFirewallRule() + + def project1ServiceGroup = dependencies.someProjectServiceGroup(project1) + def project2ServiceGroup = dependencies.someProjectServiceGroup(project2) + def globalServiceGroup = dependencies.someGlobalServiceGroup() + + def project1Tag = dependencies.someProjectTag(project1) + def project2Tag = dependencies.someProjectTag(project2) + def globalTag = dependencies.someGlobalTag() + + def "Validating a null object when creating a project-scope firewall rule" () { + def children = null + def parent = project1 + def arrayMode = false + def directMode = true + + when: "executing validating script" + def result = invokeFunction(validateSecurityScope, children, parent, directMode, arrayMode) + + then: "it succeeds" + validationSuccess(result) + } + + // Service Group, rule creation + def "Validating a same-project-scope Service Group when creating a project-scope firewall rule" () { + given: + def children = project1ServiceGroup + def parent = project1 + def arrayMode = false + def directMode = true + + when: "executing validating script" + def result = invokeFunction(validateSecurityScope, children, parent, directMode, arrayMode) + + then: "it succeeds" + validationSuccess(result) + } + def "Validating a different-project-scope Service Group when creating a project-scope firewall rule" () { + given: + def children = project2ServiceGroup + def parent = project1 + def arrayMode = false + def directMode = true + + when: "executing validating script" + def result = invokeFunction(validateSecurityScope, children, parent, directMode, arrayMode) + + then: "it fails" + validationFailure(result) + } + def "Validating a global-scope Service Group when creating a project-scope firewall rule" () { + given: + def children = globalServiceGroup + def parent = project1 + def arrayMode = false + def directMode = true + + when: "executing validating script" + def result = invokeFunction(validateSecurityScope, children, parent, directMode, arrayMode) + + then: "it succeeds" + validationSuccess(result) + } + def "Validating a project-scope Service Group when creating a global-scope firewall rule" () { + given: + def children = project1ServiceGroup + def parent = connection + def arrayMode = false + def directMode = true + + when: "executing validating script" + def result = invokeFunction(validateSecurityScope, children, parent, directMode, arrayMode) + + then: "it fails" + validationFailure(result) + } + def "Validating a global-scope Service Group when creating a global-scope firewall rule" () { + given: + def children = globalServiceGroup + def parent = connection + def arrayMode = false + def directMode = true + + when: "executing validating script" + def result = invokeFunction(validateSecurityScope, children, parent, directMode, arrayMode) + + then: "it succeeds" + validationSuccess(result) + } + + // Tags, rule creation + def "Validating a same-project-scope Tag when creating a project-scope firewall rule" () { + given: + def children = [project1Tag] + def parent = project1 + def arrayMode = true + def directMode = true + + when: "executing validating script" + def result = invokeFunction(validateSecurityScope, children, parent, directMode, arrayMode) + + then: "it succeeds" + validationSuccess(result) + } + def "Validating a different-project-scope Tag when creating a project-scope firewall rule" () { + given: + def children = [project2Tag] + def parent = project1 + def arrayMode = true + def directMode = true + + when: "executing validating script" + def result = invokeFunction(validateSecurityScope, children, parent, directMode, arrayMode) + + then: "it fails" + validationFailure(result) + } + def "Validating a global-scope Tag when creating a project-scope firewall rule" () { + given: + def children = [globalTag] + def parent = project1 + def arrayMode = true + def directMode = true + + when: "executing validating script" + def result = invokeFunction(validateSecurityScope, children, parent, directMode, arrayMode) + + then: "it succeeds" + validationSuccess(result) + } + def "Validating a project-scope Tag when creating a global-scope firewall rule" () { + given: + def children = [project1Tag] + def parent = connection + def arrayMode = true + def directMode = true + + when: "executing validating script" + def result = invokeFunction(validateSecurityScope, children, parent, directMode, arrayMode) + + then: "it fails" + validationFailure(result) + } + def "Validating a global-scope Tag when creating a global-scope firewall rule" () { + given: + def children = [globalTag] + def parent = connection + def arrayMode = true + def directMode = true + + when: "executing validating script" + def result = invokeFunction(validateSecurityScope, children, parent, directMode, arrayMode) + + then: "it succeeds" + validationSuccess(result) + } + def "Validating a list of various Tags, one of which is wrong, when creating a project-scope firewall rule" () { + given: + def children = [project1Tag, project2Tag, globalTag] + def parent = project1 + def arrayMode = true + def directMode = true + + when: "executing validating script" + def result = invokeFunction(validateSecurityScope, children, parent, directMode, arrayMode) + + then: "it fails, naming the wrong tag" + validationFailureWith(result, securityScopeValidationMessage(project2Tag.name)) + } + + + // Service Group, rule edition + def "Validating a same-project-scope Service Group when editing a project-scope firewall rule" () { + given: + def children = project1ServiceGroup + def parent = projectFirewallRule + def arrayMode = false + def directMode = false + + when: "executing validating script" + def result = invokeFunction(validateSecurityScope, children, parent, directMode, arrayMode) + + then: "it succeeds" + validationSuccess(result) + } + def "Validating a different-project-scope Service Group when editing a project-scope firewall rule" () { + given: + def children = project2ServiceGroup + def parent = projectFirewallRule + def arrayMode = false + def directMode = false + + when: "executing validating script" + def result = invokeFunction(validateSecurityScope, children, parent, directMode, arrayMode) + + then: "it fails" + validationFailure(result) + } + def "Validating a global-scope Service Group when editing a project-scope firewall rule" () { + given: + def children = globalServiceGroup + def parent = projectFirewallRule + def arrayMode = false + def directMode = false + + when: "executing validating script" + def result = invokeFunction(validateSecurityScope, children, parent, directMode, arrayMode) + + then: "it succeeds" + validationSuccess(result) + } + def "Validating a project-scope Service Group when editing a global-scope firewall rule" () { + given: + def children = project1ServiceGroup + def parent = globalFirewallRule + def arrayMode = false + def directMode = false + + when: "executing validating script" + def result = invokeFunction(validateSecurityScope, children, parent, directMode, arrayMode) + + then: "it fails" + validationFailure(result) + } + def "Validating a global-scope Service Group when editing a global-scope firewall rule" () { + given: + def children = globalServiceGroup + def parent = globalFirewallRule + def arrayMode = false + def directMode = false + + when: "executing validating script" + def result = invokeFunction(validateSecurityScope, children, parent, directMode, arrayMode) + + then: "it succeeds" + validationSuccess(result) + } + + // Tags, rule edition + def "Validating a same-project-scope Tag when editing a project-scope firewall rule" () { + given: + def children = [project1Tag] + def parent = projectFirewallRule + def arrayMode = true + def directMode = false + + when: "executing validating script" + def result = invokeFunction(validateSecurityScope, children, parent, directMode, arrayMode) + + then: "it succeeds" + validationSuccess(result) + } + def "Validating a different-project-scope Tag when editing a project-scope firewall rule" () { + given: + def children = [project2Tag] + def parent = projectFirewallRule + def arrayMode = true + def directMode = false + + when: "executing validating script" + def result = invokeFunction(validateSecurityScope, children, parent, directMode, arrayMode) + + then: "it fails" + validationFailure(result) + } + def "Validating a global-scope Tag when editing a project-scope firewall rule" () { + given: + def children = [globalTag] + def parent = projectFirewallRule + def arrayMode = true + def directMode = false + + when: "executing validating script" + def result = invokeFunction(validateSecurityScope, children, parent, directMode, arrayMode) + + then: "it succeeds" + validationSuccess(result) + } + def "Validating a project-scope Tag when editing a global-scope firewall rule" () { + given: + def children = [project1Tag] + def parent = globalFirewallRule + def arrayMode = true + def directMode = false + + when: "executing validating script" + def result = invokeFunction(validateSecurityScope, children, parent, directMode, arrayMode) + + then: "it fails" + validationFailure(result) + } + def "Validating a global-scope Tag when editing a global-scope firewall rule" () { + given: + def children = [globalTag] + def parent = globalFirewallRule + def arrayMode = true + def directMode = false + + when: "executing validating script" + def result = invokeFunction(validateSecurityScope, children, parent, directMode, arrayMode) + + then: "it succeeds" + validationSuccess(result) + } + def "Validating a list of various Tags, one of which is wrong, when editing a project-scope firewall rule" () { + given: + def children = [project1Tag, project2Tag, globalTag] + def parent = projectFirewallRule + def arrayMode = true + def directMode = false + + when: "executing validating script" + def result = invokeFunction(validateSecurityScope, children, parent, directMode, arrayMode) + + then: "it fails, naming the wrong tag" + validationFailureWith(result, securityScopeValidationMessage(project2Tag.name)) + } +} diff --git a/o11nplugin-contrail-tests/src/test/groovy/net/juniper/contrail/vro/tests/workflows/AddServiceToServiceGroupSpec.groovy b/o11nplugin-contrail-tests/src/test/groovy/net/juniper/contrail/vro/tests/workflows/AddServiceToServiceGroupSpec.groovy index bf5c694d..3dd9dab5 100644 --- a/o11nplugin-contrail-tests/src/test/groovy/net/juniper/contrail/vro/tests/workflows/AddServiceToServiceGroupSpec.groovy +++ b/o11nplugin-contrail-tests/src/test/groovy/net/juniper/contrail/vro/tests/workflows/AddServiceToServiceGroupSpec.groovy @@ -5,7 +5,6 @@ package net.juniper.contrail.vro.tests.workflows import net.juniper.contrail.api.Status -import net.juniper.contrail.api.types.NetworkPolicy import net.juniper.contrail.api.types.ServiceGroup class AddServiceToServiceGroupSpec extends WorkflowSpec { @@ -19,7 +18,7 @@ class AddServiceToServiceGroupSpec extends WorkflowSpec { def "Adding service to a service group"() { given: - def serviceGroup = dependencies.someServiceGroup() + def serviceGroup = dependencies.someGlobalServiceGroup() def initialSize = serviceGroup.getFirewallServiceList()?.getFirewallService()?.size() ?: 0 connectorMock.read(_) >> Status.success() diff --git a/o11nplugin-contrail-tests/src/test/groovy/net/juniper/contrail/vro/tests/workflows/EditServiceOfServiceGroupSpec.groovy b/o11nplugin-contrail-tests/src/test/groovy/net/juniper/contrail/vro/tests/workflows/EditServiceOfServiceGroupSpec.groovy index 841feea3..a7b685c0 100644 --- a/o11nplugin-contrail-tests/src/test/groovy/net/juniper/contrail/vro/tests/workflows/EditServiceOfServiceGroupSpec.groovy +++ b/o11nplugin-contrail-tests/src/test/groovy/net/juniper/contrail/vro/tests/workflows/EditServiceOfServiceGroupSpec.groovy @@ -24,7 +24,7 @@ class EditServiceOfServiceGroupSpec extends WorkflowSpec { def "Editing service of service group"() { given: - def serviceGroup = dependencies.someServiceGroup() + def serviceGroup = dependencies.someGlobalServiceGroup() connectorMock.read(_) >> Status.success() connectorMock.update(_) >> Status.success() diff --git a/o11nplugin-contrail-tests/src/test/groovy/net/juniper/contrail/vro/tests/workflows/RemoveServiceFromServiceGroupSpec.groovy b/o11nplugin-contrail-tests/src/test/groovy/net/juniper/contrail/vro/tests/workflows/RemoveServiceFromServiceGroupSpec.groovy index e75b4c99..362c6fe2 100644 --- a/o11nplugin-contrail-tests/src/test/groovy/net/juniper/contrail/vro/tests/workflows/RemoveServiceFromServiceGroupSpec.groovy +++ b/o11nplugin-contrail-tests/src/test/groovy/net/juniper/contrail/vro/tests/workflows/RemoveServiceFromServiceGroupSpec.groovy @@ -19,7 +19,7 @@ class RemoveServiceFromServiceGroupSpec extends WorkflowSpec { def "Removing service from service group"() { given: - def serviceGroup = dependencies.someServiceGroup() + def serviceGroup = dependencies.someGlobalServiceGroup() def initialSize = serviceGroup.getFirewallServiceList()?.getFirewallService()?.size() ?: 0 connectorMock.read(_) >> Status.success() diff --git a/o11nplugin-contrail-tests/src/test/kotlin/net/juniper/contrail/vro/tests/workflows/Dependencies.kt b/o11nplugin-contrail-tests/src/test/kotlin/net/juniper/contrail/vro/tests/workflows/Dependencies.kt index 421c3bfc..88cd032e 100644 --- a/o11nplugin-contrail-tests/src/test/kotlin/net/juniper/contrail/vro/tests/workflows/Dependencies.kt +++ b/o11nplugin-contrail-tests/src/test/kotlin/net/juniper/contrail/vro/tests/workflows/Dependencies.kt @@ -5,6 +5,7 @@ package net.juniper.contrail.vro.tests.workflows import net.juniper.contrail.vro.gen.AddressGroup_Wrapper +import net.juniper.contrail.vro.gen.ConfigRoot_Wrapper import net.juniper.contrail.vro.gen.Connection_Wrapper import net.juniper.contrail.vro.gen.FirewallRule_Wrapper import net.juniper.contrail.vro.gen.FloatingIpPool_Wrapper @@ -12,6 +13,7 @@ import net.juniper.contrail.vro.gen.FloatingIp_Wrapper import net.juniper.contrail.vro.gen.IpamSubnetType_Wrapper import net.juniper.contrail.vro.gen.NetworkIpam_Wrapper import net.juniper.contrail.vro.gen.NetworkPolicy_Wrapper +import net.juniper.contrail.vro.gen.PolicyManagement_Wrapper import net.juniper.contrail.vro.gen.PortTuple_Wrapper import net.juniper.contrail.vro.gen.Project_Wrapper import net.juniper.contrail.vro.gen.SecurityGroup_Wrapper @@ -20,6 +22,7 @@ import net.juniper.contrail.vro.gen.ServiceHealthCheck_Wrapper import net.juniper.contrail.vro.gen.ServiceInstance_Wrapper import net.juniper.contrail.vro.gen.ServiceTemplate_Wrapper import net.juniper.contrail.vro.gen.SubnetType_Wrapper +import net.juniper.contrail.vro.gen.Tag_Wrapper import net.juniper.contrail.vro.gen.Utils_Wrapper import net.juniper.contrail.vro.gen.VirtualMachineInterfacePropertiesType_Wrapper import net.juniper.contrail.vro.gen.VirtualMachineInterface_Wrapper @@ -29,6 +32,18 @@ import java.util.UUID fun randomStringUuid() = UUID.randomUUID().toString() class Dependencies(private val connection: Connection_Wrapper, private val utils: Utils_Wrapper) { + val defaultPolicyManagement: PolicyManagement_Wrapper = PolicyManagement_Wrapper().apply { + uuid = randomStringUuid() + name = "default-policy-management" + setParentConnection(this@Dependencies.connection) + } + + val configRoot: ConfigRoot_Wrapper = ConfigRoot_Wrapper().apply { + uuid = randomStringUuid() + name = "config-root" + setParentConnection(this@Dependencies.connection) + } + fun someProject(): Project_Wrapper = Project_Wrapper().apply { uuid = randomStringUuid() name = "someProject$uuid" @@ -113,21 +128,48 @@ class Dependencies(private val connection: Connection_Wrapper, private val utils } @JvmOverloads - fun someFirewallRule(parent: Project_Wrapper = someProject()) = FirewallRule_Wrapper().apply { + fun someProjectFirewallRule(parent: Project_Wrapper = someProject()) = FirewallRule_Wrapper().apply { uuid = randomStringUuid() - name = "someFirewallRule$uuid" + name = "someProjectFirewallRule$uuid" setParentProject(parent) } + fun someGlobalFirewallRule() = FirewallRule_Wrapper().apply { + uuid = randomStringUuid() + name = "someGlobalFirewallRule$uuid" + setParentPolicyManagement(defaultPolicyManagement) + } + fun someServiceTemplate() = ServiceTemplate_Wrapper().apply { uuid = randomStringUuid() name = "someServiceTemplate$uuid" setParentConnection(this@Dependencies.connection) } - fun someServiceGroup() = ServiceGroup_Wrapper().apply { + @JvmOverloads + fun someProjectServiceGroup(parent: Project_Wrapper = someProject()) = ServiceGroup_Wrapper().apply { + uuid = randomStringUuid() + name = "someProjectServiceGroup$uuid" + setParentProject(parent) + } + + fun someGlobalServiceGroup() = ServiceGroup_Wrapper().apply { + uuid = randomStringUuid() + name = "someGlobalServiceGroup$uuid" + setParentPolicyManagement(defaultPolicyManagement) + } + + @JvmOverloads + fun someProjectTag(parent: Project_Wrapper = someProject()) = Tag_Wrapper().apply { + uuid = randomStringUuid() + name = "someProjectTag$uuid" + setParentProject(parent) + } + + fun someGlobalTag() = Tag_Wrapper().apply { uuid = randomStringUuid() - name = "someServiceGroup$uuid" + name = "someGlobalTag$uuid" + setParentConfigRoot(configRoot) setParentConnection(this@Dependencies.connection) } diff --git a/o11nplugin-contrail-workflows/src/main/js/actions/matchesSecurityScope.js b/o11nplugin-contrail-workflows/src/main/js/actions/matchesSecurityScope.js new file mode 100644 index 00000000..f09ed2d7 --- /dev/null +++ b/o11nplugin-contrail-workflows/src/main/js/actions/matchesSecurityScope.js @@ -0,0 +1,30 @@ +if (children === null) { + return null; +} + +if (!arrayMode) { + children = [children]; +} + +var expectedUuid = null; +if (directMode && typeof parent.uuid !== "undefined") { + expectedUuid = parent.uuid; +} else { + // extract project uuid from rule + expectedUuid = parent.parentUuid; +} + +var someBadChild = null; +for (var idx in children) { + // Child is considered erroneous if it is non-global (project-scope) AND it's project is different to the one we test for + var child = children[idx]; + if (child.parentType === "project" && expectedUuid !== child.parentUuid) { + someBadChild = child; + } +} + +if (someBadChild != null) { + return someBadChild.name + " comes from an inaccessible project." +} else { + return null; +} \ No newline at end of file diff --git a/o11nplugin-contrail-workflows/src/main/kotlin/net/juniper/contrail/vro/workflows/custom/FirewallRule.kt b/o11nplugin-contrail-workflows/src/main/kotlin/net/juniper/contrail/vro/workflows/custom/FirewallRule.kt index 14269ad5..403f8eaf 100644 --- a/o11nplugin-contrail-workflows/src/main/kotlin/net/juniper/contrail/vro/workflows/custom/FirewallRule.kt +++ b/o11nplugin-contrail-workflows/src/main/kotlin/net/juniper/contrail/vro/workflows/custom/FirewallRule.kt @@ -16,6 +16,7 @@ import net.juniper.contrail.api.types.VirtualNetwork import net.juniper.contrail.vro.config.constants.Connection import net.juniper.contrail.vro.config.constants.EndpointType import net.juniper.contrail.vro.config.constants.ServiceType +import net.juniper.contrail.vro.config.constants.rule import net.juniper.contrail.vro.schema.Schema import net.juniper.contrail.vro.schema.propertyDescription import net.juniper.contrail.vro.schema.simpleTypeConstraints @@ -33,7 +34,7 @@ import net.juniper.contrail.vro.workflows.model.string val defaultEndpointType = EndpointType.None.value val allowedEndpointTypes = EndpointType.values().map { it.value } -const val serviceTypeParameterName = "serviceType" +val serviceTypeParameterName = "serviceType" val defaultServiceType = ServiceType.Manual.value val allowedServiceTypes = ServiceType.values().map { it.value } @@ -53,7 +54,7 @@ internal fun createPolicyManagementFirewallRule(schema: Schema): WorkflowDefinit mandatory = true } } - output("rule", reference()) { + output(rule, reference()) { description = "Rule created by this workflow" } firewallRuleParameters(schema, parentConnectionField, false) @@ -71,7 +72,7 @@ internal fun createProjectFirewallRule(schema: Schema): WorkflowDefinition { mandatory = true } } - output("rule", reference()) { + output(rule, reference()) { description = "Rule created by this workflow" } firewallRuleParameters(schema, parentProjectField, false) @@ -84,115 +85,120 @@ internal fun editFirewallRule(schema: Schema): WorkflowDefinition { return customWorkflow(workflowName).withScriptFile("editFirewallRule") { step("Rule") { - parameter("rule", reference()) { + parameter(rule, reference()) { description = "Rule to edit" mandatory = true } } - firewallRuleParameters(schema, "rule", true) + firewallRuleParameters(schema, rule, true) } } -private fun PresentationParametersBuilder.firewallRuleParameters(schema: Schema, visibilityDependencyField: String, loadCurrentValues: Boolean) { +private fun PresentationParametersBuilder.firewallRuleParameters(schema: Schema, parentField: String, editing: Boolean) { + val projectValidationDirectMode = !editing step("Basic attributes") { - visibility = WhenNonNull(visibilityDependencyField) + visibility = WhenNonNull(parentField) parameter("action", string) { description = schema.propertyDescription("simpleAction") additionalQualifiers += schema.simpleTypeConstraints("simpleAction") - if (loadCurrentValues) dataBinding = firewallRulePropertyDataBinding("actionList.simpleAction") + if (editing) dataBinding = firewallRulePropertyDataBinding("actionList.simpleAction") } parameter("direction", string) { description = "Direction" mandatory = true additionalQualifiers += schema.simpleTypeConstraints("direction") - if (loadCurrentValues) dataBinding = firewallRulePropertyDataBinding("direction") + if (editing) dataBinding = firewallRulePropertyDataBinding("direction") } } step("Endpoints") { - visibility = WhenNonNull(visibilityDependencyField) - endpointParameters(schema, 1, loadCurrentValues) - endpointParameters(schema, 2, loadCurrentValues) + visibility = WhenNonNull(parentField) + endpointParameters(schema, parentField, 1, editing) + endpointParameters(schema, parentField, 2, editing) } step("Service") { - visibility = WhenNonNull(visibilityDependencyField) + visibility = WhenNonNull(parentField) parameter(serviceTypeParameterName, string) { description = "Service Type" mandatory = true predefinedAnswers = allowedServiceTypes defaultValue = defaultServiceType - if (loadCurrentValues) dataBinding = firewallRulePropertyDataBinding("serviceType()") + if (editing) dataBinding = firewallRulePropertyDataBinding("serviceType()") } parameter("serviceProtocol", string) { description = schema.propertyDescription("protocol") defaultValue = "any" visibility = FromStringParameter(serviceTypeParameterName, ServiceType.Manual.value) mandatory = true - if (loadCurrentValues) dataBinding = firewallRulePropertyDataBinding("service.protocol") + if (editing) dataBinding = firewallRulePropertyDataBinding("service.protocol") } parameter("serviceSrcPorts", string) { description = schema.propertyDescription("src-ports") defaultValue = "any" visibility = FromStringParameter(serviceTypeParameterName, ServiceType.Manual.value) mandatory = true - if (loadCurrentValues) dataBinding = firewallRulePropertyDataBinding("serviceSrcPorts()") + if (editing) dataBinding = firewallRulePropertyDataBinding("serviceSrcPorts()") } parameter("serviceDstPorts", string) { description = schema.propertyDescription("dst-ports") defaultValue = "any" visibility = FromStringParameter(serviceTypeParameterName, ServiceType.Manual.value) mandatory = true - if (loadCurrentValues) dataBinding = firewallRulePropertyDataBinding("serviceDstPorts()") + if (editing) dataBinding = firewallRulePropertyDataBinding("serviceDstPorts()") } parameter("serviceReference", reference()) { description = "Service Group" visibility = FromStringParameter(serviceTypeParameterName, ServiceType.Reference.value) mandatory = true - if (loadCurrentValues) dataBinding = firewallRulePropertyDataBinding("serviceGroup[0]") + validWhen = matchesSecurityScope(parentField, projectValidationDirectMode) + if (editing) dataBinding = firewallRulePropertyDataBinding("serviceGroup[0]") } } step("Match Tags") { - visibility = WhenNonNull(visibilityDependencyField) + visibility = WhenNonNull(parentField) parameter("matchTags", array(string)) { description = "Match Tags" predefinedAnswers = allowedMatchTags sameValues = false - if (loadCurrentValues) dataBinding = firewallRulePropertyDataBinding("matchTags.tagList") + if (editing) dataBinding = firewallRulePropertyDataBinding("matchTags.tagList") } } } -private fun ParameterAggregator.endpointParameters(schema: Schema, endpointNumber: Int, loadCurrentValues: Boolean) { +private fun ParameterAggregator.endpointParameters(schema: Schema, parentField: String, endpointNumber: Int, editing: Boolean) { val endpointName = endpointParameterName(endpointNumber) val endpointTypeParameterName = "${endpointName}Type" + val projectValidationDirectMode = !editing parameter(endpointTypeParameterName, string) { description = "End Point $endpointNumber type" mandatory = true predefinedAnswers = allowedEndpointTypes defaultValue = defaultEndpointType - if (loadCurrentValues) dataBinding = firewallRulePropertyDataBinding("endpointType($endpointNumber)") + if (editing) dataBinding = firewallRulePropertyDataBinding("endpointType($endpointNumber)") } parameter("${endpointName}Tags", array(reference())) { description = schema.propertyDescription("tags") visibility = FromStringParameter(endpointTypeParameterName, EndpointType.Tag.value) mandatory = true - if (loadCurrentValues) dataBinding = firewallRulePropertyDataBinding("endpointTags($endpointNumber)") + sameValues = false + validWhen = matchesSecurityScope(parentField, projectValidationDirectMode) + if (editing) dataBinding = firewallRulePropertyDataBinding("endpointTags($endpointNumber)") } parameter("${endpointName}VirtualNetwork", reference()) { description = schema.propertyDescription("virtual-network") visibility = FromStringParameter(endpointTypeParameterName, EndpointType.VirtualNetwork.value) mandatory = true - if (loadCurrentValues) dataBinding = firewallRulePropertyDataBinding("endpointNetwork($endpointNumber)") + if (editing) dataBinding = firewallRulePropertyDataBinding("endpointNetwork($endpointNumber)") } parameter("${endpointName}AddressGroup", reference()) { description = schema.propertyDescription("address-group") visibility = FromStringParameter(endpointTypeParameterName, EndpointType.AddressGroup.value) mandatory = true - if (loadCurrentValues) dataBinding = firewallRulePropertyDataBinding("endpointAddressGroup($endpointNumber)") + if (editing) dataBinding = firewallRulePropertyDataBinding("endpointAddressGroup($endpointNumber)") } } private fun endpointParameterName(endpointNumber: Int) = "endpoint$endpointNumber" private fun BasicParameterBuilder.firewallRulePropertyDataBinding(path: String) = - FromComplexPropertyValue("rule", path, type) + FromComplexPropertyValue(rule, path, type) diff --git a/o11nplugin-contrail-workflows/src/main/kotlin/net/juniper/contrail/vro/workflows/custom/Validation.kt b/o11nplugin-contrail-workflows/src/main/kotlin/net/juniper/contrail/vro/workflows/custom/Validation.kt index 2d2e9d39..3234dc5f 100644 --- a/o11nplugin-contrail-workflows/src/main/kotlin/net/juniper/contrail/vro/workflows/custom/Validation.kt +++ b/o11nplugin-contrail-workflows/src/main/kotlin/net/juniper/contrail/vro/workflows/custom/Validation.kt @@ -13,11 +13,14 @@ import net.juniper.contrail.vro.config.isInCidr import net.juniper.contrail.vro.config.isValidIp import net.juniper.contrail.vro.config.isSingleAddressNetworkPolicyRule import net.juniper.contrail.vro.config.isSingleAddressSecurityGroupRule +import net.juniper.contrail.vro.config.matchesSecurityScope import net.juniper.contrail.vro.config.areValidCommunityAttributes import net.juniper.contrail.vro.config.networkHasNotAllcationMode import net.juniper.contrail.vro.config.ipamHasAllocationMode import net.juniper.contrail.vro.config.ipamHasNotAllocationMode +import net.juniper.contrail.vro.workflows.dsl.ArrayParameterBuilder import net.juniper.contrail.vro.workflows.dsl.BasicParameterBuilder +import net.juniper.contrail.vro.workflows.dsl.ReferenceArrayParameterBuilder import net.juniper.contrail.vro.workflows.dsl.ReferenceParameterBuilder fun BasicParameterBuilder.isSubnet() = @@ -32,6 +35,12 @@ fun ReferenceParameterBuilder.ipamHasAllocationMode(mode: String) = fun ReferenceParameterBuilder.ipamHasNotAllocationMode(mode: String) = validationActionCallTo(ipamHasNotAllocationMode).string(mode) +fun ReferenceParameterBuilder.matchesSecurityScope(parentField: String, directMode: Boolean) = + validationActionCallTo(matchesSecurityScope).parameter(parentField).boolean(directMode).boolean(false) + +fun ReferenceArrayParameterBuilder.matchesSecurityScope(parentField: String, directMode: Boolean) = + validationActionCallTo(matchesSecurityScope).parameter(parentField).boolean(directMode).boolean(true) + fun BasicParameterBuilder.isCidr() = validationActionCallTo(isValidCidr) @@ -47,14 +56,14 @@ fun BasicParameterBuilder.addressIsFreeInSubnet(subnet: String, allocati fun BasicParameterBuilder.addressInSubnet(subnet: String) = validationActionCallTo(isInCidr).parameter(subnet) -fun BasicParameterBuilder>.allocationPoolInSubnet(subnet: String) = - validationActionCallTo(isValidAllocactionPool).parameter(subnet) - fun BasicParameterBuilder.isSingleAddressNetworkPolicyRuleOf(networkPolicy: String) = validationActionCallTo(isSingleAddressNetworkPolicyRule).parameter(networkPolicy) fun BasicParameterBuilder.isSingleAddressSecurityGroupRuleOf(securityGroup: String) = validationActionCallTo(isSingleAddressSecurityGroupRule).parameter(securityGroup) -fun BasicParameterBuilder>.isCommunityAttribute() = +fun ArrayParameterBuilder.allocationPoolInSubnet(subnet: String) = + validationActionCallTo(isValidAllocactionPool).parameter(subnet) + +fun ArrayParameterBuilder.isCommunityAttribute() = validationActionCallTo(areValidCommunityAttributes) \ No newline at end of file diff --git a/o11nplugin-contrail-workflows/src/main/kotlin/net/juniper/contrail/vro/workflows/custom/ValidationActions.kt b/o11nplugin-contrail-workflows/src/main/kotlin/net/juniper/contrail/vro/workflows/custom/ValidationActions.kt index dff3f8bc..1d47af72 100644 --- a/o11nplugin-contrail-workflows/src/main/kotlin/net/juniper/contrail/vro/workflows/custom/ValidationActions.kt +++ b/o11nplugin-contrail-workflows/src/main/kotlin/net/juniper/contrail/vro/workflows/custom/ValidationActions.kt @@ -18,6 +18,7 @@ import net.juniper.contrail.vro.config.isValidMac import net.juniper.contrail.vro.config.isSingleAddressNetworkPolicyRule import net.juniper.contrail.vro.config.isSingleAddressSecurityGroupRule import net.juniper.contrail.vro.config.isValidSubnet +import net.juniper.contrail.vro.config.matchesSecurityScope import net.juniper.contrail.vro.config.serviceHasInterfaceWithName import net.juniper.contrail.vro.config.templateHasInterfaceWithName import net.juniper.contrail.vro.config.networkHasNotAllcationMode @@ -125,4 +126,14 @@ val networkHasNotAllcationModeAction = ActionDefinition( name = networkHasNotAllcationMode, resultType = string, parameters = listOf("virtualNetwork" ofType reference(), "mode" ofType string) +) + +val matchesSecurityScope = ActionDefinition( + name = matchesSecurityScope, + resultType = string, + parameters = listOf( + "children" ofType any, + "parent" ofType any, + "directMode" ofType boolean, + "arrayMode" ofType boolean) ) \ No newline at end of file