authorize.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. <?php
  2. /**
  3. * @file
  4. * Administrative script for running authorized file operations.
  5. *
  6. * Using this script, the site owner (the user actually owning the files on the
  7. * webserver) can authorize certain file-related operations to proceed with
  8. * elevated privileges, for example to deploy and upgrade modules or themes.
  9. * Users should not visit this page directly, but instead use an administrative
  10. * user interface which knows how to redirect the user to this script as part of
  11. * a multistep process. This script actually performs the selected operations
  12. * without loading all of Drupal, to be able to more gracefully recover from
  13. * errors. Access to the script is controlled by a global killswitch in
  14. * settings.php ('allow_authorize_operations') and via the 'administer software
  15. * updates' permission.
  16. *
  17. * There are helper functions for setting up an operation to run via this
  18. * system in modules/system/system.module. For more information, see:
  19. * @link authorize Authorized operation helper functions @endlink
  20. */
  21. use Drupal\Core\DrupalKernel;
  22. use Drupal\Core\Form\EnforcedResponseException;
  23. use Drupal\Core\Url;
  24. use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
  25. use Symfony\Component\HttpFoundation\Request;
  26. use Symfony\Component\HttpFoundation\Response;
  27. use Drupal\Core\Site\Settings;
  28. // Change the directory to the Drupal root.
  29. chdir('..');
  30. $autoloader = require_once 'autoload.php';
  31. /**
  32. * Global flag to identify update.php and authorize.php runs.
  33. *
  34. * Identifies update.php and authorize.php runs, avoiding unwanted operations
  35. * such as css/js preprocessing and translation, and solves some theming issues.
  36. * The flag is checked in other places in Drupal code (not just authorize.php).
  37. */
  38. const MAINTENANCE_MODE = 'update';
  39. /**
  40. * Determines if the current user is allowed to run authorize.php.
  41. *
  42. * The killswitch in settings.php overrides all else, otherwise, the user must
  43. * have access to the 'administer software updates' permission.
  44. *
  45. * @param \Symfony\Component\HttpFoundation\Request $request
  46. * The incoming request.
  47. *
  48. * @return bool
  49. * TRUE if the current user can run authorize.php, and FALSE if not.
  50. */
  51. function authorize_access_allowed(Request $request) {
  52. $account = \Drupal::service('authentication')->authenticate($request);
  53. if ($account) {
  54. \Drupal::currentUser()->setAccount($account);
  55. }
  56. return Settings::get('allow_authorize_operations', TRUE) && \Drupal::currentUser()->hasPermission('administer software updates');
  57. }
  58. try {
  59. $request = Request::createFromGlobals();
  60. $kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod');
  61. $kernel->prepareLegacyRequest($request);
  62. }
  63. catch (HttpExceptionInterface $e) {
  64. $response = new Response('', $e->getStatusCode());
  65. $response->prepare($request)->send();
  66. exit;
  67. }
  68. // We have to enable the user and system modules, even to check access and
  69. // display errors via the maintenance theme.
  70. \Drupal::moduleHandler()->addModule('system', 'core/modules/system');
  71. \Drupal::moduleHandler()->addModule('user', 'core/modules/user');
  72. \Drupal::moduleHandler()->load('system');
  73. \Drupal::moduleHandler()->load('user');
  74. // Initialize the maintenance theme for this administrative script.
  75. drupal_maintenance_theme();
  76. $content = [];
  77. $show_messages = TRUE;
  78. $is_allowed = authorize_access_allowed($request);
  79. // Build content.
  80. if ($is_allowed) {
  81. // Load both the Form API and Batch API.
  82. require_once __DIR__ . '/includes/form.inc';
  83. require_once __DIR__ . '/includes/batch.inc';
  84. if (isset($_SESSION['authorize_page_title'])) {
  85. $page_title = $_SESSION['authorize_page_title'];
  86. }
  87. else {
  88. $page_title = t('Authorize file system changes');
  89. }
  90. // See if we've run the operation and need to display a report.
  91. if (isset($_SESSION['authorize_results']) && $results = $_SESSION['authorize_results']) {
  92. // Clear the session out.
  93. unset($_SESSION['authorize_results']);
  94. unset($_SESSION['authorize_operation']);
  95. unset($_SESSION['authorize_filetransfer_info']);
  96. if (!empty($results['page_title'])) {
  97. $page_title = $results['page_title'];
  98. }
  99. if (!empty($results['page_message'])) {
  100. \Drupal::messenger()->addMessage($results['page_message']['message'], $results['page_message']['type']);
  101. }
  102. $content['authorize_report'] = [
  103. '#theme' => 'authorize_report',
  104. '#messages' => $results['messages'],
  105. ];
  106. if (is_array($results['tasks'])) {
  107. $links = $results['tasks'];
  108. }
  109. else {
  110. // Since this is being called outside of the primary front controller,
  111. // the base_url needs to be set explicitly to ensure that links are
  112. // relative to the site root.
  113. // @todo Simplify with https://www.drupal.org/node/2548095
  114. $default_options = [
  115. '#type' => 'link',
  116. '#options' => [
  117. 'absolute' => TRUE,
  118. 'base_url' => $GLOBALS['base_url'],
  119. ],
  120. ];
  121. $links = [
  122. $default_options + [
  123. '#url' => Url::fromRoute('system.admin'),
  124. '#title' => t('Administration pages'),
  125. ],
  126. $default_options + [
  127. '#url' => Url::fromRoute('<front>'),
  128. '#title' => t('Front page'),
  129. ],
  130. ];
  131. }
  132. $content['next_steps'] = [
  133. '#theme' => 'item_list',
  134. '#items' => $links,
  135. '#title' => t('Next steps'),
  136. ];
  137. }
  138. // If a batch is running, let it run.
  139. elseif ($request->query->has('batch')) {
  140. $content = _batch_page($request);
  141. // If _batch_page() returns a response object (likely a JsonResponse for
  142. // JavaScript-based batch processing), send it immediately.
  143. if ($content instanceof Response) {
  144. $content->send();
  145. exit;
  146. }
  147. }
  148. else {
  149. if (empty($_SESSION['authorize_operation']) || empty($_SESSION['authorize_filetransfer_info'])) {
  150. $content = ['#markup' => t('It appears you have reached this page in error.')];
  151. }
  152. elseif (!$batch = batch_get()) {
  153. // We have a batch to process, show the filetransfer form.
  154. try {
  155. $content = \Drupal::formBuilder()->getForm('Drupal\Core\FileTransfer\Form\FileTransferAuthorizeForm');
  156. }
  157. catch (EnforcedResponseException $e) {
  158. $e->getResponse()->send();
  159. exit;
  160. }
  161. }
  162. }
  163. // We defer the display of messages until all operations are done.
  164. $show_messages = !(($batch = batch_get()) && isset($batch['running']));
  165. }
  166. else {
  167. \Drupal::logger('access denied')->warning('authorize.php');
  168. $page_title = t('Access denied');
  169. $content = ['#markup' => t('You are not allowed to access this page.')];
  170. }
  171. $bare_html_page_renderer = \Drupal::service('bare_html_page_renderer');
  172. $response = $bare_html_page_renderer->renderBarePage($content, $page_title, 'maintenance_page', [
  173. '#show_messages' => $show_messages,
  174. ]);
  175. if (!$is_allowed) {
  176. $response->setStatusCode(403);
  177. }
  178. $response->send();