Commit 28e928775d9a58b957666d6fb5f013e26ce034c1

Authored by Neil Blakey-Milner
1 parent d68677d7

Rework the login/session verification code to make it easier to

understand, and to make it more dynamic in terms of error conditions
later on.


git-svn-id: https://kt-dms.svn.sourceforge.net/svnroot/kt-dms/trunk@5175 c91229c3-7414-0410-bfa2-8a42b809f60b
lib/authentication/authenticationprovider.inc.php
@@ -87,4 +87,20 @@ class KTAuthenticationProvider extends KTStandardDispatcher { @@ -87,4 +87,20 @@ class KTAuthenticationProvider extends KTStandardDispatcher {
87 function do_performEditSourceProvider() { 87 function do_performEditSourceProvider() {
88 return $this->errorRedirectTo('viewsource', _kt("Provider does not support editing"), 'source_id=' . $_REQUEST['source_id']); 88 return $this->errorRedirectTo('viewsource', _kt("Provider does not support editing"), 'source_id=' . $_REQUEST['source_id']);
89 } 89 }
  90 +
  91 + /**
  92 + * Perform provider-specific on-logout activities
  93 + *
  94 + * @param User The user who has just logged in
  95 + */
  96 + function login($oUser) {
  97 + }
  98 +
  99 + /**
  100 + * Perform provider-specific on-logout activities
  101 + *
  102 + * @param User The user who is about to be logged out
  103 + */
  104 + function logout($oUser) {
  105 + }
90 } 106 }
lib/authentication/authenticationutil.inc.php
@@ -42,16 +42,26 @@ class KTAuthenticationUtil { @@ -42,16 +42,26 @@ class KTAuthenticationUtil {
42 return KTAuthenticationUtil::getAuthenticatorForSource($iSourceId); 42 return KTAuthenticationUtil::getAuthenticatorForSource($iSourceId);
43 } 43 }
44 44
  45 + function &getAuthenticationProviderForUser($oUser) {
  46 + $iSourceId = $oUser->getAuthenticationSourceId();
  47 + return KTAuthenticationUtil::getAuthenticationProviderForSource($iSourceId);
  48 + }
  49 +
