From ee91d179d364b5e1348946c9af33d79a84a01787 Mon Sep 17 00:00:00 2001
From: Jared Hancock <jared@osticket.com>
Date: Fri, 25 Apr 2014 09:42:18 -0500
Subject: [PATCH] Add some framework for external authentication

DISABLE_SESSION define is changed so that existing session are continued
but new sessions are not saved. This allows external auth backends to
redirect to an external site and that site redirect back to a `/api` URL and
the user's session will be continued.
---
 api/api.inc.php              |   2 -
 api/http.php                 |   2 +
 include/class.auth.php       | 104 ++++++++++++++++++++++++++++++++++-
 include/class.ostsession.php |   8 ++-
 4 files changed, 109 insertions(+), 7 deletions(-)

diff --git a/api/api.inc.php b/api/api.inc.php
index ecde89f9b..fac03bccd 100644
--- a/api/api.inc.php
+++ b/api/api.inc.php
@@ -17,8 +17,6 @@ file_exists('../main.inc.php') or die('System Error');
 
 // Disable sessions for the API. API should be considered stateless and
 // shouldn't chew up database records to store sessions
-if (!function_exists('noop')) { function noop() {} }
-session_set_save_handler('noop','noop','noop','noop','noop','noop');
 define('DISABLE_SESSION', true);
 
 require_once('../main.inc.php');
diff --git a/api/http.php b/api/http.php
index 90926d1e5..2efd1a98c 100644
--- a/api/http.php
+++ b/api/http.php
@@ -25,6 +25,8 @@ $dispatcher = patterns('',
          ))
         );
 
+Signal::send('api', $dispatcher);
+
 # Call the respective function
 print $dispatcher->resolve($ost->get_path_info());
 ?>
diff --git a/include/class.auth.php b/include/class.auth.php
index b28d0c987..e77fec723 100644
--- a/include/class.auth.php
+++ b/include/class.auth.php
@@ -308,8 +308,32 @@ abstract class AuthenticationBackend {
     abstract static function signOut($user);
 }
 
