authorize.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  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. use Symfony\Cmf\Component\Routing\RouteObjectInterface;
  29. use Symfony\Component\Routing\Route;
  30. // Change the directory to the Drupal root.
  31. chdir('..');
  32. $autoloader = require_once 'autoload.php';
  33. /**
  34. * Global flag to identify update.php and authorize.php runs.
  35. *
  36. * Identifies update.php and authorize.php runs, avoiding unwanted operations
  37. * such as css/js preprocessing and translation, and solves some theming issues.
  38. * The flag is checked in other places in Drupal code (not just authorize.php).
  39. */
  40. const MAINTENANCE_MODE = 'update';
  41. /**
  42. * Determines if the current user is allowed to run authorize.php.
  43. *
  44. * The killswitch in settings.php overrides all else, otherwise, the user must
  45. * have access to the 'administer software updates' permission.
  46. *
  47. * @param \Symfony\Component\HttpFoundation\Request $request
  48. * The incoming request.
  49. *
  50. * @return bool
  51. * TRUE if the current user can run authorize.php, and FALSE if not.
  52. */
  53. function authorize_access_allowed(Request $request) {
  54. $account = \Drupal::service('authentication')->authenticate($request);
  55. if ($account) {
  56. \Drupal::currentUser()->setAccount($account);
  57. }
  58. return Settings::get('allow_authorize_operations', TRUE) && \Drupal::currentUser()->hasPermission('administer software updates');
  59. }
  60. try {
  61. $request = Request::createFromGlobals();
  62. $kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod');
  63. $kernel->boot();
  64. // A route is required for route matching.
  65. $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('<none>'));
  66. $request->attributes->set(RouteObjectInterface::ROUTE_NAME, '<none>');
  67. $kernel->preHandle($request);
  68. // Ensure our request includes the session if appropriate.
  69. if (PHP_SAPI !== 'cli') {
  70. $request->setSession($kernel->getContainer()->get('session'));
  71. }
  72. }
  73. catch (HttpExceptionInterface $e) {
  74. $response = new Response('', $e->getStatusCode());
  75. $response->prepare($request)->send();
  76. exit;
  77. }
  78. // We have to enable the user and system modules, even to check access and
  79. // display errors via the maintenance theme.
  80. \Drupal::moduleHandler()->addModule('system', 'core/modules/system');
  81. \Drupal::moduleHandler()->addModule('user', 'core/modules/user');
  82. \Drupal::moduleHandler()->load('system');
  83. \Drupal::moduleHandler()->load('user');
  84. // Initialize the maintenance theme for this administrative script.
  85. drupal_maintenance_theme();
  86. $content = [];
  87. $show_messages = TRUE;
  88. $is_allowed = authorize_access_allowed($request);
  89. // Build content.
  90. if ($is_allowed) {
  91. // Load both the Form API and Batch API.
  92. require_once __DIR__ . '/includes/form.inc';
  93. require_once __DIR__ . '/includes/batch.inc';
  94. $page_title = $request->getSession()->get('authorize_page_title', t('Authorize file system changes'));
  95. // See if we've run the operation and need to display a report.
  96. if ($results = $request->getSession()->remove('authorize_results')) {
  97. // Clear the session out.
  98. $request->getSession()->remove('authorize_operation');
  99. $request->getSession()->remove('authorize_filetransfer_info');
  100. if (!empty($results['page_title'])) {
  101. $page_title = $results['page_title'];
  102. }
  103. if (!empty($results['page_message'])) {
  104. \Drupal::messenger()->addMessage($results['page_message']['message'], $results['page_message']['type']);
  105. }
  106. $content['authorize_report'] = [
  107. '#theme' => 'authorize_report',
  108. '#messages' => $results['messages'],
  109. ];
  110. if (is_array($results['tasks'])) {
  111. $links = $results['tasks'];
  112. }
  113. else {
  114. // Since this is being called outside of the primary front controller,
  115. // the base_url needs to be set explicitly to ensure that links are
  116. // relative to the site root.
  117. // @todo Simplify with https://www.drupal.org/node/2548095
  118. $default_options = [
  119. '#type' => 'link',
  120. '#options' => [
  121. 'absolute' => TRUE,
  122. 'base_url' => $GLOBALS['base_url'],
  123. ],
  124. ];
  125. $links = [
  126. $default_options + [
  127. '#url' => Url::fromRoute('system.admin'),
  128. '#title' => t('Administration pages'),
  129. ],
  130. $default_options + [
  131. '#url' => Url::fromRoute('<front>'),
  132. '#title' => t('Front page'),
  133. ],
  134. ];
  135. }
  136. $content['next_steps'] = [
  137. '#theme' => 'item_list',
  138. '#items' => $links,
  139. '#title' => t('Next steps'),
  140. ];
  141. }
  142. // If a batch is running, let it run.
  143. elseif ($request->query->has('batch')) {
  144. $content = _batch_page($request);
  145. // If _batch_page() returns a response object (likely a JsonResponse for
  146. // JavaScript-based batch processing), send it immediately.
  147. if ($content instanceof Response) {
  148. $content->send();
  149. exit;
  150. }
  151. }
  152. else {
  153. if (!$request->getSession()->has('authorize_operation') || !$request->getSession()->has('authorize_filetransfer_info')) {
  154. $content = ['#markup' => t('It appears you have reached this page in error.')];
  155. }
  156. elseif (!$batch = batch_get()) {
  157. // We have a batch to process, show the filetransfer form.
  158. try {
  159. $content = \Drupal::formBuilder()->getForm('Drupal\Core\FileTransfer\Form\FileTransferAuthorizeForm');
  160. }
  161. catch (EnforcedResponseException $e) {
  162. $e->getResponse()->send();
  163. exit;
  164. }
  165. }
  166. }
  167. // We defer the display of messages until all operations are done.
  168. $show_messages = !(($batch = batch_get()) && isset($batch['running']));
  169. }
  170. else {
  171. \Drupal::logger('access denied')->warning('authorize.php');
  172. $page_title = t('Access denied');
  173. $content = ['#markup' => t('You are not allowed to access this page.')];
  174. }
  175. $bare_html_page_renderer = \Drupal::service('bare_html_page_renderer');
  176. $response = $bare_html_page_renderer->renderBarePage($content, $page_title, 'maintenance_page', [
  177. '#show_messages' => $show_messages,
  178. ]);
  179. if (!$is_allowed) {
  180. $response->setStatusCode(403);
  181. }
  182. $response->send();