Passed
Push — master ( 63c666...b91fac )
by Pieter van der
03:51
created

Tiqr_UserSecretStorage_Pdo::getUserSecret()   A

Complexity

Conditions 4
Paths 7

Size

Total Lines 22
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 5.2596

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 14
c 1
b 0
f 0
dl 0
loc 22
ccs 8
cts 14
cp 0.5714
rs 9.7998
cc 4
nc 7
nop 1
crap 5.2596
1
<?php
2
/**
3
 * This file is part of the tiqr project.
4
 * 
5
 * The tiqr project aims to provide an open implementation for 
6
 * authentication using mobile devices. It was initiated by 
7
 * SURFnet and developed by Egeniq.
8
 *
9
 * More information: http://www.tiqr.org
10
 *
11
 * @author Patrick Honing <[email protected]>
12
 * 
13
 * @package tiqr
14
 *
15
 * @license New BSD License - See LICENSE file for details.
16
 *
17
 * @copyright (C) 2010-2012 SURFnet BV
18
 * 
19
 * Create SQL table (MySQL):
20
 * CREATE TABLE `tiqrusersecret` (`userid` varchar(10) PRIMARY KEY, `secret` varchar(100))
21
 * 
22
 */
23
24
use Psr\Log\LoggerInterface;
25
26
/**
27
 * This user storage implementation implements a user secret storage using PDO.
28
 * It is usable for any database with a PDO driver
29
 * 
30
 * @author Patrick Honing <[email protected]>
31
 *
32
 * You can create separate tables for Tiqr_UserSecretStorage_Pdo and Tiqr_UserStorage_Pdo
33
 * You can also combine the two tables by adding a "secret" column to the user storage table
34
 * @see Tiqr_UserStorage_Pdo
35
 *
36
 * Mysql Create statement usersecret table
37
38
CREATE TABLE IF NOT EXISTS usersecret (
39
    id integer NOT NULL PRIMARY KEY AUTO_INCREMENT,
40
    userid varchar(30) NOT NULL UNIQUE,
41
    secret varchar(128),
42
);
43
44
 * @see Tiqr_UserSecretStorage::getSecretStorage()
45
 * @see Tiqr_UserSecretStorage_Interface
46
 *
47
 * Supported options:
48
 * path : Path to the directory where the user data is stored
49
 * Supported options:
50
 * table    : The name of the user table in the database. Optional. Defaults to "tiqrusersecret".
51
 * dsn      : The dsn, see the PDO interface documentation
52
 * username : The database username
53
 * password : The database password
54
 *
55
 */
56
57
class Tiqr_UserSecretStorage_Pdo extends Tiqr_UserSecretStorage_Abstract
58
{
59
    private $tableName;
60
61
    private $handle;
62
63
    /**
64
     * @param Tiqr_UserSecretStorage_Encryption_Interface $encryption
65
     * @param LoggerInterface $logger
66
     * @param PDO $handle
67
     */
68 11
    public function __construct(
69
        Tiqr_UserSecretStorage_Encryption_Interface $encryption,
70
        LoggerInterface $logger,
71
        PDO $handle,
72
        string $tableName,
73
        array $decryption = array()
74
    ) {
75 11
        parent::__construct($logger, $encryption, $decryption);
76
77
        // Set our own properties
78 11
        $this->handle = $handle;
79 11
        $this->tableName = $tableName;
80
    }
81
82
    /**
83
     * @see Tiqr_UserSecretStorage_Interface::userExists()
84
     *
85
     * Note: duplicate of Tiqr_UserStorage_Pdo::userExists()
86
     */
87 5
    public function userExists(string $userId): bool
88
    {
89
        try {
90 5
            $sth = $this->handle->prepare('SELECT userid FROM ' . $this->tableName . ' WHERE userid = ?');
91 5
            $sth->execute(array($userId));
92 5
            return (false !== $sth->fetchColumn());
93
        }
94
        catch (Exception $e) {
95
            $this->logger->error('PDO error checking user exists', array('exception'=>$e, 'userId'=>$userId));
96
            throw ReadWriteException::fromOriginalException($e);
97
        }
98
    }
99
100
    /**
101
     * Get the user's secret
102
     *
103
     * @param String $userId
104
     * @return string
105
     * @throws Exception
106
     */
107 3
    protected function getUserSecret(string $userId): string
108
    {
109
        try {
110 3
            $sth = $this->handle->prepare('SELECT secret FROM ' . $this->tableName . ' WHERE userid = ?');
111 3
            $sth->execute(array($userId));
112 3
            $res=$sth->fetchColumn();
113 3
            if ($res === false) {
114
                // No result
115
                $this->logger->error(sprintf('No result getting secret for user "%s"', $userId));
116 3
                throw new RuntimeException('User not found');
117
            }
118
        }
119
        catch (Exception $e) {
120
            $this->logger->error('PDO error getting user', array('exception' => $e, 'userId' => $userId));
121
            throw ReadWriteException::fromOriginalException($e);
122
        }
123
124 3
        if (!is_string($res)) {
125
            $this->logger->error(sprintf('No secret found for user "%s"', $userId));
126
            throw new RuntimeException('Secret not found');
127
        }
128 3
        return $res;
129
    }
130
131
    /**
132
     *
133
     * @throws Exception
134
     */
135 5
    protected function setUserSecret(string $userId, string $secret): void
136
    {
137
        // UserSecretStorage can be used in a separate table. In this case the table has its own userid column
138
        // This means that when a user has been created using in the UserStorage, it does not exists in the
139
        // UserSecretStorage so userExists will be false and we need to use an INSERT query.
140
141
        // It is also possible to use one table for both the UserStorage and the UserSecretStorage, in that case the
142
        // userid column is shared between the UserStorage and UserSecretStorage and the user must first have been created
143
        // in the UserStorage because:
144
        // - UserStorage_Pdo::create() no longer supports overwriting an existing user
145
        // - The INSERT will fail when displayname has a NOT NULL constraint
146
        try {
147 5
            if ($this->userExists($userId)) {
148 2
                $sth = $this->handle->prepare('UPDATE ' . $this->tableName . ' SET secret = ? WHERE userid = ?');
149
            } else {
150 3
                $sth = $this->handle->prepare('INSERT INTO ' . $this->tableName . ' (secret,userid) VALUES (?,?)');
151
            }
152 4
            $sth->execute(array($secret, $userId));
153
        }
154 2
        catch (Exception $e) {
155 2
            $this->logger->error(
156 2
                sprintf('Unable to persist user secret for user "%s" in user secret storage (PDO)', $userId),
157 2
                array('exception'=>$e)
158 2
            );
159 2
            throw ReadWriteException::fromOriginalException($e);
160
        }
161
    }
162
163
    /**
164
     * @see Tiqr_UserSecretStorage_Interface::healthCheck()
165
     */
166 2
    public function healthCheck(string &$statusMessage = ''): bool
167
    {
168
        // Check whether the table exists by reading a random row
169
        try {
170 2
            $sth = $this->handle->prepare('SELECT secret FROM '.$this->tableName.' LIMIT 1');
171 1
            $sth->execute();
172
        }
173 1
        catch (Exception $e) {
174 1
            $statusMessage = "UserSecretStorage_PDO error: " . $e->getMessage();
175 1
            return false;
176
        }
177
178 1
        return true;
179
    }
180
}
181