cilogon /
service-lib
| 1 | <?php |
||||||
| 2 | |||||||
| 3 | namespace CILogon\Service; |
||||||
| 4 | |||||||
| 5 | use CILogon\Service\DBProps; |
||||||
| 6 | use CILogon\Service\Util; |
||||||
| 7 | use DB; |
||||||
| 8 | |||||||
| 9 | /** |
||||||
| 10 | * SessionMgr |
||||||
| 11 | * |
||||||
| 12 | * This class is an implementation of a PHP Session |
||||||
| 13 | * handler using MySQL as the storage mechanism. There are |
||||||
| 14 | * several required functions implemented as documented at |
||||||
| 15 | * http://us3.php.net/manual/en/function.session-set-save-handler.php |
||||||
| 16 | * and http://us3.php.net/manual/en/class.sessionhandlerinterface.php. |
||||||
| 17 | * Implementation details were gleaned from several |
||||||
| 18 | * web pages, in particular: |
||||||
| 19 | * http://www.devshed.com/c/a/PHP/Storing-PHP-Sessions-in-a-Database/ |
||||||
| 20 | * Also, the PEAR HTTP_Session2 package inspired several |
||||||
| 21 | * tweaks, such as the crc check to prevent database |
||||||
| 22 | * writes when the session data had not changed. |
||||||
| 23 | * |
||||||
| 24 | * In order to use this class, you must first configure |
||||||
| 25 | * MySQL with correct privileges and a new table. |
||||||
| 26 | * |
||||||
| 27 | * # mysql -u root -p |
||||||
| 28 | * ### password is found in /var/www/config/cilogon.xml |
||||||
| 29 | * mysql> use oauth; |
||||||
| 30 | * mysql> GRANT ALL PRIVILEGES ON oauth.phpsessions |
||||||
| 31 | * -> TO 'cilogon'@'localhost' WITH GRANT OPTION; |
||||||
| 32 | * mysql> COMMIT; |
||||||
| 33 | * mysql> CREATE TABLE oauth.phpsessions ( |
||||||
| 34 | * -> id VARCHAR(32) NOT NULL, |
||||||
| 35 | * -> data BLOB NOT NULL, |
||||||
| 36 | * -> expires INTEGER NOT NULL, |
||||||
| 37 | * -> PRIMARY KEY (id) |
||||||
| 38 | * -> ) ENGINE=MyISAM; |
||||||
| 39 | * mysql> COMMIT; |
||||||
| 40 | * mysql> \q |
||||||
| 41 | * |
||||||
| 42 | * To use this class, simply create a new instance |
||||||
| 43 | * before the call to session_start(). |
||||||
| 44 | * |
||||||
| 45 | * require_once 'SessionMgr.php'; |
||||||
| 46 | * $sessionmgr = new SessionMgr(); |
||||||
| 47 | * session_start(); |
||||||
| 48 | * |
||||||
| 49 | * The session data is written to database upon script |
||||||
| 50 | * completion or when session_write_close() is called. |
||||||
| 51 | */ |
||||||
| 52 | class SessionMgr |
||||||
| 53 | { |
||||||
| 54 | /** |
||||||
| 55 | * @var DB|null $db A PEAR DB database connection object |
||||||
| 56 | */ |
||||||
| 57 | protected $db = null; |
||||||
| 58 | |||||||
| 59 | /** |
||||||
| 60 | * @var string|null $crc Session data cache id |
||||||
| 61 | */ |
||||||
| 62 | protected $crc = null; |
||||||
| 63 | |||||||
| 64 | /** |
||||||
| 65 | * __construct |
||||||
| 66 | * |
||||||
| 67 | * Default constructor. This method calls |
||||||
| 68 | * session_set_save_handler() with the methods in this class as |
||||||
| 69 | * parameters. |
||||||
| 70 | */ |
||||||
| 71 | public function __construct() |
||||||
| 72 | { |
||||||
| 73 | session_set_save_handler( |
||||||
| 74 | array(&$this, 'open'), |
||||||
| 75 | array(&$this, 'close'), |
||||||
| 76 | array(&$this, 'read'), |
||||||
| 77 | array(&$this, 'write'), |
||||||
| 78 | array(&$this, 'destroy'), |
||||||
| 79 | array(&$this, 'gc') |
||||||
| 80 | ); |
||||||
| 81 | // The following prevents unexpected effects when using |
||||||
| 82 | // objects as save handlers |
||||||
| 83 | register_shutdown_function('session_write_close'); |
||||||
| 84 | } |
||||||
| 85 | |||||||
| 86 | /** |
||||||
| 87 | * open |
||||||
| 88 | * |
||||||
| 89 | * This method opens the database connection. |
||||||
| 90 | * |
||||||
| 91 | * @param string $save_path The path where PHP session files are to be |
||||||
| 92 | * saved. (Ignored in the MySQL case.) |
||||||
| 93 | * @param string $session_id The PHP session identifier. |
||||||
| 94 | * @return bool True if database connection opened successfully, |
||||||
| 95 | * false otherwise. |
||||||
| 96 | */ |
||||||
| 97 | public function open($save_path, $session_id) |
||||||
|
0 ignored issues
–
show
The parameter
$session_id is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. Loading history...
|
|||||||
| 98 | { |
||||||
| 99 | $retval = true; // Assume connect to database succeeded |
||||||
| 100 | |||||||
| 101 | $storetype = STORAGE_PHPSESSIONS; |
||||||
|
0 ignored issues
–
show
|
|||||||
| 102 | $dbprops = new DBProps($storetype); |
||||||
| 103 | $this->db = $dbprops->getDBConnect(); |
||||||
| 104 | |||||||
| 105 | if (is_null($this->db)) { |
||||||
| 106 | $retval = false; |
||||||
| 107 | } |
||||||
| 108 | return $retval; |
||||||
| 109 | } |
||||||
| 110 | |||||||
| 111 | /** |
||||||
| 112 | * close |
||||||
| 113 | * |
||||||
| 114 | * This method closes the database connection. |
||||||
| 115 | * |
||||||
| 116 | * @return bool True if database connection was closed successfully, |
||||||
| 117 | * false otherwise. |
||||||
| 118 | */ |
||||||
| 119 | public function close() |
||||||
| 120 | { |
||||||
| 121 | $retval = true; // Assume close database succeeded |
||||||
| 122 | |||||||
| 123 | if (is_null($this->db)) { // Can't close a null database |
||||||
| 124 | $retval = false; |
||||||
| 125 | } else { |
||||||
| 126 | $retval = $this->db->disconnect(); |
||||||
|
0 ignored issues
–
show
The method
disconnect() does not exist on DB. Did you maybe mean connect()?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. Loading history...
|
|||||||
| 127 | $this->db = null; |
||||||
| 128 | } |
||||||
| 129 | |||||||
| 130 | return $retval; |
||||||
| 131 | } |
||||||
| 132 | |||||||
| 133 | /** |
||||||
| 134 | * read |
||||||
| 135 | * |
||||||
| 136 | * This method reads the PHP session data from the database |
||||||
| 137 | * associated with the passed-in identifier. It calculates a cache |
||||||
| 138 | * string using crc32 (so we can check if session data has been |
||||||
| 139 | * updated). If there is a problem reading the data, empty string |
||||||
| 140 | * is returned. |
||||||
| 141 | * |
||||||
| 142 | * @param string $session_id The PHP session identifier. |
||||||
| 143 | * @return string The PHP session data associated with the identifier, |
||||||
| 144 | * or empty string on error. |
||||||
| 145 | */ |
||||||
| 146 | public function read($session_id) |
||||||
| 147 | { |
||||||
| 148 | $retval = ''; |
||||||
| 149 | |||||||
| 150 | if (!is_null($this->db)) { |
||||||
| 151 | $time = time(); |
||||||
| 152 | $quoteid = $this->db->quoteSmart($session_id); |
||||||
|
0 ignored issues
–
show
The method
quoteSmart() does not exist on DB.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. Loading history...
|
|||||||
| 153 | $query = "SELECT data FROM phpsessions " . |
||||||
| 154 | "WHERE id = $quoteid AND expires >= $time"; |
||||||
| 155 | $retval = $this->db->getOne($query); |
||||||
|
0 ignored issues
–
show
The method
getOne() does not exist on DB.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. Loading history...
|
|||||||
| 156 | if (DB::isError($retval)) { |
||||||
| 157 | $retval = ''; |
||||||
| 158 | } else { |
||||||
| 159 | $this->crc = strlen($retval) . crc32($retval); |
||||||
| 160 | } |
||||||
| 161 | } |
||||||
| 162 | settype($retval, 'string'); |
||||||
| 163 | return $retval; |
||||||
| 164 | } |
||||||
| 165 | |||||||
| 166 | /** |
||||||
| 167 | * write |
||||||
| 168 | * |
||||||
| 169 | * This method is called when the PHP session data should be |
||||||
| 170 | * written to the database (usually upon script completion, or when |
||||||
| 171 | * session_write_close() is called). It tries to be 'smart' by |
||||||
| 172 | * updating only the information that has changed, e.g. update |
||||||
| 173 | * just the expiration time if session data has not changed. |
||||||
| 174 | * |
||||||
| 175 | * @param string $session_id The PHP session identifier. |
||||||
| 176 | * @param string $session_data The PHP session data to be written. |
||||||
| 177 | * @return bool True upon successful write of data to the database, |
||||||
| 178 | * or false on error. |
||||||
| 179 | */ |
||||||
| 180 | public function write($session_id, $session_data) |
||||||
| 181 | { |
||||||
| 182 | $retval = true; // Assume write to database succeeded |
||||||
| 183 | |||||||
| 184 | if (is_null($this->db)) { // Can't write to a null database |
||||||
| 185 | $retval = false; |
||||||
| 186 | } else { |
||||||
| 187 | $time = time(); |
||||||
| 188 | $newtime = $time + get_cfg_var('session.gc_maxlifetime'); |
||||||
| 189 | $quoteid = $this->db->quoteSmart($session_id); |
||||||
| 190 | $query = ''; |
||||||
| 191 | if ( |
||||||
| 192 | (!is_null($this->crc)) && |
||||||
| 193 | ($this->crc === (strlen($session_data) . crc32($session_data))) |
||||||
| 194 | ) { |
||||||
| 195 | // $_SESSION hasn't been touched, so update the expires column |
||||||
| 196 | $query = "UPDATE phpsessions SET expires = $newtime " . |
||||||
| 197 | "WHERE id = $quoteid"; |
||||||
| 198 | } else { |
||||||
| 199 | // Check if the table row already exists |
||||||
| 200 | $query = "SELECT COUNT(id) FROM phpsessions " . |
||||||
| 201 | "WHERE id = $quoteid"; |
||||||
| 202 | $result = $this->db->getOne($query); |
||||||
| 203 | if (DB::isError($result)) { |
||||||
| 204 | $retval = false; |
||||||
| 205 | } else { |
||||||
| 206 | $quotedata = $this->db->quoteSmart($session_data); |
||||||
| 207 | if (intval($result) == 0) { |
||||||
| 208 | // Insert a new row into the table |
||||||
| 209 | $query = "INSERT INTO phpsessions (id,data,expires) " . |
||||||
| 210 | "VALUES($quoteid,$quotedata,$newtime)"; |
||||||
| 211 | } else { |
||||||
| 212 | // Update existing row with new data and expires |
||||||
| 213 | $query = "UPDATE phpsessions " . |
||||||
| 214 | "SET data = $quotedata, expires = $newtime " . |
||||||
| 215 | "WHERE id = $quoteid"; |
||||||
| 216 | } |
||||||
| 217 | } |
||||||
| 218 | } |
||||||
| 219 | |||||||
| 220 | if ($retval) { |
||||||
| 221 | $result = $this->db->query($query); |
||||||
|
0 ignored issues
–
show
The method
query() does not exist on DB.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. Loading history...
|
|||||||
| 222 | if (DB::isError($result)) { |
||||||
| 223 | $retval = false; |
||||||
| 224 | } |
||||||
| 225 | } |
||||||
| 226 | } |
||||||
| 227 | |||||||
| 228 | return $retval; |
||||||
| 229 | } |
||||||
| 230 | |||||||
| 231 | /** |
||||||
| 232 | * destroy |
||||||
| 233 | * |
||||||
| 234 | * This method deletes a session identifier from the database. |
||||||
| 235 | * |
||||||
| 236 | * @param string $session_id The PHP session identifier. |
||||||
| 237 | * @return bool True upon successful deletion of data from the |
||||||
| 238 | * database, or false on error. |
||||||
| 239 | */ |
||||||
| 240 | public function destroy($session_id) |
||||||
| 241 | { |
||||||
| 242 | $retval = true; // Assume delete session_id from database succeeded |
||||||
| 243 | |||||||
| 244 | if (is_null($this->db)) { // Can't delete from a null database |
||||||
| 245 | $retval = false; |
||||||
| 246 | } else { |
||||||
| 247 | $quoteid = $this->db->quoteSmart($session_id); |
||||||
| 248 | $query = "DELETE FROM phpsessions WHERE id = $quoteid"; |
||||||
| 249 | $result = $this->db->query($query); |
||||||
| 250 | if (DB::isError($result)) { |
||||||
| 251 | $retval = false; |
||||||
| 252 | } |
||||||
| 253 | } |
||||||
| 254 | |||||||
| 255 | return $retval; |
||||||
| 256 | } |
||||||
| 257 | |||||||
| 258 | /** |
||||||
| 259 | * gc |
||||||
| 260 | * |
||||||
| 261 | * This method is invoked internally by PHP periodically in order |
||||||
| 262 | * to purge old session data. It simply looks for rows where the |
||||||
| 263 | * 'expires' column is older than the current time (less 10 |
||||||
| 264 | * seconds to allow for multiple threads). |
||||||
| 265 | * |
||||||
| 266 | * @param int $maxlifetime The lifetime of the PHP session (ignored since |
||||||
| 267 | * the 'expires' column is set using the |
||||||
| 268 | * session.gc_maxlifetime value). |
||||||
| 269 | * @return bool True upon successful garbage collection run on the |
||||||
| 270 | * database, or false on error. |
||||||
| 271 | */ |
||||||
| 272 | public function gc($maxlifetime) |
||||||
|
0 ignored issues
–
show
The parameter
$maxlifetime is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. Loading history...
|
|||||||
| 273 | { |
||||||
| 274 | $retval = true; // Assume garbage collection succeeded |
||||||
| 275 | |||||||
| 276 | if (is_null($this->db)) { // Can't garbage collect on a null database |
||||||
| 277 | $retval = false; |
||||||
| 278 | } else { |
||||||
| 279 | $time = time() - 10; // Allow extra time for multi-threads |
||||||
| 280 | $query = "DELETE FROM phpsessions WHERE expires < $time"; |
||||||
| 281 | $result = $this->db->query($query); |
||||||
| 282 | if (DB::isError($result)) { |
||||||
| 283 | $retval = false; |
||||||
| 284 | } |
||||||
| 285 | } |
||||||
| 286 | |||||||
| 287 | return $retval; |
||||||
| 288 | } |
||||||
| 289 | } |
||||||
| 290 |
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.