xref: /freebsd/usr.sbin/bsdconfig/usermgmt/share/group.subr (revision 7bac4bd22a284139effe1fb94d221fc2ed784e46)
1if [ ! "$_USERMGMT_GROUP_SUBR" ]; then _USERMGMT_GROUP_SUBR=1
2#
3# Copyright (c) 2012 Ron McDowell
4# Copyright (c) 2012-2014 Devin Teske
5# All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions
9# are met:
10# 1. Redistributions of source code must retain the above copyright
11#    notice, this list of conditions and the following disclaimer.
12# 2. Redistributions in binary form must reproduce the above copyright
13#    notice, this list of conditions and the following disclaimer in the
14#    documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26# SUCH DAMAGE.
27#
28# $FreeBSD$
29#
30############################################################ INCLUDES
31
32BSDCFG_SHARE="/usr/share/bsdconfig"
33. $BSDCFG_SHARE/common.subr || exit 1
34f_dprintf "%s: loading includes..." usermgmt/group.subr
35f_include $BSDCFG_SHARE/dialog.subr
36f_include $BSDCFG_SHARE/usermgmt/group_input.subr
37
38BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt"
39f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr
40
41############################################################ CONFIGURATION
42
43# set some reasonable defaults if /etc/adduser.conf does not exist.
44[ -f /etc/adduser.conf ] && f_include /etc/adduser.conf
45: ${passwdtype:="yes"}
46
47############################################################ FUNCTIONS
48
49# f_group_add [$group]
50#
51# Add a group. If both $group (as a first argument) and $VAR_GROUP are unset
52# or NULL and we are running interactively, prompt the user to enter the name
53# of a new group and (if $VAR_NO_CONFIRM is unset or NULL) prompt the user to
54# answer some questions about the new group. Variables that can be used to
55# script user input:
56#
57# 	VAR_GROUP [Optional if running interactively]
58# 		The group to add. Ignored if given non-NULL first-argument.
59#	VAR_GROUP_GID [Optional]
60# 		Numerical group ID to use. If NULL or unset, the group ID is
61# 		automatically chosen.
62# 	VAR_GROUP_MEMBERS [Optional]
63# 		Comma separated list of users that are a member of this group.
64# 	VAR_GROUP_PASSWORD [Optional]
65# 		newgrp(1) password to set for the group. Default if NULL or
66# 		unset is to disable newgrp(1) password authentication.
67#
68# Returns success if the group was successfully added.
69#
70f_group_add()
71{
72	local funcname=f_group_add
73	local title # Calculated below
74	local alert=f_show_msg no_confirm=
75
76	f_getvar $VAR_NO_CONFIRM no_confirm
77	[ "$no_confirm" ] && alert=f_show_info
78
79	local input
80	f_getvar 3:-\$$VAR_GROUP input "$1"
81
82	#
83	# NB: pw(8) has a ``feature'' wherein `-n name' can be taken as GID
84	# instead of name. Work-around is to also pass `-g GID' at the same
85	# time (any GID will do; but `-1' is appropriate for this context).
86	#
87	if [ "$input" ] && f_quietly pw groupshow -n "$input" -g -1; then
88		f_show_err "$msg_group_already_used" "$input"
89		return $FAILURE
90	fi
91
92	local group_name="$input"
93	while f_interactive && [ ! "$group_name" ]; do
94		f_dialog_input_group_name group_name "$group_name" ||
95			return $SUCCESS
96		[ "$group_name" ] ||
97			f_show_err "$msg_please_enter_a_group_name"
98	done
99
100	local group_password group_gid group_members
101	f_getvar $VAR_GROUP_PASSWORD	group_password
102	f_getvar $VAR_GROUP_GID		group_gid
103	f_getvar $VAR_GROUP_MEMBERS	group_members
104
105	local group_password_disable=
106	f_interactive || [ "$group_password" ] || group_password_disable=1
107
108	if f_interactive && [ ! "$no_confirm" ]; then
109		f_dialog_noyes \
110			"$msg_use_default_values_for_all_account_details"
111		retval=$?
112		if [ $retval -eq $DIALOG_ESC ]; then
113			return $SUCCESS
114		elif [ $retval -ne $DIALOG_OK ]; then
115			#
116			# Ask series of questions to pre-fill the editor screen
117			#
118			# Defaults used in each dialog should allow the user to
119			# simply hit ENTER to proceed and cancelling a single
120			# dialog cause them to return to the previous menu.
121			#
122
123			if [ "$passwdtype" = "yes" ]; then
124				f_dialog_input_group_password group_password \
125					group_password_disable ||
126					return $FAILURE
127			fi
128			f_dialog_input_group_gid group_gid "$group_gid" ||
129				return $FAILURE
130			f_dialog_input_group_members group_members \
131				"$group_members" || return $FAILURE
132		fi
133	fi
134
135	#
136	# Loop until the user decides to Exit, Cancel, or presses ESC
137	#
138	title="$msg_add $msg_group: $group_name"
139	if f_interactive; then
140		local mtag retval defaultitem=
141		while :; do
142			f_dialog_title "$title"
143			f_dialog_menu_group_add "$defaultitem"
144			retval=$?
145			f_dialog_title_restore
146			f_dialog_menutag_fetch mtag
147			f_dprintf "retval=%u mtag=[%s]" $retval "$mtag"
148			defaultitem="$mtag"
149
150			# Return if user either pressed ESC or chose Cancel/No
151			[ $retval -eq $DIALOG_OK ] || return $FAILURE
152
153			case "$mtag" in
154			X) # Add/Exit
155			   local cmd="pw groupadd -n '$group_name'"
156			   [ "$group_gid" ] && cmd="$cmd -g '$group_gid'"
157			   [ "$group_members" ] &&
158			   	cmd="$cmd -M '$group_members'"
159
160			   # Execute the command (break on success)
161			   if [ "$group_password_disable" ]; then
162			   	f_eval_catch $funcname pw '%s -h -' "$cmd"
163			   elif [ "$group_password" ]; then
164			   	echo "$group_password" |
165			   		f_eval_catch $funcname \
166			   			pw '%s -h 0' "$cmd"
167			   else
168			   	f_eval_catch $funcname pw '%s' "$cmd"
169			   fi && break
170			   ;;
171			1) # Group Name (prompt for new group name)
172			   f_dialog_input_group_name input "$group_name" ||
173			   	continue
174			   if f_quietly pw groupshow -n "$input" -g -1; then
175			   	f_show_err "$msg_group_already_used" "$input"
176			   	continue
177			   fi
178			   group_name="$input"
179			   title="$msg_add $msg_group: $group_name"
180			   ;;
181			2) # Password
182			   f_dialog_input_group_password group_password \
183			   	group_password_disable
184			   ;;
185			3) # Group ID
186			   f_dialog_input_group_gid group_gid "$group_gid"
187			   ;;
188			4) # Group Members
189			   f_dialog_input_group_members group_members \
190					"$group_members"
191			   ;;
192			esac
193		done
194	else
195		# Form the command
196		local cmd="pw groupadd -n '$group_name'"
197		[ "$group_gid" ] && cmd="$cmd -g '$group_gid'"
198		[ "$group_members" ] && cmd="$cmd -M '$group_members'"
199
200		# Execute the command
201		local retval err
202		if [ "$group_password_disable" ]; then
203			f_eval_catch -k err $funcname pw '%s -h -' "$cmd"
204		elif [ "$group_password" ]; then
205			echo "$group_password" | f_eval_catch -k err \
206				$funcname pw '%s -h 0' "$cmd"
207		else
208			f_eval_catch -k err $funcname pw '%s' "$cmd"
209		fi
210		retval=$?
211		if [ $retval -ne $SUCCESS ]; then
212			f_show_err "%s" "$err"
213			return $retval
214		fi
215	fi
216
217	f_dialog_title "$title"
218	$alert "$msg_group_added"
219	f_dialog_title_restore
220	[ "$no_confirm" -a "$USE_DIALOG" ] && sleep 2
221
222	return $SUCCESS
223}
224
225# f_group_delete [$group]
226#
227# Delete a group. If both $group (as a first argument) and $VAR_GROUP are unset
228# or NULL and we are running interactively, prompt the user to select a group
229# from a list of available groups. Variables that can be used to script user
230# input:
231#
232# 	VAR_GROUP [Optional if running interactively]
233# 		The group to delete. Ignored if given non-NULL first-argument.
234#
235# Returns success if the group was successfully deleted.
236#
237f_group_delete()
238{
239	local funcname=f_group_delete
240	local title # Calculated below
241	local alert=f_show_msg no_confirm=
242
243	f_getvar $VAR_NO_CONFIRM no_confirm
244	[ "$no_confirm" ] && alert=f_show_info
245
246	local input
247	f_getvar 3:-\$$VAR_GROUP input "$1"
248
249	local group_name group_password group_gid group_members
250	if [ "$input" ] && ! f_input_group "$input"; then
251		f_show_err "$msg_group_not_found" "$input"
252		return $FAILURE
253	fi
254
255	#
256	# Loop until the user decides to Exit, Cancel, or presses ESC
257	#
258	title="$msg_delete $msg_group: $group_name"
259	if f_interactive; then
260		local mtag retval defaultitem=
261		while :; do
262        		f_dialog_title "$title"
263			f_dialog_menu_group_delete "$group_name" "$defaultitem"
264			retval=$?
265			f_dialog_title_restore
266			f_dialog_menutag_fetch mtag
267			f_dprintf "retval=%u mtag=[%s]" $retval "$mtag"
268			defaultitem="$mtag"
269
270			# Return if user either pressed ESC or chose Cancel/No
271			[ $retval -eq $DIALOG_OK ] || return $FAILURE
272
273			case "$mtag" in
274			X) # Delete/Exit
275			   f_eval_catch $funcname pw 'pw groupdel "%s"' \
276					"$group_name" && break
277			   ;;
278			1) # Group Name (select different group from list)
279			   f_dialog_menu_group_list "$group_name" || continue
280			   f_dialog_menutag_fetch mtag
281
282			   [ "$mtag" = "X $msg_exit" ] && continue
283
284			   if ! f_input_group "$mtag"; then
285				f_show_err "$msg_group_not_found" "$mtag"
286				# Attempt to fall back to previous selection
287				f_input_group "$input" || return $FAILURE
288			   else
289				input="$mtag"
290			   fi
291			   ;;
292			esac
293		done
294	else
295		local retval err
296		f_eval_catch -k err $funcname pw \
297			'pw groupdel "%s"' "$group_name"
298		retval=$?
299		if [ $retval -ne $SUCCESS ]; then
300			f_show_err "%s" "$err"
301			return $retval
302		fi
303	fi
304
305        f_dialog_title "$title"
306	$alert "$msg_group_deleted"
307	f_dialog_title_restore
308	[ "$no_confirm" -a "$USE_DIALOG" ] && sleep 2
309
310	return $SUCCESS
311}
312
313# f_group_edit [$group]
314#
315# Modify a group. If both $group (as a first argument) and $VAR_GROUP are unset
316# or NULL and we are running interactively, prompt the user to select a group
317# from a list of available groups. Variables that can be used to script user
318# input:
319#
320# 	VAR_GROUP [Optional if running interactively]
321# 		The group to modify. Ignored if given non-NULL first-argument.
322#	VAR_GROUP_GID [Optional]
323# 		Numerical group ID to set. If NULL or unset, the group ID is
324# 		unchanged.
325# 	VAR_GROUP_MEMBERS [Optional]
326# 		Comma separated list of users that are a member of this group.
327# 		If set but NULL, group memberships are reset (no users will be
328# 		a member of this group). If unset, group membership is
329# 		unmodified.
330# 	VAR_GROUP_PASSWORD [Optional]
331# 		newgrp(1) password to set for the group. If unset, the password
332# 		is unmodified. If NULL, the newgrp(1) password is disabled.
333#
334# Returns success if the group was successfully modified.
335#
336f_group_edit()
337{
338	local funcname=f_group_edit
339	local title # Calculated below
340	local alert=f_show_msg no_confirm=
341
342	f_getvar $VAR_NO_CONFIRM no_confirm
343	[ "$no_confirm" ] && alert=f_show_info
344
345	local input
346	f_getvar 3:-\$$VAR_GROUP input "$1"
347
348	#
349	# NB: pw(8) has a ``feature'' wherein `-n name' can be taken as GID
350	# instead of name. Work-around is to also pass `-g GID' at the same
351	# time (any GID will do; but `-1' is appropriate for this context).
352	#
353	if [ "$input" ] && ! f_quietly pw groupshow -n "$input" -g -1; then
354		f_show_err "$msg_group_not_found" "$input"
355		return $FAILURE
356	fi
357
358	if f_interactive && [ ! "$input" ]; then
359		f_dialog_menu_group_list || return $SUCCESS
360		f_dialog_menutag_fetch input
361		[ "$input" = "X $msg_exit" ] && return $SUCCESS
362	elif [ ! "$input" ]; then
363		f_show_err "$msg_no_group_specified"
364		return $FAILURE
365	fi
366
367	local group_name group_password group_gid group_members
368	if ! f_input_group "$input"; then
369		f_show_err "$msg_group_not_found" "$input"
370		return $FAILURE
371	fi
372
373	f_isset $VAR_GROUP_GID && f_getvar $VAR_GROUP_GID group_gid
374	local null_members=
375	if f_isset $VAR_GROUP_MEMBERS; then
376		f_getvar $VAR_GROUP_MEMBERS group_members
377		[ "$group_members" ] || null_members=1
378	fi
379	local group_password_disable=
380	if f_isset $VAR_GROUP_PASSWORD; then
381		f_getvar $VAR_GROUP_PASSWORD group_password
382		[ "$group_password" ] || group_password_disable=1
383	fi
384
385	#
386	# Loop until the user decides to Exit, Cancel, or presses ESC
387	#
388	title="$msg_edit_view $msg_group: $group_name"
389	if f_interactive; then
390		local mtag retval defaultitem=
391		while :; do
392			f_dialog_title "$title"
393			f_dialog_menu_group_edit "$defaultitem"
394			retval=$?
395			f_dialog_title_restore
396			f_dialog_menutag_fetch mtag
397			f_dprintf "retval=%u mtag=[%s]" $retval "$mtag"
398			defaultitem="$mtag"
399
400			# Return if user either pressed ESC or chose Cancel/No
401			[ $retval -eq $DIALOG_OK ] || return $FAILURE
402
403			case "$mtag" in
404			X) # Save/Exit
405			   local cmd="pw groupmod -n '$group_name'"
406			   [ "$group_gid" ] && cmd="$cmd -g '$group_gid'"
407			   [ "$group_members" -o "$null_members" ] &&
408			   	cmd="$cmd -M '$group_members'"
409
410			   # Execute the command (break on success)
411			   if [ "$group_password_disable" ]; then
412			   	f_eval_catch $funcname pw '%s -h -' "$cmd"
413			   elif [ "$group_password" ]; then
414			   	echo "$group_password" | f_eval_catch \
415			   		$funcname pw '%s -h 0' "$cmd"
416			   else
417			   	f_eval_catch $funcname pw '%s' "$cmd"
418			   fi && break
419			   ;;
420			1) # Group Name (select different group from list)
421			   f_dialog_menu_group_list "$group_name" || continue
422			   f_dialog_menutag_fetch mtag
423
424			   [ "$mtag" = "X $msg_exit" ] && continue
425
426			   if ! f_input_group "$mtag"; then
427			   	f_show_err "$msg_group_not_found" "$mtag"
428			   	# Attempt to fall back to previous selection
429			   	f_input_group "$input" || return $FAILURE
430			   else
431			   	input="$mtag"
432			   fi
433			   title="$msg_edit_view $msg_group: $group_name"
434			   ;;
435			2) # Password
436			   f_dialog_input_group_password group_password \
437			   	group_password_disable
438			   ;;
439			3) # Group ID
440			   f_dialog_input_group_gid group_gid "$group_gid"
441			   ;;
442			4) # Group Members
443			   f_dialog_input_group_members group_members \
444			   	"$group_members" && [ ! "$group_members" ] &&
445			   	null_members=1
446			   ;;
447			esac
448		done
449	else
450		# Form the command
451		local cmd="pw groupmod -n '$group_name'"
452		[ "$group_gid" ] && cmd="$cmd -g '$group_gid'"
453		[ "$group_members" -o "$null_members" ] &&
454			cmd="$cmd -M '$group_members'"
455
456		# Execute the command
457		local retval err
458		if [ "$group_password_disable" ]; then
459			f_eval_catch -k err $funcname pw '%s -h -' "$cmd"
460		elif [ "$group_password" -o "$null_password" ]; then
461			echo "$group_password" | f_eval_catch -k err \
462				$funcname pw '%s -h 0' "$cmd"
463		else
464			f_eval_catch -k err $funcname pw '%s' "$cmd"
465		fi
466		retval=$?
467		if [ $retval -ne $SUCCESS ]; then
468			f_show_err "%s" "$err"
469			return $retval
470		fi
471	fi
472
473	f_dialog_title "$title"
474	$alert "$msg_group_updated"
475	f_dialog_title_restore
476	[ "$no_confirm" -a "$USE_DIALOG" ] && sleep 2
477
478	return $SUCCESS
479}
480
481############################################################ MAIN
482
483f_dprintf "%s: Successfully loaded." usermgmt/group.subr
484
485fi # ! $_USERMGMT_GROUP_SUBR
486