listener   A
last analyzed

Complexity

Total Complexity 30

Size/Duplication

Total Lines 229
Duplicated Lines 4.8 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
wmc 30
lcom 1
cbo 1
dl 11
loc 229
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
B auth_login_session_create_before() 0 31 8
A __construct() 11 11 1
A getSubscribedEvents() 0 8 1
A add_permission() 0 6 1
D user_setup_after() 0 66 18
A generate_fatal_error() 0 20 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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\event;
12
13
use paul999\tfa\helper\session_helper_interface;
14
use phpbb\config\config;
15
use phpbb\db\driver\driver_interface;
16
use phpbb\event\data;
17
use phpbb\request\request_interface;
18
use phpbb\template\template;
19
use phpbb\user;
20
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
21
22
/**
23
 * Event listener
24
 */
25
class listener implements EventSubscriberInterface
26
{
27
	/**
28
	 * @var session_helper_interface
29
	 */
30
	private $session_helper;
31
32
	/**
33
	 * @var user
34
	 */
35
	private $user;
36
37
	/**
38
	 * @var request_interface
39
	 */
40
	private $request;
41
42
	/**
43
	 * @var driver_interface
44
	 */
45
	private $db;
46
47
	/**
48
	 * @var template
49
	 */
50
	private $template;
51
52
	/**
53
	 * @var config
54
	 */
55
	private $config;
56
57
	/**
58
	 * @var string
59
	 */
60
	private $php_ext;
61
62
	/**
63
	 * @var string
64
	 */
65
	private $root_path;
66
67
	/**
68
	 * Constructor
69
	 *
70
	 * @access   public
71
	 *
72
	 * @param session_helper_interface          $session_helper
73
	 * @param user                              $user
74
	 * @param request_interface                 $request
75
	 * @param driver_interface $db
76
	 * @param template $template
77
	 * @param config $config
78
	 * @param string                            $php_ext
79
	 * @param string                            $root_path
80
	 */
81 View Code Duplication
	public function __construct(session_helper_interface $session_helper, user $user, request_interface $request, driver_interface $db, template $template, config $config, $php_ext, $root_path)
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...
82
	{
83
		$this->session_helper		= $session_helper;
84
		$this->user					= $user;
85
		$this->request				= $request;
86
		$this->config				= $config;
87
		$this->db					= $db;
88
		$this->template				= $template;
89
		$this->php_ext				= $php_ext;
90
		$this->root_path			= $root_path;
91
	}
92
93
	/**
94
	 * Assign functions defined in this class to event listeners in the core
95
	 *
96
	 * @return array
97
	 * @static
98
	 * @access public
99
	 */
100
	public static function getSubscribedEvents()
101
	{
102
		return array(
103
			'core.auth_login_session_create_before'		=> 'auth_login_session_create_before',
104
			'core.user_setup_after'						=> 'user_setup_after',
105
			'core.permissions'			        		=> 'add_permission',
106
		);
107
	}
108
109
	/**
110
	 * @param data $event
111
	 */
112
	public function add_permission($event)
113
	{
114
		$permissions = $event['permissions'];
115
		$permissions['a_tfa'] = array('lang' => 'ACL_A_TFA', 'cat' => 'misc');
116
		$event['permissions'] = $permissions;
117
	}
118
119
	/**
120
	 * @param data $event
121
	 */
122
	public function user_setup_after($event)
123
	{
124
		$this->user->add_lang_ext('paul999/tfa', 'common');
125
		if (strpos($this->user->page['page'], 'paul999/tfa/save') !== false)
126
		{
127
			// We are at our controller. Don't do anything.  In all cases.
128
			@define('SKIP_CHECK_DISABLED', true);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
129
			return;
130
		}
131
132
		// We skip this when tfa is disabled or we are at a page related to login (This includes logout :))
133
		if ($this->config['tfa_mode'] == session_helper_interface::MODE_DISABLED || defined('IN_LOGIN'))
134
		{
135
			return;
136
		}
137
138
		if ($this->user->data['is_bot'] == false && $this->user->data['user_id'] != ANONYMOUS && $this->session_helper->is_tfa_required($this->user->data['user_id'], false, $this->user->data) && !$this->session_helper->is_tfa_registered($this->user->data['user_id']))
139
		{
140
			@define('SKIP_CHECK_DISABLED', true);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
141
			if ($this->user->page['page_name'] === 'memberlist.' . $this->php_ext && $this->request->variable('mode', '') == 'contactadmin')
142
			{
143
				// We are at the contact admin page. We will allow this in all cases.
144
				return;
145
			}
146
147
			$this->user->set_cookie('rn', $this->user->data['session_id'], time() + 3600 * 24, true);
148
149
			$msg_title =  $this->user->lang['INFORMATION'];
150
			if ($this->session_helper->is_tfa_key_registred($this->user->data['user_id']))
151
			{
152
				// the user has keys registered, but they are not usable (Might be due to browser requirements, or others)
153
				// We will not allow them to register a new key. They will need to contact the admin instead unfortunately.
154
				$url = phpbb_get_board_contact_link($this->config, $this->root_path, $this->php_ext);
155
				$msg_text = $this->user->lang('TFA_REQUIRED_KEY_AVAILABLE_BUT_UNUSABLE', '<a href="' . $url . '">', '</a>');
156
				$this->user->session_kill();
157
				$this->generate_fatal_error($msg_title, $msg_text);
158
			}
159
160
			$sql = 'SELECT module_id FROM ' . MODULES_TABLE . " WHERE module_langname = 'UCP_TFA' OR module_langname = 'UCP_TFA_MANAGE'";
161
			$result = $this->db->sql_query($sql, 3600);
162
			$allowed_i = array();
163
164
			while ($row = $this->db->sql_fetchrow($result))
165
			{
166
				$allowed_i[] = $row['module_id'];
167
			}
168
			$this->db->sql_freeresult($result);
169
			$ucp_mode = '-paul999-tfa-ucp-tfa_module';
170
			$allowed_i[] = $ucp_mode;
171
172
			if ($this->user->page['page_name'] === 'ucp.' . $this->php_ext && in_array($this->request->variable('i', ''), $allowed_i))
173
			{
174
				return; // We are at our UCP page, so skip any other checks. This page is always available
175
			}
176
			$url = append_sid("{$this->root_path}ucp.{$this->php_ext}", "i={$ucp_mode}");
177
			$msg_text = $this->user->lang('TFA_REQUIRED_KEY_MISSING', '<a href="' . $url . '">', '</a>');
178
179
			$this->generate_fatal_error($msg_title, $msg_text);
180
		}
181
182
		// If the user had no key when logged in, but now has a key, we will force him to use the key.
183
		if ($this->user->data['is_bot'] == false && $this->user->data['user_id'] != ANONYMOUS && $this->request->variable($this->config['cookie_name'] . '_rn', '', false, request_interface::COOKIE) !== '' && $this->session_helper->is_tfa_required($this->user->data['user_id'], false, $this->user->data))
184
		{
185
			$this->session_helper->generate_page($this->user->data['user_id'], false, $this->user->data['session_autologin'], $this->user->data['session_viewonline'], $this->user->page['page'], true);
186
		}
187
	}
188
189
	/**
190
	 * @param data $event
191
	 *
192
	 * @return data $event|null
193
	 * @throw http_exception
194
	 */
195
	public function auth_login_session_create_before($event)
196
	{
197
		if ($this->config['tfa_mode'] == session_helper_interface::MODE_DISABLED)
198
		{
199
			return $event;
200
		}
201
		if ($event['admin'] && $this->config['tfa_acp'] == session_helper_interface::ACP_DISABLED)
202
		{
203
			// two factor authentication is disabled for the ACP.
204
			return $event;
205
		}
206
		if (isset($event['login'], $event['login']['status']) && $event['login']['status'] == LOGIN_SUCCESS)
207
		{
208
			// We have a LOGIN_SUCCESS result.
209
			if ($this->session_helper->is_tfa_required($event['login']['user_row']['user_id'], $event['admin'], $event['user_row']))
210
			{
211
				if (!$this->session_helper->is_tfa_registered($event['login']['user_row']['user_id']))
212
				{
213
					// While 2FA is enabled, the user has no methods added.
214
					// We simply return and continue the login procedure (The normal way :)),
215
					// and will disable all pages until he has added a 2FA key.
216
					return $event;
217
				}
218
				else
219
				{
220
					$this->session_helper->generate_page($event['login']['user_row']['user_id'], $event['admin'], $event['autologin'], !$this->request->is_set_post('viewonline'), $this->request->variable('redirect', ''));
221
				}
222
			}
223
		}
224
		return null;
225
	}
226
227
	/**
228
	 * Generate a fatal error. This method will always exit.
229
	 *
230
	 * @param $msg_title string Error title
231
	 * @param $msg_text string Error message
232
	 */
233
	private function generate_fatal_error($msg_title, $msg_text)
234
	{
235
		page_header($msg_title);
236
237
		$this->template->set_filenames(array(
238
				'body' => 'message_body.html')
239
		);
240
241
		$this->template->assign_vars(array(
242
			'MESSAGE_TITLE' => $msg_title,
243
			'MESSAGE_TEXT' => $msg_text,
244
			'S_USER_WARNING' => true,
245
			'S_USER_NOTICE' => false,
246
		));
247
248
		// We do not want the cron script to be called on error messages
249
		define('IN_CRON', true);
250
251
		page_footer();
252
	}
253
}
254