Passed
Push — master ( 88c28f...afe883 )
by Maurício
07:32
created

TwoFactor::getAvailableBackends()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 8
c 0
b 0
f 0
dl 0
loc 13
rs 10
cc 4
nc 8
nop 0
1
<?php
2
/* vim: set expandtab sw=4 ts=4 sts=4: */
3
/**
4
 * Two authentication factor handling
5
 *
6
 * @package PhpMyAdmin
7
 */
8
declare(strict_types=1);
9
10
namespace PhpMyAdmin;
11
12
use PhpMyAdmin\Message;
13
use PhpMyAdmin\Plugins\TwoFactor\Application;
14
use PhpMyAdmin\Plugins\TwoFactor\Invalid;
15
use PhpMyAdmin\Plugins\TwoFactor\Key;
16
use PhpMyAdmin\Plugins\TwoFactorPlugin;
17
use PhpMyAdmin\UserPreferences;
18
use PragmaRX\Google2FAQRCode\Google2FA;
19
use Samyoul\U2F\U2FServer\U2FServer;
20
21
/**
22
 * Two factor authentication wrapper class
23
 *
24
 * @package PhpMyAdmin
25
 */
26
class TwoFactor
27
{
28
    /**
29
     * @var string
30
     */
31
    public $user;
32
33
    /**
34
     * @var array
35
     */
36
    public $config;
37
38
    /**
39
     * @var boolean
40
     */
41
    protected $_writable;
42
43
    /**
44
     * @var TwoFactorPlugin
45
     */
46
    protected $_backend;
47
48
    /**
49
     * @var array
50
     */
51
    protected $_available;
52
53
    /**
54
     * @var UserPreferences
55
     */
56
    private $userPreferences;
57
58
    /**
59
     * Creates new TwoFactor object
60
     *
61
     * @param string $user User name
62
     */
63
    public function __construct($user)
64
    {
65
        $this->userPreferences = new UserPreferences();
66
        $this->user = $user;
67
        $this->_available = $this->getAvailableBackends();
68
        $this->config = $this->readConfig();
69
        $this->_writable = ($this->config['type'] == 'db');
70
        $this->_backend = $this->getBackendForCurrentUser();
71
    }
72
73
    /**
74
     * Reads the configuration
75
     *
76
     * @return array
77
     */
78
    public function readConfig()
79
    {
80
        $result = [];
81
        $config = $this->userPreferences->load();
82
        if (isset($config['config_data']['2fa'])) {
83
            $result = $config['config_data']['2fa'];
84
        }
85
        $result['type'] = $config['type'];
86
        if (! isset($result['backend'])) {
87
            $result['backend'] = '';
88
        }
89
        if (! isset($result['settings'])) {
90
            $result['settings'] = [];
91
        }
92
        return $result;
93
    }
94
95
    /**
96
     * @return bool
97
     */
98
    public function isWritable(): bool
99
    {
100
        return $this->_writable;
101
    }
102
103
    /**
104
     * @return TwoFactorPlugin
105
     */
106
    public function getBackend(): TwoFactorPlugin
107
    {
108
        return $this->_backend;
109
    }
110
111
    /**
112
     * @return array
113
     */
114
    public function getAvailable(): array
115
    {
116
        return $this->_available;
117
    }
118
119
    /**
120
     * @return bool
121
     */
122
    public function showSubmit(): bool
123
    {
124
        $backend = $this->_backend;
125
        return $backend::$showSubmit;
126
    }
127
128
    /**
129
     * Returns list of available backends
130
     *
131
     * @return array
132
     */
133
    public function getAvailableBackends()
134
    {
135
        $result = [];
136
        if ($GLOBALS['cfg']['DBG']['simple2fa']) {
137
            $result[] = 'simple';
138
        }
139
        if (class_exists(Google2FA::class)) {
140
            $result[] = 'application';
141
        }
142
        if (class_exists(U2FServer::class)) {
143
            $result[] = 'key';
144
        }
145
        return $result;
146
    }
147
148
    /**
149
     * Returns list of missing dependencies
150
     *
151
     * @return array
152
     */
153
    public function getMissingDeps()
154
    {
155
        $result = [];
156
        if (! class_exists(Google2FA::class)) {
157
            $result[] = [
158
                'class' => Application::getName(),
159
                'dep' => 'pragmarx/google2fa-qrcode',
160
            ];
161
        }
162
        if (! class_exists('BaconQrCode\Renderer\Image\Png')) {
163
            $result[] = [
164
                'class' => Application::getName(),
165
                'dep' => 'bacon/bacon-qr-code',
166
            ];
167
        }
168
        if (! class_exists(U2FServer::class)) {
169
            $result[] = [
170
                'class' => Key::getName(),
171
                'dep' => 'samyoul/u2f-php-server',
172
            ];
173
        }
174
        return $result;
175
    }
176
177
    /**
178
     * Returns class name for given name
179
     *
180
     * @param string $name Backend name
181
     *
182
     * @return string
183
     */
184
    public function getBackendClass($name)
185
    {
186
        $result = TwoFactorPlugin::class;
187
        if (in_array($name, $this->_available)) {
188
            $result = 'PhpMyAdmin\\Plugins\\TwoFactor\\' . ucfirst($name);
189
        } elseif (! empty($name)) {
190
            $result = Invalid::class;
191
        }
192
        return $result;
193
    }
194
195
    /**
196
     * Returns backend for current user
197
     *
198
     * @return TwoFactorPlugin
199
     */
200
    public function getBackendForCurrentUser()
201
    {
202
        $name = $this->getBackendClass($this->config['backend']);
203
        return new $name($this);
204
    }
205
206
    /**
207
     * Checks authentication, returns true on success
208
     *
209
     * @param boolean $skip_session Skip session cache
210
     *
211
     * @return boolean
212
     */
213
    public function check($skip_session = false)
214
    {
215
        if ($skip_session) {
216
            return $this->_backend->check();
217
        }
218
        if (empty($_SESSION['two_factor_check'])) {
219
            $_SESSION['two_factor_check'] = $this->_backend->check();
220
        }
221
        return $_SESSION['two_factor_check'];
222
    }
223
224
    /**
225
     * Renders user interface to enter two-factor authentication
226
     *
227
     * @return string HTML code
228
     */
229
    public function render()
230
    {
231
        return $this->_backend->getError() . $this->_backend->render();
232
    }
233
234
    /**
235
     * Renders user interface to configure two-factor authentication
236
     *
237
     * @return string HTML code
238
     */
239
    public function setup()
240
    {
241
        return $this->_backend->getError() . $this->_backend->setup();
242
    }
243
244
    /**
245
     * Saves current configuration.
246
     *
247
     * @return true|Message
248
     */
249
    public function save()
250
    {
251
        return $this->userPreferences->persistOption('2fa', $this->config, null);
252
    }
253
254
    /**
255
     * Changes two-factor authentication settings
256
     *
257
     * The object might stay in partialy changed setup
258
     * if configuration fails.
259
     *
260
     * @param string $name Backend name
261
     *
262
     * @return boolean
263
     */
264
    public function configure($name)
265
    {
266
        $this->config = [
267
            'backend' => $name,
268
        ];
269
        if ($name === '') {
270
            $cls = $this->getBackendClass($name);
271
            $this->config['settings'] = [];
272
            $this->_backend = new $cls($this);
273
        } else {
274
            if (! in_array($name, $this->_available)) {
275
                return false;
276
            }
277
            $cls = $this->getBackendClass($name);
278
            $this->config['settings'] = [];
279
            $this->_backend = new $cls($this);
280
            if (! $this->_backend->configure()) {
281
                return false;
282
            }
283
        }
284
        $result = $this->save();
285
        if ($result !== true) {
286
            $result->display();
287
        }
288
        return true;
289
    }
290
291
    /**
292
     * Returns array with all available backends
293
     *
294
     * @return array
295
     */
296
    public function getAllBackends()
297
    {
298
        $all = array_merge([''], $this->_available);
299
        $backends = [];
300
        foreach ($all as $name) {
301
            $cls = $this->getBackendClass($name);
302
            $backends[] = [
303
                'id' => $cls::$id,
0 ignored issues
show
Bug introduced by
The property id does not exist on string.
Loading history...
304
                'name' => $cls::getName(),
305
                'description' => $cls::getDescription(),
306
            ];
307
        }
308
        return $backends;
309
    }
310
}
311