config_preview_deploy-1.0.0-alpha3/config_preview_deploy.install

config_preview_deploy.install
<?php

/**
 * @file
 * Install, update and uninstall functions for the config_preview_deploy module.
 */

use Drupal\key\Entity\Key;
use Drupal\consumers\Entity\Consumer;
use Drupal\simple_oauth\Entity\Oauth2Scope;

// Include constants for requirements.
require_once DRUPAL_ROOT . '/core/includes/install.inc';

/**
 * Implements hook_install().
 */
function config_preview_deploy_install() {
  // Ensure required modules are installed first.
  $module_installer = \Drupal::service('module_installer');
  if (!\Drupal::moduleHandler()->moduleExists('simple_oauth')) {
    $module_installer->install(['simple_oauth']);
  }
  if (!\Drupal::moduleHandler()->moduleExists('key')) {
    $module_installer->install(['key']);
  }

  // Create OAuth scope and consumer for config deployment.
  _config_preview_deploy_create_oauth_scope();
  _config_preview_deploy_create_oauth_consumer();
}

/**
 * Implements hook_uninstall().
 */
function config_preview_deploy_uninstall() {
  // Remove OAuth consumer and scope.
  _config_preview_deploy_remove_oauth_consumer();
  _config_preview_deploy_remove_oauth_scope();
}

/**
 * Creates OAuth2 scope entity for config deployment.
 */
function _config_preview_deploy_create_oauth_scope() {
  try {
    // Check if scope already exists.
    $existing = \Drupal::entityTypeManager()
      ->getStorage('oauth2_scope')
      ->load('config_preview_deploy');

    if ($existing) {
      \Drupal::logger('config_preview_deploy')->info('OAuth2 scope already exists, skipping creation.');
      return;
    }

    // Create OAuth2 scope entity.
    $scope = Oauth2Scope::create([
      'name' => 'config_preview_deploy',
      'description' => 'Deploy configuration changes from preview environment to production',
      'grant_types' => [
        'authorization_code' => [
          'status' => TRUE,
          'description' => 'Securely deploy your configuration changes to the production website',
        ],
      ],
      'umbrella' => FALSE,
      'granularity_id' => 'permission',
      'granularity_configuration' => [
        'permission' => 'accept config deployments',
      ],
    ]);
    $scope->save();

    \Drupal::logger('config_preview_deploy')->info('Created OAuth2 scope entity: @name', [
      '@name' => $scope->getName(),
    ]);

  }
  catch (\Exception $e) {
    \Drupal::logger('config_preview_deploy')->error('Failed to create OAuth2 scope: @message', [
      '@message' => $e->getMessage(),
    ]);
  }
}

/**
 * Creates OAuth consumer for config deployment operations.
 */
function _config_preview_deploy_create_oauth_consumer() {
  try {
    // Check if consumer already exists.
    $existing = \Drupal::entityTypeManager()
      ->getStorage('consumer')
      ->loadByProperties(['client_id' => 'config_preview_deploy']);

    if (!empty($existing)) {
      \Drupal::logger('config_preview_deploy')->info('OAuth consumer already exists, skipping creation.');
      return;
    }

    // Generate secure client secret.
    $client_secret = bin2hex(random_bytes(32));

    // Create OAuth consumer with config deployment scope.
    $consumer = Consumer::create([
      'label' => 'Config Preview Deploy',
      'client_id' => 'config_preview_deploy',
      'secret' => $client_secret,
    // This will be configured per environment.
      'redirect' => '',
      'scopes' => ['config_preview_deploy'],
      'is_default' => FALSE,
      'third_party' => FALSE,
      'confidential' => TRUE,
    // Always require user approval.
      'automatic_authorization' => FALSE,
    // Ask for approval every time.
      'remember_approval' => FALSE,
      'grant_types' => ['authorization_code'],
      'description' => 'OAuth consumer for secure configuration deployment between preview and production environments.',
    ]);
    $consumer->save();

    \Drupal::logger('config_preview_deploy')->info('Created OAuth consumer for config deployment with client_id: @client_id', [
      '@client_id' => 'config_preview_deploy',
    ]);

    // Store client secret using Key module.
    _config_preview_deploy_create_deployment_key($client_secret);

    // Store other OAuth configuration.
    $config = \Drupal::configFactory()->getEditable('config_preview_deploy.oauth');
    $config->set('client_id', 'config_preview_deploy');
    $config->set('scope', 'config_preview_deploy');
    $config->save();

  }
  catch (\Exception $e) {
    \Drupal::logger('config_preview_deploy')->error('Failed to create OAuth consumer: @message', [
      '@message' => $e->getMessage(),
    ]);
  }
}

