256 lines
11 KiB
Diff
256 lines
11 KiB
Diff
From c9fd30dcf2bb08bcffd64add37199b2087785d5c Mon Sep 17 00:00:00 2001
|
|
From: Lennart Poettering <lennart@poettering.net>
|
|
Date: Wed, 19 Feb 2025 21:56:14 +0100
|
|
Subject: [PATCH] userdbctl: optionally show user/group data from JSON
|
|
filerather than from system
|
|
|
|
(cherry picked from commit fd0dd2d4bce00b69f8badab1a71b8929e392af5c)
|
|
|
|
Resolves: RHEL-143029
|
|
---
|
|
man/userdbctl.xml | 19 ++++++
|
|
src/userdb/userdbctl.c | 76 +++++++++++++++++++++--
|
|
test/units/TEST-74-AUX-UTILS.userdbctl.sh | 16 +++++
|
|
3 files changed, 106 insertions(+), 5 deletions(-)
|
|
create mode 100755 test/units/TEST-74-AUX-UTILS.userdbctl.sh
|
|
|
|
diff --git a/man/userdbctl.xml b/man/userdbctl.xml
|
|
index 268da7ac3d..dd51226868 100644
|
|
--- a/man/userdbctl.xml
|
|
+++ b/man/userdbctl.xml
|
|
@@ -243,6 +243,19 @@
|
|
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
|
</varlistentry>
|
|
|
|
+ <varlistentry>
|
|
+ <term><option>--from-file=PATH</option></term>
|
|
+ <term><option>-f</option></term>
|
|
+
|
|
+ <listitem><para>When used with the <command>user</command> or <command>group</command> command, read
|
|
+ the user definition in JSON format from the specified file, instead of querying it from the
|
|
+ system. If the path is specified as <literal>-</literal>, reads the JSON data from standard
|
|
+ input. This is useful to validate and introspect JSON user or group records quickly, and check how
|
|
+ they would be interpreted on the local system.</para>
|
|
+
|
|
+ <xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
|
+ </varlistentry>
|
|
+
|
|
<xi:include href="standard-options.xml" xpointer="no-pager" />
|
|
<xi:include href="standard-options.xml" xpointer="no-legend" />
|
|
<xi:include href="standard-options.xml" xpointer="help" />
|
|
@@ -263,6 +276,9 @@
|
|
<listitem><para>List all known users records or show details of one or more specified user
|
|
records. Use <option>--output=</option> to tweak output mode.</para>
|
|
|
|
+ <para>If used in conjuntion with <option>--from-file=</option> the user record data is read in JSON
|
|
+ format from the specified file instead of querying it from the system. For details see above.</para>
|
|
+
|
|
<xi:include href="version-info.xml" xpointer="v245"/></listitem>
|
|
</varlistentry>
|
|
|
|
@@ -272,6 +288,9 @@
|
|
<listitem><para>List all known group records or show details of one or more specified group
|
|
records. Use <option>--output=</option> to tweak output mode.</para>
|
|
|
|
+ <para>If used in conjuntion with <option>--from-file=</option> the group record data is read in JSON
|
|
+ format from the specified file instead of querying it from the system. For details see above.</para>
|
|
+
|
|
<xi:include href="version-info.xml" xpointer="v245"/></listitem>
|
|
</varlistentry>
|
|
|
|
diff --git a/src/userdb/userdbctl.c b/src/userdb/userdbctl.c
|
|
index d295ca5495..0bb458eb15 100644
|
|
--- a/src/userdb/userdbctl.c
|
|
+++ b/src/userdb/userdbctl.c
|
|
@@ -44,8 +44,10 @@ static uid_t arg_uid_min = 0;
|
|
static uid_t arg_uid_max = UID_INVALID-1;
|
|
static bool arg_fuzzy = false;
|
|
static bool arg_boundaries = true;
|
|
+static sd_json_variant *arg_from_file = NULL;
|
|
|
|
STATIC_DESTRUCTOR_REGISTER(arg_services, strv_freep);
|
|
+STATIC_DESTRUCTOR_REGISTER(arg_from_file, sd_json_variant_unrefp);
|
|
|
|
static const char *user_disposition_to_color(UserDisposition d) {
|
|
assert(d >= 0);
|
|
@@ -372,7 +374,7 @@ static int display_user(int argc, char *argv[], void *userdata) {
|
|
int ret = 0, r;
|
|
|
|
if (arg_output < 0)
|
|
- arg_output = argc > 1 && !arg_fuzzy ? OUTPUT_FRIENDLY : OUTPUT_TABLE;
|
|
+ arg_output = arg_from_file || (argc > 1 && !arg_fuzzy) ? OUTPUT_FRIENDLY : OUTPUT_TABLE;
|
|
|
|
if (arg_output == OUTPUT_TABLE) {
|
|
table = table_new(" ", "name", "disposition", "uid", "gid", "realname", "home", "shell", "order");
|
|
@@ -394,7 +396,23 @@ static int display_user(int argc, char *argv[], void *userdata) {
|
|
.uid_max = arg_uid_max,
|
|
};
|
|
|
|
- if (argc > 1 && !arg_fuzzy)
|
|
+ if (arg_from_file) {
|
|
+ if (argc > 1)
|
|
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No argument expected when invoked with --from-file=, refusing.");
|
|
+
|
|
+ _cleanup_(user_record_unrefp) UserRecord *ur = user_record_new();
|
|
+ if (!ur)
|
|
+ return log_oom();
|
|
+
|
|
+ r = user_record_load(ur, arg_from_file, USER_RECORD_LOAD_MASK_SECRET|USER_RECORD_LOG);
|
|
+ if (r < 0)
|
|
+ return r;
|
|
+
|
|
+ r = show_user(ur, table);
|
|
+ if (r < 0)
|
|
+ return r;
|
|
+
|
|
+ } else if (argc > 1 && !arg_fuzzy)
|
|
STRV_FOREACH(i, argv + 1) {
|
|
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
|
|
uid_t uid;
|
|
@@ -709,7 +727,7 @@ static int display_group(int argc, char *argv[], void *userdata) {
|
|
int ret = 0, r;
|
|
|
|
if (arg_output < 0)
|
|
- arg_output = argc > 1 && !arg_fuzzy ? OUTPUT_FRIENDLY : OUTPUT_TABLE;
|
|
+ arg_output = arg_from_file || (argc > 1 && !arg_fuzzy) ? OUTPUT_FRIENDLY : OUTPUT_TABLE;
|
|
|
|
if (arg_output == OUTPUT_TABLE) {
|
|
table = table_new(" ", "name", "disposition", "gid", "description", "order");
|
|
@@ -730,7 +748,23 @@ static int display_group(int argc, char *argv[], void *userdata) {
|
|
.gid_max = arg_uid_max,
|
|
};
|
|
|
|
- if (argc > 1 && !arg_fuzzy)
|
|
+ if (arg_from_file) {
|
|
+ if (argc > 1)
|
|
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No argument expected when invoked with --from-file=, refusing.");
|
|
+
|
|
+ _cleanup_(group_record_unrefp) GroupRecord *gr = group_record_new();
|
|
+ if (!gr)
|
|
+ return log_oom();
|
|
+
|
|
+ r = group_record_load(gr, arg_from_file, USER_RECORD_LOAD_MASK_SECRET|USER_RECORD_LOG);
|
|
+ if (r < 0)
|
|
+ return r;
|
|
+
|
|
+ r = show_group(gr, table);
|
|
+ if (r < 0)
|
|
+ return r;
|
|
+
|
|
+ } else if (argc > 1 && !arg_fuzzy)
|
|
STRV_FOREACH(i, argv + 1) {
|
|
_cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
|
|
gid_t gid;
|
|
@@ -903,6 +937,9 @@ static int display_memberships(int argc, char *argv[], void *userdata) {
|
|
_cleanup_(table_unrefp) Table *table = NULL;
|
|
int ret = 0, r;
|
|
|
|
+ if (arg_from_file)
|
|
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--from-file= not supported when showing memberships, refusing.");
|
|
+
|
|
if (arg_output < 0)
|
|
arg_output = OUTPUT_TABLE;
|
|
|
|
@@ -997,6 +1034,9 @@ static int display_services(int argc, char *argv[], void *userdata) {
|
|
_cleanup_closedir_ DIR *d = NULL;
|
|
int r;
|
|
|
|
+ if (arg_from_file)
|
|
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--from-file= not supported when showing services, refusing.");
|
|
+
|
|
d = opendir("/run/systemd/userdb/");
|
|
if (!d) {
|
|
if (errno == ENOENT) {
|
|
@@ -1063,6 +1103,9 @@ static int ssh_authorized_keys(int argc, char *argv[], void *userdata) {
|
|
|
|
assert(argc >= 2);
|
|
|
|
+ if (arg_from_file)
|
|
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--from-file= not supported when showing SSH authorized keys, refusing.");
|
|
+
|
|
if (arg_chain) {
|
|
/* If --chain is specified, the rest of the command line is the chain command */
|
|
|
|
@@ -1182,6 +1225,7 @@ static int help(int argc, char *argv[], void *userdata) {
|
|
" -R Equivalent to --disposition=regular\n"
|
|
" --boundaries=BOOL Show/hide UID/GID range boundaries in output\n"
|
|
" -B Equivalent to --boundaries=no\n"
|
|
+ " -F --from-file=PATH Read JSON record from file\n"
|
|
"\nSee the %s for details.\n",
|
|
program_invocation_short_name,
|
|
ansi_highlight(),
|
|
@@ -1230,6 +1274,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|
{ "fuzzy", no_argument, NULL, 'z' },
|
|
{ "disposition", required_argument, NULL, ARG_DISPOSITION },
|
|
{ "boundaries", required_argument, NULL, ARG_BOUNDARIES },
|
|
+ { "from-file", required_argument, NULL, 'F' },
|
|
{}
|
|
};
|
|
|
|
@@ -1260,7 +1305,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|
int c;
|
|
|
|
c = getopt_long(argc, argv,
|
|
- arg_chain ? "+hjs:NISRzB" : "hjs:NISRzB", /* When --chain was used disable parsing of further switches */
|
|
+ arg_chain ? "+hjs:NISRzBF:" : "hjs:NISRzBF:", /* When --chain was used disable parsing of further switches */
|
|
options, NULL);
|
|
if (c < 0)
|
|
break;
|
|
@@ -1435,6 +1480,24 @@ static int parse_argv(int argc, char *argv[]) {
|
|
arg_boundaries = false;
|
|
break;
|
|
|
|
+ case 'F': {
|
|
+ if (isempty(optarg)) {
|
|
+ arg_from_file = sd_json_variant_unref(arg_from_file);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
|
+ const char *fn = streq(optarg, "-") ? NULL : optarg;
|
|
+ unsigned line = 0;
|
|
+ r = sd_json_parse_file(fn ? NULL : stdin, fn ?: "<stdin>", SD_JSON_PARSE_SENSITIVE, &v, &line, /* reterr_column= */ NULL);
|
|
+ if (r < 0)
|
|
+ return log_syntax(/* unit= */ NULL, LOG_ERR, fn ?: "<stdin>", line, r, "JSON parse failure.");
|
|
+
|
|
+ sd_json_variant_unref(arg_from_file);
|
|
+ arg_from_file = TAKE_PTR(v);
|
|
+ break;
|
|
+ }
|
|
+
|
|
case '?':
|
|
return -EINVAL;
|
|
|
|
@@ -1450,6 +1513,9 @@ static int parse_argv(int argc, char *argv[]) {
|
|
if (arg_disposition_mask == UINT64_MAX)
|
|
arg_disposition_mask = USER_DISPOSITION_MASK_MAX;
|
|
|
|
+ if (arg_from_file)
|
|
+ arg_boundaries = false;
|
|
+
|
|
return 1;
|
|
}
|
|
|
|
diff --git a/test/units/TEST-74-AUX-UTILS.userdbctl.sh b/test/units/TEST-74-AUX-UTILS.userdbctl.sh
|
|
new file mode 100755
|
|
index 0000000000..e4d21c2006
|
|
--- /dev/null
|
|
+++ b/test/units/TEST-74-AUX-UTILS.userdbctl.sh
|
|
@@ -0,0 +1,16 @@
|
|
+#!/usr/bin/env bash
|
|
+# SPDX-License-Identifier: LGPL-2.1-or-later
|
|
+set -eux
|
|
+set -o pipefail
|
|
+
|
|
+# shellcheck source=test/units/util.sh
|
|
+. "$(dirname "$0")"/util.sh
|
|
+
|
|
+# Make sure that -F shows same data as if we'd ask directly
|
|
+userdbctl user root -j | userdbctl -F- user | cmp - <(userdbctl user root)
|
|
+userdbctl user systemd-network -j | userdbctl -F- user | cmp - <(userdbctl user systemd-network)
|
|
+userdbctl user 65534 -j | userdbctl -F- user | cmp - <(userdbctl user 65534)
|
|
+
|
|
+userdbctl group root -j | userdbctl -F- group | cmp - <(userdbctl group root)
|
|
+userdbctl group systemd-network -j | userdbctl -F- group | cmp - <(userdbctl group systemd-network)
|
|
+userdbctl group 65534 -j | userdbctl -F- group | cmp - <(userdbctl group 65534)
|