]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #16690 from poettering/userdb-group-desc
authorAnita Zhang <the.anitazha@gmail.com>
Tue, 11 Aug 2020 07:27:54 +0000 (00:27 -0700)
committerGitHub <noreply@github.com>
Tue, 11 Aug 2020 07:27:54 +0000 (00:27 -0700)
description field for group records

12 files changed:
TODO
docs/GROUP_RECORD.md
docs/USER_RECORD.md
src/core/core-varlink.c
src/home/user-record-util.c
src/machine/machined-varlink.c
src/shared/group-record-show.c
src/shared/group-record.c
src/shared/group-record.h
src/shared/user-record.c
src/shared/user-record.h
src/userdb/userdbctl.c

diff --git a/TODO b/TODO
index d6ae200a34df6532fd2abad3e60f647c2795adb9..c0fbf8bc6ae9a5ad30678443c2cdddda6f2e09bc 100644 (file)
--- a/TODO
+++ b/TODO
@@ -372,7 +372,6 @@ Features:
   - in systemd's PAMName= logic: query passwords with ssh-askpassword, so that we can make "loginctl set-linger" mode work
   - fingerprint authentication, pattern authentication, …
   - make sure "classic" user records can also be managed by homed
-  - description field for groups
   - make size of $XDG_RUNTIME_DIR configurable in user record
   - reuse pwquality magic in firstboot
   - query password from kernel keyring first
index 2ea0b73a365173f29cd379e5b32230a42926ec0c..01800494da7de127f7f9609d6c4fe38742cf78a5 100644 (file)
@@ -22,6 +22,10 @@ UNIX/glibc NSS `struct group`, or the shadow structure `struct sgrp`'s
 `realm` → The "realm" the group belongs to, conceptually identical to the same
 field of user records. A string in DNS domain name syntax.
 
+`description` → A descriptive string for the group. This is similar to the
+`realName` field of user records, and accepts arbitrary strings, as long as
+they follow the same GECOS syntax requirements as `realName`.
+
 `disposition` → The disposition of the group, conceptually identical to the
 same field of user records. A string.
 
index f6d22c217b0026dfd6f3a4cf83b05800b9f3322e..960a82d8949cb846024550f49ae7174736a6057f 100644 (file)
@@ -221,12 +221,14 @@ optional, when unset the user should not be considered part of any realm. A
 user record with a realm set is never compatible (for the purpose of updates,
 see above) with a user record without one set, even if the `userName` field matches.
 
-`realName` → The real name of the user, a string. This should contain the user's
-real ("human") name, and corresponds loosely to the GECOS field of classic UNIX
-user records. When converting a `struct passwd` to a JSON user record this
-field is initialized from GECOS (i.e. the `pw_gecos` field), and vice versa
-when converting back. That said, unlike GECOS this field is supposed to contain
-only the real name and no other information.
+`realName` → The real name of the user, a string. This should contain the
+user's real ("human") name, and corresponds loosely to the GECOS field of
+classic UNIX user records. When converting a `struct passwd` to a JSON user
+record this field is initialized from GECOS (i.e. the `pw_gecos` field), and
+vice versa when converting back. That said, unlike GECOS this field is supposed
+to contain only the real name and no other information. This field must not
+contain control characters (such as `\n`) or colons (`:`), since those are used
+as record separators in classic `/etc/passwd` files and similar formats.
 
 `emailAddress` → The email address of the user, formatted as
 string. [`pam_systemd`](https://www.freedesktop.org/software/systemd/man/pam_systemd.html)
index eca27f4d7d116c59cc8f9a87218c0fec9044b69a..54f1cc7974c93e5f9aca81fb112440c09ddac425 100644 (file)
@@ -136,6 +136,7 @@ static int build_group_json(const char *group_name, gid_t gid, JsonVariant **ret
         return json_build(ret, JSON_BUILD_OBJECT(
                                    JSON_BUILD_PAIR("record", JSON_BUILD_OBJECT(
                                        JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(group_name)),
+                                       JSON_BUILD_PAIR("description", JSON_BUILD_STRING("Dynamic Group")),
                                        JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(gid)),
                                        JSON_BUILD_PAIR("service", JSON_BUILD_STRING("io.systemd.DynamicUser")),
                                        JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("dynamic"))))));
