diff --git a/config/r4032login.settings.yml b/config/r4032login.settings.yml new file mode 100644 index 0000000..e1fdb7a --- /dev/null +++ b/config/r4032login.settings.yml @@ -0,0 +1,6 @@ +display_denied_message: true +access_denied_message: 'Access denied. You must log in to view this page.' +redirect_authenticated_users_to: '' +user_login_path: 'user/login' +default_redirect_code: '302' +match_noredirect_pages: '' diff --git a/config/schema/r4032login.schema.yml b/config/schema/r4032login.schema.yml new file mode 100644 index 0000000..e15a9df --- /dev/null +++ b/config/schema/r4032login.schema.yml @@ -0,0 +1,29 @@ +r4032login.settings: + type: mapping + label: 'Redirect 403 to User Login (r4032login) settings' + mapping: + display_denied_message: + type: boolean + label: 'Display access denied message on login page' + access_denied_message: + type: label + label: "User login 'access denied' message" + redirect_authenticated_users_to: + type: label + label: 'Redirect authenticated users to' + user_login_path: + type: path + label: 'user/login' + default_redirect_code: + type: mapping + label: 'HTTP redirect code' + mapping: + 301: + type: integer + label: '301 Moved Permanently' + 302: + type: integer + label: '302 Found' + match_noredirect_pages: + type: text + label: 'Only the listed pages' diff --git a/lib/Drupal/r4032login/EventSubscriber/R4032LoginAccessSubscriber.php b/lib/Drupal/r4032login/EventSubscriber/R4032LoginAccessSubscriber.php new file mode 100644 index 0000000..ea5b846 --- /dev/null +++ b/lib/Drupal/r4032login/EventSubscriber/R4032LoginAccessSubscriber.php @@ -0,0 +1,99 @@ +getRequest(); + + // The controller is being handled by the HTTP kernel, so add an attribute + // to tell us this is the controller request. + $request->attributes->set('_controller_request', TRUE); + + if (!$request->attributes->has(RouteObjectInterface::ROUTE_OBJECT)) { + // If no Route is available it is likely a static resource and access is + // handled elsewhere. + return; + } + + // Wrap this in a try/catch to ensure the '_controller_request' attribute + // can always be removed. + try { + $access = $this->accessManager->check($request->attributes->get(RouteObjectInterface::ROUTE_OBJECT), $request, $this->currentUser); + } + catch (\Exception $e) { + $request->attributes->remove('_controller_request'); + throw $e; + } + + $request->attributes->remove('_controller_request'); + + if (!$access) { + $response = $this->redirect4032Login($event); + $event->setResponse($response); + } + } + + /** + * Redirects anonymous users from 403 Access Denied pages. + * + * Redirect to the /user/login page with a message explaining that they + * must log in to view the requested page and a query string parameter + * appended to the url to return after login. + * + * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event + * The Event to process. + * + * @return \Symfony\Component\HttpFoundation\RedirectResponse + * A response that redirects 403 Access Denied pages user login page. + */ + public function redirect4032Login(GetResponseEvent $event) { + $config = \Drupal::config('r4032login.settings'); + if ($this->currentUser->isAnonymous()) { + // Show custom access denied message if set. + if ($config->get('display_denied_message')) { + $message = $config->get('access_denied_message'); + drupal_set_message($message, 'error'); + } + // Handle redirection to the login form. + $login_path = $config->get('user_login_path'); + $code = $config->get('default_redirect_code'); + return new RedirectResponse(url($login_path, array('absolute' => TRUE, 'query' => drupal_get_destination())), $code); + } + else { + // Check to see if we are to redirect the user. + $redirect = $config->get('redirect_authenticated_users_to'); + if ($redirect) { + // Custom access denied page for logged in users. + return new RedirectResponse(url($redirect, array('absolute' => TRUE))); + } + else { + // Display the default access denied page. + throw new AccessDeniedHttpException(); + } + } + } +} diff --git a/lib/Drupal/r4032login/EventSubscriber/R4032LoginSubscriber.php b/lib/Drupal/r4032login/EventSubscriber/R4032LoginSubscriber.php new file mode 100644 index 0000000..596d313 --- /dev/null +++ b/lib/Drupal/r4032login/EventSubscriber/R4032LoginSubscriber.php @@ -0,0 +1,68 @@ +state = $state; + } + + /** + * Verifies that the current user can access the requested path. + * + * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event + * The Event to process. + * + * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException + * Thrown when the access got denied. + */ + public function onKernelRequestAccessCheck(GetResponseEvent $event) { + drupal_set_message(t('R4032LoginSubscriber event')); + } + + /** + * Registers methods as kernel listeners. + * + * @return array + * An array of event listener definitions. + */ + public static function getSubscribedEvents() { + $events[KernelEvents::EXCEPTION][] = array('onKernelRequestAccessCheck', 0); + return $events; + } + + /** + * Implements \Drupal\Core\DestructableInterface::destruct(). + */ + public function destruct() { + $this->state->set('r4032login.subscriber.destructed', TRUE); + } +} diff --git a/lib/Drupal/r4032login/R4032LoginServiceProvider.php b/lib/Drupal/r4032login/R4032LoginServiceProvider.php new file mode 100644 index 0000000..f603237 --- /dev/null +++ b/lib/Drupal/r4032login/R4032LoginServiceProvider.php @@ -0,0 +1,28 @@ +has('access_subscriber')) { + // Override the AccessSubscriber service class. + $definition = $container->getDefinition('access_subscriber'); + $definition->setClass('Drupal\r4032login\EventSubscriber\R4032LoginAccessSubscriber'); + } + } +} diff --git a/r4032login.info b/r4032login.info deleted file mode 100644 index 3ef3b62..0000000 --- a/r4032login.info +++ /dev/null @@ -1,8 +0,0 @@ -name = Redirect 403 to User Login -description = Redirect anonymous users from 403 Access Denied pages to the /user/login page. -core = 7.x - -files[] = r4032login.install -files[] = r4032login.module - -configure = admin/config/system/site-information diff --git a/r4032login.info.yml b/r4032login.info.yml new file mode 100644 index 0000000..d1912d3 --- /dev/null +++ b/r4032login.info.yml @@ -0,0 +1,5 @@ +name: 'Redirect 403 to User Login' +type: module +description: 'Redirect anonymous users from 403 Access Denied pages to the /user/login page.' +core: 8.x +configure: system.admin_config_system diff --git a/r4032login.install b/r4032login.install index 940a74b..bd00e84 100644 --- a/r4032login.install +++ b/r4032login.install @@ -6,33 +6,15 @@ */ /** - * Implements hook_enable(). + * Implements hook_install(). */ -function r4032login_enable() { - variable_set('site_403', 'r4032login'); -} - -/** - * Implements hook_disable(). - */ -function r4032login_disable() { - variable_del('site_403'); +function r4032login_install() { + \Drupal::config('system.site')->set('page.403', 'r4032login')->save(); } /** * Implements hook_uninstall(). */ function r4032login_uninstall() { - variable_del('r4032login_display_denied_message'); - variable_del('r4032login_access_denied_message'); - variable_del('r4032login_user_login_path'); - variable_del('r4032login_default_redirect_code'); - variable_del('r4032login_match_noredirect_pages'); -} - -/** - * Drupal 7 has better handling for visiting the user registration page while logged in. - */ -function r4032login_update_7000() { - variable_del('r4032login_user_register_message'); + \Drupal::config('system.site')->clear('page.403')->save(); } diff --git a/r4032login.module b/r4032login.module index 497c85f..2fa4f7a 100644 --- a/r4032login.module +++ b/r4032login.module @@ -6,87 +6,101 @@ */ /** - * Implements hook_menu(). - */ -function r4032login_menu() { - $items = array(); - $items['r4032login'] = array( - 'page callback' => 'r4032login_redirect', - 'access callback' => 'r4032login_access_callback', - 'type' => MENU_CALLBACK, - 'title' => 'Access denied', - 'description' => 'You are not authorized to access this page.', - ); - return $items; -} - -/** - * Without an access callback on the menu item for r4032login_redirect, - * the redirect will be 403 and just have the default access denied page anyway. - */ -function r4032login_access_callback() { - return TRUE; -} - -/** * Implements hook_form_FORM_ID_alter(). + * + * Alters the System module's site information settings form to add additional + * r4032login settings. + * + * @see r4032login_form_system_site_information_settings_form_submit() */ function r4032login_form_system_site_information_settings_alter(&$form, &$form_state, $form_id) { - $form['error_page']['r4032login_display_denied_message'] = array( + $config = \Drupal::config('r4032login.settings'); + + $form['error_page']['r4032login'] = array( + '#type' => 'details', + '#title' => t('Redirect 403 to User Login'), + '#description' => t('Redirect anonymous users from 403 Access Denied pages to the user/login page.'), + '#collapsible' => TRUE, + '#collapsed' => FALSE, + ); + $form['error_page']['r4032login']['r4032login_display_denied_message'] = array( '#type' => 'checkbox', '#weight' => 5, '#title' => t('Display access denied message on login page'), - '#default_value' => variable_get('r4032login_display_denied_message', TRUE), + '#description' => t('Displays an access denied message on the user login page.'), + '#default_value' => $config->get('display_denied_message'), ); - $form['error_page']['r4032login_access_denied_message'] = array( + $form['error_page']['r4032login']['r4032login_access_denied_message'] = array( '#type' => 'textarea', '#rows' => 1, '#weight' => 6, '#title' => t("User login 'access denied' message"), - '#default_value' => variable_get('r4032login_access_denied_message', t('Access denied. You must log in to view this page.')), + '#description' => t('The message text displayed to users who are denied access to the page.'), + '#default_value' => $config->get('access_denied_message'), '#states' => array( - 'invisible' => array('input[name="r4032login_display_denied_message"]' => array( - 'checked' => FALSE, - )), + 'invisible' => array( + 'input[name="r4032login_display_denied_message"]' => array('checked' => FALSE), + ), ), ); - $form['error_page']['r4032login_redirect_authenticated_users_to'] = array( + $form['error_page']['r4032login']['r4032login_redirect_authenticated_users_to'] = array( '#type' => 'textfield', '#weight' => 7, '#title' => t("Redirect authenticated users to"), '#description' => t('If an authenticated user tries to access a page they can not, redirect them to the given page. Use <front> for the front page, leave blank for a default access denied page.'), - '#default_value' => variable_get('r4032login_redirect_authenticated_users_to', ''), + '#default_value' => $config->get('redirect_authenticated_users_to'), ); - $form['error_page']['r4032login_user_login_path'] = array( + $form['error_page']['r4032login']['r4032login_user_login_path'] = array( '#type' => 'textfield', '#weight' => 8, '#title' => t("Path to user login form"), - '#description' => t('The path to the user login form. Omit the beginning slash, ie: user/login'), - '#default_value' => variable_get('r4032login_user_login_path', 'user/login'), + '#description' => t('The path to the user login form. Omit the beginning slash, ie: user/login.'), + '#default_value' => $config->get('user_login_path'), ); - $options = array('301' => '301 Moved Permanently', '302' => '302 Found'); - $form['error_page']['r4032login_default_redirect_code'] = array( + $options = array('302' => '302 Found', '301' => '301 Moved Permanently'); + $form['error_page']['r4032login']['r4032login_default_redirect_code'] = array( '#type' => 'select', '#weight' => 9, '#title' => t("HTTP redirect code"), - '#description' => t('The redirect code to send. 301 responses may be cached by browsers and proxies, so 302 is normally the correct choice.'), + '#description' => t('The redirect code to send by default. 301 responses may be cached by browsers and proxies, so 302 is normally the correct choice.'), '#options' => $options, - '#default_value' => variable_get('r4032login_default_redirect_code', 302), + '#default_value' => $config->get('default_redirect_code'), ); - $form['error_page']['matching_paths'] = array( - '#type' => 'fieldset', + $form['error_page']['r4032login']['matching_paths'] = array( + '#type' => 'details', '#title' => t('Skip redirect for matching pages'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#weight' => 10, ); - $form['error_page']['matching_paths']['r4032login_match_noredirect_pages'] = array( + $form['error_page']['r4032login']['matching_paths']['r4032login_match_noredirect_pages'] = array( '#type' => 'textarea', '#title' => '' . t('Only the listed pages') . '', - '#default_value' => variable_get('r4032login_match_noredirect_pages', ''), - '#description' => t('Instead of redirecting, the user will get an access deined response and see the standard login form. This may be useful when the response code is important - such as for removing outdated content from search engines. Use the path node/* for all content.') . ' ' . t("Specify pages by using their paths. Enter one path per line. The '*' character is a wildcard. Example paths are %blog for the blog page and %blog-wildcard for every personal blog. %front is the front page.", array('%blog' => 'blog', '%blog-wildcard' => 'blog/*', '%front' => '')), + '#default_value' => $config->get('match_noredirect_pages'), + '#description' => t('Instead of redirecting, the user will get an access defined response and see the standard login form. This may be useful when the response code is important - such as for removing outdated content from search engines. Use the path node/* for all content.') . ' ' . t("Specify pages by using their paths. Enter one path per line. The '*' character is a wildcard. Example paths are %blog for the blog page and %blog-wildcard for every personal blog. %front is the front page.", array( + '%blog' => 'blog', + '%blog-wildcard' => + 'blog/*', + '%front' => '', + )), ); - return system_settings_form($form); + $form['#submit'][] = 'r4032login_form_system_site_information_settings_form_submit'; +} + +/** + * Form submission handler for system_site_information_settings(). + * + * @see r4032login_form_system_site_information_settings_alter() + */ +function r4032login_form_system_site_information_settings_form_submit($form, &$form_state) { + \Drupal::config('r4032login.settings') + ->set('display_denied_message', $form_state['values']['r4032login_display_denied_message']) + ->set('access_denied_message', $form_state['values']['r4032login_access_denied_message']) + ->set('redirect_authenticated_users_to', $form_state['values']['r4032login_redirect_authenticated_users_to']) + ->set('user_login_path', $form_state['values']['r4032login_user_login_path']) + ->set('default_redirect_code', $form_state['values']['r4032login_default_redirect_code']) + ->set('match_noredirect_pages', $form_state['values']['r4032login_match_noredirect_pages']) + ->save(); } /** @@ -99,73 +113,3 @@ function r4032login_theme() { ), ); } - -/** - * MENU_CALLBACK for /r4032login - * - * Redirect anonymous users from 403 Access Denied pages to the /user/login page - * with a message explaining that they must log in to view the requested page - * and a query string parameter appended to the url to return after login. - */ -function r4032login_redirect() { - global $user, $language; - if (user_is_anonymous()) { - // Show the access denied message. - if (variable_get('r4032login_display_denied_message', TRUE) && empty($_POST)) { - $message = variable_get('r4032login_access_denied_message', t('Access denied. You must log in to view this page.')); - drupal_set_message($message, 'error'); - } - $page_match = FALSE; - $pages = variable_get('r4032login_match_noredirect_pages', ''); - if ($pages) { - // When on an access denied page, Drupal stores the original path in - // $_GET['destination'] in drupal_deliver_html_page(). - // Convert the Drupal path to lowercase. - $path = drupal_strtolower(drupal_get_path_alias($_GET['destination'])); - // Compare the lowercase internal and lowercase path alias (if any). - $page_match = drupal_match_path($path, $pages); - if ($path != $_GET['destination']) { - $page_match = $page_match || drupal_match_path($_GET['destination'], $pages); - } - } - if ($page_match) { - // Display the default login page. - return drupal_get_form('user_login'); - } - // Handle redirection to the login form. - // using drupal_goto() with destination set causes a recursive redirect loop - $login_path = variable_get('r4032login_user_login_path', 'user/login'); - $code = variable_get('r4032login_default_redirect_code', 302); - // The code in drupal_get_destination() doesn't preserve any query string - // on 403 pages, so reproduce the part we want here. - $path = $_GET['destination']; - $query = drupal_http_build_query(drupal_get_query_parameters(NULL, array('q', 'destination'))); - if ($query != '') { - $path .= '?' . $query; - } - $destination = array('destination' => $path); - header('Location: ' . url($login_path, array('query' => $destination, 'absolute' => TRUE)), TRUE, $code); - drupal_exit(); - } - else { - // Check to see if we are to redirect the user. - $redirect = variable_get('r4032login_redirect_authenticated_users_to', ''); - if (empty($redirect)) { - // Display the default access denied page. - return theme('r4032login_denied'); - } - else { - // Custom access denied page for logged in users. - header('Location: ' . url($redirect, array('absolute' => TRUE))); - drupal_exit(); - } - } -} - -/** - * Display themed Access denied page. - */ -function theme_r4032login_denied() { - drupal_set_title(t('Access denied')); - return '