45 function &getAuthenticatorForSource($oSource) { 50 function &getAuthenticatorForSource($oSource) {
  51 + $oProvider =& KTAuthenticationUtil::getAuthenticationProviderForSource($oSource);
  52 + return $oProvider->getAuthenticator($oSource);
  53 + }
  54 +
  55 + function &getAuthenticationProviderForSource($oSource) {
46 if ($oSource) { 56 if ($oSource) {
47 $oSource =& KTUtil::getObject('KTAuthenticationSource', $oSource); 57 $oSource =& KTUtil::getObject('KTAuthenticationSource', $oSource);
48 $sProvider = $oSource->getAuthenticationProvider(); 58 $sProvider = $oSource->getAuthenticationProvider();
49 $oRegistry =& KTAuthenticationProviderRegistry::getSingleton(); 59 $oRegistry =& KTAuthenticationProviderRegistry::getSingleton();
50 $oProvider =& $oRegistry->getAuthenticationProvider($sProvider); 60 $oProvider =& $oRegistry->getAuthenticationProvider($sProvider);
51 } else { 61 } else {
52 - $oProvider = new KTBuiltinAuthenticationProvider; 62 + $oProvider =& new KTBuiltinAuthenticationProvider;
53 } 63 }
54 - return $oProvider->getAuthenticator($oSource); 64 + return $oProvider;
55 } 65 }
56 66
57 function synchroniseGroupToSource($oGroup) { 67 function synchroniseGroupToSource($oGroup) {
lib/dispatcher.inc.php
@@ -197,19 +197,13 @@ class KTStandardDispatcher extends KTDispatcher { @@ -197,19 +197,13 @@ class KTStandardDispatcher extends KTDispatcher {
197 } 197 }
198 198
199 function loginRequired() { 199 function loginRequired() {
200 - $url = generateControllerUrl("login");  
201 - $redirect = urlencode($_SERVER['REQUEST_URI']);  
202 - if ((strlen($redirect) > 1)) {  
203 - $url = $url . "&redirect=" . $redirect;  
204 - }  
205 - redirect($url);  
206 - exit(0); 200 + checkSessionAndRedirect(true);
207 } 201 }
208 202
209 function dispatch () { 203 function dispatch () {
210 - $session = new Session();  
211 - $sessionStatus = $session->verify($bDownload);  
212 - if ($sessionStatus === false) { 204 + $this->session = new Session();
  205 + $sessionStatus = $this->session->verify();
  206 + if ($sessionStatus !== true) {
213 $this->loginRequired(); 207 $this->loginRequired();
214 } 208 }
215 209
lib/session/Session.inc
@@ -10,7 +10,6 @@ @@ -10,7 +10,6 @@
10 * it under the terms of the GNU General Public License as published by 10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; using version 2 of the License. 11 * the Free Software Foundation; using version 2 of the License.
12 * 12 *
13 - *  
14 * This program is distributed in the hope that it will be useful, 13 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
@@ -21,9 +20,8 @@ @@ -21,9 +20,8 @@
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * 21 *
23 * @version $Revision$ 22 * @version $Revision$
24 - * @author Michael Joseph <michael@jamwarehouse.com>, Jam Warehouse (Pty) Ltd, South Africa  
25 - * @package lib.session  
26 */ 23 */
  24 +
27 class Session { 25 class Session {
28 26
29 /** 27 /**
@@ -32,13 +30,14 @@ class Session { @@ -32,13 +30,14 @@ class Session {
32 * @param int the id of the user to create a session for 30 * @param int the id of the user to create a session for
33 * @return string the generated sessionID 31 * @return string the generated sessionID
34 */ 32 */
35 - function create($iUserID) { 33 + function create($oUser) {
  34 + $iUserId = $oUser->getId();
36 global $default; 35 global $default;
37 36
38 session_start(); 37 session_start();
39 38
40 // bind user id to session 39 // bind user id to session
41 - $_SESSION["userID"] = $iUserID; 40 + $_SESSION["userID"] = $iUserId;
42 $_SESSION["KTErrorMessage"] = array(); 41 $_SESSION["KTErrorMessage"] = array();
43 42
44 // use the PHP generated session id 43 // use the PHP generated session id
@@ -47,13 +46,12 @@ class Session { @@ -47,13 +46,12 @@ class Session {
47 // retrieve client ip 46 // retrieve client ip
48 $ip = $this->getClientIP(); 47 $ip = $this->getClientIP();
49 48
50 - $default->log->debug("Session::create() new session for $iUserID, from $ip, sessionID=$sessionID"); 49 + $default->log->debug("Session::create() new session for $iUserId, from $ip, sessionID=$sessionID");
51 50
52 // insert session information into db 51 // insert session information into db
53 - $sql = $default->db;  
54 $aParams = array( 52 $aParams = array(
55 'session_id' => $sessionID, 53 'session_id' => $sessionID,
56 - 'user_id' => $iUserID, 54 + 'user_id' => $iUserId,
57 'lastused' => date("Y-m-d H:i:s", time()), 55 'lastused' => date("Y-m-d H:i:s", time()),
58 'ip' => $ip, 56 'ip' => $ip,
59 ); 57 );
@@ -62,6 +60,10 @@ class Session { @@ -62,6 +60,10 @@ class Session {
62 if (PEAR::isError($result)) { 60 if (PEAR::isError($result)) {
63 die("Error creating session: " . $result->toString()); 61 die("Error creating session: " . $result->toString());
64 } 62 }
  63 +
  64 + $oProvider =& KTAuthenticationUtil::getAuthenticationProviderForUser($oUser);
  65 + $oProvider->login($oUser);
  66 +
65 return $sessionID; 67 return $sessionID;
66 } 68 }
67 69
@@ -106,85 +108,75 @@ class Session { @@ -106,85 +108,75 @@ class Session {
106 * @param boolean optional parameter set if we're downloading a file 108 * @param boolean optional parameter set if we're downloading a file
107 * @return int session verification status 109 * @return int session verification status
108 */ 110 */
109 - function verify($bDownload = false) {  
110 - global $default, $lang_sesstimeout, $lang_sessinuse, $lang_err_sess_notvalid; 111 + function verify() {
  112 + global $default;
  113 +
111 // this is a workaround for an SSL download bug with IE. 114 // this is a workaround for an SSL download bug with IE.
112 session_cache_limiter('none'); 115 session_cache_limiter('none');
113 session_start(); 116 session_start();
114 header("Cache-Control: must-revalidate"); 117 header("Cache-Control: must-revalidate");
115 header("Expires: " . gmdate("D, d M Y H:i:s", time() - 3600) . " GMT"); 118 header("Expires: " . gmdate("D, d M Y H:i:s", time() - 3600) . " GMT");
116 $sessionID = session_id(); 119 $sessionID = session_id();
117 - if (strlen($sessionID) > 0) {  
118 - // initialise return status  
119 - $sessionStatus = 0;  
120 -  
121 - // this should be an existing session, so check the db  
122 - $sql = $default->db;  
123 - $sql->query(array("SELECT * FROM $default->sessions_table WHERE session_id = ?", $sessionID));/*ok*/  
124 - $numrows = $sql->num_rows($sql);  
125 -  
126 - // FIXME: if there aren't more rows that the max sessions for this user  
127 - if ($numrows >= 1) {  
128 - $default->log->debug("Session::verify found session in db");  
129 - while($sql->next_record()) {  
130 - $iUserID = $sql->f("user_id");  
131 - $ip = $this->getClientIP();  
132 - // check that ip matches  
133 - if ($ip == trim($sql->f("ip"))) {  
134 - // now check if the timeout has been exceeded  
135 - $lastused = $sql->f("lastused");  
136 - $diff = time() - strtotime($lastused);  
137 - if($diff <= $default->sessionTimeout) {  
138 - // session has been verified, update status  
139 - $sessionStatus = 1;  
140 - // use userID to refresh user details and set on session  
141 -  
142 - // ??: will this change during a user session?  
143 - // only set the userID if its not in the array already  
144 - if (!$_SESSION["userID"]) {  
145 - $_SESSION["userID"] = $iUserID;  
146 - }  
147 -  
148 - // update last used timestamp  
149 - $aFV = array(  
150 - 'lastused' => getCurrentDateTime(),  
151 - );  
152 - $aWFV = array(  
153 - 'user_id' => $iUserID,  
154 - 'session_id' => $sessionID,  
155 - );  
156 - $res = DBUtil::whereUpdate($default->sessions_table, $aFV, $aWFV);  
157 - // add the array to the session  
158 - $_SESSION["sessionStatus"] = $sessionStatus;  
159 - } else {  
160 - // session timed out status  
161 - $sessionStatus = 2;  
162 - // destroy this session  
163 - $this->destroy();  
164 - $_SESSION["errorMessage"] = $lang_sesstimeout;  
165 - }  
166 - } else {  
167 - // session in use status  
168 - $sessionStatus = 3;  
169 - $_SESSION["errorMessage"] = $lang_sessinuse;  
170 - }  
171 - }  
172 - } else {  
173 - // the session doesn't exist in the db  
174 - $default->log->info("Session::verify sessionID=$sessionID, not in db");  
175 - $sessionStatus = false;  
176 - }  
177 - } else { 120 +
  121 + if (empty($sessionID)) {
178 $default->log->info("Session::verify session not in db"); 122 $default->log->info("Session::verify session not in db");
179 - // there is no session  
180 - $sessionStatus = false; 123 + return PEAR::raiseError('You need to login to access this page');
  124 + }
  125 +
  126 + // this should be an existing session, so check the db
  127 + $aRows = DBUtil::getResultArray(array("SELECT * FROM $default->sessions_table WHERE session_id = ?", $sessionID));
  128 + $numrows = count($aRows);
  129 +
  130 + // FIXME: if there aren't more rows that the max sessions for this user
  131 + if ($numrows < 1) {
  132 + // the session doesn't exist in the db
  133 + $default->log->info("Session::verify sessionID=$sessionID, not in db");
  134 + return PEAR::raiseError('You need to login to access this page');
  135 + return false;
181 } 136 }
182 -  
183 - // remove old sessions  
184 - Session::removeStaleSessions();  
185 137
186 - // return the status  
187 - return $sessionStatus; 138 + $default->log->debug("Session::verify found session in db");
  139 + $aRow = $aRows[0];
  140 + // foreach ($aRows as $aRow) {
  141 +
  142 + $iUserID = $aRow["user_id"];
  143 +
  144 + // check that ip matches
  145 + $ip = $this->getClientIP();
  146 + if ($ip != trim($aRow["ip"])) {
  147 + return PEAR::raiseError("You are coming from a different IP address than the session requires");
  148 + return false;
  149 + }
  150 +
  151 +
  152 + // now check if the timeout has been exceeded
  153 + $lastused = $aRow["lastused"];
  154 + $diff = time() - strtotime($lastused);
  155 + if($diff <= $default->sessionTimeout) {
  156 + // update last used timestamp
  157 + $aFV = array(
  158 + 'lastused' => getCurrentDateTime(),
  159 + );
  160 + $aWFV = array(
  161 + 'user_id' => $iUserID,
  162 + 'session_id' => $sessionID,
  163 + );
  164 + $res = DBUtil::whereUpdate($default->sessions_table, $aFV, $aWFV);
  165 + // add the array to the session
  166 + $_SESSION["sessionStatus"] = $sessionStatus;
  167 +
  168 + Session::removeStaleSessions();
  169 +
  170 + return true;
  171 + } else {
  172 + return PEAR::raiseError('Session timed out');
  173 + }
  174 +
  175 + // }
  176 +
  177 + Session::removeStaleSessions();
  178 +
  179 + return false;
188 } 180 }
189 181
190 /** 182 /**
lib/session/control.inc
@@ -126,37 +126,42 @@ function checkSessionAndRedirect($bRedirect, $bDownload = false) { @@ -126,37 +126,42 @@ function checkSessionAndRedirect($bRedirect, $bDownload = false) {
126 global $default; 126 global $default;
127 127
128 $session = new Session(); 128 $session = new Session();
129 - $sessionStatus = $session->verify($bDownload); 129 + $sessionStatus = $session->verify();
130 130
131 - if ($sessionStatus != 1) {  
132 - // verification failed  
133 - $default->log->debug("checkSession:: session check failed");  
134 - if ($bRedirect) {  
135 - // redirect to login with error message  
136 - if ($sessionStatus == 2) {  
137 - // session timed out  
138 - $url = generateControllerUrl("login", "errorMessage=" . urlencode("Session timed out"));  
139 - } else {  
140 - $url = generateControllerUrl("login");  
141 - }  
142 - $redirect = urlencode(KTUtil::addQueryStringSelf($_SERVER["QUERY_STRING"]));  
143 - if ((strlen($redirect) > 1)) {  
144 - $default->log->debug("checkSession:: redirect url=$redirect");  
145 - // this session verification failure represents either the first visit to  
146 - // the site OR a session timeout etc. (in which case we still want to bounce  
147 - // the user to the login page, and then back to whatever page they're on now)  
148 - $url = $url . "&redirect=" . $redirect;  
149 - }  
150 - $default->log->debug("checkSession:: about to redirect to $url");  
151 - redirect($url);  
152 - exit;  
153 - } else {  
154 - return false;  
155 - }  
156 - } else { 131 + if ($sessionStatus === true) {
157 $default->log->debug("checkSession:: returning true"); 132 $default->log->debug("checkSession:: returning true");
158 return true; 133 return true;
159 } 134 }
  135 +
  136 + // verification failed
  137 + $default->log->debug("checkSession:: session check failed");
  138 + if (empty($bRedirect)) {
  139 + return false;
  140 + }
  141 +
  142 + $sErrorMessage = "";
  143 + if (PEAR::isError($sessionStatus)) {
  144 + $sErrorMessage = $sessionStatus->getMessage();
  145 + }
  146 + // redirect to login with error message
  147 + if ($sErrorMessage) {
  148 + // session timed out
  149 + $url = generateControllerUrl("login", "errorMessage=" . urlencode($sErrorMessage));
  150 + } else {
  151 + $url = generateControllerUrl("login");
  152 + }
  153 +
  154 + $redirect = urlencode(KTUtil::addQueryStringSelf($_SERVER["QUERY_STRING"]));
  155 + if ((strlen($redirect) > 1)) {
  156 + $default->log->debug("checkSession:: redirect url=$redirect");
  157 + // this session verification failure represents either the first visit to
  158 + // the site OR a session timeout etc. (in which case we still want to bounce
  159 + // the user to the login page, and then back to whatever page they're on now)
  160 + $url = $url . "&redirect=" . $redirect;
  161 + }
  162 + $default->log->debug("checkSession:: about to redirect to $url");
  163 + redirect($url);
  164 + exit;
160 } 165 }
161 166
162 /** 167 /**
login.php
@@ -38,11 +38,11 @@ class LoginPageDispatcher extends KTDispatcher { @@ -38,11 +38,11 @@ class LoginPageDispatcher extends KTDispatcher {
38 38
39 function check() { 39 function check() {
40 // bounce out immediately. 40 // bounce out immediately.
41 - $session = new Session();  
42 - if ($session->verify() == 1) { // erk. neil - DOUBLE CHECK THIS PLEASE. 41 + $this->session = new Session();
  42 + if ($this->session->verify() == 1) { // erk. neil - DOUBLE CHECK THIS PLEASE.
43 exit(redirect(generateControllerLink('dashboard'))); 43 exit(redirect(generateControllerLink('dashboard')));
44 } else { 44 } else {
45 - $session->destroy(); // toast it - its probably a hostile session. 45 + $this->session->destroy(); // toast it - its probably a hostile session.
46 } 46 }
47 return true; 47 return true;
48 } 48 }
@@ -139,7 +139,7 @@ class LoginPageDispatcher extends KTDispatcher { @@ -139,7 +139,7 @@ class LoginPageDispatcher extends KTDispatcher {
139 } 139 }
140 140
141 $session = new Session(); 141 $session = new Session();
142 - $sessionID = $session->create($oUser->getId()); 142 + $sessionID = $session->create($oUser);
143 143
144 // DEPRECATED initialise page-level authorisation array 144 // DEPRECATED initialise page-level authorisation array
145 $_SESSION["pageAccess"] = NULL; 145 $_SESSION["pageAccess"] = NULL;
@@ -156,6 +156,9 @@ class LoginPageDispatcher extends KTDispatcher { @@ -156,6 +156,9 @@ class LoginPageDispatcher extends KTDispatcher {
156 } else { 156 } else {
157 $url = generateControllerUrl("dashboard"); 157 $url = generateControllerUrl("dashboard");
158 } 158 }
  159 + $oAuthenticator =& KTAuthenticationUtil::getAuthenticatorForUser($oUser);
  160 + $oAuthenticator->login($oUser);
  161 +
159 exit(redirect($url)); 162 exit(redirect($url));
160 } 163 }
161 } 164 }