index a9f6f05e137435625a457e7f3eb9661f586fe245..516ffaa8a6ce979d00852be0708ae847cd0b946c 100644 (file)
@@ -104,7 +104,7 @@ int user_record_synthesize(
 }
 
 int group_record_synthesize(GroupRecord *g, UserRecord *h) {
-        _cleanup_free_ char *un = NULL, *rr = NULL, *group_name_and_realm = NULL;
+        _cleanup_free_ char *un = NULL, *rr = NULL, *group_name_and_realm = NULL, *description = NULL;
         char smid[SD_ID128_STRING_MAX];
         sd_id128_t mid;
         int r;
@@ -133,10 +133,15 @@ int group_record_synthesize(GroupRecord *g, UserRecord *h) {
                         return -ENOMEM;
         }
 
+        description = strjoin("Primary Group of User ", un);
+        if (!description)
+                return -ENOMEM;
+
         r = json_build(&g->json,
                        JSON_BUILD_OBJECT(
                                        JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(un)),
                                        JSON_BUILD_PAIR_CONDITION(!!rr, "realm", JSON_BUILD_STRING(rr)),
+                                       JSON_BUILD_PAIR("description", JSON_BUILD_STRING(description)),
                                        JSON_BUILD_PAIR("binding", JSON_BUILD_OBJECT(
                                                                        JSON_BUILD_PAIR(sd_id128_to_string(mid, smid), JSON_BUILD_OBJECT(
                                                                                                        JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(user_record_gid(h))))))),
index 058ee5c8ea2993dc83307c1668eae96b1fce2038..de8cdeb87da2ced379cd055f164132b1669ce8de 100644 (file)
@@ -93,6 +93,8 @@ static int user_lookup_name(Manager *m, const char *name, uid_t *ret_uid, char *
         int r;
 
         assert(m);
+        assert(ret_uid);
+        assert(ret_real_name);
 
         if (!valid_user_group_name(name, 0))
                 return -ESRCH;
@@ -186,7 +188,7 @@ static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, Var
         return varlink_reply(link, v);
 }
 
-static int build_group_json(const char *group_name, gid_t gid, JsonVariant **ret) {
+static int build_group_json(const char *group_name, gid_t gid, const char *description, JsonVariant **ret) {
         assert(group_name);
         assert(gid_is_valid(gid));
         assert(ret);
@@ -195,6 +197,7 @@ static int build_group_json(const char *group_name, gid_t gid, JsonVariant **ret
                                    JSON_BUILD_PAIR("record", JSON_BUILD_OBJECT(
                                        JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(group_name)),
                                        JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(gid)),
+                                       JSON_BUILD_PAIR_CONDITION(!isempty(description), "description", JSON_BUILD_STRING(description)),
                                        JSON_BUILD_PAIR("service", JSON_BUILD_STRING("io.systemd.Machine")),
                                        JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("container"))))));
     }
@@ -211,8 +214,8 @@ static bool group_match_lookup_parameters(LookupParameters *p, const char *name,
         return true;
 }
 
