userdb: add birthDate field to JSON user records
Add a birthDate field to the JSON user record, stored internally as a struct tm with INT_MIN/negative sentinels for unset fields. The field is serialized as a YYYY-MM-DD string in JSON and validated via parse_birth_date(), which shares its core logic with parse_calendar_date() through a new parse_calendar_date_full() function. For birth dates, timegm() is called directly (rather than mktime_or_timegm_usec) to support pre-epoch dates. The wday field is used to distinguish timegm() failure from a valid (time_t) -1 return. birthDate is excluded from user_record_self_modifiable_fields(), so only administrators can set or change it via homectl. The field remains in the regular (non-privileged) JSON section, keeping it readable by the user and applications.
This commit is contained in:
+26
-7
@@ -1893,9 +1893,8 @@ TimestampStyle timestamp_style_from_string(const char *s) {
|
||||
return t;
|
||||
}
|
||||
|
||||
int parse_calendar_date(const char *s, usec_t *ret) {
|
||||
int parse_calendar_date_full(const char *s, bool allow_pre_epoch, usec_t *ret_usec, struct tm *ret_tm) {
|
||||
struct tm parsed_tm = {}, copy_tm;
|
||||
usec_t usec;
|
||||
const char *k;
|
||||
int r;
|
||||
|
||||
@@ -1906,9 +1905,22 @@ int parse_calendar_date(const char *s, usec_t *ret) {
|
||||
return -EINVAL;
|
||||
|
||||
copy_tm = parsed_tm;
|
||||
r = mktime_or_timegm_usec(©_tm, /* utc= */ true, &usec);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
usec_t usec = USEC_INFINITY;
|
||||
|
||||
if (allow_pre_epoch) {
|
||||
/* For birth dates we use timegm() directly since we need to accept pre-epoch dates.
|
||||
* timegm() returns (time_t) -1 both on error and for one second before the epoch.
|
||||
* Initialize wday to -1 beforehand: if it remains -1 after the call, it's a genuine
|
||||
* error; if timegm() changed it, the date was successfully normalized. */
|
||||
copy_tm.tm_wday = -1;
|
||||
if (timegm(©_tm) == (time_t) -1 && copy_tm.tm_wday == -1)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
r = mktime_or_timegm_usec(©_tm, /* utc= */ true, &usec);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Refuse non-normalized dates, e.g. Feb 30 */
|
||||
if (copy_tm.tm_mday != parsed_tm.tm_mday ||
|
||||
@@ -1916,8 +1928,15 @@ int parse_calendar_date(const char *s, usec_t *ret) {
|
||||
copy_tm.tm_year != parsed_tm.tm_year)
|
||||
return -EINVAL;
|
||||
|
||||
if (ret)
|
||||
*ret = usec;
|
||||
if (ret_usec)
|
||||
*ret_usec = usec;
|
||||
if (ret_tm) {
|
||||
/* Reset to unset, then fill in only the date fields we parsed and validated */
|
||||
*ret_tm = BIRTH_DATE_UNSET;
|
||||
ret_tm->tm_mday = parsed_tm.tm_mday;
|
||||
ret_tm->tm_mon = parsed_tm.tm_mon;
|
||||
ret_tm->tm_year = parsed_tm.tm_year;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+18
-1
@@ -1,6 +1,7 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <limits.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "basic-forward.h"
|
||||
@@ -180,7 +181,23 @@ const char* etc_localtime(void);
|
||||
|
||||
int mktime_or_timegm_usec(struct tm *tm, bool utc, usec_t *ret);
|
||||
int localtime_or_gmtime_usec(usec_t t, bool utc, struct tm *ret);
|
||||
int parse_calendar_date(const char *s, usec_t *ret);
|
||||
|
||||
int parse_calendar_date_full(const char *s, bool allow_pre_epoch, usec_t *ret_usec, struct tm *ret_tm);
|
||||
|
||||
static inline int parse_calendar_date(const char *s, usec_t *ret) {
|
||||
return parse_calendar_date_full(s, /* allow_pre_epoch= */ false, ret, NULL);
|
||||
}
|
||||
|
||||
#define BIRTH_DATE_UNSET \
|
||||
(const struct tm) { \
|
||||
.tm_year = INT_MIN, \
|
||||
}
|
||||
|
||||
#define BIRTH_DATE_IS_SET(tm) ((tm).tm_year != INT_MIN)
|
||||
|
||||
static inline int parse_birth_date(const char *s, struct tm *ret) {
|
||||
return parse_calendar_date_full(s, /* allow_pre_epoch= */ true, NULL, ret);
|
||||
}
|
||||
|
||||
uint32_t usec_to_jiffies(usec_t usec);
|
||||
usec_t jiffies_to_usec(uint32_t jiffies);
|
||||
|
||||
Reference in New Issue
Block a user