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 87 function do_performEditSourceProvider() {
88 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 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 50 function &getAuthenticatorForSource($oSource) {
  51 + $oProvider =& KTAuthenticationUtil::getAuthenticationProviderForSource($oSource);
  52 + return $oProvider->getAuthenticator($oSource);
  53 + }
  54 +
  55 + function &getAuthenticationProviderForSource($oSource) {
46 56 if ($oSource) {
47 57 $oSource =& KTUtil::getObject('KTAuthenticationSource', $oSource);
48 58 $sProvider = $oSource->getAuthenticationProvider();
49 59 $oRegistry =& KTAuthenticationProviderRegistry::getSingleton();
50 60 $oProvider =& $oRegistry->getAuthenticationProvider($sProvider);
51 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 67 function synchroniseGroupToSource($oGroup) {
... ...
lib/dispatcher.inc.php
... ... @@ -197,19 +197,13 @@ class KTStandardDispatcher extends KTDispatcher {
197 197 }
198 198  
199 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 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 207 $this->loginRequired();
214 208 }
215 209  
... ...
lib/session/Session.inc
... ... @@ -10,7 +10,6 @@
10 10 * it under the terms of the GNU General Public License as published by
11 11 * the Free Software Foundation; using version 2 of the License.
12 12 *
13   - *
14 13 * This program is distributed in the hope that it will be useful,
15 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
... ... @@ -21,9 +20,8 @@
21 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 21 *
23 22 * @version $Revision$
24   - * @author Michael Joseph <michael@jamwarehouse.com>, Jam Warehouse (Pty) Ltd, South Africa
25   - * @package lib.session
26 23 */
  24 +
27 25 class Session {
28 26  
29 27 /**
... ... @@ -32,13 +30,14 @@ class Session {
32 30 * @param int the id of the user to create a session for
33 31 * @return string the generated sessionID
34 32 */
35   - function create($iUserID) {
  33 + function create($oUser) {
  34 + $iUserId = $oUser->getId();
36 35 global $default;
37 36  
38 37 session_start();
39 38  
40 39 // bind user id to session
41   - $_SESSION["userID"] = $iUserID;
  40 + $_SESSION["userID"] = $iUserId;
42 41 $_SESSION["KTErrorMessage"] = array();
43 42  
44 43 // use the PHP generated session id
... ... @@ -47,13 +46,12 @@ class Session {
47 46 // retrieve client ip
48 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 51 // insert session information into db
53   - $sql = $default->db;
54 52 $aParams = array(
55 53 'session_id' => $sessionID,
56   - 'user_id' => $iUserID,
  54 + 'user_id' => $iUserId,
57 55 'lastused' => date("Y-m-d H:i:s", time()),
58 56 'ip' => $ip,
59 57 );
... ... @@ -62,6 +60,10 @@ class Session {
62 60 if (PEAR::isError($result)) {
63 61 die("Error creating session: " . $result->toString());
64 62 }
  63 +
  64 + $oProvider =& KTAuthenticationUtil::getAuthenticationProviderForUser($oUser);
  65 + $oProvider->login($oUser);
  66 +
65 67 return $sessionID;
66 68 }
67 69  
... ... @@ -106,85 +108,75 @@ class Session {
106 108 * @param boolean optional parameter set if we're downloading a file
107 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 114 // this is a workaround for an SSL download bug with IE.
112 115 session_cache_limiter('none');
113 116 session_start();
114 117 header("Cache-Control: must-revalidate");
115 118 header("Expires: " . gmdate("D, d M Y H:i:s", time() - 3600) . " GMT");
116 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 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 126 global $default;
127 127  
128 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 132 $default->log->debug("checkSession:: returning true");
158 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 38  
39 39 function check() {
40 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 43 exit(redirect(generateControllerLink('dashboard')));
44 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 47 return true;
48 48 }
... ... @@ -139,7 +139,7 @@ class LoginPageDispatcher extends KTDispatcher {
139 139 }
140 140  
141 141 $session = new Session();
142   - $sessionID = $session->create($oUser->getId());
  142 + $sessionID = $session->create($oUser);
143 143  
144 144 // DEPRECATED initialise page-level authorisation array
145 145 $_SESSION["pageAccess"] = NULL;
... ... @@ -156,6 +156,9 @@ class LoginPageDispatcher extends KTDispatcher {
156 156 } else {
157 157 $url = generateControllerUrl("dashboard");
158 158 }
  159 + $oAuthenticator =& KTAuthenticationUtil::getAuthenticatorForUser($oUser);
  160 + $oAuthenticator->login($oUser);
  161 +
159 162 exit(redirect($url));
160 163 }
161 164 }
... ...