Upgrade phpCAS
[piwik-CASLogin.git] / CAS / CAS / PGTStorage / Db.php
1 <?php
2
3 /**
4  * Licensed to Jasig under one or more contributor license
5  * agreements. See the NOTICE file distributed with this work for
6  * additional information regarding copyright ownership.
7  *
8  * Jasig licenses this file to you under the Apache License,
9  * Version 2.0 (the "License"); you may not use this file except in
10  * compliance with the License. You may obtain a copy of the License at:
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  * PHP Version 5
21  *
22  * @file     CAS/PGTStorage/Db.php
23  * @category Authentication
24  * @package  PhpCAS
25  * @author   Daniel Frett <daniel.frett@gmail.com>
26  * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
27  * @link     https://wiki.jasig.org/display/CASC/phpCAS
28  */
29
30 define('CAS_PGT_STORAGE_DB_DEFAULT_TABLE', 'cas_pgts');
31
32 /**
33  * Basic class for PGT database storage
34  * The CAS_PGTStorage_Db class is a class for PGT database storage.
35  *
36  * @class    CAS_PGTStorage_Db
37  * @category Authentication
38  * @package  PhpCAS
39  * @author   Daniel Frett <daniel.frett@gmail.com>
40  * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
41  * @link     https://wiki.jasig.org/display/CASC/phpCAS
42  *
43  * @ingroup internalPGTStorageDb
44  */
45
46 class CAS_PGTStorage_Db extends CAS_PGTStorage_AbstractStorage
47 {
48     /**
49      * @addtogroup internalCAS_PGTStorageDb
50      * @{
51      */
52
53     /**
54      * the PDO object to use for database interactions
55      */
56     private $_pdo;
57
58     /**
59      * This method returns the PDO object to use for database interactions.
60      *
61      * @return the PDO object
62      */
63     private function _getPdo()
64     {
65         return $this->_pdo;
66     }
67
68     /**
69      * database connection options to use when creating a new PDO object
70      */
71     private $_dsn;
72     private $_username;
73     private $_password;
74     private $_table_options;
75
76     /**
77      * the table to use for storing/retrieving pgt's
78      */
79     private $_table;
80
81     /**
82      * This method returns the table to use when storing/retrieving PGT's
83      *
84      * @return the name of the pgt storage table.
85      */
86     private function _getTable()
87     {
88         return $this->_table;
89     }
90
91     // ########################################################################
92     //  DEBUGGING
93     // ########################################################################
94
95     /**
96      * This method returns an informational string giving the type of storage
97      * used by the object (used for debugging purposes).
98      *
99      * @return an informational string.
100      */
101     public function getStorageType()
102     {
103         return "db";
104     }
105
106     /**
107      * This method returns an informational string giving informations on the
108      * parameters of the storage.(used for debugging purposes).
109      *
110      * @return an informational string.
111      * @public
112      */
113     public function getStorageInfo()
114     {
115         return 'table=`'.$this->_getTable().'\'';
116     }
117
118     // ########################################################################
119     //  CONSTRUCTOR
120     // ########################################################################
121
122     /**
123      * The class constructor.
124      *
125      * @param CAS_Client $cas_parent     the CAS_Client instance that creates
126      * the object.
127      * @param string     $dsn_or_pdo     a dsn string to use for creating a PDO
128      * object or a PDO object
129      * @param string     $username       the username to use when connecting to
130      * the database
131      * @param string     $password       the password to use when connecting to
132      * the database
133      * @param string     $table          the table to use for storing and
134      * retrieving PGT's
135      * @param string     $driver_options any driver options to use when
136      * connecting to the database
137      */
138     public function __construct($cas_parent, $dsn_or_pdo, $username='', $password='', $table='', $driver_options=null)
139     {
140         phpCAS::traceBegin();
141         // call the ancestor's constructor
142         parent::__construct($cas_parent);
143
144         // set default values
145         if ( empty($table) ) {
146             $table = CAS_PGT_STORAGE_DB_DEFAULT_TABLE;
147         }
148         if ( !is_array($driver_options) ) {
149             $driver_options = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION);
150         }
151
152         // store the specified parameters
153         if ($dsn_or_pdo instanceof PDO) {
154             $this->_pdo = $dsn_or_pdo;
155         } else {
156             $this->_dsn = $dsn_or_pdo;
157             $this->_username = $username;
158             $this->_password = $password;
159             $this->_driver_options = $driver_options;
160         }
161
162         // store the table name
163         $this->_table = $table;
164
165         phpCAS::traceEnd();
166     }
167
168     // ########################################################################
169     //  INITIALIZATION
170     // ########################################################################
171
172     /**
173      * This method is used to initialize the storage. Halts on error.
174      *
175      * @return void
176      */
177     public function init()
178     {
179         phpCAS::traceBegin();
180         // if the storage has already been initialized, return immediatly
181         if ($this->isInitialized()) {
182             return;
183         }
184
185         // initialize the base object
186         parent::init();
187
188         // create the PDO object if it doesn't exist already
189         if (!($this->_pdo instanceof PDO)) {
190             try {
191                 $this->_pdo = new PDO($this->_dsn, $this->_username, $this->_password, $this->_driver_options);
192             }
193             catch(PDOException $e) {
194                 phpCAS::error('Database connection error: ' . $e->getMessage());
195             }
196         }
197
198         phpCAS::traceEnd();
199     }
200
201     // ########################################################################
202     //  PDO database interaction
203     // ########################################################################
204
205     /**
206      * attribute that stores the previous error mode for the PDO handle while
207      * processing a transaction
208      */
209     private $_errMode;
210
211     /**
212      * This method will enable the Exception error mode on the PDO object
213      *
214      * @return void
215      */
216     private function _setErrorMode()
217     {
218         // get PDO object and enable exception error mode
219         $pdo = $this->_getPdo();
220         $this->_errMode = $pdo->getAttribute(PDO::ATTR_ERRMODE);
221         $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
222     }
223
224     /**
225      * this method will reset the error mode on the PDO object
226      *
227      * @return void
228      */
229     private function _resetErrorMode()
230     {
231         // get PDO object and reset the error mode to what it was originally
232         $pdo = $this->_getPdo();
233         $pdo->setAttribute(PDO::ATTR_ERRMODE, $this->_errMode);
234     }
235
236     // ########################################################################
237     //  database queries
238     // ########################################################################
239     // these queries are potentially unsafe because the person using this library
240     // can set the table to use, but there is no reliable way to escape SQL
241     // fieldnames in PDO yet
242
243     /**
244      * This method returns the query used to create a pgt storage table
245      *
246      * @return the create table SQL, no bind params in query
247      */
248     protected function createTableSql()
249     {
250         return 'CREATE TABLE ' . $this->_getTable() . ' (pgt_iou VARCHAR(255) NOT NULL PRIMARY KEY, pgt VARCHAR(255) NOT NULL)';
251     }
252
253     /**
254      * This method returns the query used to store a pgt
255      *
256      * @return the store PGT SQL, :pgt and :pgt_iou are the bind params contained in the query
257      */
258     protected function storePgtSql()
259     {
260         return 'INSERT INTO ' . $this->_getTable() . ' (pgt_iou, pgt) VALUES (:pgt_iou, :pgt)';
261     }
262
263     /**
264      * This method returns the query used to retrieve a pgt. the first column of the first row should contain the pgt
265      *
266      * @return the retrieve PGT SQL, :pgt_iou is the only bind param contained in the query
267      */
268     protected function retrievePgtSql()
269     {
270         return 'SELECT pgt FROM ' . $this->_getTable() . ' WHERE pgt_iou = :pgt_iou';
271     }
272
273     /**
274      * This method returns the query used to delete a pgt.
275      *
276      * @return the delete PGT SQL, :pgt_iou is the only bind param contained in the query
277      */
278     protected function deletePgtSql()
279     {
280         return 'DELETE FROM ' . $this->_getTable() . ' WHERE pgt_iou = :pgt_iou';
281     }
282
283     // ########################################################################
284     //  PGT I/O
285     // ########################################################################
286
287     /**
288      * This method creates the database table used to store pgt's and pgtiou's
289      *
290      * @return void
291      */
292     public function createTable()
293     {
294         phpCAS::traceBegin();
295
296         // initialize this PGTStorage object if it hasn't been initialized yet
297         if ( !$this->isInitialized() ) {
298             $this->init();
299         }
300
301         // initialize the PDO object for this method
302         $pdo = $this->_getPdo();
303         $this->_setErrorMode();
304
305         try {
306             $pdo->beginTransaction();
307
308             $query = $pdo->query($this->createTableSQL());
309             $query->closeCursor();
310
311             $pdo->commit();
312         }
313         catch(PDOException $e) {
314             // attempt rolling back the transaction before throwing a phpCAS error
315             try {
316                 $pdo->rollBack();
317             }
318             catch(PDOException $e) {
319             }
320             phpCAS::error('error creating PGT storage table: ' . $e->getMessage());
321         }
322
323         // reset the PDO object
324         $this->_resetErrorMode();
325
326         phpCAS::traceEnd();
327     }
328
329     /**
330      * This method stores a PGT and its corresponding PGT Iou in the database.
331      * Echoes a warning on error.
332      *
333      * @param string $pgt     the PGT
334      * @param string $pgt_iou the PGT iou
335      *
336      * @return void
337      */
338     public function write($pgt, $pgt_iou)
339     {
340         phpCAS::traceBegin();
341
342         // initialize the PDO object for this method
343         $pdo = $this->_getPdo();
344         $this->_setErrorMode();
345
346         try {
347             $pdo->beginTransaction();
348
349             $query = $pdo->prepare($this->storePgtSql());
350             $query->bindValue(':pgt', $pgt, PDO::PARAM_STR);
351             $query->bindValue(':pgt_iou', $pgt_iou, PDO::PARAM_STR);
352             $query->execute();
353             $query->closeCursor();
354
355             $pdo->commit();
356         }
357         catch(PDOException $e) {
358             // attempt rolling back the transaction before throwing a phpCAS error
359             try {
360                 $pdo->rollBack();
361             }
362             catch(PDOException $e) {
363             }
364             phpCAS::error('error writing PGT to database: ' . $e->getMessage());
365         }
366
367         // reset the PDO object
368         $this->_resetErrorMode();
369
370         phpCAS::traceEnd();
371     }
372
373     /**
374      * This method reads a PGT corresponding to a PGT Iou and deletes the
375      * corresponding db entry.
376      *
377      * @param string $pgt_iou the PGT iou
378      *
379      * @return the corresponding PGT, or FALSE on error
380      */
381     public function read($pgt_iou)
382     {
383         phpCAS::traceBegin();
384         $pgt = false;
385
386         // initialize the PDO object for this method
387         $pdo = $this->_getPdo();
388         $this->_setErrorMode();
389
390         try {
391             $pdo->beginTransaction();
392
393             // fetch the pgt for the specified pgt_iou
394             $query = $pdo->prepare($this->retrievePgtSql());
395             $query->bindValue(':pgt_iou', $pgt_iou, PDO::PARAM_STR);
396             $query->execute();
397             $pgt = $query->fetchColumn(0);
398             $query->closeCursor();
399
400             // delete the specified pgt_iou from the database
401             $query = $pdo->prepare($this->deletePgtSql());
402             $query->bindValue(':pgt_iou', $pgt_iou, PDO::PARAM_STR);
403             $query->execute();
404             $query->closeCursor();
405
406             $pdo->commit();
407         }
408         catch(PDOException $e) {
409             // attempt rolling back the transaction before throwing a phpCAS error
410             try {
411                 $pdo->rollBack();
412             }
413             catch(PDOException $e) {
414             }
415             phpCAS::trace('error reading PGT from database: ' . $e->getMessage());
416         }
417
418         // reset the PDO object
419         $this->_resetErrorMode();
420
421         phpCAS::traceEnd();
422         return $pgt;
423     }
424
425     /** @} */
426
427 }
428
429 ?>