-class RemoteAuthenticationBackend {
-    var $create_unknown_user = false;
+/**
+ * ExternalAuthenticationBackend
+ *
+ * External authentication backends are backends such as Google+ which
+ * require a redirect to a remote site and a redirect back to osTicket in
+ * order for a  user to be authenticated. For such backends, neither the
+ * username and password fields nor single sign on alone can be used to
+ * authenticate the user.
+ */
+interface ExternalAuthentication {
+
+    /**
+     * Requests the backend to render an external link box. When the user
+     * clicks this box, the backend will be prompted to redirect the user to
+     * the remote site for authentication there.
+     */
+    function renderExternalLink();
+
+    /**
+     * Function: triggerAuth
+     *
+     * Called when a user clicks the button rendered in the
+     * ::renderExternalLink() function. This method should initiate the
+     * remote authentication mechanism.
+     */
+    function triggerAuth();
 }
 
 abstract class StaffAuthenticationBackend  extends AuthenticationBackend {
@@ -454,6 +478,50 @@ abstract class StaffAuthenticationBackend  extends AuthenticationBackend {
     }
 }
 
+abstract class ExternalStaffAuthenticationBackend
+        extends StaffAuthenticationBackend
+        implements ExternalAuthentication {
+
+    static $fa_icon = "signin";
+    static $sign_in_image_url = false;
+    static $service_name = "External";
+
+    function renderExternalLink() { ?>
+        <a class="external-sign-in" title="Sign in with <?php echo static::$service_name; ?>"
+                href="login.php?do=ext&amp;bk=<?php echo urlencode(static::$id); ?>">
+<?php if (static::$sign_in_image_url) { ?>
+        <img class="sign-in-image" src="<?php echo static::$sign_in_image_url;
+            ?>" alt="Sign in with <?php echo static::$service_name; ?>"/>
+<?php } else { ?>
+            <div class="external-auth-box">
+            <span class="external-auth-icon">
+                <i class="icon-<?php echo static::$fa_icon; ?> icon-large icon-fixed-with"></i>
+            </span>
+            <span class="external-auth-name">
+                Sign in with <?php echo static::$service_name; ?>
+            </span>
+            </div>
+<?php } ?>
+        </a><?php
+    }
+
+    function triggerAuth() {
+        $_SESSION['ext:bk:class'] = get_class($this);
+    }
+}
+Signal::connect('api', function($dispatcher) {
+    $dispatcher->append(
+        url('^/auth/ext$', function() {
+            if ($class = $_SESSION['ext:bk:class']) {
+                $bk = StaffAuthenticationBackend::getBackend($class::$id)
+                    ?: UserAuthenticationBackend::getBackend($class::$id);
+                if ($bk instanceof ExternalAuthentication)
+                    $bk->triggerAuth();
+            }
+        })
+    );
+});
+
 abstract class UserAuthenticationBackend  extends AuthenticationBackend {
 
     static private $_registry = array();
@@ -580,6 +648,38 @@ abstract class UserAuthenticationBackend  extends AuthenticationBackend {
     }
 }
 
+abstract class ExternalUserAuthenticationBackend
+        extends UserAuthenticationBackend
+        implements ExternalAuthentication {
+
+    static $fa_icon = "signin";
+    static $sign_in_image_url = false;
+    static $service_name = "External";
+
+    function renderExternalLink() { ?>
+        <a class="external-sign-in" title="Sign in with <?php echo static::$service_name; ?>"
+                href="login.php?do=ext&amp;bk=<?php echo urlencode(static::$id); ?>">
+<?php if (static::$sign_in_image_url) { ?>
+        <img class="sign-in-image" src="<?php echo static::$sign_in_image_url;
+            ?>" alt="Sign in with <?php echo static::$service_name; ?>"/>
+<?php } else { ?>
+            <div class="external-auth-box">
+            <span class="external-auth-icon">
+                <i class="icon-<?php echo static::$fa_icon; ?> icon-large icon-fixed-with"></i>
+            </span>
+            <span class="external-auth-name">
+                Sign in with <?php echo static::$service_name; ?>
+            </span>
+            </div>
+<?php } ?>
+        </a><?php
+    }
+
+    function triggerAuth() {
+        $_SESSION['ext:bk:class'] = get_class($this);
+    }
+}
+
 /**
  * This will be an exception in later versions of PHP
  */
diff --git a/include/class.ostsession.php b/include/class.ostsession.php
index beb344f9f..dc53af14e 100644
--- a/include/class.ostsession.php
+++ b/include/class.ostsession.php
@@ -35,9 +35,6 @@ class osTicketSession {
         if (OsticketConfig::getDBVersion())
             return session_start();
 
-        elseif (defined('DISABLE_SESSION'))
-            return;
-
         # Cookies
         // Avoid setting a cookie domain without a dot, thanks
         // http://stackoverflow.com/a/1188145
@@ -80,6 +77,7 @@ class osTicketSession {
     }
 
     function read($id){
+        $this->isnew = true;
         if (!$this->data || $this->id != $id) {
             $sql='SELECT session_data FROM '.SESSION_TABLE
                 .' WHERE session_id='.db_input($id)
@@ -91,6 +89,7 @@ class osTicketSession {
             $this->id = $id;
         }
         $this->data_hash = md5($id.$this->data);
+        $this->isnew = false;
         return $this->data;
     }
 
@@ -100,6 +99,9 @@ class osTicketSession {
         if (md5($id.$data) == $this->data_hash)
             return;
 
+        elseif (defined('DISABLE_SESSION') && $this->isnew)
+            return;
+
         $ttl = ($this && get_class($this) == 'osTicketSession')
             ? $this->getTTL() : SESSION_TTL;
 
-- 
GitLab