' . t('You are not authorized to access this page.') . '

'; -} diff --git a/r4032login.services.yml b/r4032login.services.yml new file mode 100644 index 0000000..95ffb1d --- /dev/null +++ b/r4032login.services.yml @@ -0,0 +1,7 @@ +services: + r4032login.subscriber: + class: Drupal\r4032login\EventSubscriber\R4032LoginSubscriber + arguments: ['@state'] + tags: + - { name: event_subscriber } + - { name: needs_destruction } diff --git a/r4032login.variable.inc b/r4032login.variable.inc deleted file mode 100644 index 65159de..0000000 --- a/r4032login.variable.inc +++ /dev/null @@ -1,75 +0,0 @@ - 'boolean', - 'title' => t('Display access denied message on login page', array(), $options), - 'default' => 1, - 'description' => t('Displays an access denied message on the user login page.', array(), $options), - 'group' => 'r4032login', - ); - $variables['r4032login_access_denied_message'] = array( - 'type' => 'string', - 'title' => t("User login 'access denied' message", array(), $options), - 'default' => t('Access denied. You must log in to view this page.', array(), $options), - 'description' => t('The message text displayed to users who are denied access to the page.', array(), $options), - 'group' => 'r4032login', - ); - $variables['r4032login_redirect_authenticated_users_to'] = array( - 'type' => 'drupal_path', - 'title' => t('Redirect authenticated users to', array(), $options), - 'default' => '', - 'description' => t('If an authenticated user tries to access a page they can not, redirect them to the given page. Use <front> for the front page, leave blank for a default access denied page.', array(), $options), - 'group' => 'r4032login', - ); - $variables['r4032login_user_login_path'] = array( - 'type' => 'drupal_path', - 'title' => t('Path to user login form', array(), $options), - 'default' => 'user/login', - 'description' => t('The path to the user login form. Omit the beginning slash, ie: user/login', array(), $options), - 'group' => 'r4032login', - ); - $variables['r4032login_default_redirect_code'] = array( - 'type' => 'select', - 'title' => t("HTTP default redirect code"), - 'description' => t('The redirect code to send by default. 301 responses may be cached by browsers and proxies.'), - 'default_value' => 302, - 'group' => 'r4032login', - ); - $variables['r4032login_match_redirect_code'] = array( - 'type' => 'select', - 'title' => t("HTTP redirect code to use for matching pages"), - 'description' => t('The redirect code to send for paths that match. 301 responses may be cached by browsers and proxies.'), - 'default' => 301, - ); - $variables['r4032login_match_redirect_pages'] = array( - 'type' => 'drupal_path', - 'title' => t('Specify matching pages by using their paths.'), - 'default' => '', - 'description' => t("Specify pages by using their paths. Enter one path per line. The '*' character is a wildcard. Example paths are %blog for the blog page and %blog-wildcard for every personal blog. %front is the front page.", array('%blog' => 'blog', '%blog-wildcard' => 'blog/*', '%front' => '')), - 'group' => 'r4032login', - ); - return $variables; -} - -/** - * Implements hook_variable_group_info(). - */ -function r4032login_variable_group_info() { - $groups['r4032login'] = array( - 'title' => t('Redirect 403 to User Login'), - 'description' => t('Redirect anonymous users from 403 Access Denied pages to the user/login page.'), - 'access' => 'administer site configuration', - 'path' => array('admin/config/system/site-information'), - ); - - return $groups; -}