Completed
Pull Request — master (#27)
by Paul
02:15
created

session_helper::is_tfa_required()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 28
rs 8.4444
c 0
b 0
f 0
cc 8
nc 8
nop 3
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\helper;
12
13
use paul999\tfa\exceptions\module_exception;
14
use paul999\tfa\modules\module_interface;
15
use phpbb\auth\auth;
16
use phpbb\config\config;
17
use phpbb\controller\helper;
18
use phpbb\db\driver\driver_interface;
19
use phpbb\di\service_collection;
20
use phpbb\template\template;
21
use phpbb\user;
22
23
/**
24
 * helper method which is used to detect if a user needs to use 2FA
25
 */
26
class session_helper implements session_helper_interface
27
{
28
	/**
29
	 * @var driver_interface
30
	 */
31
	private $db;
32
33
	/**
34
	 * @var config
35
	 */
36
	private $config;
37
38
	/**
39
	 * @var user
40
	 */
41
	private $user;
42
43
	/**
44
	 * @var array
45
	 */
46
	private $modules;
47
48
	private $module_data = [];
49
50
	/**
51
	 * @var string
52
	 */
53
	private $registration_table;
54
55
	/**
56
	 * @var string
57
	 */
58
	private $user_table;
59
60
	/**
61
	 * @var array
62
	 */
63
	private $user_array = [];
64
65
	/**
66
	 * @var \phpbb\template\template
67
	 */
68
	private $template;
69
70
	/**
71
	 * @var \phpbb\controller\helper
72
	 */
73
	private $controller_helper;
74
75
	/**
76
	 * Constructor
77
	 *
78
	 * @access public
79
	 *
80
	 * @param driver_interface         $db
81
	 * @param config                   $config
82
	 * @param user                     $user
83
	 * @param service_collection       $modules
84
	 * @param \phpbb\template\template $template
85
	 * @param \phpbb\controller\helper $controller_helper
86
	 * @param string                   $registration_table
87
	 * @param string                   $user_table
88
	 */
89 View Code Duplication
	public function __construct(driver_interface $db, config $config, user $user, service_collection $modules, template $template, helper $controller_helper, $registration_table, $user_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...
90
	{
91
		$this->db					= $db;
92
		$this->user					= $user;
93
		$this->config				= $config;
94
		$this->template 			= $template;
95
		$this->controller_helper 	= $controller_helper;
96
		$this->registration_table	= $registration_table;
97
		$this->user_table			= $user_table;
98
		$this->module_data			= $modules;
0 ignored issues
show
Documentation Bug introduced by
It seems like $modules of type object<phpbb\di\service_collection> is incompatible with the declared type array of property $module_data.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
99
100
101
	}
102
103
	/**
104
	 * Register the tagged modules if they are enabled.
105
	 */
106
	private function validate_modules()
107
	{
108
		/**
109
		 * @var module_interface $module
110
		 */
111
		foreach ($this->module_data as $module)
112
		{
113
			if ($module instanceof module_interface)
114
			{
115
				// Only add them if they are actually a module_interface.
116
				$priority = $module->get_priority();
117
				if (isset($this->modules[$module->get_priority()]))
118
				{
119
					throw new module_exception(400, 'TFA_DOUBLE_PRIORITY', array($priority, get_class($module), get_class($this->modules[$priority])));
120
				}
121
				if ($module->is_enabled())
122
				{
123
					$this->modules[$priority] = $module;
124
				}
125
			}
126
		}
127
	}
128
129
	/**
130
	 * @param $requested_module
131
	 * @return null|module_interface
132
	 */
133
	public function find_module($requested_module)
134
	{
135
		/**
136
		 * @var module_interface $module
137
		 */
138
		foreach ($this->get_modules() as $module)
139
		{
140
			if ($module->get_name() == $requested_module)
141
			{
142
				return $module;
143
			}
144
		}
145
		return null;
146
	}
147
148
	/**
149
	 * @return array
150
	 */
151
	public function get_modules()
152
	{
153
		if (empty($this->modules)) {
154
			$this->validate_modules();
155
		}
156
		return $this->modules;
157
	}
158
159
	/**
160
	 * @param int $user_id
161
	 * @param bool $admin
162
	 * @param array $userdata
163
	 * @return bool
164
	 */
165
	public function is_tfa_required($user_id, $admin = false, $userdata = array())
166
	{
167
		if (sizeof($this->get_modules()) == 0)
168
		{
169
			return false;
170
		}
171
		switch ($this->config['tfa_mode'])
172
		{
173
			case session_helper_interface::MODE_DISABLED:
174
				return false;
175
176
			case session_helper_interface::MODE_NOT_REQUIRED:
177
				return $this->is_tfa_registered($user_id);
178
179
			case session_helper_interface::MODE_REQUIRED_FOR_ACP_LOGIN:
180
			case session_helper_interface::MODE_REQUIRED_FOR_ADMIN:
181
				return $this->do_permission_check($user_id, $userdata, 'a_');
182
183
			case session_helper_interface::MODE_REQUIRED_FOR_MODERATOR:
184
				return $this->do_permission_check($user_id, $userdata, array('m_', 'a_'));
185
186
			case session_helper_interface::MODE_REQUIRED:
187
				return true;
188
189
			default:
190
				return false;
191
		}
192
	}
193
194
	/**
195
	 * Check if the user has two factor authentication added to his account.
196
	 *
197
	 * @param int $user_id
198
	 * @return bool
199
	 */
200
	public function is_tfa_registered($user_id)
201
	{
202
		if (isset($this->user_array[$user_id]))
203
		{
204
			return $this->user_array[$user_id];
205
		}
206
207
		$this->user_array[$user_id] = false; // Preset to false.
208
209
		/**
210
		 * @var int $priority
211
		 * @var module_interface $module
212
		 */
213
		foreach ($this->get_modules() as $priority => $module)
214
		{
215
			$this->user_array[$user_id] = $this->user_array[$user_id] || $module->is_usable($user_id);
216
		}
217
		return $this->user_array[$user_id];
218
	}
219
220
	/**
221
	 * Check if the user has any key registred, even if the module is not available.
222
	 *
223
	 * @param int $user_id
224
	 * @return bool
225
	 */
226
	public function is_tfa_key_registred($user_id)
227
	{
228
		/**
229
		 * @var int $priority
230
		 * @var module_interface $module
231
		 */
232
		foreach ($this->get_modules() as $priority => $module)
233
		{
234
			if ($module->key_registered($user_id))
235
			{
236
				return true;
237
			}
238
		}
239
		return false;
240
	}
241
242
243
	/**
244
	 * @param int $user_id
245
	 * @param bool $admin
246
	 * @param bool $auto_login
247
	 * @param bool $viewonline
248
	 * @param string $redirect
249
	 * @param bool $secure
250
	 * @throws \Exception
251
	 */
252
	public function generate_page($user_id, $admin, $auto_login, $viewonline, $redirect, $secure = false)
253
	{
254
		$this->user->add_lang_ext('paul999/tfa', 'common');
255
		$modules = $this->get_modules();
256
257
		/**
258
		 * @var module_interface $row
259
		 */
260
		foreach ($modules as $row)
261
		{
262
			if ($row->is_usable($user_id))
263
			{
264
				$this->template->assign_block_vars('tfa_options', array_merge(array(
265
					'ID'	=> $row->get_name(),
266
					'NAME'	=> $this->user->lang($row->get_translatable_name()),
267
					'U_SUBMIT_AUTH'	=> $this->controller_helper->route('paul999_tfa_read_controller_submit', array(
268
						'user_id'		=> (int) $user_id,
269
						'admin'			=> (int) $admin,
270
						'auto_login'	=> (int) $auto_login,
271
						'viewonline'	=> (int) $viewonline,
272
						'class'			=> $row->get_name(),
273
					)),
274
					'S_HIDDEN_FIELDS' => build_hidden_fields(['sid' => $this->user->session_id]),
275
				), $row->login_start($user_id)));
276
			}
277
		}
278
279
		add_form_key('tfa_login_page');
280
281
		$random = sha1(random_bytes(32));
282
283
		$sql_ary = array(
284
			'tfa_random' 	=> $random,
285
			'tfa_uid'		=> $user_id,
286
		);
287
		$sql = 'UPDATE ' . SESSIONS_TABLE . ' SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . "
288
			WHERE
289
				session_id = '" . $this->db->sql_escape($this->user->data['session_id']) . "' AND
290
				session_user_id = " . (int) $this->user->data['user_id'];
291
		$this->db->sql_query($sql);
292
293
		$this->template->assign_vars(array(
294
			'REDIRECT'		=> $redirect,
295
			'RANDOM'		=> $random,
296
			'RELOGIN_NOTE'	=> $secure,
297
		));
298
299
		page_header('TFA_KEY_REQUIRED');
300
301
		$this->template->set_filenames(array(
302
			'body' => '@paul999_tfa/authenticate_main.html'
303
		));
304
		page_footer(false); // Do not include cron on this page!
305
	}
306
307
	/**
308
	 * Return the userdata for a specific user.
309
	 *
310
	 * @param int $user_id
311
	 * @param array $userdata
312
	 * @return array
313
	 */
314
	private function user_data($user_id, $userdata = array())
315
	{
316
		if (empty($userdata))
317
		{
318
			$sql = 'SELECT * FROM ' . $this->user_table . ' WHERE user_id = ' . (int) $user_id;
319
			$result = $this->db->sql_query($sql);
320
			$userdata = $this->db->sql_fetchrow($result);
321
			$this->db->sql_freeresult($result);
322
		}
323
		return $userdata;
324
	}
325
326
	/**
327
	 * @param int $user_id
328
	 * @param array $userdata
329
	 * @param string|array $permission
330
	 * @return bool
331
	 */
332
	private function do_permission_check($user_id, $userdata, $permission)
333
	{
334
		if ($this->is_tfa_registered($user_id))
335
		{
336
			return true;
337
		}
338
		$userdata = $this->user_data($user_id, $userdata);
339
		$auth = new auth();
340
		$auth->acl($userdata);
341
342
		if (!is_array($permission))
343
		{
344
			$permission = array($permission);
345
		}
346
		foreach ($permission as $perm)
347
		{
348
			if ($auth->acl_get($perm))
349
			{
350
				return true;
351
			}
352
		}
353
		return false;
354
	}
355
}
356