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
30define('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
46class 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 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 $_driver_options;
75
76    /**
77     * @var string 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 string 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 string 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 string 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(
139        $cas_parent, $dsn_or_pdo, $username='', $password='', $table='',
140        $driver_options=null
141    ) {
142        phpCAS::traceBegin();
143        // call the ancestor's constructor
144        parent::__construct($cas_parent);
145
146        // set default values
147        if ( empty($table) ) {
148            $table = CAS_PGT_STORAGE_DB_DEFAULT_TABLE;
149        }
150        if ( !is_array($driver_options) ) {
151            $driver_options = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION);
152        }
153
154        // store the specified parameters
155        if ($dsn_or_pdo instanceof PDO) {
156            $this->_pdo = $dsn_or_pdo;
157        } else {
158            $this->_dsn = $dsn_or_pdo;
159            $this->_username = $username;
160            $this->_password = $password;
161            $this->_driver_options = $driver_options;
162        }
163
164        // store the table name
165        $this->_table = $table;
166
167        phpCAS::traceEnd();
168    }
169
170    // ########################################################################
171    //  INITIALIZATION
172    // ########################################################################
173
174    /**
175     * This method is used to initialize the storage. Halts on error.
176     *
177     * @return void
178     */
179    public function init()
180    {
181        phpCAS::traceBegin();
182        // if the storage has already been initialized, return immediatly
183        if ($this->isInitialized()) {
184            return;
185        }
186
187        // initialize the base object
188        parent::init();
189
190        // create the PDO object if it doesn't exist already
191        if (!($this->_pdo instanceof PDO)) {
192            try {
193                $this->_pdo = new PDO(
194                    $this->_dsn, $this->_username, $this->_password,
195                    $this->_driver_options
196                );
197            }
198            catch(PDOException $e) {
199                phpCAS::error('Database connection error: ' . $e->getMessage());
200            }
201        }
202
203        phpCAS::traceEnd();
204    }
205
206    // ########################################################################
207    //  PDO database interaction
208    // ########################################################################
209
210    /**
211     * attribute that stores the previous error mode for the PDO handle while
212     * processing a transaction
213     */
214    private $_errMode;
215
216    /**
217     * This method will enable the Exception error mode on the PDO object
218     *
219     * @return void
220     */
221    private function _setErrorMode()
222    {
223        // get PDO object and enable exception error mode
224        $pdo = $this->_getPdo();
225        $this->_errMode = $pdo->getAttribute(PDO::ATTR_ERRMODE);
226        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
227    }
228
229    /**
230     * this method will reset the error mode on the PDO object
231     *
232     * @return void
233     */
234    private function _resetErrorMode()
235    {
236        // get PDO object and reset the error mode to what it was originally
237        $pdo = $this->_getPdo();
238        $pdo->setAttribute(PDO::ATTR_ERRMODE, $this->_errMode);
239    }
240
241    // ########################################################################
242    //  database queries
243    // ########################################################################
244    // these queries are potentially unsafe because the person using this library
245    // can set the table to use, but there is no reliable way to escape SQL
246    // fieldnames in PDO yet
247
248    /**
249     * This method returns the query used to create a pgt storage table
250     *
251     * @return string the create table SQL, no bind params in query
252     */
253    protected function createTableSql()
254    {
255        return 'CREATE TABLE ' . $this->_getTable()
256            . ' (pgt_iou VARCHAR(255) NOT NULL PRIMARY KEY, pgt VARCHAR(255) NOT NULL)';
257    }
258
259    /**
260     * This method returns the query used to store a pgt
261     *
262     * @return string the store PGT SQL, :pgt and :pgt_iou are the bind params contained
263     *         in the query
264     */
265    protected function storePgtSql()
266    {
267        return 'INSERT INTO ' . $this->_getTable()
268            . ' (pgt_iou, pgt) VALUES (:pgt_iou, :pgt)';
269    }
270
271    /**
272     * This method returns the query used to retrieve a pgt. the first column
273     * of the first row should contain the pgt
274     *
275     * @return string the retrieve PGT SQL, :pgt_iou is the only bind param contained
276     *         in the query
277     */
278    protected function retrievePgtSql()
279    {
280        return 'SELECT pgt FROM ' . $this->_getTable() . ' WHERE pgt_iou = :pgt_iou';
281    }
282
283    /**
284     * This method returns the query used to delete a pgt.
285     *
286     * @return string the delete PGT SQL, :pgt_iou is the only bind param contained in
287     *         the query
288     */
289    protected function deletePgtSql()
290    {
291        return 'DELETE FROM ' . $this->_getTable() . ' WHERE pgt_iou = :pgt_iou';
292    }
293
294    // ########################################################################
295    //  PGT I/O
296    // ########################################################################
297
298    /**
299     * This method creates the database table used to store pgt's and pgtiou's
300     *
301     * @return void
302     */
303    public function createTable()
304    {
305        phpCAS::traceBegin();
306
307        // initialize this PGTStorage object if it hasn't been initialized yet
308        if ( !$this->isInitialized() ) {
309            $this->init();
310        }
311
312        // initialize the PDO object for this method
313        $pdo = $this->_getPdo();
314        $this->_setErrorMode();
315
316        try {
317            $pdo->beginTransaction();
318
319            $query = $pdo->query($this->createTableSQL());
320            $query->closeCursor();
321
322            $pdo->commit();
323        }
324        catch(PDOException $e) {
325            // attempt rolling back the transaction before throwing a phpCAS error
326            try {
327                $pdo->rollBack();
328            }
329            catch(PDOException $e) {
330            }
331            phpCAS::error('error creating PGT storage table: ' . $e->getMessage());
332        }
333
334        // reset the PDO object
335        $this->_resetErrorMode();
336
337        phpCAS::traceEnd();
338    }
339
340    /**
341     * This method stores a PGT and its corresponding PGT Iou in the database.
342     * Echoes a warning on error.
343     *
344     * @param string $pgt     the PGT
345     * @param string $pgt_iou the PGT iou
346     *
347     * @return void
348     */
349    public function write($pgt, $pgt_iou)
350    {
351        phpCAS::traceBegin();
352
353        // initialize the PDO object for this method
354        $pdo = $this->_getPdo();
355        $this->_setErrorMode();
356
357        try {
358            $pdo->beginTransaction();
359
360            $query = $pdo->prepare($this->storePgtSql());
361            $query->bindValue(':pgt', $pgt, PDO::PARAM_STR);
362            $query->bindValue(':pgt_iou', $pgt_iou, PDO::PARAM_STR);
363            $query->execute();
364            $query->closeCursor();
365
366            $pdo->commit();
367        }
368        catch(PDOException $e) {
369            // attempt rolling back the transaction before throwing a phpCAS error
370            try {
371                $pdo->rollBack();
372            }
373            catch(PDOException $e) {
374            }
375            phpCAS::error('error writing PGT to database: ' . $e->getMessage());
376        }
377
378        // reset the PDO object
379        $this->_resetErrorMode();
380
381        phpCAS::traceEnd();
382    }
383
384    /**
385     * This method reads a PGT corresponding to a PGT Iou and deletes the
386     * corresponding db entry.
387     *
388     * @param string $pgt_iou the PGT iou
389     *
390     * @return string|false the corresponding PGT, or FALSE on error
391     */
392    public function read($pgt_iou)
393    {
394        phpCAS::traceBegin();
395        $pgt = false;
396
397        // initialize the PDO object for this method
398        $pdo = $this->_getPdo();
399        $this->_setErrorMode();
400
401        try {
402            $pdo->beginTransaction();
403
404            // fetch the pgt for the specified pgt_iou
405            $query = $pdo->prepare($this->retrievePgtSql());
406            $query->bindValue(':pgt_iou', $pgt_iou, PDO::PARAM_STR);
407            $query->execute();
408            $pgt = $query->fetchColumn(0);
409            $query->closeCursor();
410
411            // delete the specified pgt_iou from the database
412            $query = $pdo->prepare($this->deletePgtSql());
413            $query->bindValue(':pgt_iou', $pgt_iou, PDO::PARAM_STR);
414            $query->execute();
415            $query->closeCursor();
416
417            $pdo->commit();
418        }
419        catch(PDOException $e) {
420            // attempt rolling back the transaction before throwing a phpCAS error
421            try {
422                $pdo->rollBack();
423            }
424            catch(PDOException $e) {
425            }
426            phpCAS::trace('error reading PGT from database: ' . $e->getMessage());
427        }
428
429        // reset the PDO object
430        $this->_resetErrorMode();
431
432        phpCAS::traceEnd();
433        return $pgt;
434    }
435
436    /** @} */
437
438}
439
440?>
441