/**
 * Removes OAuth consumer for config deployment operations.
 */
function _config_preview_deploy_remove_oauth_consumer() {
  try {
    // Find and delete the consumer.
    $consumers = \Drupal::entityTypeManager()
      ->getStorage('consumer')
      ->loadByProperties(['client_id' => 'config_preview_deploy']);

    foreach ($consumers as $consumer) {
      $consumer->delete();
      \Drupal::logger('config_preview_deploy')->info('Removed OAuth consumer: @label', [
        '@label' => $consumer->label(),
      ]);
    }

    // Remove OAuth configuration.
    \Drupal::configFactory()->getEditable('config_preview_deploy.oauth')->delete();

  }
  catch (\Exception $e) {
    \Drupal::logger('config_preview_deploy')->error('Failed to remove OAuth consumer: @message', [
      '@message' => $e->getMessage(),
    ]);
  }
}

/**
 * Removes OAuth2 scope entity for config deployment.
 */
function _config_preview_deploy_remove_oauth_scope() {
  try {
    // Find and delete the scope.
    $scope = \Drupal::entityTypeManager()
      ->getStorage('oauth2_scope')
      ->load('config_preview_deploy');

    if ($scope) {
      $scope->delete();
      \Drupal::logger('config_preview_deploy')->info('Removed OAuth2 scope: @name', [
        '@name' => $scope->getName(),
      ]);
    }

  }
  catch (\Exception $e) {
    \Drupal::logger('config_preview_deploy')->error('Failed to remove OAuth2 scope: @message', [
      '@message' => $e->getMessage(),
    ]);
  }
}

/**
 * Creates deployment key using Key module.
 *
 * Only creates config-stored key if DRUPAL_CONFIG_DEPLOY_SECRET env var is not
 * set.
 *
 * @param string $client_secret
 *   The generated client secret to store as fallback.
 */
function _config_preview_deploy_create_deployment_key($client_secret) {
  // Check if environment variable is set.
  $env_secret = getenv('DRUPAL_CONFIG_DEPLOY_SECRET');

  if ($env_secret) {
    // Environment variable is set, create key that uses env provider.
    $key = Key::create([
      'id' => 'config_deploy_secret',
      'label' => 'Configuration Deployment Secret',
      'description' => 'Secret for secure configuration deployment authentication',
      'key_type' => 'authentication',
      'key_provider' => 'env',
      'key_provider_settings' => [
        'env_variable' => 'DRUPAL_CONFIG_DEPLOY_SECRET',
      ],
    ]);
    $key->save();

    \Drupal::logger('config_preview_deploy')->info('Created deployment key using environment variable DRUPAL_CONFIG_DEPLOY_SECRET');
  }
  else {
    // No environment variable, store in configuration.
    $key = Key::create([
      'id' => 'config_deploy_secret',
      'label' => 'Configuration Deployment Secret',
      'description' => 'Secret for secure configuration deployment authentication',
      'key_type' => 'authentication',
      'key_provider' => 'config',
      'key_provider_settings' => [],
    ]);
    $key->save();

    // Set the key value using the Key module API.
    $key->setKeyValue($client_secret);
    $key->save();

    \Drupal::logger('config_preview_deploy')->info('Created deployment key using configuration storage (set DRUPAL_CONFIG_DEPLOY_SECRET env var for better security)');
  }
}

/**
 * Switch from static to dynamic OAuth scopes and fix consumer configuration.
 */
