]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
userdb: add support for looking up users or groups by uuid.
authorErin Shepherd <erin.shepherd@e43.eu>
Fri, 11 Apr 2025 19:18:32 +0000 (19:18 +0000)
committerErin Shepherd <erin.shepherd@e43.eu>
Mon, 21 Jul 2025 19:29:42 +0000 (19:29 +0000)
This propagates the UUID lookup parameter through the API permitting
lookups to be done by uuid.

docs/USER_GROUP_API.md
src/shared/user-record.c
src/shared/user-record.h
src/shared/userdb.c
src/userdb/userwork.c

index 1109ad81da7d378be2a33865b94b47f6f27310e4..3341c779863a11eb963164e5771581aa0ac7d93d 100644 (file)
@@ -175,6 +175,7 @@ method GetUserRecord(
         dispositionMask: ?[]string,
         uidMin: ?int,
         uidMax: ?int,
+        uuid: ?string,
         service : string
 ) -> (
         record : object,
@@ -188,6 +189,7 @@ method GetGroupRecord(
         dispositionMask: ?[]string,
         gidMin: ?int,
         gidMax: ?int,
+        uuid: ?string,
         service : string
 ) -> (
         record : object,
@@ -222,7 +224,7 @@ If neither of the two parameters are set the whole user database is enumerated.
 In this case the method call needs to be made with `more` set, so that multiple method call replies may be generated as
 effect, each carrying one user record.
 
-The `fuzzyNames`, `dispositionMask`, `uidMin`, `uidMax` fields permit
+The `fuzzyNames`, `dispositionMask`, `uidMin`, `uidMax` and `uuid` fields permit
 *additional* filtering of the returned set of user records. The `fuzzyNames`
 parameter shall be one or more strings that shall be searched for in "fuzzy"
 way. What specifically this means is left for the backend to decide, but
@@ -232,19 +234,20 @@ carry identifying information for the user. The `dispositionMask` field shall
 be one of more user record `disposition` strings. If specified only user
 records matching one of the specified dispositions should be enumerated. The
 `uidMin` and `uidMax` fields specify a minimum and maximum value for the UID of
-returned records. Inline searching for `uid` and `userName` support for
+returned records. The `uuid` field specifies to search for the user record associated
+with the specified UUID. Inline searching for `uid` and `userName` support for
 filtering with these four additional parameters is optional, and clients are
 expected to be able to do client-side filtering in case the parameters are not
 supported by a service. The service should return the usual `InvalidParameter`
 error for the relevant parameter if one is passed and it does not support
 it. If a request is made specifying `uid` or `userName` and a suitable record
 is found, but the specified filter via `fuzzyNames`, `dispositionMask`,
-`uidMin`, or `uidMax` does not match, a `NonMatchingRecordFound` error should
+`uidMin`, `uidMax` or `uuid` does not match, a `NonMatchingRecordFound` error should
 be returned.
 
 Or to say this differently: the *primary search keys* are
 `userName`/`groupName` and `uid`/`gid` and the *secondary search filters* are
-`fuzzyNames`, `dispositionMask`, `uidMin`, `uidMax`. If no entry matching
+`fuzzyNames`, `dispositionMask`, `uidMin`, `uidMax`, `uuid`. If no entry matching
 either of the primary search keys are found `NoRecordFound()` is returned. If
 one is found that matches one but not the other primary search key
 `ConflictingRecordFound()` is returned. If an entry is found that matches the
index b55875d6cef658ab9c9f11b7d1116b34eff4de05..1fd292dba8c87ca3ef35f7053fc4950442aa3c29 100644 (file)
@@ -2777,7 +2777,8 @@ bool userdb_match_is_set(const UserDBMatch *match) {
         return !strv_isempty(match->fuzzy_names) ||
                 !FLAGS_SET(match->disposition_mask, USER_DISPOSITION_MASK_ALL) ||
                 match->uid_min > 0 ||
-                match->uid_max < UID_INVALID-1;
+                match->uid_max < UID_INVALID-1 ||
+                !sd_id128_is_null(match->uuid);
 }
 
 void userdb_match_done(UserDBMatch *match) {
@@ -2836,6 +2837,9 @@ bool user_record_match(UserRecord *u, const UserDBMatch *match) {
         if (!BIT_SET(match->disposition_mask, user_record_disposition(u)))
                 return false;
 
+        if (!sd_id128_is_null(match->uuid) && !sd_id128_equal(match->uuid, u->uuid))
+                return false;
+
         if (!strv_isempty(match->fuzzy_names)) {
 
                 /* Note this array of names is sparse, i.e. various entries listed in it will be
index f411214453f3ecb56321d5f6bacfbe4c3c7f64ef..e4384e50b75a13d8e3c2f0ab77797b8acdee2f73 100644 (file)
@@ -513,6 +513,7 @@ typedef struct UserDBMatch {
                 uid_t uid_max;
                 gid_t gid_max;
         };
+        sd_id128_t uuid;
 } UserDBMatch;
 
 #define USER_DISPOSITION_MASK_ALL ((UINT64_C(1) << _USER_DISPOSITION_MAX) - UINT64_C(1))
@@ -522,6 +523,7 @@ typedef struct UserDBMatch {
                 .disposition_mask = USER_DISPOSITION_MASK_ALL,  \
                 .uid_min = 0,                                   \
                 .uid_max = UID_INVALID-1,                       \
+                .uuid = SD_ID128_NULL,                          \
        }
 
 /* Maybe useful when we want to resolve root and system user/group but want to refuse nobody user/group. */
index 49850ff216f92cd008f849d912ff50e51930f37b..c7a1595211a15532e9e5f4cc9f95ce5c14ec772a 100644 (file)
@@ -799,27 +799,29 @@ nomatch:
         return 0;
 }
 
-static int query_append_disposition_mask(sd_json_variant **query, uint64_t mask) {
+static int query_append_common(sd_json_variant **query, const UserDBMatch *match) {
         int r;
+        _cleanup_strv_free_ char **dispositions = NULL;
 
         assert(query);
 
-        if (FLAGS_SET(mask, USER_DISPOSITION_MASK_ALL))
-                return 0;
-
-        _cleanup_strv_free_ char **dispositions = NULL;
-        for (UserDisposition d = 0; d < _USER_DISPOSITION_MAX; d++) {
-                if (!BITS_SET(mask, d))
-                        continue;
+        uint64_t mask = match->disposition_mask;
+        if (FLAGS_SET(mask, USER_DISPOSITION_MASK_ALL)) {
+                for (UserDisposition d = 0; d < _USER_DISPOSITION_MAX; d++) {
+                        if (!BIT_SET(mask, d))
+                                continue;
 
-                r = strv_extend(&dispositions, user_disposition_to_string(d));
-                if (r < 0)
-                        return r;
+                        r = strv_extend(&dispositions, user_disposition_to_string(d));
+                        if (r < 0)
+                                return r;
+                }
         }
 
         return sd_json_variant_merge_objectbo(
                         query,
-                        SD_JSON_BUILD_PAIR_STRV("dispositionMask", dispositions));
+                        SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(match->fuzzy_names), "fuzzyNames", SD_JSON_BUILD_STRV(match->fuzzy_names)),
+                        SD_JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(match->uuid), "uuid", SD_JSON_BUILD_UUID(match->uuid)),
+                        SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(dispositions), "dispositionMask", SD_JSON_BUILD_STRV(dispositions)));
 }
 
 static int query_append_uid_match(sd_json_variant **query, const UserDBMatch *match) {
@@ -832,13 +834,12 @@ static int query_append_uid_match(sd_json_variant **query, const UserDBMatch *ma
 
         r = sd_json_variant_merge_objectbo(
                         query,
-                        SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(match->fuzzy_names), "fuzzyNames", SD_JSON_BUILD_STRV(match->fuzzy_names)),
                         SD_JSON_BUILD_PAIR_CONDITION(match->uid_min > 0, "uidMin", SD_JSON_BUILD_UNSIGNED(match->uid_min)),
                         SD_JSON_BUILD_PAIR_CONDITION(match->uid_max < UID_INVALID-1, "uidMax", SD_JSON_BUILD_UNSIGNED(match->uid_max)));
         if (r < 0)
                 return r;
 
-        return query_append_disposition_mask(query, match->disposition_mask);
+        return query_append_common(query, match);
 }
 
 static int userdb_by_name_fallbacks(
@@ -1298,13 +1299,13 @@ static int query_append_gid_match(sd_json_variant **query, const UserDBMatch *ma
 
         r = sd_json_variant_merge_objectbo(
                         query,
-                        SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(match->fuzzy_names), "fuzzyNames", SD_JSON_BUILD_STRV(match->fuzzy_names)),
                         SD_JSON_BUILD_PAIR_CONDITION(match->gid_min > 0, "gidMin", SD_JSON_BUILD_UNSIGNED(match->gid_min)),
                         SD_JSON_BUILD_PAIR_CONDITION(match->gid_max < GID_INVALID-1, "gidMax", SD_JSON_BUILD_UNSIGNED(match->gid_max)));
+
         if (r < 0)
                 return r;
 
-        return query_append_disposition_mask(query, match->disposition_mask);
+        return query_append_common(query, match);
 }
 
 static int groupdb_by_name_fallbacks(
index 0282cc37dcde71280c6d9f3207eabbe7968e2af4..fc698e841da07e31913cace87c2e3cbc0c2ea097 100644 (file)
@@ -149,6 +149,7 @@ static int vl_method_get_user_record(sd_varlink *link, sd_json_variant *paramete
                 { "dispositionMask", SD_JSON_VARIANT_ARRAY,         json_dispatch_dispositions_mask,     offsetof(LookupParameters, match.disposition_mask), 0             },
                 { "uidMin",          _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid,            offsetof(LookupParameters, match.uid_min),          0             },
                 { "uidMax",          _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid,            offsetof(LookupParameters, match.uid_max),          0             },
+                { "uuid",            SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,              offsetof(LookupParameters, match.uuid),             0             },
                 {}
         };
 
@@ -293,6 +294,7 @@ static int vl_method_get_group_record(sd_varlink *link, sd_json_variant *paramet
                 { "dispositionMask", SD_JSON_VARIANT_ARRAY,         json_dispatch_dispositions_mask,     offsetof(LookupParameters, match.disposition_mask), 0             },
                 { "gidMin",          _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid,            offsetof(LookupParameters, match.gid_min),          0             },
                 { "gidMax",          _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid,            offsetof(LookupParameters, match.gid_max),          0             },
+                { "uuid",            SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,              offsetof(LookupParameters, match.uuid),             0             },
                 {}
         };