Project

General

Profile

Download (16.1 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-2013 BSD Perimeter
7
 * Copyright (c) 2013-2016 Electric Sheep Fencing
8
 * Copyright (c) 2014-2022 Rubicon Communications, LLC (Netgate)
9
 * Copyright (c) 2005 Paul Taylor <paultaylor@winn-dixie.com>
10
 * Copyright (c) 2008 Shrew Soft Inc
11
 * All rights reserved.
12
 *
13
 * originally based on m0n0wall (http://m0n0.ch/wall)
14
 * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net>.
15
 * All rights reserved.
16
 *
17
 * Licensed under the Apache License, Version 2.0 (the "License");
18
 * you may not use this file except in compliance with the License.
19
 * You may obtain a copy of the License at
20
 *
21
 * http://www.apache.org/licenses/LICENSE-2.0
22
 *
23
 * Unless required by applicable law or agreed to in writing, software
24
 * distributed under the License is distributed on an "AS IS" BASIS,
25
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
26
 * See the License for the specific language governing permissions and
27
 * limitations under the License.
28
 */
29

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

    
38
require_once("guiconfig.inc");
39
require_once("pfsense-utils.inc");
40

    
41
$logging_level = LOG_WARNING;
42
$logging_prefix = gettext("Local User Database");
43

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

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

    
51
if ($act == 'dup') {
52
	$dup = $id;
53
	$act = 'edit';
54
}
55

    
56
function cpusercmp($a, $b) {
57
	return strcasecmp($a['name'], $b['name']);
58
}
59

    
60
function admin_groups_sort() {
61
	global $a_group;
62

    
63
	if (!is_array($a_group)) {
64
		return;
65
	}
66

    
67
	usort($a_group, "cpusercmp");
68
}
69

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

    
80
if (!empty($_POST) && $read_only) {
81
	$input_errors = array(gettext("Insufficient privileges to make the requested change (read only)."));
82
}
83

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

    
86
	if (!isset($id) || !isset($_REQUEST['groupname']) ||
87
	    !isset($a_group[$id]) ||
88
	    ($_REQUEST['groupname'] != $a_group[$id]['name'])) {
89
		pfSenseHeader("system_groupmanager.php");
90
		exit;
91
	}
92

    
93
	local_group_del($a_group[$id]);
94
	$groupdeleted = $a_group[$id]['name'];
95
	unset($a_group[$id]);
96
	/*
97
	 * Reindex the array to avoid operating on an incorrect index
98
	 * https://redmine.pfsense.org/issues/7733
99
	 */
100
	$a_group = array_values($a_group);
101

    
102
	$savemsg = sprintf(gettext("Successfully deleted group: %s"),
103
	    $groupdeleted);
104
	write_config($savemsg);
105
	syslog($logging_level, "{$logging_prefix}: {$savemsg}");
106
}
107

    
108
if (($_POST['act'] == "delpriv") && !$read_only && !$dup) {
109

    
110
	if (!isset($id) || !isset($a_group[$id])) {
111
		pfSenseHeader("system_groupmanager.php");
112
		exit;
113
	}
114

    
115
	$privdeleted =
116
	    $priv_list[$a_group[$id]['priv'][$_REQUEST['privid']]]['name'];
117
	unset($a_group[$id]['priv'][$_REQUEST['privid']]);
118

    
119
	if (is_array($a_group[$id]['member'])) {
120
		foreach ($a_group[$id]['member'] as $uid) {
121
			$user = getUserEntryByUID($uid);
122
			if ($user) {
123
				local_user_set($user);
124
			}
125
		}
126
	}
127

    
128
	$savemsg = sprintf(gettext("Removed Privilege \"%s\" from group %s"),
129
	    $privdeleted, $a_group[$id]['name']);
130
	write_config($savemsg);
131
	syslog($logging_level, "{$logging_prefix}: {$savemsg}");
132

    
133
	$act = "edit";
134
}
135

    
136
if ($act == "edit") {
137
	if (isset($id) && isset($a_group[$id])) {
138
		if (!$dup) {
139
			$pconfig['name'] = $a_group[$id]['name'];
140
			$pconfig['gid'] = $a_group[$id]['gid'];
141
			$pconfig['gtype'] = empty($a_group[$id]['scope'])
142
			    ? "local" : $a_group[$id]['scope'];
143
		} else {
144
			$pconfig['gtype'] = ($a_group[$id]['scope'] == 'system')
145
			    ? "local" : $a_group[$id]['scope'];
146
		}
147
		$pconfig['priv'] = $a_group[$id]['priv'];
148
		$pconfig['description'] = $a_group[$id]['description'];
149
		$pconfig['members'] = $a_group[$id]['member'];
150
	}
151
}
152

    
153
if (isset($_POST['dellall_x']) && !$read_only) {
154

    
155
	$del_groups = $_POST['delete_check'];
156
	$deleted_groups = array();
157

    
158
	if (!empty($del_groups)) {
159
		foreach ($del_groups as $groupid) {
160
			if (isset($a_group[$groupid]) &&
161
			    $a_group[$groupid]['scope'] != "system") {
162
				$deleted_groups[] = $a_group[$groupid]['name'];
163
				local_group_del($a_group[$groupid]);
164
				unset($a_group[$groupid]);
165
			}
166
		}
167

    
168
		$savemsg = sprintf(gettext("Successfully deleted %s: %s"),
169
		    (count($deleted_groups) == 1)
170
		    ? gettext("group") : gettext("groups"),
171
		    implode(', ', $deleted_groups));
172
		/*
173
		 * Reindex the array to avoid operating on an incorrect index
174
		 * https://redmine.pfsense.org/issues/7733
175
		 */
176
		$a_group = array_values($a_group);
177
		write_config($savemsg);
178
		syslog($logging_level, "{$logging_prefix}: {$savemsg}");
179
	}
180
}
181

    
182
if (isset($_POST['save']) && !$read_only) {
183
	unset($input_errors);
184
	$pconfig = $_POST;
185

    
186
	/* input validation */
187
	$reqdfields = explode(" ", "groupname");
188
	$reqdfieldsn = array(gettext("Group Name"));
189

    
190
	do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
191

    
192
	if ($_POST['gtype'] != "remote") {
193
		if (preg_match("/[^a-zA-Z0-9\.\-_]/", $_POST['groupname'])) {
194
			$input_errors[] = sprintf(gettext(
195
			    "The (%s) group name contains invalid characters."),
196
			    $_POST['gtype']);
197
		}
198
		if (strlen($_POST['groupname']) > 16) {
199
			$input_errors[] = gettext(
200
			    "The group name is longer than 16 characters.");
201
		}
202
	} else {
203
		if (preg_match("/[^a-zA-Z0-9\.\- _]/", $_POST['groupname'])) {
204
			$input_errors[] = sprintf(gettext(
205
			    "The (%s) group name contains invalid characters."),
206
			    $_POST['gtype']);
207
		}
208
	}
209

    
210
	/* Check the POSTed members to ensure they are valid and exist */
211
	if (is_array($_POST['members'])) {
212
		foreach ($_POST['members'] as $newmember) {
213
			if (!is_numeric($newmember) ||
214
			    empty(getUserEntryByUID($newmember))) {
215
				$input_errors[] = gettext("One or more " .
216
				    "invalid group members was submitted.");
217
			}
218
		}
219
	}
220

    
221
	if (!$input_errors && !(isset($id) && $a_group[$id])) {
222
		/* make sure there are no dupes */
223
		foreach ($a_group as $group) {
224
			if ($group['name'] == $_POST['groupname']) {
225
				$input_errors[] = gettext("Another entry " .
226
				    "with the same group name already exists.");
227
				break;
228
			}
229
		}
230
	}
231

    
232
	if (!$input_errors) {
233
		$group = array();
234
		if (isset($id) && $a_group[$id]) {
235
			$group = $a_group[$id];
236
		}
237

    
238
		$group['name'] = $_POST['groupname'];
239
		$group['description'] = $_POST['description'];
240
		$group['scope'] = $_POST['gtype'];
241

    
242
		if (empty($_POST['members'])) {
243
			unset($group['member']);
244
		} else if ($group['gid'] != 1998) { // all group
245
			$group['member'] = $_POST['members'];
246
		}
247

    
248
		if (isset($id) && $a_group[$id]) {
249
			$a_group[$id] = $group;
250
		} else {
251
			$group['gid'] = $config['system']['nextgid']++;
252
			if ($_POST['dup']) {
253
				$group['priv'] = $a_group[$_POST['dup']]['priv'];
254
			}
255
			$a_group[] = $group;
256
		}
257

    
258
		admin_groups_sort();
259

    
260
		local_group_set($group);
261

    
262
		/*
263
		 * Refresh users in this group since their privileges may have
264
		 * changed.
265
		 */
266
		if (is_array($group['member'])) {
267
			init_config_arr(array('system', 'user'));
268
			$a_user = &$config['system']['user'];
269
			foreach ($a_user as & $user) {
270
				if (in_array($user['uid'], $group['member'])) {
271
					local_user_set($user);
272
				}
273
			}
274
		}
275

    
276
		/* Sort it alphabetically */
277
		usort($config['system']['group'], function($a, $b) {
278
			return strcmp($a['name'], $b['name']);
279
		});
280

    
281
		$savemsg = sprintf(gettext("Successfully %s group %s"),
282
		    (strlen($id) > 0) ? gettext("edited") : gettext("created"),
283
		    $group['name']);
284
		write_config($savemsg);
285
		syslog($logging_level, "{$logging_prefix}: {$savemsg}");
286

    
287
		header("Location: system_groupmanager.php");
288
		exit;
289
	}
290

    
291
	$pconfig['name'] = $_POST['groupname'];
292
}
293

    
294
function build_priv_table() {
295
	global $a_group, $id, $read_only, $dup;
296

    
297
	$privhtml = '<div class="table-responsive">';
298
	$privhtml .=	'<table class="table table-striped table-hover table-condensed">';
299
	$privhtml .=		'<thead>';
300
	$privhtml .=			'<tr>';
301
	$privhtml .=				'<th>' . gettext('Name') . '</th>';
302
	$privhtml .=				'<th>' . gettext('Description') . '</th>';
303
	$privhtml .=				'<th>' . gettext('Action') . '</th>';
304
	$privhtml .=			'</tr>';
305
	$privhtml .=		'</thead>';
306
	$privhtml .=		'<tbody>';
307

    
308
	$user_has_root_priv = false;
309

    
310
	foreach (get_user_privdesc($a_group[$id]) as $i => $priv) {
311
		$privhtml .=		'<tr>';
312
		$privhtml .=			'<td>' . htmlspecialchars($priv['name']) . '</td>';
313
		$privhtml .=			'<td>' . htmlspecialchars($priv['descr']);
314
		if (isset($priv['warn']) && ($priv['warn'] == 'standard-warning-root')) {
315
			$privhtml .=			' ' . gettext('(admin privilege)');
316
			$user_has_root_priv = true;
317
		}
318
		$privhtml .=			'</td>';
319
		if (!$read_only && !$dup) {
320
			$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>';
321
		}
322
		$privhtml .=		'</tr>';
323

    
324
	}
325

    
326
	if ($user_has_root_priv) {
327
		$privhtml .=		'<tr>';
328
		$privhtml .=			'<td colspan="2">';
329
		$privhtml .=				'<b>' . gettext('Security notice: Users in this group effectively have administrator-level access') . '</b>';
330
		$privhtml .=			'</td>';
331
		$privhtml .=			'<td>';
332
		$privhtml .=			'</td>';
333
		$privhtml .=		'</tr>';
334

    
335
	}
336

    
337
	$privhtml .=		'</tbody>';
338
	$privhtml .=	'</table>';
339
	$privhtml .= '</div>';
340

    
341
	$privhtml .= '<nav class="action-buttons">';
342
	if (!$read_only && !$dup) {
343
		$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>';
344
	}
345
	$privhtml .= '</nav>';
346

    
347
	return($privhtml);
348
}
349

    
350
$pgtitle = array(gettext("System"), gettext("User Manager"), gettext("Groups"));
351
$pglinks = array("", "system_usermanager.php", "system_groupmanager.php");
352

    
353
if ($act == "new" || $act == "edit") {
354
	$pgtitle[] = gettext('Edit');
355
	$pglinks[] = "@self";
356
}
357

    
358
include("head.inc");
359

    
360
if ($input_errors) {
361
	print_input_errors($input_errors);
362
}
363

    
364
if ($savemsg) {
365
	print_info_box($savemsg, 'success');
366
}
367

    
368
$tab_array = array();
369
if (!isAllowedPage("system_usermanager.php")) {
370
	$tab_array[] = array(gettext("User Password"), false, "system_usermanager_passwordmg.php");
371
} else {
372
	$tab_array[] = array(gettext("Users"), false, "system_usermanager.php");
373
}
374
$tab_array[] = array(gettext("Groups"), true, "system_groupmanager.php");
375
$tab_array[] = array(gettext("Settings"), false, "system_usermanager_settings.php");
376
$tab_array[] = array(gettext("Authentication Servers"), false, "system_authservers.php");
377
display_top_tabs($tab_array);
378

    
379
if (!($act == "new" || $act == "edit")) {
380
?>
381
<div class="panel panel-default">
382
	<div class="panel-heading"><h2 class="panel-title"><?=gettext('Groups')?></h2></div>
383
	<div class="panel-body">
384
		<div class="table-responsive">
385
			<table class="table table-striped table-hover table-condensed sortable-theme-bootstrap table-rowdblclickedit" data-sortable>
386
				<thead>
387
					<tr>
388
						<th><?=gettext("Group name")?></th>
389
						<th><?=gettext("Description")?></th>
390
						<th><?=gettext("Member Count")?></th>
391
						<th><?=gettext("Actions")?></th>
392
					</tr>
393
				</thead>
394
				<tbody>
395
<?php
396
	foreach ($a_group as $i => $group):
397
		if ($group["name"] == "all") {
398
			$groupcount = count($config['system']['user']);
399
		} elseif (is_array($group['member'])) {
400
			$groupcount = count($group['member']);
401
		} else {
402
			$groupcount = 0;
403
		}
404
?>
405
					<tr>
406
						<td>
407
							<?=htmlspecialchars($group['name'])?>
408
						</td>
409
						<td>
410
							<?=htmlspecialchars($group['description'])?>
411
						</td>
412
						<td>
413
							<?=$groupcount?>
414
						</td>
415
						<td>
416
							<a class="fa fa-pencil" title="<?=gettext("Edit group"); ?>" href="?act=edit&amp;groupid=<?=$i?>"></a>
417
							<a class="fa fa-clone" title="<?=gettext("Copy group"); ?>" href="?act=dup&amp;groupid=<?=$i?>"></a>
418
							<?php if (($group['scope'] != "system") && !$read_only): ?>
419
								<a class="fa fa-trash"	title="<?=gettext("Delete group")?>" href="?act=delgroup&amp;groupid=<?=$i?>&amp;groupname=<?=$group['name']?>" usepost></a>
420
							<?php endif;?>
421
						</td>
422
					</tr>
423
<?php
424
	endforeach;
425
?>
426
				</tbody>
427
			</table>
428
		</div>
429
	</div>
430
</div>
431

    
432
<nav class="action-buttons">
433
	<?php if (!$read_only): ?>
434
	<a href="?act=new" class="btn btn-success btn-sm">
435
		<i class="fa fa-plus icon-embed-btn"></i>
436
		<?=gettext("Add")?>
437
	</a>
438
	<?php endif; ?>
439
</nav>
440
<?php
441
	include('foot.inc');
442
	exit;
443
}
444

    
445
$form = new Form;
446
$form->setAction('system_groupmanager.php?act=edit');
447
if (!$dup) {
448
	$form->addGlobal(new Form_Input(
449
		'groupid',
450
		null,
451
		'hidden',
452
		$id
453
	));
454
} else {
455
	$form->addGlobal(new Form_Input(
456
		'dup',
457
		null,
458
		'hidden',
459
		$dup
460
	));
461
}
462

    
463
if (isset($id) && $a_group[$id]) {
464
	$form->addGlobal(new Form_Input(
465
		'id',
466
		null,
467
		'hidden',
468
		$id
469
	));
470

    
471
	$form->addGlobal(new Form_Input(
472
		'gid',
473
		null,
474
		'hidden',
475
		$pconfig['gid']
476
	));
477
}
478

    
479
$section = new Form_Section('Group Properties');
480

    
481
$section->addInput($input = new Form_Input(
482
	'groupname',
483
	'*Group name',
484
	'text',
485
	$pconfig['name']
486
));
487

    
488
if ($pconfig['gtype'] == "system") {
489
	$input->setReadonly();
490

    
491
	$section->addInput(new Form_Input(
492
		'gtype',
493
		'*Scope',
494
		'text',
495
		$pconfig['gtype']
496
	))->setReadonly();
497
} else {
498
	$section->addInput(new Form_Select(
499
		'gtype',
500
		'*Scope',
501
		$pconfig['gtype'],
502
		["local" => gettext("Local"), "remote" => gettext("Remote")]
503
	))->setHelp("<span class=\"text-danger\">Warning: Changing this " .
504
	    "setting may affect the local groups file, in which case a " .
505
	    "reboot may be required for the changes to take effect.</span>");
506
}
507

    
508
$section->addInput(new Form_Input(
509
	'description',
510
	'Description',
511
	'text',
512
	$pconfig['description']
513
))->setHelp('Group description, for administrative information only');
514

    
515
$form->add($section);
516

    
517
/* all users group */
518
if ($pconfig['gid'] != 1998) {
519
	/* Group membership */
520
	$group = new Form_Group('Group membership');
521

    
522
	/*
523
	 * Make a list of all the groups configured on the system, and a list of
524
	 * those which this user is a member of
525
	 */
526
	$systemGroups = array();
527
	$usersGroups = array();
528

    
529
	foreach ($config['system']['user'] as $user) {
530
		if (is_array($pconfig['members']) && in_array($user['uid'],
531
		    $pconfig['members'])) {
532
			/* Add it to the user's list */
533
			$usersGroups[ $user['uid'] ] = $user['name'];
534
		} else {
535
			/* Add it to the 'not a member of' list */
536
			$systemGroups[ $user['uid'] ] = $user['name'];
537
		}
538
	}
539

    
540
	$group->add(new Form_Select(
541
		'notmembers',
542
		null,
543
		array_combine((array)$pconfig['groups'],
544
		    (array)$pconfig['groups']),
545
		$systemGroups,
546
		true
547
	))->setHelp('Not members');
548

    
549
	$group->add(new Form_Select(
550
		'members',
551
		null,
552
		array_combine((array)$pconfig['groups'],
553
		    (array)$pconfig['groups']),
554
		$usersGroups,
555
		true
556
	))->setHelp('Members');
557

    
558
	$section->add($group);
559

    
560
	$group = new Form_Group('');
561

    
562
	$group->add(new Form_Button(
563
		'movetoenabled',
564
		'Move to "Members"',
565
		null,
566
		'fa-angle-double-right'
567
	))->setAttribute('type','button')->removeClass('btn-primary')->addClass(
568
	    'btn-info btn-sm');
569

    
570
	$group->add(new Form_Button(
571
		'movetodisabled',
572
		'Move to "Not members',
573
		null,
574
		'fa-angle-double-left'
575
	))->setAttribute('type','button')->removeClass('btn-primary')->addClass(
576
	    'btn-info btn-sm');
577

    
578
	$group->setHelp(
579
	    'Hold down CTRL (PC)/COMMAND (Mac) key to select multiple items.');
580
	$section->add($group);
581

    
582
}
583

    
584
if (isset($pconfig['gid']) || $dup) {
585
	$section = new Form_Section('Assigned Privileges');
586

    
587
	$section->addInput(new Form_StaticText(
588
		null,
589
		build_priv_table()
590
	));
591

    
592

    
593
	$form->add($section);
594
}
595

    
596
print $form;
597
?>
598
<script type="text/javascript">
599
//<![CDATA[
600
events.push(function() {
601

    
602
	// On click . .
603
	$("#movetodisabled").click(function() {
604
		moveOptions($('[name="members[]"] option'),
605
		    $('[name="notmembers[]"]'));
606
	});
607

    
608
	$("#movetoenabled").click(function() {
609
		moveOptions($('[name="notmembers[]"] option'),
610
		    $('[name="members[]"]'));
611
	});
612

    
613
	// On submit mark all the user's groups as "selected"
614
	$('form').submit(function() {
615
		AllServers($('[name="members[]"] option'), true);
616
	});
617
});
618
//]]>
619
</script>
620
<?php
621
include('foot.inc');
(200-200/228)