function config_preview_deploy_update_10011() {
  // Ensure required modules are installed first.
  $module_installer = \Drupal::service('module_installer');
  if (!\Drupal::moduleHandler()->moduleExists('simple_oauth')) {
    $module_installer->install(['simple_oauth']);
  }
  // Note: We use dynamic scopes (config entities), not static scopes.
  if (!\Drupal::moduleHandler()->moduleExists('key')) {
    $module_installer->install(['key']);
  }

  // Create OAuth2 scope entity (dynamic scope).
  _config_preview_deploy_create_oauth_scope();

  // Disable static scope module if enabled.
  if (\Drupal::moduleHandler()->moduleExists('simple_oauth_static_scope')) {
    $module_installer->uninstall(['simple_oauth_static_scope']);
    \Drupal::logger('config_preview_deploy')->info('Disabled simple_oauth_static_scope module in favor of dynamic scopes');
  }

  // Set scope provider to dynamic.
  \Drupal::configFactory()->getEditable('simple_oauth.settings')
    ->set('scope_provider', 'dynamic')
    ->save();

  // Find existing OAuth consumer and fix/create it.
  $consumers = \Drupal::entityTypeManager()
    ->getStorage('consumer')
    ->loadByProperties(['client_id' => 'config_preview_deploy']);

  if (!empty($consumers)) {
    $consumer = reset($consumers);

    // Get or generate client secret.
    $key_storage = \Drupal::entityTypeManager()->getStorage('key');
    $existing_key = $key_storage->load('config_deploy_secret');
    if ($existing_key) {
      $client_secret = $existing_key->getKeyValue();
    }
    else {
      $client_secret = bin2hex(random_bytes(32));
      _config_preview_deploy_create_deployment_key($client_secret);
    }

    // Preserve existing redirect URIs.
    $existing_redirects = $consumer->get('redirect')->getValue();

    // Fix the consumer configuration.
    $consumer->set('third_party', FALSE);
    $consumer->set('confidential', TRUE);
    $consumer->set('grant_types', ['authorization_code']);
    $consumer->set('secret', $client_secret);
    // Only reset redirect if none exist, otherwise preserve existing ones.
    if (empty($existing_redirects)) {
      $consumer->set('redirect', '');
    }
    $consumer->set('scopes', ['config_preview_deploy']);
    // Always require user approval.
    $consumer->set('automatic_authorization', FALSE);
    // Ask for approval every time.
    $consumer->set('remember_approval', FALSE);
    $consumer->save();

    \Drupal::logger('config_preview_deploy')->info('Updated OAuth consumer: third_party=FALSE, confidential=TRUE, grant_types=authorization_code, secret=set');
  }
  else {
    // Create new consumer with correct settings.
    _config_preview_deploy_create_oauth_consumer();
  }

  // Clear all caches to pick up dynamic scope configuration.
  \Drupal::cache('discovery')->deleteAll();
  if (\Drupal::hasService('plugin.manager.oauth2_scope')) {
    \Drupal::service('plugin.manager.oauth2_scope')->clearCachedDefinitions();
  }
  \Drupal::service('cache_tags.invalidator')->invalidateTags(['oauth2_scope_plugins', 'oauth2_scope']);

  \Drupal::logger('config_preview_deploy')->info('Switched to dynamic OAuth scopes and updated consumer configuration.');
}

/**
 * Implements hook_requirements().
 */
function config_preview_deploy_requirements($phase) {
  $requirements = [];

  if ($phase === 'runtime') {
    /** @var \Drupal\config_preview_deploy\Service\PatchTool $patchTool */
    $patchTool = \Drupal::service('config_preview_deploy.patch_tool');
    $toolInfo = $patchTool->getToolInfo();

    $requirements['config_preview_deploy_patch_tool'] = [
      'title' => t('Config Preview Deploy - Patch Tool'),
      'severity' => $toolInfo['available'] ? REQUIREMENT_OK : REQUIREMENT_ERROR,
      'value' => $toolInfo['available'] ? t('Available (@version)', ['@version' => $toolInfo['version']]) : t('Not Available'),
      'description' => $toolInfo['available']
        ? t('GNU patch tool is available and working correctly.')
        : t('GNU patch tool is required for Config Preview Deploy. Install with: <code>apt install patch</code> (Debian/Ubuntu) or <code>yum install patch</code> (CentOS/RHEL) or <code>brew install gpatch</code> (macOS).'),
    ];

    // Check if OAuth consumer exists (only if Simple OAuth module is enabled).
    $entity_type_manager = \Drupal::entityTypeManager();
    if ($entity_type_manager->hasDefinition('consumer')) {
      $consumers = $entity_type_manager
        ->getStorage('consumer')
        ->loadByProperties(['client_id' => 'config_preview_deploy']);

      if (empty($consumers)) {
        $requirements['config_preview_deploy_oauth'] = [
          'title' => t('Config Preview Deploy OAuth'),
          'value' => t('OAuth consumer missing'),
          'description' => t('OAuth consumer for config deployment is missing. Try reinstalling the module.'),
          'severity' => REQUIREMENT_ERROR,
        ];
      }
      else {
        $requirements['config_preview_deploy_oauth'] = [
          'title' => t('Config Preview Deploy OAuth'),
          'value' => t('OAuth consumer configured'),
          'description' => t('OAuth authentication is properly configured for config deployment.'),
          'severity' => REQUIREMENT_OK,
        ];
      }
    }
  }

  return $requirements;
}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc