Skip to content
Snippets Groups Projects
Commit cae8ef04 authored by baldwinlouie's avatar baldwinlouie Committed by Yas Naoi
Browse files

Issue #3153027 by baldwinlouie, yas: Add more validations in the add/edit...

Issue #3153027 by baldwinlouie, yas: Add more validations in the add/edit operation of Security Group
parent 569ef8b3
No related branches found
Tags 6.32
No related merge requests found
Showing
with 1154 additions and 512 deletions
......@@ -3460,25 +3460,19 @@ function aws_cloud_remove_table_reordering(array &$table) {
*/
function _aws_cloud_is_row_empty(array $ip_element) {
$is_empty = TRUE;
$elements = [
'cidr_ip',
'cidr_ip_v6',
'group_id',
'group_name',
'prefix_list_id',
'peering_status',
'uid',
'vpc_id',
'peering_connection_id',
];
foreach ($elements ?: [] as $element) {
if (!empty($ip_element[$element]['#value'])) {
$is_empty = FALSE;
break;
}
if ($ip_element['source'] === 'ip4' && !empty($ip_element['cidr_ip'])) {
$is_empty = FALSE;
}
elseif ($ip_element['source'] === 'ip6' && !empty($ip_element['cidr_ip_v6'])) {
$is_empty = FALSE;
}
elseif ($ip_element['source'] === 'group' && !empty($ip_element['group_id'])) {
$is_empty = FALSE;
}
elseif ($ip_element['source'] === 'prefix' && !empty($ip_element['prefix_list_id'])) {
$is_empty = FALSE;
}
return $is_empty;
}
......
......@@ -21,8 +21,11 @@
if (from_port !== '' && to_port !== '') {
$('.' + table_id + ' tr.row-' + row_count + ' .form-item-' + field + '-' + row_count + '-from-port input').val('');
$('.' + table_id + ' tr.row-' + row_count + ' .form-item-' + field + '-' + row_count + '-to-port input').val('');
$('.' + table_id + ' tr.row-' + row_count + ' .form-item-' + field + '-' + row_count + '-cidr-ip input').val('');
$('.' + table_id + ' tr.row-' + row_count + ' .form-item-' + field + '-' + row_count + '-cidr-ip-v6 input').val('');
$('.' + table_id + ' tr.row-' + row_count + ' .form-item-' + field + '-' + row_count + '-group-id input').val('');
$('.' + table_id + ' tr.row-' + row_count + ' .form-item-' + field + '-' + row_count + '-prefix-list-id input').val('');
$('.' + table_id + ' tr.row-' + row_count).addClass('hide');
// Display a message telling user to save the page before the rule change is applied.
if (!$('.messages--warning').length) {
$('<div class="messages messages--warning" role="alert" style=""><abbr class="warning">*</abbr> Click "Save" to apply rule changes.</div>').prependTo('#edit-rules .details-wrapper');
......
......@@ -163,7 +163,7 @@ class SecurityGroup extends CloudContentEntityBase implements SecurityGroupInter
$fields['name'] = BaseFieldDefinition::create('string')
->setLabel(t('Name'))
->setDescription(t('The name of the Volume entity.'))
->setDescription(t('The name of the Security Group entity.'))
->setSettings([
'default_value' => '',
'max_length' => 255,
......@@ -205,6 +205,14 @@ class SecurityGroup extends CloudContentEntityBase implements SecurityGroupInter
'type' => 'string',
'weight' => -5,
])
// Re-enabled form field; the field can take advantage of validation.
->setDisplayOptions(
'form', [
'type' => 'string_textfield',
]
)
->setRequired(TRUE)
->addConstraint('SecurityGroupName')
->setReadOnly(TRUE);
$fields['vpc_id'] = BaseFieldDefinition::create('string')
......
......@@ -34,20 +34,14 @@ class SecurityGroupCopyForm extends SecurityGroupEditForm {
$entity = $this->entity;
unset($form['security_group']['name']);
unset($form['security_group']['group_id']);
$form['security_group']['group_name'] = [
'#type' => 'textfield',
'#title' => $this->t('Security Group Name'),
'#size' => 60,
'#default_value' => $this->t('Copy of @name',
[
'@name' => $entity->getGroupName(),
]),
'#required' => TRUE,
'#weight' => 0,
];
$form['group_name']['#access'] = TRUE;
$form['security_group']['group_name'] = $form['group_name'];
$form['security_group']['group_name']['widget'][0]['value']['#default_value'] = $this->t(
'Copy of @name',
[
'@name' => $entity->getGroupName(),
]);
unset($form['security_group']['name'], $form['security_group']['group_id'], $form['group_name']);
$vpcs = $this->ec2Service->getVpcs();
if (count($vpcs) === 0) {
......@@ -99,21 +93,20 @@ class SecurityGroupCopyForm extends SecurityGroupEditForm {
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
parent::validateForm($form, $form_state);
$entity = $this->entity->createDuplicate();
$this->source_entity = $this->entity;
$this->entity = $entity;
$this->trimTextfields($form, $form_state);
$result = $this->ec2Service->createSecurityGroup([
'GroupName' => $entity->getGroupName(),
'GroupName' => $form_state->getValue('group_name')[0]['value'],
'VpcId' => $entity->getVpcId(),
'Description' => $entity->getDescription(),
]);
if (isset($result['GroupId'])
&& ($entity->setGroupId($result['GroupId']))
&& ($entity->set('name', $entity->getGroupName()))) {
&& ($entity->set('name', $form_state->getValue('group_name')[0]['value']))) {
// Keep intentionally blank.
}
else {
......@@ -143,25 +136,8 @@ class SecurityGroupCopyForm extends SecurityGroupEditForm {
$this->changeSelfGroupId($form);
// Update the inbound permissions.
$inbound_result = $this->updateInboundPermissions($existing_group);
// Update the outbound permissions. This only applies to
// VPC security groups.
$outbound_result = TRUE;
if (!empty($entity->getVpcId())) {
$outbound_result = $this->updateOutboundPermissions($existing_group);
}
if (!$inbound_result && !$outbound_result) {
$this->messenger->addError($this->t('Inbound and Outbound Rules could not be saved because of the error from AWS.'));
}
elseif (!$inbound_result) {
$this->messenger->addError($this->t('Inbound Rules could not be saved because of the error from AWS.'));
}
elseif (!$outbound_result) {
$this->messenger->addError($this->t('Outbound Rules could not be saved because of the error from AWS.'));
}
// Update Ingress and Egress permissions.
$this->updateIngressEgressPermissions($entity, $existing_group);
// Have the system refresh the security group.
$this->ec2Service->updateSecurityGroups([
......
......@@ -44,13 +44,8 @@ class SecurityGroupCreateForm extends AwsCloudContentForm {
'#weight' => $weight++,
];
$form['security_group']['group_name'] = [
'#type' => 'textfield',
'#title' => $this->t('Security Group Name'),
'#size' => 60,
'#default_value' => $entity->getGroupName(),
'#required' => TRUE,
];
$form['security_group']['group_name'] = $form['group_name'];
unset($form['group_name']);
$vpcs = $this->ec2Service->getVpcs();
if (count($vpcs) === 0) {
......@@ -80,8 +75,7 @@ class SecurityGroupCreateForm extends AwsCloudContentForm {
$this->addOthersFieldset($form, $weight++, $cloud_context);
// Unset these until and present them on the edit security group form.
unset($form['ip_permission']);
unset($form['outbound_permission']);
unset($form['ip_permission'], $form['outbound_permission']);
if (isset($form['actions'])) {
$form['actions']['submit']['#weight'] = $weight++;
......
......@@ -212,9 +212,31 @@ class IpPermission extends FieldItemBase {
public function isEmpty() {
$to_port = $this->get('to_port')->getValue();
$from_port = $this->get('from_port')->getValue();
// If to_port and from_port is not set, then
// consider it empty.
return empty($to_port) && empty($from_port);
$empty = empty($to_port) && empty($from_port);
// Make sure the corresponding field is empty or not.
$source = $this->get('source')->getValue();
switch ($source) {
case ('ip4'):
return $empty && empty($this->get('cidr_ip')->getValue());
case ('ip6'):
return $empty && empty($this->get('cidr_ip_v6')->getValue());
case ('group'):
return $empty && empty($this->get('group_id')->getValue());
case ('prefix'):
return $empty && empty($this->get('prefix_list_id')->getValue());
default:
break;
}
return $empty;
}
/**
......@@ -332,7 +354,7 @@ class IpPermission extends FieldItemBase {
* Get the peering_connection_id value.
*/
public function getPeeringConnectionId() {
return $this->get('peering_connection_id')->value();
return $this->get('peering_connection_id')->getValue();
}
/**
......
......@@ -46,13 +46,14 @@ class IpPermissionItem extends WidgetBase {
'tcp' => $this->t('TCP'),
'udp' => $this->t('UDP'),
'icmp' => $this->t('ICMP'),
'58' => $this->t('ICMPv6'),
'icmpv6' => $this->t('ICMPv6'),
];
$source = [
'ip4' => 'IP',
'ip6' => 'IPv6',
'group' => 'Group',
'prefix' => 'Prefix List Id',
];
}
......@@ -84,7 +85,6 @@ class IpPermissionItem extends WidgetBase {
'#maxlength' => 5,
'#placeholder' => 65535,
];
$element['source'] = [
'#type' => 'select',
'#title' => $this->t('Source'),
......@@ -97,7 +97,6 @@ class IpPermissionItem extends WidgetBase {
],
'#default_value' => $items[$delta]->source ?? NULL,
];
$element['cidr_ip'] = [
'#type' => 'textfield',
'#title' => $this->t('CIDR IP'),
......@@ -111,7 +110,6 @@ class IpPermissionItem extends WidgetBase {
],
],
];
$element['cidr_ip_v6'] = [
'#type' => 'textfield',
'#title' => $this->t('CIDR IPv6'),
......@@ -131,6 +129,11 @@ class IpPermissionItem extends WidgetBase {
'#size' => 20,
'#default_value' => $items[$delta]->prefix_list_id ?? NULL,
'#maxlength' => $this->getFieldSetting('max_length'),
'#states' => [
'visible' => [
'select[name="' . $field_name . '[' . $delta . ']' . '[source]"]' => ['value' => 'prefix'],
],
],
];
}
$element['group_id'] = [
......
......@@ -14,6 +14,20 @@ use Symfony\Component\Validator\Constraint;
*/
class IpPermissionDataConstraint extends Constraint {
/**
* A message: "The To Port is not numeric".
*
* @var string
*/
public $toPortEmpty = 'The To Port is empty.';
/**
* A message: "The From Port is not numeric".
*
* @var string
*/
public $fromPortEmpty = 'The From Port is empty.';
/**
* A message: "The To Port is not numeric".
*
......@@ -28,6 +42,34 @@ class IpPermissionDataConstraint extends Constraint {
*/
public $fromPortNotNumeric = 'The From Port is not numeric.';
/**
* A message: "The From Port needs to be -1 to support all ICMP types.".
*
* @var string
*/
public $negativeFromPortICMP = 'The From Port needs to be -1 to support all ICMP types.';
/**
* A message: "The To Port is out of range.
*
* @var string
*/
public $toPortOutOfRange = 'The To Port is out of range. For ICMP, the To Port must be less than 255.';
/**
* A message: "The From Port is out of range.
*
* @var string
*/
public $fromPortOutOfRange = 'The From Port is out of range. For ICMP, the From Port must be less than 255.';
/**
* A message: "The To Port needs to be -1 to support all ICMP codes.".
*
* @var string
*/
public $negativeToPortICMP = 'The To Port needs to be -1 to support all ICMP codes.';
/**
* A message: "CIDR IP is empty".
*
......@@ -63,12 +105,19 @@ class IpPermissionDataConstraint extends Constraint {
*/
public $groupIdIsEmpty = 'Group ID is empty.';
/**
* A message: "Group ID belongs to a different VPC.".
*
* @var string
*/
public $differentGroupVPC = 'Group @target_group - @target_group_id belongs to a different VPC than @source_group.';
/**
* A message: "No security group found".
*
* @var string
*/
public $noSecurityGroup = 'No security group found.';
public $noSecurityGroup = 'No security group: @group_id found.';
/**
* A message: "From Port is greater than To Port".
......@@ -77,4 +126,39 @@ class IpPermissionDataConstraint extends Constraint {
*/
public $toPortGreater = 'From Port is greater than To Port.';
/**
* A message: "Duplicate IP rules found".
*
* @var string
*/
public $duplicateIP4 = 'Duplicate IP rules found.';
/**
* A message: "Duplicate IPv6 rules found.".
*
* @var string
*/
public $duplicateIP6 = 'Duplicate IPv6 rules found.';
/**
* A message: "Duplicate group rules found.".
*
* @var string
*/
public $duplicateGroup = 'Duplicate group rules found.';
/**
* A message: "Duplicate prefix rules found.".
*
* @var string
*/
public $duplicatePrefix = 'Duplicate prefix rules found.';
/**
* A message: "No Prefix List Id found.".
*
* @var string
*/
public $noPrefixListId = 'No Prefix List Id found.';
}
......@@ -3,17 +3,71 @@
namespace Drupal\aws_cloud\Plugin\Validation\Constraint;
use Drupal\aws_cloud\Plugin\Field\FieldType\IpPermission;
use Drupal\Core\Entity\EntityInterface;
use Drupal\cloud\Service\CloudService;
use Drupal\cloud\Traits\CloudContentEntityTrait;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\TypedData\Validation\TypedDataAwareValidatorTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
/**
* Validates each permission field.
*/
class IpPermissionDataConstraintValidator extends ConstraintValidator {
class IpPermissionDataConstraintValidator extends ConstraintValidator implements ContainerInjectionInterface {
use TypedDataAwareValidatorTrait;
use CloudContentEntityTrait;
/**
* Drupal\Core\Entity\EntityTypeManagerInterface definition.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Cloud Service.
*
* @var \Drupal\cloud\Service\CloudService
*/
protected $cloudService;
/**
* Current route.
*
* @var \Drupal\Core\Routing\RouteMatchInterface
*/
protected $currentRoute;
/**
* Constructs a new constraint validator.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* Entity Type Manager instance.
* @param \Drupal\cloud\Service\CloudService $cloud_service
* Cloud Service.
* @param \Drupal\Core\Routing\RouteMatchInterface $current_route
* The current route.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, CloudService $cloud_service, RouteMatchInterface $current_route) {
$this->entityTypeManager = $entity_type_manager;
$this->cloudService = $cloud_service;
$this->currentRoute = $current_route;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity_type.manager'),
$container->get('cloud'),
$container->get('current_route_match')
);
}
/**
* {@inheritdoc}
......@@ -32,9 +86,71 @@ class IpPermissionDataConstraintValidator extends ConstraintValidator {
elseif ($source === 'ip6') {
$this->validateCidrIpv6($item, $constraint);
}
else {
elseif ($source === 'prefix') {
$this->validatePrefixId($item, $constraint);
}
elseif ($source === 'group') {
$this->validateGroup($item, $constraint);
}
// Check for duplicates.
$this->checkDuplicates($item, $constraint, $source);
}
/**
* Check for empty Prefix Id.
*
* @param \Drupal\aws_cloud\Plugin\Field\FieldType\IpPermission $ip_permission
* IP Permission object.
* @param \Symfony\Component\Validator\Constraint $constraint
* Constraint object.
*/
private function validatePrefixId(IpPermission $ip_permission, Constraint $constraint) {
if (empty($ip_permission->getPrefixListId())) {
$this->context->addViolation($constraint->noPrefixListId);
}
}
/**
* Check for duplicate rules.
*
* @param \Drupal\aws_cloud\Plugin\Field\FieldType\IpPermission $ip_permission
* IP Permission object.
* @param \Symfony\Component\Validator\Constraint $constraint
* Constraint object.
* @param string $source
* Whether the permission is ip4, ipv6 or group.
*/
private function checkDuplicates(IpPermission $ip_permission, Constraint $constraint, $source) {
$entries = $ip_permission->getParent();
foreach ($entries as $key => $entry) {
// Don't validate itself.
if ($ip_permission->getName() !== $entry->getName()) {
// First check if there are duplicate to/from ports.
$dup_ports = ($entry->getToPort() === $ip_permission->getToPort() &&
$entry->getFromPort() === $ip_permission->getFromPort());
// If there are dup_ports, check the corresponding ip4, ip6 or groupid.
if ($dup_ports === TRUE) {
if ($source === 'ip4' && $entry->getCidrIp() === $ip_permission->getCidrIp()) {
$this->context->addViolation($constraint->duplicateIP4);
break;
}
elseif ($source == 'ip6' && $entry->getCidrIpv6() === $ip_permission->getCidrIpv6()) {
$this->context->addViolation($constraint->duplicateIP6);
break;
}
elseif ($source == 'prefix' && $entry->getPrefixListId() === $ip_permission->getPrefixListId()) {
$this->context->addViolation($constraint->duplicatePrefix);
break;
}
elseif ($source == 'group' && $entry->getGroupId() === $ip_permission->getGroupId()) {
$this->context->addViolation($constraint->duplicateGroup);
break;
}
}
}
}
}
/**
......@@ -49,20 +165,59 @@ class IpPermissionDataConstraintValidator extends ConstraintValidator {
$to_port = $ip_permission->getToPort();
$from_port = $ip_permission->getFromPort();
// Make sure the ports are not empty.
// 0 is an allowed port, but empty(0) will evaluate it
// as empty.
if ($from_port !== '0' && empty($from_port)) {
$this->context->addViolation($constraint->fromPortEmpty);
}
if ($to_port !== '0' && empty($to_port)) {
$this->context->addViolation($constraint->toPortEmpty);
}
// Make sure the ports are numeric.
if (!is_numeric($from_port)) {
$this->context->addViolation($constraint->fromPortNotNumeric, [
'%value' => $from_port,
'@field_name' => 'from_port',
]);
$this->context->addViolation($constraint->fromPortNotNumeric);
}
if (!is_numeric($to_port)) {
$this->context->addViolation($constraint->toPortNotNumeric, [
'%value' => $to_port,
'@field_name' => 'to_port',
]);
$this->context->addViolation($constraint->toPortNotNumeric);
}
// Validate if from_port is less than to_port.
if ($from_port > $to_port) {
// Cast the ports to integers to do additional validations.
$from_port = (int) $from_port;
$to_port = (int) $to_port;
if ($ip_permission->getIpProtocol() === 'icmp') {
// For ICMP, the $to_port and $from_port
// are used to hold ICMP code/type.
// If -1 is passed, which means all code/type, both $to_port
// and $from_port needs to be -1.
if ($from_port === -1 && $to_port >= 0) {
$this->context->addViolation($constraint->negativeToPortICMP);
}
elseif ($to_port === -1 && $from_port >= 0) {
$this->context->addViolation($constraint->negativeFromPortICMP);
}
if ($from_port > 255) {
// Code out of range.
$this->context->addViolation($constraint->fromPortOutOfRange);
}
if ($to_port > 255) {
// Type out of range.
$this->context->addViolation($constraint->toPortOutOfRange);
}
}
elseif ($ip_permission->getIpProtocol() === 'icmpv6') {
// For ICMPv6, the ports need to be -1.
if ($from_port !== -1) {
$this->context->addViolation($constraint->negativeFromPortICMP);
}
if ($to_port !== -1) {
$this->context->addViolation($constraint->negativeToPortICMP);
}
}
elseif ($from_port > $to_port) {
$this->context->addViolation($constraint->toPortGreater, [
'%value' => $from_port,
'@field_name' => 'from_port',
......@@ -90,15 +245,13 @@ class IpPermissionDataConstraintValidator extends ConstraintValidator {
'@field_name' => 'cidr_ipv6',
]);
}
else {
// Validate ip6.
if (!$this->validateCidr($cidr_ipv6)) {
$this->context->addViolation($constraint->ip6Value, [
'%value' => $cidr_ipv6,
'@field_name' => 'cidr_ipv6',
]);
}
elseif (!$this->validateCidr($cidr_ipv6)) {
$this->context->addViolation($constraint->ip6Value, [
'%value' => $cidr_ipv6,
'@field_name' => 'cidr_ipv6',
]);
}
}
/**
......@@ -117,14 +270,11 @@ class IpPermissionDataConstraintValidator extends ConstraintValidator {
'@field_name' => 'cidr_ip',
]);
}
else {
// Validate ip4.
if (!$this->validateCidr($cidr_ip)) {
$this->context->addViolation($constraint->ip4Value, [
'%value' => $cidr_ip,
'@field_name' => 'cidr_ip',
]);
}
elseif (!$this->validateCidr($cidr_ip)) {
$this->context->addViolation($constraint->ip4Value, [
'%value' => $cidr_ip,
'@field_name' => 'cidr_ip',
]);
}
}
......@@ -138,8 +288,13 @@ class IpPermissionDataConstraintValidator extends ConstraintValidator {
*/
private function validateGroup(IpPermission $ip_permission, Constraint $constraint) {
// Group ID or name.
$security_group = $this->getSecurityGroupEntity();
if ($security_group !== FALSE) {
$entity = $ip_permission->getEntity();
$entity_type = $entity->getEntityTypeId();
$security_group = $this->currentRoute
->getParameter($entity_type);
if (!empty($security_group)) {
/* @var \Drupal\aws_cloud\Entity\Ec2\SecurityGroup $security_group */
// Check that group_id is not empty.
$group_id = $ip_permission->getGroupId();
......@@ -149,9 +304,27 @@ class IpPermissionDataConstraintValidator extends ConstraintValidator {
'@field_name' => 'group_id',
]);
}
else {
// If $group_id is passed, make sure the group vpc = the current
// security group VPC.
/* @var \Drupal\aws_cloud\Entity\Ec2\SecurityGroup $target_group */
$target_group = $this->getSecurityGroupByGroupId($entity_type, $group_id, $security_group->getCloudContext());
if ($target_group === FALSE) {
$this->context->addViolation($constraint->noSecurityGroup, [
'@group_id' => $group_id,
]);
}
elseif ($target_group->getVpcId() !== $security_group->getVpcId()) {
$this->context->addViolation($constraint->differentGroupVPC, [
'@target_group' => $target_group->getGroupName(),
'@target_group_id' => $target_group->getGroupId(),
'@source_group' => $security_group->getGroupName(),
]);
}
}
}
else {
// Cannot load security group. Error out.
// Cannot load security group. Error out.
$this->context->addViolation($constraint->noSecurityGroup, [
'%value' => $ip_permission->getGroupName(),
'@field_name' => 'group_name',
......@@ -160,18 +333,36 @@ class IpPermissionDataConstraintValidator extends ConstraintValidator {
}
/**
* Helper method that loads the security group entity from the url parameter.
* Load a security group by the Security Group Id.
*
* @return bool
* FALSE if not found | aws_cloud_security_group object if found.
* @param string $entity_type
* The entity_type to lookup.
* @param string $group_id
* The group id.
* @param string $cloud_context
* The cloud context to load.
*
* @return \Drupal\aws_cloud\Entity\Ec2\SecurityGroup|false
* The loaded security group or FALSE.
*/
private function getSecurityGroupEntity() {
private function getSecurityGroupByGroupId($entity_type, $group_id, $cloud_context) {
$security_group = FALSE;
foreach (\Drupal::routeMatch()->getParameters() ?: [] as $param) {
if ($param instanceof EntityInterface) {
$security_group = $param;
try {
$groups = $this->entityTypeManager
->getStorage($entity_type)
->loadByProperties(
[
'group_id' => $group_id,
'cloud_context' => $cloud_context,
]
);
if (!empty($groups)) {
$security_group = array_shift($groups);
}
}
catch (\Exception $e) {
$this->handleException($e);
}
return $security_group;
}
......@@ -186,7 +377,7 @@ class IpPermissionDataConstraintValidator extends ConstraintValidator {
* @return bool
* TRUE or FALSE.
*/
private function validateCidr($cidr) {
private function validateCidr($cidr) : bool {
$parts = explode('/', $cidr);
if (count($parts) !== 2) {
......
<?php
namespace Drupal\aws_cloud\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraint;
/**
* Security Group entity validation.
*
* @Constraint(
* id = "SecurityGroupName",
* label = @Translation("Security Group", context = "Validation"),
* )
*/
class SecurityGroupNameConstraint extends Constraint {
/**
* A message: 'Cannot create group with Security Group Name "default".'.
*
* @var string
*/
public $defaultName = 'Cannot create group with Security Group Name "default".';
/**
* A message: "The group name already exists".
*
* @var string
*/
public $duplicateGroupName = 'The Security Group Name "@name" already exists.';
}
<?php
namespace Drupal\aws_cloud\Plugin\Validation\Constraint;
use Drupal\cloud\Service\CloudService;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\TypedData\Validation\TypedDataAwareValidatorTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
/**
* Security Group name validation class.
*/
class SecurityGroupNameConstraintValidator extends ConstraintValidator implements ContainerInjectionInterface {
use TypedDataAwareValidatorTrait;
use StringTranslationTrait;
/**
* Drupal\Core\Entity\EntityTypeManagerInterface definition.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Cloud Service.
*
* @var \Drupal\cloud\Service\CloudService
*/
protected $cloudService;
/**
* Constructs a new constraint validator.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* Entity Type Manager instance.
* @param \Drupal\cloud\Service\CloudService $cloud_service
* Cloud Service.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, CloudService $cloud_service) {
$this->entityTypeManager = $entity_type_manager;
$this->cloudService = $cloud_service;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity_type.manager'),
$container->get('cloud')
);
}
/**
* {@inheritdoc}
*/
public function validate($items, Constraint $constraint) {
/* @var \Drupal\aws_cloud\Entity\Ec2\SecurityGroup $entity */
$entity = $items->getEntity();
if (isset($entity) && $entity->isNew()) {
// AWS does not allow group name set to 'default'.
$group_name = $items->first()->value;
if ($this->validateDefaultGroupName($group_name) === TRUE) {
$this->context->addViolation($constraint->defaultName);
}
// Test if the group name is a duplicate.
if ($this->validateDuplicateGroupName($group_name, $entity->getCloudContext()) === TRUE) {
$this->context->addViolation($constraint->duplicateGroupName, ['@name' => $group_name]);
}
$violations = $this->context->getViolations();
}
}
/**
* Test if the group name exists.
*
* @param string $name
* The group name to test.
* @param string $cloud_context
* The cloud_context to test in.
*
* @return bool
* True if it has a duplicate name, false otherwise.
*/
private function validateDuplicateGroupName($name, $cloud_context) : bool {
$has_duplicate_name = FALSE;
try {
$groups = $this->entityTypeManager
->getStorage('aws_cloud_security_group')
->loadByProperties(
[
'name' => $name,
'cloud_context' => $cloud_context,
]
);
if (!empty($groups)) {
$has_duplicate_name = TRUE;
}
}
catch (\Exception $e) {
$this->cloudService->handleException($e);
}
return $has_duplicate_name;
}
/**
* Check if the group name is set to 'default'.
*
* @param string $name
* The group name.
*
* @return bool
* Whether default or not.
*/
private function validateDefaultGroupName($name) : bool {
$has_default = FALSE;
if ($name === 'default') {
$has_default = TRUE;
}
return $has_default;
}
}
......@@ -2,6 +2,7 @@
namespace Drupal\Tests\aws_cloud\Functional\Ec2;
use Drupal\aws_cloud\Entity\Ec2\SecurityGroup;
use Drupal\Tests\aws_cloud\Functional\AwsCloudTestBase;
use Drupal\Tests\cloud\Functional\Utils;
......@@ -179,7 +180,7 @@ class SecurityGroupIpPermissionsTest extends AwsCloudTestBase {
$this->reloadMockData();
$defaults = $this->latestTemplateVars;
$defaults['group_name'] = $add[$i]['group_name'];
$defaults['group_name'] = $add[$i]['group_name[0][value]'];
$add[$i]['vpc_id'] = $defaults['vpc_id'];
$this->drupalGet("/clouds/aws_cloud/$cloud_context/security_group/add");
......@@ -191,7 +192,7 @@ class SecurityGroupIpPermissionsTest extends AwsCloudTestBase {
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->pageTextNotContains($this->t('Notice'));
$this->assertSession()->pageTextNotContains($this->t('Warning'));
$t_args = ['@type' => 'Security Group', '%label' => $add[$i]['group_name']];
$t_args = ['@type' => 'Security Group', '%label' => $add[$i]['group_name[0][value]']];
$this->assertSession()->pageTextContains(strip_tags($this->t('The @type %label has been created.', $t_args)));
$edit_url = "/clouds/aws_cloud/$cloud_context/security_group/$num/edit";
......@@ -208,9 +209,9 @@ class SecurityGroupIpPermissionsTest extends AwsCloudTestBase {
// Test case 3. (Combination of mixing above Test case 1. and 2.).
$rules = $this->createRulesTestFormData(self::$awsCloudSecurityGroupRulesMix, $edit_url, 1, self::$awsCloudSecurityGroupRulesRepeatCount);
// Test case3.2 edit rules.
$params = $this->editRuleParams($rules);
$params['name'] = $add[$i]['group_name'];
// Test case3.2 edit rules. Do not include group rules for testing.
$params = $this->editRuleParams($rules, FALSE);
$params['name'] = $add[$i]['group_name[0][value]'];
$this->drupalPostForm($edit_url,
$params,
$this->t('Save'));
......@@ -246,18 +247,18 @@ class SecurityGroupIpPermissionsTest extends AwsCloudTestBase {
*
* @throws \Behat\Mink\Exception\ExpectationException
* @throws \Behat\Mink\Exception\ResponseTextException
* @throws \Drupal\Core\Entity\EntityStorageException
*/
private function repeatTestIpPermissionsValidate($max_test_repeat_count = 1): void {
$cloud_context = $this->cloudContext;
$add = $this->createSecurityGroupTestFormData(self::$awsCloudSecurityGroupRepeatCount);
$dup = $this->createSecurityGroupTestFormData(self::$awsCloudSecurityGroupRepeatCount);
for ($i = 0; $i < $max_test_repeat_count; $i++) {
$this->reloadMockData();
$defaults = $this->latestTemplateVars;
$defaults['group_name'] = $add[$i]['group_name'];
$defaults['group_name'] = $add[$i]['group_name[0][value]'];
$add[$i]['vpc_id'] = $defaults['vpc_id'];
$this->drupalGet("/clouds/aws_cloud/$cloud_context/security_group/add");
......@@ -269,7 +270,7 @@ class SecurityGroupIpPermissionsTest extends AwsCloudTestBase {
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->pageTextNotContains($this->t('Notice'));
$this->assertSession()->pageTextNotContains($this->t('Warning'));
$t_args = ['@type' => 'Security Group', '%label' => $add[$i]['group_name']];
$t_args = ['@type' => 'Security Group', '%label' => $add[$i]['group_name[0][value]']];
$this->assertSession()->pageTextContains(strip_tags($this->t('The @type %label has been created.', $t_args)));
// Verify From port validation error.
......@@ -282,7 +283,7 @@ class SecurityGroupIpPermissionsTest extends AwsCloudTestBase {
$this->drupalPostForm($this->getUrl(), $rules, $this->t('Save'));
$this->assertSession()->pageTextContains($this->t('The From Port is not numeric.'));
// Verify From port validation error.
// Verify To port validation error.
$rules = [
'ip_permission[0][from_port]' => Utils::getRandomFromPort(),
'ip_permission[0][to_port]' => $this->random->name(2, TRUE),
......@@ -292,6 +293,26 @@ class SecurityGroupIpPermissionsTest extends AwsCloudTestBase {
$this->drupalPostForm($this->getUrl(), $rules, $this->t('Save'));
$this->assertSession()->pageTextContains($this->t('The To Port is not numeric.'));
// Verify empty From port validation error.
$rules = [
'ip_permission[0][from_port]' => '',
'ip_permission[0][to_port]' => Utils::getRandomToPort(),
'ip_permission[0][cidr_ip]' => Utils::getRandomCidr(),
'ip_permission[0][source]' => 'ip4',
];
$this->drupalPostForm($this->getUrl(), $rules, $this->t('Save'));
$this->assertSession()->pageTextContains($this->t('The From Port is empty.'));
// Verify empty To port validation error.
$rules = [
'ip_permission[0][from_port]' => Utils::getRandomToPort(),
'ip_permission[0][to_port]' => '',
'ip_permission[0][cidr_ip]' => Utils::getRandomCidr(),
'ip_permission[0][source]' => 'ip4',
];
$this->drupalPostForm($this->getUrl(), $rules, $this->t('Save'));
$this->assertSession()->pageTextContains($this->t('The To Port is empty.'));
// Verify CIDR IP empty test.
$rules = [
'ip_permission[0][from_port]' => Utils::getRandomFromPort(),
......@@ -351,6 +372,121 @@ class SecurityGroupIpPermissionsTest extends AwsCloudTestBase {
];
$this->drupalPostForm($this->getUrl(), $rules, $this->t('Save'));
$this->assertSession()->pageTextContains($this->t('From Port is greater than To Port.'));
// Verify ICMP needs -1 for from_port and to_port.
$rules = [
'ip_permission[0][from_port]' => -1,
'ip_permission[0][to_port]' => Utils::getRandomToPort(),
'ip_permission[0][cidr_ip]' => Utils::getRandomCidr(),
'ip_permission[0][ip_protocol]' => 'icmp',
'ip_permission[0][source]' => 'ip4',
];
$this->drupalPostForm($this->getUrl(), $rules, $this->t('Save'));
$this->assertSession()->pageTextContains($this->t('The To Port needs to be -1 to support all ICMP codes.'));
// Verify ICMP needs -1 for from_port.
$rules = [
'ip_permission[0][from_port]' => Utils::getRandomFromPort(),
'ip_permission[0][to_port]' => -1,
'ip_permission[0][cidr_ip]' => Utils::getRandomCidr(),
'ip_permission[0][ip_protocol]' => 'icmp',
'ip_permission[0][source]' => 'ip4',
];
$this->drupalPostForm($this->getUrl(), $rules, $this->t('Save'));
$this->assertSession()->pageTextContains($this->t('The From Port needs to be -1 to support all ICMP types.'));
// Verify ICMP from_port <= 255.
$rules = [
'ip_permission[0][from_port]' => Utils::getRandomFromPort(256),
'ip_permission[0][to_port]' => Utils::getRandomToPort(),
'ip_permission[0][cidr_ip]' => Utils::getRandomCidr(),
'ip_permission[0][ip_protocol]' => 'icmp',
'ip_permission[0][source]' => 'ip4',
];
$this->drupalPostForm($this->getUrl(), $rules, $this->t('Save'));
$this->assertSession()->pageTextContains($this->t('The From Port is out of range. For ICMP, the From Port must be less than 255.'));
// Verify ICMP to_port <= 255.
$rules = [
'ip_permission[0][from_port]' => Utils::getRandomFromPort(0, 255),
'ip_permission[0][to_port]' => Utils::getRandomToPort(256),
'ip_permission[0][cidr_ip]' => Utils::getRandomCidr(),
'ip_permission[0][ip_protocol]' => 'icmp',
'ip_permission[0][source]' => 'ip4',
];
$this->drupalPostForm($this->getUrl(), $rules, $this->t('Save'));
$this->assertSession()->pageTextContains($this->t('The To Port is out of range. For ICMP, the To Port must be less than 255'));
// Verify ICMPV6 To Port.
$rules = [
'ip_permission[0][from_port]' => -1,
'ip_permission[0][to_port]' => Utils::getRandomToPort(),
'ip_permission[0][cidr_ip]' => Utils::getRandomCidr(),
'ip_permission[0][ip_protocol]' => 'icmpv6',
'ip_permission[0][source]' => 'ip6',
];
$this->drupalPostForm($this->getUrl(), $rules, $this->t('Save'));
$this->assertSession()->pageTextContains($this->t('The To Port needs to be -1 to support all ICMP codes.'));
// Verify ICMPV6 From Port.
$rules = [
'ip_permission[0][from_port]' => Utils::getRandomFromPort(0, 255),
'ip_permission[0][to_port]' => -1,
'ip_permission[0][cidr_ip]' => Utils::getRandomCidr(),
'ip_permission[0][ip_protocol]' => 'icmpv6',
'ip_permission[0][source]' => 'ip6',
];
$this->drupalPostForm($this->getUrl(), $rules, $this->t('Save'));
$this->assertSession()->pageTextContains($this->t('The From Port needs to be -1 to support all ICMP types.'));
// Verify Prefix List Id.
$rules = [
'ip_permission[0][from_port]' => Utils::getRandomFromPort(),
'ip_permission[0][to_port]' => Utils::getRandomToPort(),
'ip_permission[0][cidr_ip]' => Utils::getRandomCidr(),
'ip_permission[0][ip_protocol]' => 'tcp',
'ip_permission[0][prefix_list_id]' => '',
'ip_permission[0][source]' => 'prefix',
];
$this->drupalPostForm($this->getUrl(), $rules, $this->t('Save'));
$this->assertSession()->pageTextContains($this->t('No Prefix List Id found.'));
// Add a second set of security groups to test for VPC when adding a
// GroupId in permissions.
$dup_defaults = $this->getMockDataTemplateVars();
$dup[$i]['vpc_id'] = $dup_defaults['vpc_id'];
$dup[$i]['group_id'] = $this->addSecurityGroupMockData($dup[$i]['group_name[0][value]'], $dup[$i]['description'], $dup[$i]['vpc_id']);
// Verify Groups does not exist.
$rules = [
'ip_permission[0][from_port]' => Utils::getRandomFromPort(),
'ip_permission[0][to_port]' => Utils::getRandomToPort(),
'ip_permission[0][group_id]' => $dup[$i]['group_id'],
'ip_permission[0][source]' => 'group',
];
$this->drupalPostForm($this->getUrl(), $rules, $this->t('Save'));
$this->assertSession()->pageTextContains($this->t('No security group: @group_id found.', [
'@group_id' => $dup[$i]['group_id'],
]));
// Now add the group and make sure the VPC validation happens.
$security_group = $this->createSecurityGroupTestEntity(SecurityGroup::class, $i, $dup[$i]['group_id'], $dup[$i]['group_name[0][value]'], $dup[$i]['vpc_id'], $cloud_context);
$security_group->set('description', $dup[$i]['description']);
$security_group->save();
// Verify Groups do not belong in the same VPC.
$rules = [
'ip_permission[0][from_port]' => Utils::getRandomFromPort(),
'ip_permission[0][to_port]' => Utils::getRandomToPort(),
'ip_permission[0][group_id]' => $dup[$i]['group_id'],
'ip_permission[0][source]' => 'group',
];
$this->drupalPostForm($this->getUrl(), $rules, $this->t('Save'));
$this->assertSession()->pageTextContains($this->t('Group @target_group - @target_group_id belongs to a different VPC than @source_group.', [
'@target_group' => $dup[$i]['group_name[0][value]'],
'@target_group_id' => $dup[$i]['group_id'],
'@source_group' => $add[$i]['group_name[0][value]'],
]));
}
}
......@@ -387,7 +523,7 @@ class SecurityGroupIpPermissionsTest extends AwsCloudTestBase {
$this->reloadMockData();
$defaults = $this->latestTemplateVars;
$defaults['group_name'] = $add[$i]['group_name'];
$defaults['group_name'] = $add[$i]['group_name[0][value]'];
$add[$i]['vpc_id'] = $defaults['vpc_id'];
$this->drupalGet("/clouds/aws_cloud/$cloud_context/security_group/add");
......@@ -399,7 +535,7 @@ class SecurityGroupIpPermissionsTest extends AwsCloudTestBase {
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->pageTextNotContains($this->t('Notice'));
$this->assertSession()->pageTextNotContains($this->t('Warning'));
$t_args = ['@type' => 'Security Group', '%label' => $add[$i]['group_name']];
$t_args = ['@type' => 'Security Group', '%label' => $add[$i]['group_name[0][value]']];
$this->assertSession()->pageTextContains(strip_tags($this->t('The @type %label has been created.', $t_args)));
$edit_url = "/clouds/aws_cloud/$cloud_context/security_group/$num/edit";
......@@ -436,13 +572,15 @@ class SecurityGroupIpPermissionsTest extends AwsCloudTestBase {
*
* @param array $rules
* The array of rules.
* @param bool $include_group
* Boolean to include group rule.
*
* @return array
* The edited params.
*
* @throws \Exception
*/
private function editRuleParams(array &$rules): array {
private function editRuleParams(array &$rules, $include_group = TRUE): array {
$params = [];
$inbound_index = 0;
$outbound_index = 0;
......@@ -477,7 +615,7 @@ class SecurityGroupIpPermissionsTest extends AwsCloudTestBase {
}
}
else {
$this->getRandomRule($rule);
$this->getRandomRule($rule, FALSE);
}
foreach ($rule ?: [] as $key => $value) {
if ($key === 'type') {
......
......@@ -87,7 +87,7 @@ class SecurityGroupTest extends AwsCloudTestBase {
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->pageTextNotContains($this->t('Notice'));
$this->assertSession()->pageTextNotContains($this->t('Warning'));
$t_args = ['@type' => 'Security Group', '%label' => $add[$i]['group_name']];
$t_args = ['@type' => 'Security Group', '%label' => $add[$i]['group_name[0][value]']];
$this->assertSession()->pageTextContains(strip_tags($this->t('The @type %label has been created.', $t_args)));
// Make sure listing.
......@@ -97,7 +97,7 @@ class SecurityGroupTest extends AwsCloudTestBase {
$this->assertSession()->pageTextNotContains($this->t('Warning'));
// 3 times.
for ($j = 0; $j < $i + 1; $j++) {
$this->assertSession()->pageTextContains($add[$i]['group_name']);
$this->assertSession()->pageTextContains($add[$i]['group_name[0][value]']);
}
}
......@@ -109,7 +109,7 @@ class SecurityGroupTest extends AwsCloudTestBase {
$this->assertSession()->pageTextNotContains($this->t('Warning'));
for ($j = 0; $j < $num; $j++) {
$this->assertSession()->pageTextContains($add[$j]['group_name']);
$this->assertSession()->pageTextContains($add[$j]['group_name[0][value]']);
}
}
......@@ -202,7 +202,7 @@ class SecurityGroupTest extends AwsCloudTestBase {
// Add new Security Groups.
$add = $this->createSecurityGroupTestFormData(self::$awsCloudSecurityGroupRepeatCount);
for ($i = 0; $i < self::$awsCloudSecurityGroupRepeatCount; $i++) {
$this->addSecurityGroupMockData($add[$i]['group_name'], $add[$i]['description']);
$this->addSecurityGroupMockData($add[$i]['group_name[0][value]'], $add[$i]['description']);
}
// Make sure listing.
......@@ -211,7 +211,7 @@ class SecurityGroupTest extends AwsCloudTestBase {
$this->assertSession()->pageTextNotContains($this->t('Notice'));
$this->assertSession()->pageTextNotContains($this->t('Warning'));
for ($i = 0; $i < self::$awsCloudSecurityGroupRepeatCount; $i++) {
$this->assertSession()->pageTextNotContains($add[$i]['group_name']);
$this->assertSession()->pageTextNotContains($add[$i]['group_name[0][value]']);
}
// Click 'Refresh'.
......@@ -219,7 +219,7 @@ class SecurityGroupTest extends AwsCloudTestBase {
$this->assertSession()->pageTextContains($this->t('Updated Security Groups.'));
// Make sure listing.
for ($i = 0; $i < self::$awsCloudSecurityGroupRepeatCount; $i++) {
$this->assertSession()->pageTextContains($add[$i]['group_name']);
$this->assertSession()->pageTextContains($add[$i]['group_name[0][value]']);
}
// Make sure detailed and edit view.
......@@ -251,16 +251,16 @@ class SecurityGroupTest extends AwsCloudTestBase {
$num++;
$data = [
'description' => "Description #$num - {$this->random->name(64, TRUE)}",
'group_name' => "group-name-#$num - {$this->random->name(15, TRUE)}",
'group_name[0][value]' => "group-name-#$num - {$this->random->name(15, TRUE)}",
];
$this->addSecurityGroupMockData($data['group_name'], $data['description']);
$this->addSecurityGroupMockData($data['group_name[0][value]'], $data['description']);
// Make sure listing.
$this->drupalGet("/clouds/aws_cloud/$cloud_context/security_group");
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->pageTextNotContains($this->t('Notice'));
$this->assertSession()->pageTextNotContains($this->t('Warning'));
$this->assertSession()->pageTextNotContains($data['group_name']);
$this->assertSession()->pageTextNotContains($data['group_name[0][value]']);
// Click 'Refresh'.
$this->clickLink($this->t('Refresh'));
......@@ -268,7 +268,7 @@ class SecurityGroupTest extends AwsCloudTestBase {
$add = array_merge($add, [$data]);
// Make sure listing.
for ($i = 0; $i < self::$awsCloudSecurityGroupRepeatCount + 1; $i++) {
$this->assertSession()->pageTextContains($add[$i]['group_name']);
$this->assertSession()->pageTextContains($add[$i]['group_name[0][value]']);
}
// Update tags.
......@@ -309,7 +309,7 @@ class SecurityGroupTest extends AwsCloudTestBase {
// Make sure listing.
for ($i = 0; $i < self::$awsCloudSecurityGroupRepeatCount; $i++) {
$this->assertSession()->linkNotExists($add[$i]['tags_name']);
$this->assertSession()->linkExists($add[$i]['group_name']);
$this->assertSession()->linkExists($add[$i]['group_name[0][value]']);
}
// Delete name tags.
......@@ -323,7 +323,7 @@ class SecurityGroupTest extends AwsCloudTestBase {
$this->drupalGet("/clouds/aws_cloud/$cloud_context/security_group");
for ($i = 0; $i < self::$awsCloudSecurityGroupRepeatCount; $i++) {
$this->assertSession()->linkNotExists($add[$i]['tags_name']);
$this->assertSession()->linkExists($add[$i]['group_name']);
$this->assertSession()->linkExists($add[$i]['group_name[0][value]']);
}
// Click 'Refresh'.
......@@ -331,7 +331,7 @@ class SecurityGroupTest extends AwsCloudTestBase {
// Make sure listing.
for ($i = 0; $i < self::$awsCloudSecurityGroupRepeatCount; $i++) {
$this->assertSession()->linkNotExists($add[$i]['tags_name']);
$this->assertSession()->linkExists($add[$i]['group_name']);
$this->assertSession()->linkExists($add[$i]['group_name[0][value]']);
}
// Delete SecurityGroup in mock data.
......@@ -345,7 +345,7 @@ class SecurityGroupTest extends AwsCloudTestBase {
$this->assertSession()->pageTextNotContains($this->t('Notice'));
$this->assertSession()->pageTextNotContains($this->t('Warning'));
for ($i = 0; $i < self::$awsCloudSecurityGroupRepeatCount + 1; $i++) {
$this->assertSession()->pageTextContains($add[$i]['group_name']);
$this->assertSession()->pageTextContains($add[$i]['group_name[0][value]']);
}
// Click 'Refresh'.
......@@ -353,7 +353,7 @@ class SecurityGroupTest extends AwsCloudTestBase {
$this->assertSession()->pageTextContains($this->t('Updated Security Groups.'));
// Make sure listing.
for ($i = 0; $i < self::$awsCloudSecurityGroupRepeatCount + 1; $i++) {
$this->assertSession()->pageTextNotContains($add[$i]['group_name']);
$this->assertSession()->pageTextNotContains($add[$i]['group_name[0][value]']);
}
}
......@@ -402,9 +402,9 @@ class SecurityGroupTest extends AwsCloudTestBase {
$vpc->save();
// Add mock data.
$add[$i]['group_id'] = $this->addSecurityGroupMockData($add[$i]['group_name'], $add[$i]['description'], $add[$i]['vpc_id']);
$add[$i]['group_id'] = $this->addSecurityGroupMockData($add[$i]['group_name[0][value]'], $add[$i]['description'], $add[$i]['vpc_id']);
// Create entity.
$security_group = $this->createSecurityGroupTestEntity(SecurityGroup::class, $i, $add[$i]['group_id'], $add[$i]['group_name'], $add[$i]['vpc_id'], $cloud_context);
$security_group = $this->createSecurityGroupTestEntity(SecurityGroup::class, $i, $add[$i]['group_id'], $add[$i]['group_name[0][value]'], $add[$i]['vpc_id'], $cloud_context);
$security_group->set('description', $add[$i]['description']);
$security_group->save();
......@@ -420,7 +420,7 @@ class SecurityGroupTest extends AwsCloudTestBase {
// Click 'Copy'.
$this->clickLink($this->t('Copy'));
$this->assertSession()->pageTextContains('Copy AWS Cloud Security Group');
$this->assertSession()->fieldValueEquals('group_name', "Copy of {$add[$i]['group_name']}");
$this->assertSession()->fieldValueEquals('group_name[0][value]', "Copy of {$add[$i]['group_name[0][value]']}");
$copy[$i]['vpc_id'] = $add[$i]['vpc_id'];
......@@ -434,7 +434,7 @@ class SecurityGroupTest extends AwsCloudTestBase {
$this->assertSession()->pageTextNotContains($this->t('Notice'));
$this->assertSession()->pageTextNotContains($this->t('Warning'));
$t_args = ['@type' => 'Security Group', '%label' => $copy[$i]['group_name']];
$t_args = ['@type' => 'Security Group', '%label' => $copy[$i]['group_name[0][value]']];
$this->assertSession()->pageTextContains(strip_tags($this->t('The @type %label has been created.', $t_args)));
foreach ($params ?: [] as $key => $value) {
......@@ -444,7 +444,7 @@ class SecurityGroupTest extends AwsCloudTestBase {
}
$this->drupalGet("/clouds/aws_cloud/$cloud_context/security_group");
$this->assertSession()->pageTextContains($copy[$i]['group_name']);
$this->assertSession()->pageTextContains($copy[$i]['group_name[0][value]']);
}
}
......@@ -467,9 +467,9 @@ class SecurityGroupTest extends AwsCloudTestBase {
for ($i = 0, $num = 1; $i < self::$awsCloudSecurityGroupRepeatCount; $i++, $num++) {
// Add mock data.
$add[$i]['group_id'] = $this->addSecurityGroupMockData($add[$i]['group_name'], $add[$i]['description']);
$add[$i]['group_id'] = $this->addSecurityGroupMockData($add[$i]['group_name[0][value]'], $add[$i]['description']);
// Create entity.
$security_group = $this->createSecurityGroupTestEntity(SecurityGroup::class, $i, $add[$i]['group_id'], $add[$i]['group_name'], '', $cloud_context);
$security_group = $this->createSecurityGroupTestEntity(SecurityGroup::class, $i, $add[$i]['group_id'], $add[$i]['group_name[0][value]'], '', $cloud_context);
$security_group->set('description', $add[$i]['description']);
$security_group->save();
......@@ -478,6 +478,54 @@ class SecurityGroupTest extends AwsCloudTestBase {
}
}
/**
* Test duplicate security group names and 'default' name.
*/
public function testSecurityGroupName() : void {
$cloud_context = $this->cloudContext;
$add = $this->createSecurityGroupTestFormData(self::$awsCloudSecurityGroupRepeatCount);
$addVpc = $this->createVpcTestFormData(self::$awsCloudSecurityGroupRepeatCount);
for ($i = 0, $num = 1; $i < self::$awsCloudSecurityGroupRepeatCount; $i++, $num++) {
// Create entity.
$this->reloadMockData();
$defaults = $this->latestTemplateVars;
$add[$i]['vpc_id'] = $defaults['vpc_id'];
$this->addVpcMockData($addVpc[$i], $add[$i]['vpc_id']);
$this->addSecurityGroupMockData($add[$i]['group_name[0][value]'], $add[$i]['description'], $add[$i]['vpc_id']);
// Switch to 'default'.
$real_group_name = $add[$i]['group_name[0][value]'];
$add[$i]['group_name[0][value]'] = 'default';
// Try to save it a second time through the form.
$this->drupalPostForm("/clouds/aws_cloud/$cloud_context/security_group/add",
$add[$i],
$this->t('Save'));
$this->assertSession()->pageTextContains($this->t('Cannot create group with Security Group Name "default".'));
// Save it for real.
$add[$i]['group_name[0][value]'] = $real_group_name;
$this->drupalPostForm("/clouds/aws_cloud/$cloud_context/security_group/add",
$add[$i],
$this->t('Save'));
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->pageTextNotContains($this->t('Notice'));
$this->assertSession()->pageTextNotContains($this->t('Warning'));
$t_args = ['@type' => 'Security Group', '%label' => $add[$i]['group_name[0][value]']];
$this->assertSession()->pageTextContains(strip_tags($this->t('The @type %label has been created.', $t_args)));
// Try to save it a second time through the form.
$this->drupalPostForm("/clouds/aws_cloud/$cloud_context/security_group/add",
$add[$i],
$this->t('Save'));
$this->assertSession()->pageTextContains($this->t('The Security Group Name "@name" already exists.', [
'@name' => $add[$i]['group_name[0][value]'],
]));
}
}
/**
* Create rule parameters.
*
......
......@@ -245,7 +245,7 @@ trait AwsCloudTestFormDataTrait {
$data[$i]['name'] = "group-name-#$num - {$this->random->name(15, TRUE)}";
}
else {
$data[$i]['group_name'] = "group-name-#$num - {$this->random->name(15, TRUE)}";
$data[$i]['group_name[0][value]'] = "group-name-#$num - {$this->random->name(15, TRUE)}";
}
}
return $data;
......@@ -782,16 +782,6 @@ trait AwsCloudTestFormDataTrait {
'from_port' => Utils::getRandomFromPort(),
'to_port' => Utils::getRandomToPort(),
],
[
'source' => 'group',
'user_id' => $this->random->name(8, TRUE),
'group_id' => $group_id,
'vpc_id' => "vpc-{$this->getRandomId()}",
'peering_connection_id' => "pcx-{$this->getRandomId()}",
'peering_status' => 'active',
'from_port' => Utils::getRandomFromPort(),
'to_port' => Utils::getRandomToPort(),
],
];
if ($rules_type === self::$awsCloudSecurityGroupRulesInbound
......@@ -967,11 +957,19 @@ trait AwsCloudTestFormDataTrait {
*
* @param array $rule
* The array of rule.
* @param bool $include_group
* Boolean to include group rule.
*
* @throws \Exception
*/
protected function getRandomRule(array &$rule): void {
$sources = ['ip4', 'ip6', 'group'];
protected function getRandomRule(array &$rule, $include_group = TRUE): void {
$sources = ['ip4', 'ip6'];
// Group can cause testing issues because a valid group_id is required
// for AWS tests. Added a boolean flag to disable group permissions from
// being included in the rules.
if ($include_group === TRUE) {
$sources[] = 'group';
}
$rule = ['type' => $rule['type']];
$source = $sources[array_rand($sources)];
$rule['source'] = $source;
......
......@@ -179,7 +179,7 @@ class OpenStackSecurityGroupIpPermissionsTest extends OpenStackTestBase {
$this->reloadMockData();
$defaults = $this->latestTemplateVars;
$defaults['group_name'] = $add[$i]['group_name'];
$defaults['group_name'] = $add[$i]['group_name[0][value]'];
$add[$i]['vpc_id'] = $defaults['vpc_id'];
$this->drupalGet("/clouds/openstack/$cloud_context/security_group/add");
......@@ -191,7 +191,7 @@ class OpenStackSecurityGroupIpPermissionsTest extends OpenStackTestBase {
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->pageTextNotContains($this->t('Notice'));
$this->assertSession()->pageTextNotContains($this->t('Warning'));
$t_args = ['@type' => 'Security Group', '%label' => $add[$i]['group_name']];
$t_args = ['@type' => 'Security Group', '%label' => $add[$i]['group_name[0][value]']];
$this->assertSession()->pageTextContains(strip_tags($this->t('The @type %label has been created.', $t_args)));
$edit_url = "/clouds/openstack/$cloud_context/security_group/$num/edit";
......@@ -210,8 +210,8 @@ class OpenStackSecurityGroupIpPermissionsTest extends OpenStackTestBase {
$rules = $this->createRulesTestFormData(self::$openStackSecurityGroupRulesMix, $edit_url, 1, self::$openStackSecurityGroupRulesRepeatCount);
// Test case3.2 edit rules.
$params = $this->editRuleParams($rules);
$params['name'] = $add[$i]['group_name'];
$params = $this->editRuleParams($rules, FALSE);
$params['name'] = $add[$i]['group_name[0][value]'];
$this->drupalPostForm($edit_url,
$params,
$this->t('Save'));
......@@ -258,7 +258,7 @@ class OpenStackSecurityGroupIpPermissionsTest extends OpenStackTestBase {
$this->reloadMockData();
$defaults = $this->latestTemplateVars;
$defaults['group_name'] = $add[$i]['group_name'];
$defaults['group_name'] = $add[$i]['group_name[0][value]'];
$add[$i]['vpc_id'] = $defaults['vpc_id'];
$this->drupalGet("/clouds/openstack/$cloud_context/security_group/add");
......@@ -270,7 +270,7 @@ class OpenStackSecurityGroupIpPermissionsTest extends OpenStackTestBase {
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->pageTextNotContains($this->t('Notice'));
$this->assertSession()->pageTextNotContains($this->t('Warning'));
$t_args = ['@type' => 'Security Group', '%label' => $add[$i]['group_name']];
$t_args = ['@type' => 'Security Group', '%label' => $add[$i]['group_name[0][value]']];
$this->assertSession()->pageTextContains(strip_tags($this->t('The @type %label has been created.', $t_args)));
// Verify From port validation error.
......@@ -388,7 +388,7 @@ class OpenStackSecurityGroupIpPermissionsTest extends OpenStackTestBase {
$this->reloadMockData();
$defaults = $this->latestTemplateVars;
$defaults['group_name'] = $add[$i]['group_name'];
$defaults['group_name'] = $add[$i]['group_name[0][value]'];
$add[$i]['vpc_id'] = $defaults['vpc_id'];
$this->drupalGet("/clouds/openstack/$cloud_context/security_group/add");
......@@ -400,7 +400,7 @@ class OpenStackSecurityGroupIpPermissionsTest extends OpenStackTestBase {
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->pageTextNotContains($this->t('Notice'));
$this->assertSession()->pageTextNotContains($this->t('Warning'));
$t_args = ['@type' => 'Security Group', '%label' => $add[$i]['group_name']];
$t_args = ['@type' => 'Security Group', '%label' => $add[$i]['group_name[0][value]']];
$this->assertSession()->pageTextContains(strip_tags($this->t('The @type %label has been created.', $t_args)));
$edit_url = "/clouds/openstack/$cloud_context/security_group/$num/edit";
......@@ -437,13 +437,15 @@ class OpenStackSecurityGroupIpPermissionsTest extends OpenStackTestBase {
*
* @param array $rules
* The array of rules.
* @param bool $include_group
* Boolean to include group rule.
*
* @return array
* The edited params.
*
* @throws \Exception
*/
protected function editRuleParams(array &$rules): array {
protected function editRuleParams(array &$rules, $include_group = TRUE): array {
$params = [];
$inbound_index = 0;
$outbound_index = 0;
......@@ -478,7 +480,7 @@ class OpenStackSecurityGroupIpPermissionsTest extends OpenStackTestBase {
}
}
else {
$this->getRandomRule($rule);
$this->getRandomRule($rule, FALSE);
}
foreach ($rule ?: [] as $key => $value) {
if ($key === 'type') {
......
......@@ -87,8 +87,8 @@ class OpenStackSecurityGroupTest extends OpenStackTestBase {
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->pageTextNotContains($this->t('Notice'));
$this->assertSession()->pageTextNotContains($this->t('Warning'));
$this->assertSession()->pageTextContains($add[$i]['group_name']);
$t_args = ['@type' => 'Security Group', '%label' => $add[$i]['group_name']];
$this->assertSession()->pageTextContains($add[$i]['group_name[0][value]']);
$t_args = ['@type' => 'Security Group', '%label' => $add[$i]['group_name[0][value]']];
$this->assertSession()->pageTextContains(strip_tags($this->t('The @type %label has been created.', $t_args)));
// Make sure listing.
......@@ -98,7 +98,7 @@ class OpenStackSecurityGroupTest extends OpenStackTestBase {
$this->assertSession()->pageTextNotContains($this->t('Warning'));
// 3 times.
for ($j = 0; $j < $i + 1; $j++) {
$this->assertSession()->pageTextContains($add[$i]['group_name']);
$this->assertSession()->pageTextContains($add[$i]['group_name[0][value]']);
}
}
......@@ -110,7 +110,7 @@ class OpenStackSecurityGroupTest extends OpenStackTestBase {
$this->assertSession()->pageTextNotContains($this->t('Warning'));
for ($j = 0; $j < $num; $j++) {
$this->assertSession()->pageTextContains($add[$j]['group_name']);
$this->assertSession()->pageTextContains($add[$j]['group_name[0][value]']);
}
}
......@@ -204,7 +204,7 @@ class OpenStackSecurityGroupTest extends OpenStackTestBase {
// Add new Security Groups.
$add = $this->createSecurityGroupTestFormData(self::$openStackSecurityGroupRepeatCount);
for ($i = 0; $i < self::$openStackSecurityGroupRepeatCount; $i++) {
$this->addSecurityGroupMockData($add[$i]['group_name'], $add[$i]['description']);
$this->addSecurityGroupMockData($add[$i]['group_name[0][value]'], $add[$i]['description']);
}
// Make sure listing.
......@@ -213,7 +213,7 @@ class OpenStackSecurityGroupTest extends OpenStackTestBase {
$this->assertSession()->pageTextNotContains($this->t('Notice'));
$this->assertSession()->pageTextNotContains($this->t('Warning'));
for ($i = 0; $i < self::$openStackSecurityGroupRepeatCount; $i++) {
$this->assertSession()->pageTextNotContains($add[$i]['group_name']);
$this->assertSession()->pageTextNotContains($add[$i]['group_name[0][value]']);
}
// Click 'Refresh'.
......@@ -221,7 +221,7 @@ class OpenStackSecurityGroupTest extends OpenStackTestBase {
$this->assertSession()->pageTextContains($this->t('Updated Security Groups.'));
// Make sure listing.
for ($i = 0; $i < self::$openStackSecurityGroupRepeatCount; $i++) {
$this->assertSession()->pageTextContains($add[$i]['group_name']);
$this->assertSession()->pageTextContains($add[$i]['group_name[0][value]']);
}
// Make sure detailed and edit view.
......@@ -255,16 +255,16 @@ class OpenStackSecurityGroupTest extends OpenStackTestBase {
$num++;
$data = [
'description' => "Description #$num - {$this->random->name(64, TRUE)}",
'group_name' => "group-name-#$num - {$this->random->name(15, TRUE)}",
'group_name[0][value]' => "group-name-#$num - {$this->random->name(15, TRUE)}",
];
$this->addSecurityGroupMockData($data['group_name'], $data['description']);
$this->addSecurityGroupMockData($data['group_name[0][value]'], $data['description']);
// Make sure listing.
$this->drupalGet("/clouds/openstack/$cloud_context/security_group");
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->pageTextNotContains($this->t('Notice'));
$this->assertSession()->pageTextNotContains($this->t('Warning'));
$this->assertSession()->pageTextNotContains($data['group_name']);
$this->assertSession()->pageTextNotContains($data['group_name[0][value]']);
// Click 'Refresh'.
$this->clickLink($this->t('Refresh'));
......@@ -272,7 +272,7 @@ class OpenStackSecurityGroupTest extends OpenStackTestBase {
$add = array_merge($add, [$data]);
// Make sure listing.
for ($i = 0; $i < self::$openStackSecurityGroupRepeatCount + 1; $i++) {
$this->assertSession()->pageTextContains($add[$i]['group_name']);
$this->assertSession()->pageTextContains($add[$i]['group_name[0][value]']);
}
// Update tags.
......@@ -313,7 +313,7 @@ class OpenStackSecurityGroupTest extends OpenStackTestBase {
// Make sure listing.
for ($i = 0; $i < self::$openStackSecurityGroupRepeatCount; $i++) {
$this->assertSession()->linkNotExists($add[$i]['tags_name']);
$this->assertSession()->linkExists($add[$i]['group_name']);
$this->assertSession()->linkExists($add[$i]['group_name[0][value]']);
}
// Delete name tags.
......@@ -327,7 +327,7 @@ class OpenStackSecurityGroupTest extends OpenStackTestBase {
$this->drupalGet("/clouds/openstack/$cloud_context/security_group");
for ($i = 0; $i < self::$openStackSecurityGroupRepeatCount; $i++) {
$this->assertSession()->linkNotExists($add[$i]['tags_name']);
$this->assertSession()->linkExists($add[$i]['group_name']);
$this->assertSession()->linkExists($add[$i]['group_name[0][value]']);
}
// Click 'Refresh'.
......@@ -335,7 +335,7 @@ class OpenStackSecurityGroupTest extends OpenStackTestBase {
// Make sure listing.
for ($i = 0; $i < self::$openStackSecurityGroupRepeatCount; $i++) {
$this->assertSession()->linkNotExists($add[$i]['tags_name']);
$this->assertSession()->linkExists($add[$i]['group_name']);
$this->assertSession()->linkExists($add[$i]['group_name[0][value]']);
}
// Delete SecurityGroup in mock data.
......@@ -349,7 +349,7 @@ class OpenStackSecurityGroupTest extends OpenStackTestBase {
$this->assertSession()->pageTextNotContains($this->t('Notice'));
$this->assertSession()->pageTextNotContains($this->t('Warning'));
for ($i = 0; $i < self::$openStackSecurityGroupRepeatCount + 1; $i++) {
$this->assertSession()->pageTextContains($add[$i]['group_name']);
$this->assertSession()->pageTextContains($add[$i]['group_name[0][value]']);
}
// Click 'Refresh'.
......@@ -357,7 +357,7 @@ class OpenStackSecurityGroupTest extends OpenStackTestBase {
$this->assertSession()->pageTextContains($this->t('Updated Security Groups.'));
// Make sure listing.
for ($i = 0; $i < self::$openStackSecurityGroupRepeatCount + 1; $i++) {
$this->assertSession()->pageTextNotContains($add[$i]['group_name']);
$this->assertSession()->pageTextNotContains($add[$i]['group_name[0][value]']);
}
}
......
......@@ -118,16 +118,36 @@ class Utils {
/**
* Get a random from port.
*
* @param int $start
* If passed, used as the min value.
* @param int $end
* If passed, used as the max value.
*
* @return int
* Random int.
*
* @throws \Exception
*/
public static function getRandomFromPort(): int {
return random_int(0, 37676);
public static function getRandomFromPort($start = 0, $end = 37676): int {
return random_int($start, $end);
}
/**
* Get a random to port.
*
* @param int $start
* If passed, used as the min value.
* @param int $end
* If passed, used as the max value.
*
* @return int
* Random int.
*
* @throws \Exception
*/
public static function getRandomToPort(): int {
return random_int(37677, 65535);
public static function getRandomToPort($start = 37677, $end = 65535): int {
return random_int($start, $end);
}
/**
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment