Project

General

Profile

Download (15.3 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * system_groupmanager.php
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2004-2019 Rubicon Communications, LLC (Netgate)
7
 * Copyright (c) 2005 Paul Taylor <paultaylor@winn-dixie.com>
8
 * Copyright (c) 2008 Shrew Soft Inc
9
 * All rights reserved.
10
 *
11
 * originally based on m0n0wall (http://m0n0.ch/wall)
12
 * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net>.
13
 * All rights reserved.
14
 *
15
 * Licensed under the Apache License, Version 2.0 (the "License");
16
 * you may not use this file except in compliance with the License.
17
 * You may obtain a copy of the License at
18
 *
19
 * http://www.apache.org/licenses/LICENSE-2.0
20
 *
21
 * Unless required by applicable law or agreed to in writing, software
22
 * distributed under the License is distributed on an "AS IS" BASIS,
23
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24
 * See the License for the specific language governing permissions and
25
 * limitations under the License.
26
 */
27

    
28
##|+PRIV
29
##|*IDENT=page-system-groupmanager
30
##|*NAME=System: Group Manager
31
##|*DESCR=Allow access to the 'System: Group Manager' page.
32
##|*WARN=standard-warning-root
33
##|*MATCH=system_groupmanager.php*
34
##|-PRIV
35

    
36
require_once("guiconfig.inc");
37
require_once("pfsense-utils.inc");
38

    
39
$logging_level = LOG_WARNING;
40
$logging_prefix = gettext("Local User Database");
41

    
42
init_config_arr(array('system', 'group'));
43
$a_group = &$config['system']['group'];
44

    
45
unset($id);
46
$id = $_REQUEST['groupid'];
47
$act = (isset($_REQUEST['act']) ? $_REQUEST['act'] : '');
48

    
49
function cpusercmp($a, $b) {
50
	return strcasecmp($a['name'], $b['name']);
51
}
52

    
53
function admin_groups_sort() {
54
	global $a_group;
55

    
56
	if (!is_array($a_group)) {
57
		return;
58
	}
59

    
60
	usort($a_group, "cpusercmp");
61
}
62

    
63
/*
64
 * Check user privileges to test if the user is allowed to make changes.
65
 * Otherwise users can end up in an inconsistent state where some changes are
66
 * performed and others denied. See https://redmine.pfsense.org/issues/9259
67
 */
68
phpsession_begin();
69
$guiuser = getUserEntry($_SESSION['Username']);
70
$read_only = (is_array($guiuser) && userHasPrivilege($guiuser, "user-config-readonly"));
71
phpsession_end();
72

    
73
if (!empty($_POST) && $read_only) {
74
	$input_errors = array(gettext("Insufficient privileges to make the requested change (read only)."));
75
}
76

    
77
if (($_POST['act'] == "delgroup") && !$read_only) {
78

    
79
	if (!isset($id) || !isset($_REQUEST['groupname']) ||
80
	    !isset($a_group[$id]) ||
81
	    ($_REQUEST['groupname'] != $a_group[$id]['name'])) {
82
		pfSenseHeader("system_groupmanager.php");
83
		exit;
84
	}
85

    
86
	local_group_del($a_group[$id]);
87
	$groupdeleted = $a_group[$id]['name'];
88
	unset($a_group[$id]);
89
	/*
90
	 * Reindex the array to avoid operating on an incorrect index
91
	 * https://redmine.pfsense.org/issues/7733
92
	 */
93
	$a_group = array_values($a_group);
94

    
95
	$savemsg = sprintf(gettext("Successfully deleted group: %s"),
96
	    $groupdeleted);
97
	write_config($savemsg);
98
	syslog($logging_level, "{$logging_prefix}: {$savemsg}");
99
}
100

    
101
if (($_POST['act'] == "delpriv") && !$read_only) {
102

    
103
	if (!isset($id) || !isset($a_group[$id])) {
104
		pfSenseHeader("system_groupmanager.php");
105
		exit;
106
	}
107

    
108
	$privdeleted =
109
	    $priv_list[$a_group[$id]['priv'][$_REQUEST['privid']]]['name'];
110
	unset($a_group[$id]['priv'][$_REQUEST['privid']]);
111

    
112
	if (is_array($a_group[$id]['member'])) {
113
		foreach ($a_group[$id]['member'] as $uid) {
114
			$user = getUserEntryByUID($uid);
115
			if ($user) {
116
				local_user_set($user);
117
			}
118
		}
119
	}
120

    
121
	$savemsg = sprintf(gettext("Removed Privilege \"%s\" from group %s"),
122
	    $privdeleted, $a_group[$id]['name']);
123
	write_config($savemsg);
124
	syslog($logging_level, "{$logging_prefix}: {$savemsg}");
125

    
126
	$act = "edit";
127
}
128

    
129
if ($act == "edit") {
130
	if (isset($id) && isset($a_group[$id])) {
131
		$pconfig['name'] = $a_group[$id]['name'];
132
		$pconfig['gid'] = $a_group[$id]['gid'];
133
		$pconfig['gtype'] = empty($a_group[$id]['scope'])
134
		    ? "local" : $a_group[$id]['scope'];
135
		$pconfig['description'] = $a_group[$id]['description'];
136
		$pconfig['members'] = $a_group[$id]['member'];
137
		$pconfig['priv'] = $a_group[$id]['priv'];
138
	}
139
}
140

    
141
if (isset($_POST['dellall_x']) && !$read_only) {
142

    
143
	$del_groups = $_POST['delete_check'];
144
	$deleted_groups = array();
145

    
146
	if (!empty($del_groups)) {
147
		foreach ($del_groups as $groupid) {
148
			if (isset($a_group[$groupid]) &&
149
			    $a_group[$groupid]['scope'] != "system") {
150
				$deleted_groups[] = $a_group[$groupid]['name'];
151
				local_group_del($a_group[$groupid]);
152
				unset($a_group[$groupid]);
153
			}
154
		}
155

    
156
		$savemsg = sprintf(gettext("Successfully deleted %s: %s"),
157
		    (count($deleted_groups) == 1)
158
		    ? gettext("group") : gettext("groups"),
159
		    implode(', ', $deleted_groups));
160
		/*
161
		 * Reindex the array to avoid operating on an incorrect index
162
		 * https://redmine.pfsense.org/issues/7733
163
		 */
164
		$a_group = array_values($a_group);
165
		write_config($savemsg);
166
		syslog($logging_level, "{$logging_prefix}: {$savemsg}");
167
	}
168
}
169

    
170
if (isset($_POST['save']) && !$read_only) {
171
	unset($input_errors);
172
	$pconfig = $_POST;
173

    
174
	/* input validation */
175
	$reqdfields = explode(" ", "groupname");
176
	$reqdfieldsn = array(gettext("Group Name"));
177

    
178
	do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
179

    
180
	if ($_POST['gtype'] != "remote") {
181
		if (preg_match("/[^a-zA-Z0-9\.\-_]/", $_POST['groupname'])) {
182
			$input_errors[] = sprintf(gettext(
183
			    "The (%s) group name contains invalid characters."),
184
			    $_POST['gtype']);
185
		}
186
		if (strlen($_POST['groupname']) > 16) {
187
			$input_errors[] = gettext(
188
			    "The group name is longer than 16 characters.");
189
		}
190
	} else {
191
		if (preg_match("/[^a-zA-Z0-9\.\- _]/", $_POST['groupname'])) {
192
			$input_errors[] = sprintf(gettext(
193
			    "The (%s) group name contains invalid characters."),
194
			    $_POST['gtype']);
195
		}
196
	}
197

    
198
	/* Check the POSTed members to ensure they are valid and exist */
199
	if (is_array($_POST['members'])) {
200
		foreach ($_POST['members'] as $newmember) {
201
			if (!is_numeric($newmember) ||
202
			    empty(getUserEntryByUID($newmember))) {
203
				$input_errors[] = gettext("One or more " .
204
				    "invalid group members was submitted.");
205
			}
206
		}
207
	}
208

    
209
	if (!$input_errors && !(isset($id) && $a_group[$id])) {
210
		/* make sure there are no dupes */
211
		foreach ($a_group as $group) {
212
			if ($group['name'] == $_POST['groupname']) {
213
				$input_errors[] = gettext("Another entry " .
214
				    "with the same group name already exists.");
215
				break;
216
			}
217
		}
218
	}
219

    
220
	if (!$input_errors) {
221
		$group = array();
222
		if (isset($id) && $a_group[$id]) {
223
			$group = $a_group[$id];
224
		}
225

    
226
		$group['name'] = $_POST['groupname'];
227
		$group['description'] = $_POST['description'];
228
		$group['scope'] = $_POST['gtype'];
229

    
230
		if (empty($_POST['members'])) {
231
			unset($group['member']);
232
		} else if ($group['gid'] != 1998) { // all group
233
			$group['member'] = $_POST['members'];
234
		}
235

    
236
		if (isset($id) && $a_group[$id]) {
237
			$a_group[$id] = $group;
238
		} else {
239
			$group['gid'] = $config['system']['nextgid']++;
240
			$a_group[] = $group;
241
		}
242

    
243
		admin_groups_sort();
244

    
245
		local_group_set($group);
246

    
247
		/*
248
		 * Refresh users in this group since their privileges may have
249
		 * changed.
250
		 */
251
		if (is_array($group['member'])) {
252
			init_config_arr(array('system', 'user'));
253
			$a_user = &$config['system']['user'];
254
			foreach ($a_user as & $user) {
255
				if (in_array($user['uid'], $group['member'])) {
256
					local_user_set($user);
257
				}
258
			}
259
		}
260

    
261
		/* Sort it alphabetically */
262
		usort($config['system']['group'], function($a, $b) {
263
			return strcmp($a['name'], $b['name']);
264
		});
265

    
266
		$savemsg = sprintf(gettext("Successfully %s group %s"),
267
		    (strlen($id) > 0) ? gettext("edited") : gettext("created"),
268
		    $group['name']);
269
		write_config($savemsg);
270
		syslog($logging_level, "{$logging_prefix}: {$savemsg}");
271

    
272
		header("Location: system_groupmanager.php");
273
		exit;
274
	}
275

    
276
	$pconfig['name'] = $_POST['groupname'];
277
}
278

    
279
function build_priv_table() {
280
	global $a_group, $id, $read_only;
281

    
282
	$privhtml = '<div class="table-responsive">';
283
	$privhtml .=	'<table class="table table-striped table-hover table-condensed">';
284
	$privhtml .=		'<thead>';
285
	$privhtml .=			'<tr>';
286
	$privhtml .=				'<th>' . gettext('Name') . '</th>';
287
	$privhtml .=				'<th>' . gettext('Description') . '</th>';
288
	$privhtml .=				'<th>' . gettext('Action') . '</th>';
289
	$privhtml .=			'</tr>';
290
	$privhtml .=		'</thead>';
291
	$privhtml .=		'<tbody>';
292

    
293
	$user_has_root_priv = false;
294

    
295
	foreach (get_user_privdesc($a_group[$id]) as $i => $priv) {
296
		$privhtml .=		'<tr>';
297
		$privhtml .=			'<td>' . htmlspecialchars($priv['name']) . '</td>';
298
		$privhtml .=			'<td>' . htmlspecialchars($priv['descr']);
299
		if (isset($priv['warn']) && ($priv['warn'] == 'standard-warning-root')) {
300
			$privhtml .=			' ' . gettext('(admin privilege)');
301
			$user_has_root_priv = true;
302
		}
303
		$privhtml .=			'</td>';
304
		if (!$read_only) {
305
			$privhtml .=			'<td><a class="fa fa-trash" title="' . gettext('Delete Privilege') . '"	href="system_groupmanager.php?act=delpriv&amp;groupid=' . $id . '&amp;privid=' . $i . '" usepost></a></td>';
306
		}
307
		$privhtml .=		'</tr>';
308

    
309
	}
310

    
311
	if ($user_has_root_priv) {
312
		$privhtml .=		'<tr>';
313
		$privhtml .=			'<td colspan="2">';
314
		$privhtml .=				'<b>' . gettext('Security notice: Users in this group effectively have administrator-level access') . '</b>';
315
		$privhtml .=			'</td>';
316
		$privhtml .=			'<td>';
317
		$privhtml .=			'</td>';
318
		$privhtml .=		'</tr>';
319

    
320
	}
321

    
322
	$privhtml .=		'</tbody>';
323
	$privhtml .=	'</table>';
324
	$privhtml .= '</div>';
325

    
326
	$privhtml .= '<nav class="action-buttons">';
327
	if (!$read_only) {
328
		$privhtml .=	'<a href="system_groupmanager_addprivs.php?groupid=' . $id . '" class="btn btn-success"><i class="fa fa-plus icon-embed-btn"></i>' . gettext("Add") . '</a>';
329
	}
330
	$privhtml .= '</nav>';
331

    
332
	return($privhtml);
333
}
334

    
335
$pgtitle = array(gettext("System"), gettext("User Manager"), gettext("Groups"));
336
$pglinks = array("", "system_usermanager.php", "system_groupmanager.php");
337

    
338
if ($act == "new" || $act == "edit") {
339
	$pgtitle[] = gettext('Edit');
340
	$pglinks[] = "@self";
341
}
342

    
343
include("head.inc");
344

    
345
if ($input_errors) {
346
	print_input_errors($input_errors);
347
}
348

    
349
if ($savemsg) {
350
	print_info_box($savemsg, 'success');
351
}
352

    
353
$tab_array = array();
354
$tab_array[] = array(gettext("Users"), false, "system_usermanager.php");
355
$tab_array[] = array(gettext("Groups"), true, "system_groupmanager.php");
356
$tab_array[] = array(gettext("Settings"), false, "system_usermanager_settings.php");
357
$tab_array[] = array(gettext("Authentication Servers"), false, "system_authservers.php");
358
display_top_tabs($tab_array);
359

    
360
if (!($act == "new" || $act == "edit")) {
361
?>
362
<div class="panel panel-default">
363
	<div class="panel-heading"><h2 class="panel-title"><?=gettext('Groups')?></h2></div>
364
	<div class="panel-body">
365
		<div class="table-responsive">
366
			<table class="table table-striped table-hover table-condensed sortable-theme-bootstrap table-rowdblclickedit" data-sortable>
367
				<thead>
368
					<tr>
369
						<th><?=gettext("Group name")?></th>
370
						<th><?=gettext("Description")?></th>
371
						<th><?=gettext("Member Count")?></th>
372
						<th><?=gettext("Actions")?></th>
373
					</tr>
374
				</thead>
375
				<tbody>
376
<?php
377
	foreach ($a_group as $i => $group):
378
		if ($group["name"] == "all") {
379
			$groupcount = count($config['system']['user']);
380
		} elseif (is_array($group['member'])) {
381
			$groupcount = count($group['member']);
382
		} else {
383
			$groupcount = 0;
384
		}
385
?>
386
					<tr>
387
						<td>
388
							<?=htmlspecialchars($group['name'])?>
389
						</td>
390
						<td>
391
							<?=htmlspecialchars($group['description'])?>
392
						</td>
393
						<td>
394
							<?=$groupcount?>
395
						</td>
396
						<td>
397
							<a class="fa fa-pencil" title="<?=gettext("Edit group"); ?>" href="?act=edit&amp;groupid=<?=$i?>"></a>
398
							<?php if (($group['scope'] != "system") && !$read_only): ?>
399
								<a class="fa fa-trash"	title="<?=gettext("Delete group")?>" href="?act=delgroup&amp;groupid=<?=$i?>&amp;groupname=<?=$group['name']?>" usepost></a>
400
							<?php endif;?>
401
						</td>
402
					</tr>
403
<?php
404
	endforeach;
405
?>
406
				</tbody>
407
			</table>
408
		</div>
409
	</div>
410
</div>
411

    
412
<nav class="action-buttons">
413
	<?php if (!$read_only): ?>
414
	<a href="?act=new" class="btn btn-success btn-sm">
415
		<i class="fa fa-plus icon-embed-btn"></i>
416
		<?=gettext("Add")?>
417
	</a>
418
	<?php endif; ?>
419
</nav>
420
<?php
421
	include('foot.inc');
422
	exit;
423
}
424

    
425
$form = new Form;
426
$form->setAction('system_groupmanager.php?act=edit');
427
$form->addGlobal(new Form_Input(
428
	'groupid',
429
	null,
430
	'hidden',
431
	$id
432
));
433

    
434
if (isset($id) && $a_group[$id]) {
435
	$form->addGlobal(new Form_Input(
436
		'id',
437
		null,
438
		'hidden',
439
		$id
440
	));
441

    
442
	$form->addGlobal(new Form_Input(
443
		'gid',
444
		null,
445
		'hidden',
446
		$pconfig['gid']
447
	));
448
}
449

    
450
$section = new Form_Section('Group Properties');
451

    
452
$section->addInput($input = new Form_Input(
453
	'groupname',
454
	'*Group name',
455
	'text',
456
	$pconfig['name']
457
));
458

    
459
if ($pconfig['gtype'] == "system") {
460
	$input->setReadonly();
461

    
462
	$section->addInput(new Form_Input(
463
		'gtype',
464
		'*Scope',
465
		'text',
466
		$pconfig['gtype']
467
	))->setReadonly();
468
} else {
469
	$section->addInput(new Form_Select(
470
		'gtype',
471
		'*Scope',
472
		$pconfig['gtype'],
473
		["local" => gettext("Local"), "remote" => gettext("Remote")]
474
	))->setHelp("<span class=\"text-danger\">Warning: Changing this " .
475
	    "setting may affect the local groups file, in which case a " .
476
	    "reboot may be required for the changes to take effect.</span>");
477
}
478

    
479
$section->addInput(new Form_Input(
480
	'description',
481
	'Description',
482
	'text',
483
	$pconfig['description']
484
))->setHelp('Group description, for administrative information only');
485

    
486
$form->add($section);
487

    
488
/* all users group */
489
if ($pconfig['gid'] != 1998) {
490
	/* Group membership */
491
	$group = new Form_Group('Group membership');
492

    
493
	/*
494
	 * Make a list of all the groups configured on the system, and a list of
495
	 * those which this user is a member of
496
	 */
497
	$systemGroups = array();
498
	$usersGroups = array();
499

    
500
	foreach ($config['system']['user'] as $user) {
501
		if (is_array($pconfig['members']) && in_array($user['uid'],
502
		    $pconfig['members'])) {
503
			/* Add it to the user's list */
504
			$usersGroups[ $user['uid'] ] = $user['name'];
505
		} else {
506
			/* Add it to the 'not a member of' list */
507
			$systemGroups[ $user['uid'] ] = $user['name'];
508
		}
509
	}
510

    
511
	$group->add(new Form_Select(
512
		'notmembers',
513
		null,
514
		array_combine((array)$pconfig['groups'],
515
		    (array)$pconfig['groups']),
516
		$systemGroups,
517
		true
518
	))->setHelp('Not members');
519

    
520
	$group->add(new Form_Select(
521
		'members',
522
		null,
523
		array_combine((array)$pconfig['groups'],
524
		    (array)$pconfig['groups']),
525
		$usersGroups,
526
		true
527
	))->setHelp('Members');
528

    
529
	$section->add($group);
530

    
531
	$group = new Form_Group('');
532

    
533
	$group->add(new Form_Button(
534
		'movetoenabled',
535
		'Move to "Members"',
536
		null,
537
		'fa-angle-double-right'
538
	))->setAttribute('type','button')->removeClass('btn-primary')->addClass(
539
	    'btn-info btn-sm');
540

    
541
	$group->add(new Form_Button(
542
		'movetodisabled',
543
		'Move to "Not members',
544
		null,
545
		'fa-angle-double-left'
546
	))->setAttribute('type','button')->removeClass('btn-primary')->addClass(
547
	    'btn-info btn-sm');
548

    
549
	$group->setHelp(
550
	    'Hold down CTRL (PC)/COMMAND (Mac) key to select multiple items.');
551
	$section->add($group);
552

    
553
}
554

    
555
if (isset($pconfig['gid'])) {
556
	$section = new Form_Section('Assigned Privileges');
557

    
558
	$section->addInput(new Form_StaticText(
559
		null,
560
		build_priv_table()
561
	));
562

    
563

    
564
	$form->add($section);
565
}
566

    
567
print $form;
568
?>
569
<script type="text/javascript">
570
//<![CDATA[
571
events.push(function() {
572

    
573
	// On click . .
574
	$("#movetodisabled").click(function() {
575
		moveOptions($('[name="members[]"] option'),
576
		    $('[name="notmembers[]"]'));
577
	});
578

    
579
	$("#movetoenabled").click(function() {
580
		moveOptions($('[name="notmembers[]"] option'),
581
		    $('[name="members[]"]'));
582
	});
583

    
584
	// On submit mark all the user's groups as "selected"
585
	$('form').submit(function() {
586
		AllServers($('[name="members[]"] option'), true);
587
	});
588
});
589
//]]>
590
</script>
591
<?php
592
include('foot.inc');
(208-208/235)