xref: /freebsd/usr.sbin/bsdconfig/usermgmt/share/group_input.subr (revision bc96366c864c07ef352edb92017357917c75b36c)
1if [ ! "$_USERMGMT_GROUP_INPUT_SUBR" ]; then _USERMGMT_GROUP_INPUT_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_input.subr
35f_include $BSDCFG_SHARE/dialog.subr
36f_include $BSDCFG_SHARE/strings.subr
37
38BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt"
39f_include_lang $BSDCFG_LIBE/include/messages.subr
40f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr
41
42############################################################ FUNCTIONS
43
44# f_input_group $group
45#
46# Given $group name or id, create the environment variables group_name,
47# group_gid, and group_members (and group_password is reset to NULL).
48#
49f_input_group()
50{
51	local funcname=f_input_group
52	local group="$1"
53
54	f_dprintf "$funcname: Getting info for group \`%s'" "$group"
55	eval "$( pw groupshow "$group" 2> /dev/null | awk -F: '
56	function set_value(var, value) {
57		gsub(/'\''/, "'\''\\'\'\''", value)
58		printf "group_%s='\'%s\''\n", var, value
59	}
60	{
61		found = $1 != ""
62		set_value("name",     $1)
63		set_value("password", "")
64		set_value("gid",      $3)
65		set_value("members",  $4)
66		exit
67	}
68	END { if (!found) print "false" }' )"
69}
70
71# f_dialog_menu_group_list [$default]
72#
73# Allows the user to select a group from a list. Optionally, if present and
74# non-NULL, initially highlight $default group.
75#
76f_dialog_menu_group_list()
77{
78	local prompt=
79	local menu_list="
80		'X $msg_exit' ''
81	" # END-QUOTE
82	local defaultitem="$1"
83	local hline="$hline_alnum_punc_tab_enter"
84
85	# Add groups from group(5)
86	menu_list="$menu_list $( pw groupshow -a | awk -F: '
87		function mprint(tag, item) {
88			gsub(/'\''/, "'\''\\'\'\''", tag)
89			gsub(/'\''/, "'\''\\'\'\''", item)
90			printf "'\'%s\'\ \'%s\''\n", tag, item
91		}
92		!/^[[:space:]]*(#|$)/ { mprint($1, $1) }
93	' )"
94
95	local height width rows
96	eval f_dialog_menu_size height width rows \
97	                        \"\$DIALOG_TITLE\"     \
98	                        \"\$DIALOG_BACKTITLE\" \
99	                        \"\$prompt\"           \
100	                        \"\$hline\"            \
101	                        $menu_list
102
103	local menu_choice
104	menu_choice=$( eval $DIALOG \
105		--title \"\$DIALOG_TITLE\"         \
106		--backtitle \"\$DIALOG_BACKTITLE\" \
107		--hline \"\$hline\"                \
108		--ok-label \"\$msg_ok\"            \
109		--cancel-label \"\$msg_cancel\"    \
110		--default-item \"\$defaultitem\"   \
111		--menu \"\$prompt\"                \
112		$height $width $rows               \
113		$menu_list                         \
114		2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
115	)
116	local retval=$?
117	f_dialog_menutag_store -s "$menu_choice"
118	return $retval
119}
120
121# f_dialog_input_group_name $var_to_set [$group_name]
122#
123# Allows the user to enter a name for a new group. If the user does not cancel
124# or press ESC, the $var_to_set variable will hold the newly-configured value
125# upon return.
126#
127f_dialog_input_group_name()
128{
129	local __var_to_set="$1" __name="$2"
130
131	#
132	# Loop until the user provides taint-free/valid input
133	#
134	local __input="$__name"
135	while :; do
136		# Return if user has either pressed ESC or chosen Cancel/No
137		f_dialog_input __input "$msg_group" "$__input" \
138		               "$hline_alnum_tab_enter" || return $?
139
140		# Check for no-change
141		if [ "$__input" = "$__name" ]; then
142			setvar "$__var_to_set" "$__input"
143			return $DIALOG_OK
144		fi
145
146		# Check for NULL entry
147		if [ ! "$__input" ]; then
148			f_show_msg "$msg_group_is_empty"
149			continue
150		fi
151
152		# Check for invalid entry
153		case "$__input" in [!a-zA-Z]*)
154			f_show_msg "$msg_group_must_start_with_letter"
155			continue
156		esac
157
158		# Check for duplicate entry
159		if f_quietly pw groupshow -n "$__input"; then
160			f_show_msg "$msg_group_already_used" "$__input"
161			continue
162		fi
163
164		setvar "$__var_to_set" "$__input"
165		break
166	done
167
168	return $DIALOG_OK
169}
170
171# f_dialog_input_group_password $var_to_set $dvar_to_set
172#
173# Prompt the user to enter a password (twice). If the user does not cancel or
174# press ESC, $var_to_set will hold the confirmed user entry. Otherwise, if the
175# user cancels or enters a NULL password (twice), they are given the choice to
176# disable password authentication for the given group, wherein $dvar_to_set has
177# a value of 1 to indicate password authentication should be disabled.
178#
179f_dialog_input_group_password()
180{
181	local __var_to_set="$1" __dvar_to_set="$2"
182	local __prompt1="$msg_group_password"
183	local __prompt2="$msg_reenter_group_password"
184	local __hline="$hline_alnum_punc_tab_enter"
185
186	local __height1 __width1
187	f_dialog_inputbox_size __height1 __width1 \
188	        	"$DIALOG_TITLE"     \
189	        	"$DIALOG_BACKTITLE" \
190	        	"$__prompt1"        \
191	        	""                  \
192	        	"$__hline"
193
194	local __height2 __width2
195	f_dialog_inputbox_size __height2 __width2 \
196	        	"$DIALOG_TITLE"     \
197	        	"$DIALOG_BACKTITLE" \
198	        	"$__prompt2"        \
199	        	""                  \
200	        	"$__hline"
201
202	#
203	# Loop until the user provides taint-free/valid input
204	#
205	local __retval __password1 __password2
206	while :; do
207		__password1=$( $DIALOG \
208			--title "$DIALOG_TITLE"         \
209			--backtitle "$DIALOG_BACKTITLE" \
210			--hline "$__hline"              \
211			--ok-label "$msg_ok"            \
212			--cancel-label "$msg_cancel"    \
213			--insecure                      \
214			--passwordbox "$__prompt1"      \
215			$__height1 $__width1            \
216			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
217		)
218		__retval=$?
219		debug= f_dialog_line_sanitize __password1
220
221		# Return if user has either pressed ESC or chosen Cancel/No
222		[ $__retval -eq $DIALOG_OK ] || return $__retval
223
224		__password2=$( $DIALOG \
225			--title "$DIALOG_TITLE"         \
226			--backtitle "$DIALOG_BACKTITLE" \
227			--hline "$__hline"              \
228			--ok-label "$msg_ok"            \
229			--cancel-label "$msg_cancel"    \
230			--insecure                      \
231			--passwordbox "$__prompt2"      \
232			$__height2 $__width2            \
233			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
234		)
235		__retval=$?
236		debug= f_dialog_line_sanitize __password2
237
238		# Return if user has either pressed ESC or chosen Cancel/No
239		[ $__retval -eq $DIALOG_OK ] || return $__retval
240
241		# Check for password mismatch
242		if [ "$__password1" != "$__password2" ]; then
243			f_show_msg "$msg_group_passwords_do_not_match"
244			continue
245		fi
246
247		# Check for NULL entry
248		if [ ! "$__password1" ]; then
249			f_dialog_yesno "$msg_disable_password_auth_for_group"
250			__retval=$?
251			if [ $__retval -eq $DIALOG_ESC ]; then
252				return $__retval
253			elif [ $__retval -eq $DIALOG_OK ]; then
254				setvar "$__dvar_to_set" 1
255			else
256				continue # back to password prompt
257			fi
258		else
259			setvar "$__dvar_to_set" ""
260		fi
261
262		setvar "$__var_to_set" "$__password1"
263		break
264	done
265
266	return $DIALOG_OK
267}
268
269# f_dialog_input_group_gid $var_to_set [$group_gid]
270#
271# Allow the user to enter a new GID for a given group. If the user does not
272# cancel or press ESC, the $var_to_set variable will hold the newly-configured
273# value upon return.
274#
275f_dialog_input_group_gid()
276{
277	local __var_to_set="$1" __input="$2"
278
279	# Return if user has either pressed ESC or chosen Cancel/No
280	f_dialog_input __input "$msg_group_id_leave_empty_for_default" \
281	               "$__input" "$hline_num_tab_enter" || return $?
282
283	setvar "$__var_to_set" "$__input"
284	return $DIALOG_OK
285}
286
287# f_dialog_input_group_members $var_to_set [$group_members]
288#
289# Allow the user to modify a list of members for a given group. If the user
290# does not cancel or press ESC, the $var_to_set variable will hold the newly-
291# configured value upon return.
292#
293f_dialog_input_group_members()
294{
295	local __var_to_set="$1" __input="$2"
296	local __prompt="$msg_group_members:"
297	local __menu_list="
298		'X' '$msg_continue'
299		'1' '$msg_select_group_members_from_list'
300		'2' '$msg_enter_group_members_manually'
301	" # END-QUOTE
302	local __defaultitem=
303	local __hline="$hline_num_arrows_tab_enter"
304
305	local __mheight __mwidth __mrows
306	eval f_dialog_menu_size __mheight __mwidth __mrows \
307	                        \"\$DIALOG_TITLE\"     \
308	                        \"\$DIALOG_BACKTITLE\" \
309	                        \"\$__prompt\"         \
310	                        \"\$__hline\"          \
311	                        $__menu_list
312
313	local __menu_choice __retval
314	while :; do
315		__menu_choice=$( eval $DIALOG \
316			--title \"\$DIALOG_TITLE\"         \
317			--backtitle \"\$DIALOG_BACKTITLE\" \
318			--hline \"\$__hline\"              \
319			--ok-label \"\$msg_ok\"            \
320			--cancel-label \"\$msg_cancel\"    \
321			--default-item \"\$__defaultitem\" \
322			--menu \"\$__prompt\"              \
323			$__mheight $__mwidth $__mrows      \
324			$__menu_list                       \
325			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
326		)
327		__retval=$?
328		f_dialog_data_sanitize __menu_choice
329		__defaultitem="$__menu_choice"
330		f_dprintf "retval=%u menu_choice=[%s]" \
331		          $__retval "$__menu_choice"
332
333		# Return if user has either pressed ESC or chosen Cancel/No
334		[ $__retval -eq $DIALOG_OK ] || return $__retval
335
336		local __group_members
337		case "$__menu_choice" in
338		X) # Exit
339			break ;;
340		1) # Select Group Members from a list
341			local __check_list= # Calculated below
342			local __user_list __u __user __length=0
343			__user_list=$( pw usershow -a |
344				awk -F: '!/^[[:space:]]*(#|$)/{print $1}' )
345			while [ $__length -ne ${#__user_list} ]; do
346				__u="${__user_list%%$NL*}" # First line
347				f_shell_escape "$__u" __user
348
349				# Format of a checklist entry: tag item status
350				__check_list="$__check_list '$__user' ''"
351				case "$__input" in
352				"$__u"|"$__u",*|*,"$__u",*|*,"$__u")
353					__check_list="$__check_list on" ;;
354				*)
355					__check_list="$__check_list off"
356				esac
357
358				__length=${#__user_list}
359				__user_list="${__user_list#*$NL}" # Kill line
360			done
361
362			local __cheight __cwidth __crows
363			eval f_dialog_checklist_size \
364				__cheight __cwidth __crows \
365				\"\$DIALOG_TITLE\"     \
366				\"\$DIALOG_BACKTITLE\" \
367				\"\$__prompt\"         \
368				\"\$__hline\"          \
369				$__check_list
370			__group_members=$( eval $DIALOG \
371				--title \"\$DIALOG_TITLE\"         \
372				--backtitle \"\$DIALOG_BACKTITLE\" \
373				--separate-output                  \
374				--hline \"\$__hline\"              \
375				--ok-label \"\$msg_ok\"            \
376				--cancel-label \"\$msg_cancel\"    \
377				--checklist \"\$__prompt\"         \
378				$__cheight $__cwidth $__crows      \
379				$__check_list                      \
380				2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
381			) || continue
382				# Return to previous menu if user either
383				# pressed ESC or chose Cancel/No
384			f_dialog_data_sanitize __group_members
385
386			#
387			# Convert the newline separated list into a comma-
388			# separated one so that if the user switches over to
389			# manual editing, list reflects checklist selections
390			#
391			f_replaceall "$__group_members" "[$NL]" "," __input
392			;;
393		2) # Enter Group Members manually
394			local __prompt2="$msg_group_members"
395			__prompt2="$__prompt2 ($msg_separated_by_commas)"
396
397			f_dialog_input __group_members \
398			               "$__prompt2" "$__input" \
399			               "$hline_num_tab_enter" || continue
400				# Return to previous menu if user either
401				# pressed ESC or chose Cancel/No
402
403			__input="$__group_members"
404			;;
405		esac
406	done
407
408	setvar "$__var_to_set" "$__input"
409	return $DIALOG_OK
410}
411
412# f_dialog_menu_group_add [$defaultitem]
413#
414# Present a menu detailing the properties of a group that is about to be added.
415# The user's menu choice is available using f_dialog_menutag_fetch(). Returns
416# success unless the user chose Cancel or pressed ESC. Data to display is taken
417# from environment variables group_name, group_gid, and group_members. If
418# $defaultitem is present and non-NULL, initially highlight the item in the
419# menu.
420#
421f_dialog_menu_group_add()
422{
423	local prompt="$msg_save_exit_or_cancel"
424	local menu_list # Calculated below
425	local defaultitem="$1"
426	local hline="$hline_arrows_tab_enter"
427
428	# Localize potentially hostile variables and escape their values
429	# to the local variable (see f_shell_escape() of `strings.subr')
430	local var
431	for var in gid members name; do
432		local _group_$var
433		eval f_shell_escape \"\$group_$var\" _group_$var
434	done
435
436	menu_list="
437		'X' '$msg_add/$msg_exit'
438		'1' '$msg_group: $_group_name'
439		'2' '$msg_password: -----'
440		'3' '$msg_group_id: $_group_gid'
441		'4' '$msg_group_members: $_group_members'
442	" # END-QUOTE
443
444	local height width rows
445	eval f_dialog_menu_size height width rows \
446	                        \"\$DIALOG_TITLE\"     \
447	                        \"\$DIALOG_BACKTITLE\" \
448	                        \"\$prompt\"           \
449	                        \"\$hline\"            \
450	                        $menu_list
451
452	local menu_choice
453	menu_choice=$( eval $DIALOG \
454		--title \"\$DIALOG_TITLE\"         \
455		--backtitle \"\$DIALOG_BACKTITLE\" \
456		--hline \"\$hline\"                \
457		--ok-label \"\$msg_ok\"            \
458		--cancel-label \"\$msg_cancel\"    \
459		--default-item \"\$defaultitem\"   \
460		--menu \"\$prompt\"                \
461		$height $width $rows               \
462		$menu_list                         \
463		2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
464	)
465	local retval=$?
466	f_dialog_data_sanitize menu_choice
467	f_dialog_menutag_store "$menu_choice"
468	return $retval
469}
470
471# f_dialog_menu_group_delete $group [$defaultitem]
472#
473# Present a menu detailing the properties of a group that is about to be
474# deleted. The user's menu choice is available using f_dialog_menutag_fetch().
475# Returns success unless the user chose Cancel or pressed ESC. Data to display
476# is populated automatically from the system accounting database for the given
477# $group argument. If $defaultitem is present and non-NULL, initially highlight
478# the item in the menu.
479#
480f_dialog_menu_group_delete()
481{
482	local prompt="$msg_delete_exit_or_cancel"
483	local menu_list # Calculated below
484	local defaultitem="$2"
485	local hline="$hline_arrows_tab_enter"
486
487	local group_name group_password group_gid group_members
488	f_input_group "$1"
489
490	# Localize potentially hostile variables and escape their values
491	# to the local variable (see f_shell_escape() of `strings.subr')
492	local var
493	for var in gid members name; do
494		local _group_$var
495		eval f_shell_escape \"\$group_$var\" _group_$var
496	done
497
498	menu_list="
499		'X' '$msg_delete/$msg_exit'
500		'1' '$msg_group: $_group_name'
501		'-' '$msg_password: -----'
502		'-' '$msg_group_id: $_group_gid'
503		'-' '$msg_group_members: $_group_members'
504	" # END-QUOTE
505
506	local height width rows
507	eval f_dialog_menu_size height width rows \
508	                        \"\$DIALOG_TITLE\"     \
509	                        \"\$DIALOG_BACKTITLE\" \
510	                        \"\$prompt\"           \
511	                        \"\$hline\"            \
512	                        $menu_list
513
514	local menu_choice
515	menu_choice=$( eval $DIALOG \
516		--title \"\$DIALOG_TITLE\"         \
517		--backtitle \"\$DIALOG_BACKTITLE\" \
518		--hline \"\$hline\"                \
519		--ok-label \"\$msg_ok\"            \
520		--cancel-label \"\$msg_cancel\"    \
521		--default-item \"\$defaultitem\"   \
522		--menu \"\$prompt\"                \
523		$height $width $rows               \
524		$menu_list                         \
525		2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
526	)
527	local retval=$?
528	f_dialog_data_sanitize menu_choice
529	f_dialog_menutag_store "$menu_choice"
530	return $retval
531}
532
533# f_dialog_menu_group_edit [$defaultitem]
534#
535# Present a menu detailing the properties of a group that is about to be
536# modified. The user's menu choice is available using f_dialog_menutag_fetch().
537# Returns success unless the user chose Cancel or pressed ESC. Data to display
538# is taken from environment variables group_name, group_gid, and group_members.
539# If $defaultitem is present and non-NULL, initially highlight the item in the
540# menu.
541#
542f_dialog_menu_group_edit()
543{
544	local prompt="$msg_save_exit_or_cancel"
545	local menu_list # Calculated below
546	local defaultitem="$1"
547	local hline="$hline_arrows_tab_enter"
548
549	# Localize potentially hostile variables and escape their values
550	# to the local variable (see f_shell_escape() of `strings.subr')
551	local var
552	for var in gid members name; do
553		local _group_$var
554		eval f_shell_escape \"\$group_$var\" _group_$var
555	done
556
557	menu_list="
558		'X' '$msg_save/$msg_exit'
559		'1' '$msg_group: $_group_name'
560		'2' '$msg_password: -----'
561		'3' '$msg_group_id: $_group_gid'
562		'4' '$msg_group_members: $_group_members'
563	" # END-QUOTE
564
565	local height width rows
566	eval f_dialog_menu_size height width rows \
567	                        \"\$DIALOG_TITLE\"     \
568	                        \"\$DIALOG_BACKTITLE\" \
569	                        \"\$prompt\"           \
570	                        \"\$hline\"            \
571	                        $menu_list
572
573	local menu_choice
574	menu_choice=$( eval $DIALOG \
575		--title \"\$DIALOG_TITLE\"         \
576		--backtitle \"\$DIALOG_BACKTITLE\" \
577		--hline \"\$hline\"                \
578		--ok-label \"\$msg_ok\"            \
579		--cancel-label \"\$msg_cancel\"    \
580		--default-item \"\$defaultitem\"   \
581		--menu \"\$prompt\"                \
582		$height $width $rows               \
583		$menu_list                         \
584		2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
585	)
586	local retval=$?
587	f_dialog_data_sanitize menu_choice
588	f_dialog_menutag_store "$menu_choice"
589	return $retval
590}
591
592############################################################ MAIN
593
594f_dprintf "%s: Successfully loaded." usermgmt/group_input.subr
595
596fi # ! $_USERMGMT_GROUP_INPUT_SUBR
597