-static int group_lookup_gid(Manager *m, gid_t gid, char **ret_name) {
-        _cleanup_free_ char *n = NULL;
+static int group_lookup_gid(Manager *m, gid_t gid, char **ret_name, char **ret_description) {
+        _cleanup_free_ char *n = NULL, *d = NULL;
         gid_t converted_gid;
         Machine *machine;
         int r;
@@ -220,6 +223,7 @@ static int group_lookup_gid(Manager *m, gid_t gid, char **ret_name) {
         assert(m);
         assert(gid_is_valid(gid));
         assert(ret_name);
+        assert(ret_description);
 
         if (gid < 0x10000) /* Host GID range */
                 return -ESRCH;
@@ -236,18 +240,27 @@ static int group_lookup_gid(Manager *m, gid_t gid, char **ret_name) {
         if (!valid_user_group_name(n, 0))
                 return -ESRCH;
 
+        if (asprintf(&d, "GID " GID_FMT " of Container %s", converted_gid, machine->name) < 0)
+                return -ENOMEM;
+        if (!valid_gecos(d))
+                d = mfree(d);
+
         *ret_name = TAKE_PTR(n);
+        *ret_description = TAKE_PTR(d);
+
         return 0;
 }
 
-static int group_lookup_name(Manager *m, const char *name, gid_t *ret_gid) {
-        _cleanup_free_ char *mn = NULL;
+static int group_lookup_name(Manager *m, const char *name, gid_t *ret_gid, char **ret_description) {
+        _cleanup_free_ char *mn = NULL, *desc = NULL;
         gid_t gid, converted_gid;
         Machine *machine;
         const char *e, *d;
         int r;
 
         assert(m);
+        assert(ret_gid);
+        assert(ret_description);
 
         if (!valid_user_group_name(name, 0))
                 return -ESRCH;
@@ -278,7 +291,13 @@ static int group_lookup_name(Manager *m, const char *name, gid_t *ret_gid) {
         if (r < 0)
                 return r;
 
+        if (asprintf(&desc, "GID " GID_FMT " of Container %s", gid, machine->name) < 0)
+                return -ENOMEM;
+        if (!valid_gecos(desc))
+                desc = mfree(desc);
+
         *ret_gid = converted_gid;
+        *ret_description = desc;
         return 0;
 }
 
@@ -295,7 +314,7 @@ static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, Va
         LookupParameters p = {
                 .gid = GID_INVALID,
         };
-        _cleanup_free_ char *found_name = NULL;
+        _cleanup_free_ char *found_name = NULL, *found_description = NULL;
         uid_t found_gid = GID_INVALID, gid;
         Manager *m = userdata;
         const char *gn;
@@ -312,9 +331,9 @@ static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, Va
                 return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
 
         if (gid_is_valid(p.gid))
-                r = group_lookup_gid(m, p.gid, &found_name);
+                r = group_lookup_gid(m, p.gid, &found_name, &found_description);
         else if (p.group_name)
-                r = group_lookup_name(m, p.group_name, (uid_t*) &found_gid);
+                r = group_lookup_name(m, p.group_name, (uid_t*) &found_gid, &found_description);
         else
                 return varlink_error(link, "io.systemd.UserDatabase.EnumerationNotSupported", NULL);
         if (r == -ESRCH)
@@ -328,7 +347,7 @@ static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, Va
         if (!group_match_lookup_parameters(&p, gn, gid))
                 return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
 
-        r = build_group_json(gn, gid, &v);
+        r = build_group_json(gn, gid, found_description, &v);
         if (r < 0)
                 return r;
 
index d0300e483c743fa17f77c76eee10b555eda3b2b1..8b59f919fa315d998e389f43d06e0edca7f8860d 100644 (file)
@@ -68,6 +68,9 @@ void group_record_show(GroupRecord *gr, bool show_full_user_info) {
                 }
         }
 
+        if (gr->description && !streq(gr->description, gr->group_name))
+                printf(" Description: %s\n", gr->description);
+
         if (!strv_isempty(gr->hashed_password))
                 printf("   Passwords: %zu\n", strv_length(gr->hashed_password));
 
index 3c9520693de78a38e4cc8635c23c46500a176da8..d999ff95f821fa4d4f51af4ac5a58c2f4dc3f0a8 100644 (file)
@@ -28,6 +28,7 @@ static GroupRecord *group_record_free(GroupRecord *g) {
         free(g->group_name);
         free(g->realm);
         free(g->group_name_and_realm_auto);
+        free(g->description);
 
         strv_free(g->members);
         free(g->service);
@@ -192,6 +193,7 @@ int group_record_load(
         static const JsonDispatch group_dispatch_table[] = {
                 { "groupName",      JSON_VARIANT_STRING,   json_dispatch_user_group_name,  offsetof(GroupRecord, group_name),       JSON_RELAX},
                 { "realm",          JSON_VARIANT_STRING,   json_dispatch_realm,            offsetof(GroupRecord, realm),            0         },
+                { "description",    JSON_VARIANT_STRING,   json_dispatch_gecos,            offsetof(GroupRecord, description),      0         },
                 { "disposition",    JSON_VARIANT_STRING,   json_dispatch_user_disposition, offsetof(GroupRecord, disposition),      0         },
                 { "service",        JSON_VARIANT_STRING,   json_dispatch_string,           offsetof(GroupRecord, service),          JSON_SAFE },
                 { "lastChangeUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64,           offsetof(GroupRecord, last_change_usec), 0         },
index b72a43e50d8d6a6700d85413cbcc911d2c50b4a5..85c91eb1f58e50b979f178491574d74f72809981 100644 (file)
@@ -13,6 +13,8 @@ typedef struct GroupRecord {
         char *realm;
         char *group_name_and_realm_auto;
 
+        char *description;
+
         UserDisposition disposition;
         uint64_t last_change_usec;
 
index 2c0de383e042316623a97cb6fecd9c58e7e2c45b..678f04e53747ceec718d563164d4cf38fd746155 100644 (file)
@@ -203,7 +203,7 @@ int json_dispatch_realm(const char *name, JsonVariant *variant, JsonDispatchFlag
         return 0;
 }
 
-static int json_dispatch_gecos(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+int json_dispatch_gecos(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
         char **s = userdata;
         const char *n;
 
index 1bfd095d27e18c1aed317428bebc78d7818de481..39580b6b76a807d2ae756e1656d900ecf9e8769f 100644 (file)
@@ -388,6 +388,7 @@ int user_record_test_password_change_required(UserRecord *h);
 
 /* The following six are user by group-record.c, that's why we export them here */
 int json_dispatch_realm(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
+int json_dispatch_gecos(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
 int json_dispatch_user_group_list(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
 int json_dispatch_user_disposition(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
 
index c973ee9c0114e27a0390f5a2904ac51a45d3ae72..12c6943ebd7f2d9be1bfa6c8d3c9c98ed2908417 100644 (file)
@@ -232,6 +232,7 @@ static int show_group(GroupRecord *gr, Table *table) {
                                 TABLE_STRING, gr->group_name,
                                 TABLE_STRING, user_disposition_to_string(group_record_disposition(gr)),
                                 TABLE_GID, gr->gid,
+                                TABLE_STRING, gr->description,
                                 TABLE_INT, (int) group_record_disposition(gr));
                 if (r < 0)
                         return table_log_add_error(r);
@@ -255,13 +256,14 @@ static int display_group(int argc, char *argv[], void *userdata) {
                 arg_output = argc > 1 ? OUTPUT_FRIENDLY : OUTPUT_TABLE;
 
         if (arg_output == OUTPUT_TABLE) {
-                table = table_new("name", "disposition", "gid", "disposition-numeric");
+                table = table_new("name", "disposition", "gid", "description", "disposition-numeric");
                 if (!table)
                         return log_oom();
 
                 (void) table_set_align_percent(table, table_get_cell(table, 0, 2), 100);
+                (void) table_set_empty_string(table, "-");
                 (void) table_set_sort(table, (size_t) 3, (size_t) 2, (size_t) -1);
-                (void) table_set_display(table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) -1);
+                (void) table_set_display(table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) -1);
         }
 
         if (argc > 1) {