Project

General

Profile

Download (15.6 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-2020 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
function cpusercmp($a, $b) {
52
	return strcasecmp($a['name'], $b['name']);
53
}
54

    
55
function admin_groups_sort() {
56
	global $a_group;
57

    
58
	if (!is_array($a_group)) {
59
		return;
60
	}
61

    
62
	usort($a_group, "cpusercmp");
63
}
64

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

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

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

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

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

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

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

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

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

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

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

    
128
	$act = "edit";
129
}
130

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

    
143
if (isset($_POST['dellall_x']) && !$read_only) {
144

    
145
	$del_groups = $_POST['delete_check'];
146
	$deleted_groups = array();
147

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

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

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

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

    
180
	do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
181

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

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

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

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

    
228
		$group['name'] = $_POST['groupname'];
229
		$group['description'] = $_POST['description'];
230
		$group['scope'] = $_POST['gtype'];
231

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

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

    
245
		admin_groups_sort();
246

    
247
		local_group_set($group);
248

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

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

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

    
274
		header("Location: system_groupmanager.php");
275
		exit;
276
	}
277

    
278
	$pconfig['name'] = $_POST['groupname'];
279
}
280

    
281
function build_priv_table() {
282
	global $a_group, $id, $read_only;
283

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

    
295
	$user_has_root_priv = false;
296

    
297
	foreach (get_user_privdesc($a_group[$id]) as $i => $priv) {
298
		$privhtml .=		'<tr>';
299
		$privhtml .=			'<td>' . htmlspecialchars($priv['name']) . '</td>';
300
		$privhtml .=			'<td>' . htmlspecialchars($priv['descr']);
301
		if (isset($priv['warn']) && ($priv['warn'] == 'standard-warning-root')) {
302
			$privhtml .=			' ' . gettext('(admin privilege)');
303
			$user_has_root_priv = true;
304
		}
305
		$privhtml .=			'</td>';
306
		if (!$read_only) {
307
			$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>';
308
		}
309
		$privhtml .=		'</tr>';
310

    
311
	}
312

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

    
322
	}
323

    
324
	$privhtml .=		'</tbody>';
325
	$privhtml .=	'</table>';
326
	$privhtml .= '</div>';
327

    
328
	$privhtml .= '<nav class="action-buttons">';
329
	if (!$read_only) {
330
		$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>';
331
	}
332
	$privhtml .= '</nav>';
333

    
334
	return($privhtml);
335
}
336

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

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

    
345
include("head.inc");
346

    
347
if ($input_errors) {
348
	print_input_errors($input_errors);
349
}
350

    
351
if ($savemsg) {
352
	print_info_box($savemsg, 'success');
353
}
354

    
355
$tab_array = array();
356
if (!isAllowedPage("system_usermanager.php")) {
357
	$tab_array[] = array(gettext("User Password"), false, "system_usermanager_passwordmg.php");
358
} else {
359
	$tab_array[] = array(gettext("Users"), false, "system_usermanager.php");
360
}
361
$tab_array[] = array(gettext("Groups"), true, "system_groupmanager.php");
362
$tab_array[] = array(gettext("Settings"), false, "system_usermanager_settings.php");
363
$tab_array[] = array(gettext("Authentication Servers"), false, "system_authservers.php");
364
display_top_tabs($tab_array);
365

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

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

    
431
$form = new Form;
432
$form->setAction('system_groupmanager.php?act=edit');
433
$form->addGlobal(new Form_Input(
434
	'groupid',
435
	null,
436
	'hidden',
437
	$id
438
));
439

    
440
if (isset($id) && $a_group[$id]) {
441
	$form->addGlobal(new Form_Input(
442
		'id',
443
		null,
444
		'hidden',
445
		$id
446
	));
447

    
448
	$form->addGlobal(new Form_Input(
449
		'gid',
450
		null,
451
		'hidden',
452
		$pconfig['gid']
453
	));
454
}
455

    
456
$section = new Form_Section('Group Properties');
457

    
458
$section->addInput($input = new Form_Input(
459
	'groupname',
460
	'*Group name',
461
	'text',
462
	$pconfig['name']
463
));
464

    
465
if ($pconfig['gtype'] == "system") {
466
	$input->setReadonly();
467

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

    
485
$section->addInput(new Form_Input(
486
	'description',
487
	'Description',
488
	'text',
489
	$pconfig['description']
490
))->setHelp('Group description, for administrative information only');
491

    
492
$form->add($section);
493

    
494
/* all users group */
495
if ($pconfig['gid'] != 1998) {
496
	/* Group membership */
497
	$group = new Form_Group('Group membership');
498

    
499
	/*
500
	 * Make a list of all the groups configured on the system, and a list of
501
	 * those which this user is a member of
502
	 */
503
	$systemGroups = array();
504
	$usersGroups = array();
505

    
506
	foreach ($config['system']['user'] as $user) {
507
		if (is_array($pconfig['members']) && in_array($user['uid'],
508
		    $pconfig['members'])) {
509
			/* Add it to the user's list */
510
			$usersGroups[ $user['uid'] ] = $user['name'];
511
		} else {
512
			/* Add it to the 'not a member of' list */
513
			$systemGroups[ $user['uid'] ] = $user['name'];
514
		}
515
	}
516

    
517
	$group->add(new Form_Select(
518
		'notmembers',
519
		null,
520
		array_combine((array)$pconfig['groups'],
521
		    (array)$pconfig['groups']),
522
		$systemGroups,
523
		true
524
	))->setHelp('Not members');
525

    
526
	$group->add(new Form_Select(
527
		'members',
528
		null,
529
		array_combine((array)$pconfig['groups'],
530
		    (array)$pconfig['groups']),
531
		$usersGroups,
532
		true
533
	))->setHelp('Members');
534

    
535
	$section->add($group);
536

    
537
	$group = new Form_Group('');
538

    
539
	$group->add(new Form_Button(
540
		'movetoenabled',
541
		'Move to "Members"',
542
		null,
543
		'fa-angle-double-right'
544
	))->setAttribute('type','button')->removeClass('btn-primary')->addClass(
545
	    'btn-info btn-sm');
546

    
547
	$group->add(new Form_Button(
548
		'movetodisabled',
549
		'Move to "Not members',
550
		null,
551
		'fa-angle-double-left'
552
	))->setAttribute('type','button')->removeClass('btn-primary')->addClass(
553
	    'btn-info btn-sm');
554

    
555
	$group->setHelp(
556
	    'Hold down CTRL (PC)/COMMAND (Mac) key to select multiple items.');
557
	$section->add($group);
558

    
559
}
560

    
561
if (isset($pconfig['gid'])) {
562
	$section = new Form_Section('Assigned Privileges');
563

    
564
	$section->addInput(new Form_StaticText(
565
		null,
566
		build_priv_table()
567
	));
568

    
569

    
570
	$form->add($section);
571
}
572

    
573
print $form;
574
?>
575
<script type="text/javascript">
576
//<![CDATA[
577
events.push(function() {
578

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

    
585
	$("#movetoenabled").click(function() {
586
		moveOptions($('[name="notmembers[]"] option'),
587
		    $('[name="members[]"]'));
588
	});
589

    
590
	// On submit mark all the user's groups as "selected"
591
	$('form').submit(function() {
592
		AllServers($('[name="members[]"] option'), true);
593
	});
594
});
595
//]]>
596
</script>
597
<?php
598
include('foot.inc');
(203-203/230)