Completed
Push — master ( 4c378e...4e4c88 )
by Paul
02:27
created

backup_key::getRegistrations()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 9
Ratio 100 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 9
loc 9
rs 9.6666
cc 1
eloc 6
nc 1
nop 1
1
<?php
2
/**
3
 *
4
 * 2FA extension for the phpBB Forum Software package.
5
 *
6
 * @copyright (c) 2015 Paul Sohier
7
 * @license GNU General Public License, version 2 (GPL-2.0)
8
 *
9
 */
10
11
namespace paul999\tfa\modules;
12
13
14
use phpbb\db\driver\driver_interface;
15
use phpbb\passwords\manager;
16
use phpbb\request\request_interface;
17
use phpbb\template\template;
18
use phpbb\user;
19
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
20
21
class backup_key extends abstract_module
22
{
23
	/**
24
	 * @var \phpbb\request\request_interface
25
	 */
26
	private $request;
27
28
	/**
29
	 * @var string
30
	 */
31
	private $backup_registration_table;
32
33
	/**
34
	 * Number of keys that is generated
35
	 */
36
	const NUMBER_OF_KEYS = 6;
37
38
	/**
39
	 * @var \phpbb\passwords\manager
40
	 */
41
	private $password_manager;
42
43
	/**
44
	 * backup_key constructor.
45
	 *
46
	 * @param \phpbb\db\driver\driver_interface $db
47
	 * @param \phpbb\user                       $user
48
	 * @param \phpbb\request\request_interface  $request
49
	 * @param \phpbb\template\template          $template
50
	 * @param \phpbb\passwords\manager          $password_manager
51
	 * @param string                            $backup_registration_table
52
	 */
53 View Code Duplication
	public function __construct(driver_interface $db, user $user, request_interface $request, template $template, manager $password_manager, $backup_registration_table)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
54
	{
55
		$this->db = $db;
56
		$this->user = $user;
57
		$this->request = $request;
58
		$this->template = $template;
59
		$this->backup_registration_table = $backup_registration_table;
60
		$this->password_manager = $password_manager;
61
	}
62
63
	/**
64
	 * Get a language key for this specific module.
65
	 * @return string
66
	 */
67
	public function get_translatable_name()
68
	{
69
		return 'TFA_BACKUP_KEY';
70
	}
71
72
	/**
73
	 * Return the name of the current module
74
	 * This is for internal use only
75
	 * @return string
76
	 */
77
	public function get_name()
78
	{
79
		return 'backup_key';
80
	}
81
82
	/**
83
	 * Return if this module is enabled by the admin
84
	 * (And all server requirements are met).
85
	 *
86
	 * Do not return false in case a specific user disabled this module,
87
	 * OR if the user is unable to use this specific module,
88
	 * OR if a browser specific item is missing/incorrect.
89
	 * @return boolean
90
	 */
91
	public function is_enabled()
92
	{
93
		return true;
94
	}
95
96
	/**
97
	 * Check if the current user is able to use this module.
98
	 *
99
	 * This means that the user enabled it in the UCP,
100
	 * And has it setup up correctly.
101
	 * This method will be called during login, not during registration/
102
	 *
103
	 * @param int $user_id
104
	 *
105
	 * @return bool
106
	 */
107
	public function is_usable($user_id)
108
	{
109
		return $this->check_table_for_user($this->backup_registration_table, $user_id, ' AND valid = 1');
110
	}
111
112
	/**
113
	 * Check if the user can potentially use this.
114
	 * This method is called at registration page.
115
	 *
116
	 * You can, for example, check if the current browser is suitable.
117
	 *
118
	 * @param int|boolean $user_id Use false to ignore user
119
	 *
120
	 * @return bool
121
	 */
122
	public function is_potentially_usable($user_id = false)
123
	{
124
		return true;
125
	}
126
127
	/**
128
	 * Get the priority for this module.
129
	 * A lower priority means more chance it gets selected as default option
130
	 *
131
	 * There can be only one module with a specific priority!
132
	 * If there is already a module registered with this priority,
133
	 * a Exception might be thrown
134
	 *
135
	 * @return int
136
	 */
137
	public function get_priority()
138
	{
139
		return 1337; // We want the backup keys as priority as low as possible, because they are a backup.
140
	}
141
142
	/**
143
	 * Start of the login procedure.
144
	 *
145
	 * @param int $user_id
146
	 *
147
	 * @return array with data to be assign to the template.
148
	 */
149
	public function login_start($user_id)
150
	{
151
		return array(
152
			'S_TFA_INCLUDE_HTML'	=> '@paul999_tfa/tfa_backup_authenticate.html',
153
		);
154
	}
155
156
	/**
157
	 * Actual login procedure
158
	 *
159
	 * @param int $user_id
160
	 *
161
	 * @return boolean
162
	 */
163
	public function login($user_id)
164
	{
165
		$key = $this->request->variable('authenticate', '');
166
167
		if (empty($key))
168
		{
169
			throw new BadRequestHttpException($this->user->lang('TFA_NO_KEY_PROVIDED'));
170
		}
171
172
		foreach ($this->getRegistrations($user_id) as $registration)
173
		{
174
			if (!$registration['valid'] || $registration['last_used'])
175
			{
176
				continue;
177
			}
178 View Code Duplication
			if ($this->password_manager->check($key, $registration['secret']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
179
			{
180
				// We found a valid key.
181
				$sql_ary = array(
182
					'last_used' => time(),
183
					'valid'		=> false,
184
				);
185
				$sql = 'UPDATE ' . $this->backup_registration_table . ' 
186
							SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . ' 
187
							WHERE 
188
								registration_id = ' . (int) $registration['registration_id'];
189
				$this->db->sql_query($sql);
190
				return true;
191
			}
192
		}
193
		return false;
194
	}
195
196
	/**
197
	 * If this module can add new keys (Or other things)
198
	 *
199
	 * @return boolean
200
	 */
201
	public function can_register()
202
	{
203
		return !$this->check_table_for_user($this->backup_registration_table, $this->user->data['user_id'], ' AND valid = 1');
204
	}
205
206
	/**
207
	 * Start with the registration of a new security key.
208
	 * This page should return a name of a template, and
209
	 * it should assign the required variables for this template.
210
	 *
211
	 * @return string
212
	 */
213
	public function register_start()
214
	{
215
		$sql = [];
216
217
		for ($i = 0; $i < self::NUMBER_OF_KEYS; $i++)
218
		{
219
			$time = time();
220
			$key = bin2hex(random_bytes(6));
221
			$sql[] = array(
222
				'user_id' 		=> $this->user->data['user_id'],
223
				'valid'			=> true,
224
				'secret'		=> $this->password_manager->hash($key),
225
				'registered' 	=> $time,
226
			);
227
			$this->template->assign_block_vars('backup', [
228
				'KEY'	=> $key,
229
				'DATE'	=> $this->user->format_date($time),
230
			]);
231
		}
232
		$this->db->sql_multi_insert($this->backup_registration_table, $sql);
233
234
		return 'tfa_backup_ucp_new';
235
	}
236
237
	/**
238
	 * Do the actual registration of a new security key.
239
	 *
240
	 * @return boolean Result of the registration.
241
	 * @throws BadRequestHttpException
242
	 */
243
	public function register()
244
	{
245
		// We don't need to do anything here.
246
		return true;
247
	}
248
249
	/**
250
	 * This method is called to show the UCP page.
251
	 * You can assign template variables to the template, or do anything else here.
252
	 */
253
	public function show_ucp()
254
	{
255
		$this->show_ucp_complete($this->backup_registration_table);
256
	}
257
258
	/**
259
	 * Delete a specific row from the UCP.
260
	 * The data is based on the data provided in show_ucp.
261
	 *
262
	 * @param int $key
263
	 *
264
	 * @return void
265
	 */
266 View Code Duplication
	public function delete($key)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
267
	{
268
		$sql = 'DELETE FROM ' . $this->backup_registration_table . '
269
					WHERE user_id = ' . (int) $this->user->data['user_id'] . '
270
					AND registration_id =' . (int) $key;
271
272
		$this->db->sql_query($sql);
273
	}
274
275
	/**
276
	 * Select all registration objects from the database
277
	 * @param integer $user_id
278
	 * @return array
279
	 */
280 View Code Duplication
	private function getRegistrations($user_id)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
281
	{
282
		$sql = 'SELECT * FROM ' . $this->backup_registration_table . ' WHERE user_id = ' . (int) $user_id;
283
		$result = $this->db->sql_query($sql);
284
		$rows = $this->db->sql_fetchrowset($result);
285
286
		$this->db->sql_freeresult($result);
287
		return $rows;
288
	}
289
}
290