From 1107b3f1b880c64755e9dfa4b1ffe621bc10d930 Mon Sep 17 00:00:00 2001
From: Dick Visser <dick.visser@geant.org>
Date: Thu, 22 Nov 2018 13:06:43 +0100
Subject: [PATCH] Show a custom message to unauthorised users

---
 modules/authorize/docs/authorize.md            | 12 ++++++++++++
 .../authorize/lib/Auth/Process/Authorize.php   | 18 ++++++++++++++++++
 modules/authorize/templates/authorize_403.php  |  5 +++++
 modules/authorize/templates/authorize_403.twig |  4 ++++
 modules/authorize/www/authorize_403.php        |  3 +++
 5 files changed, 42 insertions(+)

diff --git a/modules/authorize/docs/authorize.md b/modules/authorize/docs/authorize.md
index de8cb4dcf..0e6e29986 100644
--- a/modules/authorize/docs/authorize.md
+++ b/modules/authorize/docs/authorize.md
@@ -34,6 +34,12 @@ Turn regex pattern matching on or off for the attribute values defined. For back
 
 Note: This option needs to be boolean (TRUE/FALSE) else it will be considered an attribute matching rule.
 
+### Rejection message ###
+Optionally provide a localised, custom message to an unauthorised user. This can be used to provide tailored instructions on how to fix the authorisation issue, supply specific contact details, etc.
+
+It should be an array of key/value pairs, with the keys as the language code. You can use HTML in the message. See below for an example.
+
+
 ### Attribute Rules ###
 Each additional filter configuration option is considered an attribute matching rule. For each attribute, you can specify a string or array of strings to match. If one of those attributes match one of the rules (OR operator), the user is authorized/unauthorized (depending on the deny config option).
 
@@ -72,6 +78,8 @@ An alternate way of using this filter is to deny certain users. Or even use mult
 
 The regex pattern matching can be turned off, allowing for exact attribute matching rules. This can be helpful in cases where you know what the value should be. An example of this is with the memberOf attribute or using the ldap:AttributeAddUsersGroups filter with the group attribute.
 
+Additionally, some helpful instructions are shown.
+
 	'authproc.sp' => array(
 		60 => array(
 			'class' => 'authorize:Authorize',
@@ -79,5 +87,9 @@ The regex pattern matching can be turned off, allowing for exact attribute match
 			'group' =>  array(
 				'CN=SimpleSAML Students,CN=Users,DC=example,DC=edu',
 				'CN=All Teachers,OU=Staff,DC=example,DC=edu',
+			),
+			'reject_msg' => array(
+				'en' => 'This service is only available to students and teachers. Please contact <a href="mailto:support@example.edu">support</a>.',
+				'nl' => 'Deze dienst is alleen beschikbaar voor studenten en docenten. Neem contact op met <a href="mailto:support@example.edu">support</a>.',
 			)
 	)
diff --git a/modules/authorize/lib/Auth/Process/Authorize.php b/modules/authorize/lib/Auth/Process/Authorize.php
index 151bee965..020040765 100644
--- a/modules/authorize/lib/Auth/Process/Authorize.php
+++ b/modules/authorize/lib/Auth/Process/Authorize.php
@@ -26,6 +26,13 @@ class Authorize extends \SimpleSAML\Auth\ProcessingFilter
      */
     protected $regex = true;
 
+    /**
+     * Array of localised rejection messages
+     *
+     * @var array
+     */
+    protected $reject_msg = [];
+
     /**
      * Array of valid users. Each element is a regular expression. You should
      * user \ to escape special chars, like '.' etc.
@@ -60,6 +67,13 @@ class Authorize extends \SimpleSAML\Auth\ProcessingFilter
             unset($config['regex']);
         }
 
+        // Check for the reject_msg option, get it and remove it
+        // Must be array of languages
+        if (isset($config['reject_msg']) && is_array($config['reject_msg'])) {
+            $this->reject_msg = $config['reject_msg'];
+            unset($config['reject_msg']);
+        }
+
         foreach ($config as $attribute => $values) {
             if (is_string($values)) {
                 $values = [$values];
@@ -93,6 +107,10 @@ class Authorize extends \SimpleSAML\Auth\ProcessingFilter
         assert(array_key_exists('Attributes', $request));
 
         $attributes = &$request['Attributes'];
+        // Store the rejection message array in the $request
+        if(!empty($this->reject_msg)) {
+          $request['authprocAuthorize_reject_msg'] = $this->reject_msg;
+        }
 
         foreach ($this->valid_attribute_values as $name => $patterns) {
             if (array_key_exists($name, $attributes)) {
diff --git a/modules/authorize/templates/authorize_403.php b/modules/authorize/templates/authorize_403.php
index 43ab32033..d00b5b681 100644
--- a/modules/authorize/templates/authorize_403.php
+++ b/modules/authorize/templates/authorize_403.php
@@ -13,6 +13,11 @@
 $this->data['403_header'] = $this->t('{authorize:Authorize:403_header}');
 $this->data['403_text'] = $this->t('{authorize:Authorize:403_text}');
 
+if (array_key_exists('reject_msg', $this->data)) {
+  if(isset($this->data['reject_msg'][$this->getLanguage()])) {
+    $this->data['403_text'] = $this->data['reject_msg'][$this->getLanguage()];
+  }
+}
 $this->includeAtTemplateBase('includes/header.php');
 
 echo '<h1>'.$this->data['403_header'].'</h1>';
diff --git a/modules/authorize/templates/authorize_403.twig b/modules/authorize/templates/authorize_403.twig
index 3f6402e92..0f4a77d78 100644
--- a/modules/authorize/templates/authorize_403.twig
+++ b/modules/authorize/templates/authorize_403.twig
@@ -2,7 +2,11 @@
 
 {% block content %}
   <h1>{{ '{authorize:Authorize:403_header}'|trans }}</h1>
+{% if reject_msg[currentLanguage] is defined %}
+  <p>{{ reject_msg[currentLanguage]|raw }}</p>
+{% else %}
   <p>{{ '{authorize:Authorize:403_text}'|trans }}</p>
+{% endif %}
   {% if logoutURL is defined %}
   <p>
       <a href="{{ logoutURL|escape('html') }}">{{ '{status:logout}'|trans }}</a>
diff --git a/modules/authorize/www/authorize_403.php b/modules/authorize/www/authorize_403.php
index 4598a66c8..1b49e89e5 100644
--- a/modules/authorize/www/authorize_403.php
+++ b/modules/authorize/www/authorize_403.php
@@ -18,5 +18,8 @@ if (isset($state['Source']['auth'])) {
         ['as' => $state['Source']['auth']]
     )."&logout";
 }
+if (isset($state['authprocAuthorize_reject_msg'])) {
+  $t->data['reject_msg'] = $state['authprocAuthorize_reject_msg'];
+}
 header('HTTP/1.0 403 Forbidden');
 $t->show();
-- 
GitLab