Completed
Pull Request — patch_1-1-4 (#3210)
by Emanuele
12:56
created

ManageSecurity_Controller   A

Complexity

Total Complexity 41

Size/Duplication

Total Lines 484
Duplicated Lines 5.99 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 37.8%

Importance

Changes 0
Metric Value
dl 29
loc 484
rs 9.1199
c 0
b 0
f 0
ccs 96
cts 254
cp 0.378
wmc 41
lcom 1
cbo 6

13 Methods

Rating   Name   Duplication   Size   Complexity  
A action_index() 0 47 1
A action_securitySettings_display() 29 29 2
A action_moderationSettings_display() 0 52 4
B action_spamSettings_display() 0 51 7
C action_bbSettings_display() 0 86 12
A _moderationSettings() 0 18 1
A moderationSettings_search() 0 4 1
A _securitySettings() 0 34 1
A securitySettings_search() 0 4 1
B _spamSettings() 0 42 6
A spamSettings_search() 0 4 1
A _bbSettings() 0 34 3
A bbSettings_search() 0 4 1

How to fix   Duplicated Code    Complexity   

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:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like ManageSecurity_Controller often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ManageSecurity_Controller, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Handles the Security and Moderation pages in the admin panel.  This includes
5
 * bad behavior, anti spam, security and moderation settings
6
 *
7
 * @name      ElkArte Forum
8
 * @copyright ElkArte Forum contributors
9
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
10
 *
11
 * This file contains code covered by:
12
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
13
 * license:		BSD, See included LICENSE.TXT for terms and conditions.
14
 *
15
 * @version 1.1
16
 *
17
 */
18
19
/**
20
 * ManageSecurity controller handles the Security and Moderation
21
 * pages in admin panel.
22
 *
23
 * @package Security
24
 */
25
class ManageSecurity_Controller extends Action_Controller
26
{
27
	/**
28
	 * This function passes control through to the relevant security tab.
29
	 *
30
	 * @event integrate_sa_modify_security
31
	 * @see Action_Controller::action_index()
32
	 */
33
	public function action_index()
34
	{
35
		global $context, $txt;
36
37
		loadLanguage('Help');
38
		loadLanguage('ManageSettings');
39
40
		$subActions = array(
41
			'general' => array($this, 'action_securitySettings_display', 'permission' => 'admin_forum'),
42
			'spam' => array($this, 'action_spamSettings_display', 'permission' => 'admin_forum'),
43
			'badbehavior' => array($this, 'action_bbSettings_display', 'permission' => 'admin_forum'),
44
			'moderation' => array($this, 'action_moderationSettings_display', 'enabled' => in_array('w', $context['admin_features']), 'permission' => 'admin_forum'),
45
		);
46
47
		// Action control
48
		$action = new Action('modify_security');
49
50
		// Load up all the tabs...
51
		$context[$context['admin_menu_name']]['tab_data'] = array(
52
			'title' => $txt['admin_security_moderation'],
53
			'help' => 'securitysettings',
54
			'description' => $txt['security_settings_desc'],
55
			'tabs' => array(
56
				'general' => array(
57
				),
58
				'spam' => array(
59
					'description' => $txt['antispam_Settings_desc'],
60
				),
61
				'badbehavior' => array(
62
					'description' => $txt['badbehavior_desc'],
63
				),
64
				'moderation' => array(
65
				),
66
			),
67
		);
68
69
		// By default do the basic settings, call integrate_sa_modify_security
70
		$subAction = $action->initialize($subActions, 'general');
71
72
		// Last pieces of the puzzle
73
		$context['sub_action'] = $subAction;
74
		$context['page_title'] = $txt['admin_security_moderation'];
75
		$context['sub_template'] = 'show_settings';
76
77
		// Call the right function for this sub-action.
78
		$action->dispatch($subAction);
79
	}
80
81
	/**
82
	 * Handle settings regarding general security of the site.
83
	 *
84
	 * - Uses a settings form for security options.
85
	 *
86
	 * @event integrate_save_general_security_settings
87
	 */
88 View Code Duplication
	public function action_securitySettings_display()
89
	{
90
		global $txt, $scripturl, $context;
91
92
		// Initialize the form
93
		$settingsForm = new Settings_Form(Settings_Form::DB_ADAPTER);
94
95
		// Initialize it with our settings
96
		$settingsForm->setConfigVars($this->_securitySettings());
97
98
		// Saving?
99
		if (isset($this->_req->query->save))
100
		{
101
			checkSession();
102
103
			$settingsForm->setConfigValues((array) $this->_req->post);
104
			$settingsForm->save();
105
106
			call_integration_hook('integrate_save_general_security_settings');
107
108
			writeLog();
109
			redirectexit('action=admin;area=securitysettings;sa=general');
110
		}
111
112
		$context['post_url'] = $scripturl . '?action=admin;area=securitysettings;save;sa=general';
113
		$context['settings_title'] = $txt['mods_cat_security_general'];
114
115
		$settingsForm->prepare();
116
	}
117
118
	/**
119
	 * Allows to display and eventually change the moderation settings of the forum.
120
	 *
121
	 * - Uses the moderation settings form.
122
	 *
123
	 * @event integrate_save_moderation_settings
124
	 */
125
	public function action_moderationSettings_display()
126
	{
127
		global $txt, $scripturl, $context, $modSettings;
128
129
		// Initialize the form
130
		$settingsForm = new Settings_Form(Settings_Form::DB_ADAPTER);
131
132
		// Initialize it with our settings
133
		$config_vars = $this->_moderationSettings();
134
		$settingsForm->setConfigVars($config_vars);
135
136
		// Saving?
137
		if (isset($this->_req->query->save))
138
		{
139
			checkSession();
140
141
			// Make sure these don't have an effect.
142
			if ($modSettings['warning_settings'][0] != 1)
143
			{
144
				$this->_req->post->warning_watch = 0;
145
				$this->_req->post->warning_moderate = 0;
146
				$this->_req->post->warning_mute = 0;
147
			}
148
			else
149
			{
150
				$this->_req->post->warning_watch = min($this->_req->post->warning_watch, 100);
151
				$this->_req->post->warning_moderate = $modSettings['postmod_active'] ? min($this->_req->post->warning_moderate, 100) : 0;
152
				$this->_req->post->warning_mute = min($this->_req->post->warning_mute, 100);
153
			}
154
155
			// Fix the warning setting array!
156
			$this->_req->post->warning_settings = '1,' . min(100, (int) $this->_req->post->user_limit) . ',' . min(100, (int) $this->_req->post->warning_decrement);
157
			$config_vars[] = array('text', 'warning_settings');
158
			unset($config_vars['rem1'], $config_vars['rem2']);
159
160
			call_integration_hook('integrate_save_moderation_settings');
161
162
			$settingsForm->setConfigVars($config_vars);
163
			$settingsForm->setConfigValues((array) $this->_req->post);
164
			$settingsForm->save();
165
			redirectexit('action=admin;area=securitysettings;sa=moderation');
166
		}
167
168
		// We actually store lots of these together - for efficiency.
169
		list ($modSettings['warning_enable'], $modSettings['user_limit'], $modSettings['warning_decrement']) = explode(',', $modSettings['warning_settings']);
170
171
		$context['post_url'] = $scripturl . '?action=admin;area=securitysettings;save;sa=moderation';
172
		$context['settings_title'] = $txt['moderation_settings'];
173
		$context['settings_message'] = $txt['warning_enable'];
174
175
		$settingsForm->prepare();
176
	}
177
178
	/**
179
	 * Handles admin security spam settings.
180
	 *
181
	 * - Displays a page with settings and eventually allows the admin to change them.
182
	 *
183
	 * @event integrate_save_spam_settings
184
	 */
185
	public function action_spamSettings_display()
186
	{
187
		global $txt, $scripturl, $context, $modSettings;
188
189
		// Initialize the form
190
		$settingsForm = new Settings_Form(Settings_Form::DB_ADAPTER);
191
192
		// Initialize it with our settings
193
		$config_vars = $this->_spamSettings();
194
		$settingsForm->setConfigVars($config_vars);
195
196
		// Saving?
197
		if (isset($this->_req->query->save))
198
		{
199
			checkSession();
200
201
			// Fix PM settings.
202
			$this->_req->post->pm_spam_settings = (int) $this->_req->post->max_pm_recipients . ',' . (int) $this->_req->post->pm_posts_verification . ',' . (int) $this->_req->post->pm_posts_per_hour;
203
204
			// Guest requiring verification!
205
			if (empty($this->_req->post->posts_require_captcha) && !empty($this->_req->post->guests_require_captcha))
206
				$this->_req->post->posts_require_captcha = -1;
207
208
			unset($config_vars['pm1'], $config_vars['pm2'], $config_vars['pm3'], $config_vars['guest_verify']);
209
210
			$config_vars[] = array('text', 'pm_spam_settings');
211
212
			call_integration_hook('integrate_save_spam_settings');
213
214
			// Now save.
215
			$settingsForm->setConfigValues((array) $this->_req->post);
216
			$settingsForm->save();
217
			Cache::instance()->remove('verificationQuestionIds');
218
			redirectexit('action=admin;area=securitysettings;sa=spam');
219
		}
220
221
		// Add in PM spam settings on the fly
222
		list ($modSettings['max_pm_recipients'], $modSettings['pm_posts_verification'], $modSettings['pm_posts_per_hour']) = explode(',', $modSettings['pm_spam_settings']);
223
224
		// And the same for guests requiring verification.
225
		$modSettings['guests_require_captcha'] = !empty($modSettings['posts_require_captcha']);
226
		$modSettings['posts_require_captcha'] = !isset($modSettings['posts_require_captcha']) || $modSettings['posts_require_captcha'] == -1 ? 0 : $modSettings['posts_require_captcha'];
227
228
		// Some minor javascript for the guest post setting.
229
		if ($modSettings['posts_require_captcha'])
230
			addInlineJavascript('document.getElementById(\'guests_require_captcha\').disabled = true;', true);
231
232
		$context['post_url'] = $scripturl . '?action=admin;area=securitysettings;save;sa=spam';
233
		$context['settings_title'] = $txt['antispam_Settings'];
234
		$settingsForm->prepare();
235
	}
236
237
	/**
238
	 * Change the way bad behavior ... well behaves
239
	 */
240
	public function action_bbSettings_display()
241
	{
242
		global $txt, $scripturl, $context, $modSettings, $boardurl;
243
244
		// Initialize the form
245
		$settingsForm = new Settings_Form(Settings_Form::DB_ADAPTER);
246
247
		// Initialize it with our settings
248
		$settingsForm->setConfigVars($this->_bbSettings());
249
250
		// Our callback templates are here
251
		loadTemplate('BadBehavior');
252
253
		// Any errors to display?
254
		if ($context['invalid_badbehavior_httpbl_key'])
255
		{
256
			$context['settings_message'][] = $txt['badbehavior_httpbl_key_invalid'];
257
			$context['error_type'] = 'warning';
258
		}
259
260
		// Have we blocked anything in the last 7 days?
261
		if (!empty($modSettings['badbehavior_enabled']))
262
			$context['settings_message'][] = bb2_insert_stats(true) . ' <a class="linkbutton" href="' . $boardurl . '/index.php?action=admin;area=logs;sa=badbehaviorlog;desc">' . $txt['badbehavior_details'] . '</a>';
263
264
		// Current whitelist data
265
		$whitelist = array('badbehavior_ip_wl', 'badbehavior_useragent_wl', 'badbehavior_url_wl');
266
		foreach ($whitelist as $list)
267
		{
268
			$context[$list] = array();
269
			$context[$list . '_desc'] = array();
270
271
			if (!empty($modSettings[$list]))
272
				$context[$list] = Util::unserialize($modSettings[$list]);
273
274
			if (!empty($modSettings[$list . '_desc']))
275
				$context[$list . '_desc'] = Util::unserialize($modSettings[$list . '_desc']);
276
		}
277
278
		// Saving?
279
		if (isset($this->_req->query->save))
280
		{
281
			checkSession();
282
283
			// Make sure Bad Behavior defaults are set if nothing was specified
284
			$this->_req->post->badbehavior_httpbl_threat = empty($this->_req->post->badbehavior_httpbl_threat) ? 25 : $this->_req->post->badbehavior_httpbl_threat;
285
			$this->_req->post->badbehavior_httpbl_maxage = empty($this->_req->post->badbehavior_httpbl_maxage) ? 30 : $this->_req->post->badbehavior_httpbl_maxage;
286
			$this->_req->post->badbehavior_reverse_proxy_header = empty($this->_req->post->badbehavior_reverse_proxy_header) ? 'X-Forwarded-For' : $this->_req->post->badbehavior_reverse_proxy_header;
287
288
			// Build up the whitelist options
289
			foreach ($whitelist as $list)
290
			{
291
				$this_list = array();
292
				$this_desc = array();
293
294
				if (isset($this->_req->post->{$list}))
295
				{
296
					// Clear blanks from the data field, only grab the comments that don't have blank data value
297
					$this_list = array_map('trim', array_filter($this->_req->post->{$list}));
298
					$this_desc = array_intersect_key($this->_req->post->{$list . '_desc'}, $this_list);
299
				}
300
301
				updateSettings(array($list => serialize($this_list), $list . '_desc' => serialize($this_desc)));
302
			}
303
304
			$settingsForm->setConfigValues((array) $this->_req->post);
305
			$settingsForm->save();
306
			redirectexit('action=admin;area=securitysettings;sa=badbehavior');
307
		}
308
309
		$context['post_url'] = $scripturl . '?action=admin;area=securitysettings;save;sa=badbehavior';
310
311
		// Javascript vars for the "add more xyz" buttons in the callback forms
312
		addJavascriptVar(array(
313
			'sUrlParent' => '\'add_more_url_placeholder\'',
314
			'oUrlOptionsdt' => '{name: \'badbehavior_url_wl_desc[]\', class: \'input_text\'}',
315
			'oUrlOptionsdd' => '{name: \'badbehavior_url_wl[]\', class: \'input_text\'}',
316
			'sUseragentParent' => '\'add_more_useragent_placeholder\'',
317
			'oUseragentOptionsdt' => '{name: \'badbehavior_useragent_wl_desc[]\', class: \'input_text\'}',
318
			'oUseragentOptionsdd' => '{name: \'badbehavior_useragent_wl[]\', class: \'input_text\'}',
319
			'sIpParent' => '\'add_more_ip_placeholder\'',
320
			'oIpOptionsdt' => '{name: \'badbehavior_ip_wl_desc[]\', class: \'input_text\'}',
321
			'oIpOptionsdd' => '{name: \'badbehavior_ip_wl[]\', class: \'input_text\'}'
322
		));
323
324
		$settingsForm->prepare();
325
	}
326
327
	/**
328
	 * Moderation settings.
329
	 *
330
	 * @event integrate_modify_moderation_settings add new moderation settings
331
	 */
332 1
	private function _moderationSettings()
333
	{
334 1
		global $txt;
335
336
		$config_vars = array(
337
			// Warning system?
338 1
			array('int', 'warning_watch', 'subtext' => $txt['setting_warning_watch_note'], 'help' => 'watch_enable'),
339 1
			'moderate' => array('int', 'warning_moderate', 'subtext' => $txt['setting_warning_moderate_note'], 'help' => 'moderate_enable'),
340 1
			array('int', 'warning_mute', 'subtext' => $txt['setting_warning_mute_note'], 'help' => 'mute_enable'),
341 1
			'rem1' => array('int', 'user_limit', 'subtext' => $txt['setting_user_limit_note'], 'help' => 'perday_limit'),
342 1
			'rem2' => array('int', 'warning_decrement', 'subtext' => $txt['setting_warning_decrement_note']),
343 1
			array('select', 'warning_show', 'subtext' => $txt['setting_warning_show_note'], array($txt['setting_warning_show_mods'], $txt['setting_warning_show_user'], $txt['setting_warning_show_all'])),
344 1
		);
345
346 1
		call_integration_hook('integrate_modify_moderation_settings', array(&$config_vars));
347
348 1
		return $config_vars;
349
	}
350
351
	/**
352
	 * Public method to return moderation settings, used for admin search
353
	 */
354 1
	public function moderationSettings_search()
355
	{
356 1
		return $this->_moderationSettings();
357
	}
358
359
	/**
360
	 * Security settings.
361
	 *
362
	 * @event integrate_general_security_settings add more security settings
363
	 */
364 1
	private function _securitySettings()
365
	{
366 1
		global $txt;
367
368
		// Set up the config array for use
369
		$config_vars = array(
370 1
				array('int', 'failed_login_threshold'),
371 1
				array('int', 'loginHistoryDays'),
372 1
			'',
373 1
				array('check', 'enableErrorLogging'),
374 1
				array('check', 'enableErrorQueryLogging'),
375 1
			'',
376 1
				array('int', 'admin_session_lifetime'),
377 1
				array('check', 'auto_admin_session'),
378 1
				array('check', 'securityDisable'),
379 1
				array('check', 'securityDisable_moderate'),
380 1
			'',
381 1
				array('check', 'enableOTP'),
382 1
			'',
383
				// Reactive on email, and approve on delete
384 1
				array('check', 'send_validation_onChange'),
385 1
				array('check', 'approveAccountDeletion'),
386 1
			'',
387
				// Password strength.
388 1
				array('select', 'password_strength', array($txt['setting_password_strength_low'], $txt['setting_password_strength_medium'], $txt['setting_password_strength_high'])),
389 1
				array('check', 'enable_password_conversion'),
390 1
			'',
391 1
				array('select', 'frame_security', array('SAMEORIGIN' => $txt['setting_frame_security_SAMEORIGIN'], 'DENY' => $txt['setting_frame_security_DENY'], 'DISABLE' => $txt['setting_frame_security_DISABLE'])),
392 1
		);
393
394 1
		call_integration_hook('integrate_general_security_settings', array(&$config_vars));
395
396 1
		return $config_vars;
397
	}
398
399
	/**
400
	 * Public method to return security form settings, used in admin search
401
	 */
402 1
	public function securitySettings_search()
403
	{
404 1
		return $this->_securitySettings();
405
	}
406
407
	/**
408
	 * Spam settings.
409
	 *
410
	 * @event integrate_spam_settings mmmm Spam
411
	 */
412 1
	private function _spamSettings()
413
	{
414 1
		global $txt, $modSettings;
415
416
		// Build up our options array
417
		$config_vars = array(
418 1
			array('check', 'reg_verification'),
419 1
			array('check', 'search_enable_captcha'),
420
			// This, my friend, is a cheat :p
421 1
			'guest_verify' => array('check', 'guests_require_captcha', 'postinput' => $txt['setting_guests_require_captcha_desc']),
422 1
			array('int', 'posts_require_captcha', 'postinput' => $txt['posts_require_captcha_desc'], 'onchange' => 'if (this.value > 0){ document.getElementById(\'guests_require_captcha\').checked = true; document.getElementById(\'guests_require_captcha\').disabled = true;} else {document.getElementById(\'guests_require_captcha\').disabled = false;}'),
423 1
			array('check', 'guests_report_require_captcha'),
424
			// PM Settings
425 1
			array('title', 'antispam_PM'),
426 1
				'pm1' => array('int', 'max_pm_recipients', 'postinput' => $txt['max_pm_recipients_note']),
427 1
				'pm2' => array('int', 'pm_posts_verification', 'postinput' => $txt['pm_posts_verification_note']),
428 1
				'pm3' => array('int', 'pm_posts_per_hour', 'postinput' => $txt['pm_posts_per_hour_note']),
429 1
		);
430
431
		// Cannot use moderation if post moderation is not enabled.
432 1
		if (!$modSettings['postmod_active'])
433 1
			unset($config_vars['moderate']);
434
435 1
		require_once(SUBSDIR . '/VerificationControls.class.php');
436 1
		$known_verifications = loadVerificationControls();
437
438 1
		foreach ($known_verifications as $verification)
439
		{
440 1
			$class_name = 'Verification_Controls_' . ucfirst($verification);
441 1
			$current_instance = new $class_name();
442
443 1
			$new_settings = $current_instance->settings();
444 1
			if (!empty($new_settings) && is_array($new_settings))
445 1
				foreach ($new_settings as $new_setting)
446 1
				$config_vars[] = $new_setting;
447 1
		}
448
449
		// @todo: it may be removed, it may stay, the two hooks may have different functions
450 1
		call_integration_hook('integrate_spam_settings', array(&$config_vars));
451
452 1
		return $config_vars;
453
	}
454
455
	/**
456
	 * Public method to return spam settings, used in admin search
457
	 */
458 1
	public function spamSettings_search()
459
	{
460 1
		return $this->_spamSettings();
461
	}
462
463
	/**
464
	 * Bad Behavior settings.
465
	 */
466 1
	private function _bbSettings()
467
	{
468 1
		global $txt, $context, $modSettings;
469
470
		// See if they supplied a valid looking http:BL API Key
471 1
		$context['invalid_badbehavior_httpbl_key'] = (!empty($modSettings['badbehavior_httpbl_key']) && (strlen($modSettings['badbehavior_httpbl_key']) !== 12 || !ctype_lower($modSettings['badbehavior_httpbl_key'])));
472
473
		// Build up our options array
474
		$config_vars = array(
475 1
			array('title', 'badbehavior_title'),
476 1
				array('check', 'badbehavior_enabled', 'postinput' => $txt['badbehavior_enabled_desc']),
477 1
				array('check', 'badbehavior_logging', 'postinput' => $txt['badbehavior_default_on']),
478 1
				array('check', 'badbehavior_verbose', 'postinput' => $txt['badbehavior_default_off']),
479 1
				array('check', 'badbehavior_strict', 'postinput' => $txt['badbehavior_default_off']),
480 1
				array('check', 'badbehavior_offsite_forms', 'postinput' => $txt['badbehavior_default_off']),
481 1
				array('check', 'badbehavior_eucookie', 'postinput' => $txt['badbehavior_default_off']),
482 1
			'',
483 1
				array('check', 'badbehavior_reverse_proxy', 'postinput' => $txt['badbehavior_default_off']),
484 1
				array('text', 'badbehavior_reverse_proxy_header', 30, 'postinput' => $txt['badbehavior_reverse_proxy_header_desc']),
485 1
				array('text', 'badbehavior_reverse_proxy_addresses', 30),
486 1
			'',
487 1
				array('text', 'badbehavior_httpbl_key', 12, 'invalid' => $context['invalid_badbehavior_httpbl_key']),
488 1
				array('int', 'badbehavior_httpbl_threat', 'postinput' => $txt['badbehavior_httpbl_threat_desc']),
489 1
				array('int', 'badbehavior_httpbl_maxage', 'postinput' => $txt['badbehavior_httpbl_maxage_desc']),
490 1
			array('title', 'badbehavior_whitelist_title'),
491 1
				array('desc', 'badbehavior_wl_desc'),
492 1
				array('int', 'badbehavior_postcount_wl', 'postinput' => $txt['badbehavior_postcount_wl_desc']),
493 1
				array('callback', 'badbehavior_add_ip'),
494 1
				array('callback', 'badbehavior_add_url'),
495 1
				array('callback', 'badbehavior_add_useragent'),
496 1
		);
497
498 1
		return $config_vars;
499
	}
500
501
	/**
502
	 * Public method to return bb settings, used in admin search
503
	 */
504 1
	public function bbSettings_search()
505
	{
506 1
		return $this->_bbSettings();
507
	}
508
}
509