diff --git a/drush/Commands/PolicyCommands.php b/drush/Commands/PolicyCommands.php index 290b4e0..4178fa7 100644 --- a/drush/Commands/PolicyCommands.php +++ b/drush/Commands/PolicyCommands.php @@ -3,6 +3,7 @@ namespace Drush\Commands; use Consolidation\AnnotatedCommand\CommandData; +use Drush\Drush; /** * Edit this file to reflect your organization's needs. @@ -10,29 +11,73 @@ class PolicyCommands extends DrushCommands { /** - * Prevent catastrophic braino. Note that this file has to be local to the - * machine that initiates the sql:sync command. + * Parameter to set the instance is protected. + */ + const PROTECTED_PARAMETER = "protected-instance"; + + /** + * Not execute the rsync if the instance is protected. * * @hook validate sql:sync * * @throws \Exception */ public function sqlSyncValidate(CommandData $commandData) { - if ($commandData->input()->getArgument('target') == '@prod') { - throw new \Exception(dt('Per !file, you may never overwrite the production database.', ['!file' => __FILE__])); + $target_alias = $commandData->input()->getArgument('target'); + if ($this->isInstanceProtected($target_alias)) { + throw new \Exception(dt('Alias: !alias. You can not overwrite the database.', ['!alias' => $target_alias])); } } /** - * Limit rsync operations to production site. + * Not execute the rsync if the instance is protected. * * @hook validate core:rsync * * @throws \Exception */ public function rsyncValidate(CommandData $commandData) { - if (preg_match("/^@prod/", $commandData->input()->getArgument('target'))) { - throw new \Exception(dt('Per !file, you may never rsync to the production site.', ['!file' => __FILE__])); + $target_alias = $commandData->input()->getArgument('target'); + if ($this->isInstanceProtected($target_alias)) { + throw new \Exception(dt('Alias: !alias. You can not overwrite the code or the files.', ['!alias' => $target_alias])); + } + } + + /** + * Not execute the sql:drop if the instance is protected. + * + * @hook validate sql:drop + * + * @throws \Exception + */ + public function dropValidate(CommandData $commandData) { + if ($this->isInstanceProtected('@self')) { + throw new \Exception(dt('You are not allowed to delete the database.')); } } + + /** + * Determine if an instance is protected. + * + * @param string $alias + * Alias of drush. Self if it is local. + * + * @return bool + * If it is protected. + */ + protected function isInstanceProtected(string $alias): bool { + // If alias is local, check the user context configuration. + if ($alias === "@self") { + $user_context = $this->getConfig()->getContext('user'); + return $user_context->get(PolicyCommands::PROTECTED_PARAMETER, NULL) ?? FALSE; + } + + // Check the alias configuration. + $alias_configuration = Drush::aliasManager()->getAlias($alias); + if (empty($alias_configuration)) { + return FALSE; + } + return $alias_configuration->get(PolicyCommands::PROTECTED_PARAMETER, NULL) ?? FALSE; + } + } diff --git a/drush/Commands/README.md b/drush/Commands/README.md new file mode 100644 index 0000000..2d5ca54 --- /dev/null +++ b/drush/Commands/README.md @@ -0,0 +1,6 @@ +Drush command policies have been implemented to prevent the execution of certain drush commands: 'core:sync', +'sql:sync', and 'sql:drop' in production or other environment instances. + +To apply these policies, two actions must be taken: +- In the definition of the alias for the environment where you want these policies to be applied you have to add the parameter: "protected-instance: true" +- On the server of the environment, copy the 'drush/Commands/drush.yml.dist' to the user's .drush folder of the user used for SSH connections to the server. \ No newline at end of file diff --git a/drush/Commands/drush.yml.dist b/drush/Commands/drush.yml.dist new file mode 100644 index 0000000..bc12261 --- /dev/null +++ b/drush/Commands/drush.yml.dist @@ -0,0 +1,3 @@ +# This configuration file must be copied to the user's .drush folder in the environment where we want to enable +# protection for commands that may drop or overwrite the database. +protected-instance: true