From: Erin Shepherd Date: Fri, 11 Apr 2025 19:18:32 +0000 (+0000) Subject: userdb: add support for looking up users or groups by uuid. X-Git-Tag: v259-rc1~363^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=52874bb763cf97e453ef49a9f71db7c4cc6c1322;p=thirdparty%2Fsystemd.git userdb: add support for looking up users or groups by uuid. This propagates the UUID lookup parameter through the API permitting lookups to be done by uuid. --- diff --git a/docs/USER_GROUP_API.md b/docs/USER_GROUP_API.md index 1109ad81da7..3341c779863 100644 --- a/docs/USER_GROUP_API.md +++ b/docs/USER_GROUP_API.md @@ -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 diff --git a/src/shared/user-record.c b/src/shared/user-record.c index b55875d6cef..1fd292dba8c 100644 --- a/src/shared/user-record.c +++ b/src/shared/user-record.c @@ -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 diff --git a/src/shared/user-record.h b/src/shared/user-record.h index f411214453f..e4384e50b75 100644 --- a/src/shared/user-record.h +++ b/src/shared/user-record.h @@ -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. */ diff --git a/src/shared/userdb.c b/src/shared/userdb.c index 49850ff216f..c7a1595211a 100644 --- a/src/shared/userdb.c +++ b/src/shared/userdb.c @@ -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( diff --git a/src/userdb/userwork.c b/src/userdb/userwork.c index 0282cc37dcd..fc698e841da 100644 --- a/src/userdb/userwork.c +++ b/src/userdb/userwork.c @@ -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 }, {} };