Use "" instead of "0" for all default namespace signatures

This makes all endpoints consistent w.r.t. how the namespace is handled:
invoking with "namespace": 0 and omitting namespace are now equivalent
(and use the same signature value, with "" for the namespace value) for
all endpoints.
This commit is contained in:
Jason Rhinelander 2022-05-05 17:30:44 -03:00
parent bc7424d3a8
commit 0264de1daa
No known key found for this signature in database
GPG key ID: C4992CE7A88D4262
3 changed files with 31 additions and 54 deletions

View file

@ -19,7 +19,6 @@ using nlohmann::json;
using oxenc::bt_dict;
using oxenc::bt_dict_consumer;
using oxenc::bt_list;
using oxenc::bt_list_consumer;
using oxenc::bt_value;
using std::chrono::system_clock;
@ -41,19 +40,6 @@ namespace {
: is_str_array<T> ? "string array"sv
: "string"sv;
template <typename... T>
constexpr bool is_monostate_var = false;
template <typename... T>
constexpr bool is_monostate_var<std::variant<std::monostate, T...>> = true;
template <typename T>
using maybe_type = std::conditional_t<is_monostate_var<T>, T, std::optional<T>>;
template <typename T, typename SFINAE = void>
constexpr std::nullopt_t maybe_not = std::nullopt;
template <typename T>
constexpr std::monostate maybe_not<T, std::enable_if_t<is_monostate_var<T>>>{};
template <typename T>
constexpr bool is_parseable_v =
std::is_unsigned_v<T> || std::is_integral_v<T> || is_timestamp<T> || is_str_array<T> ||
@ -65,11 +51,11 @@ namespace {
// number but given a bool). Returns nullopt if the field isn't present or is present and
// set to null.
template <typename T>
maybe_type<T> parse_field(const json& params, const char* name) {
std::optional<T> parse_field(const json& params, const char* name) {
static_assert(is_parseable_v<T>);
auto it = params.find(name);
if (it == params.end() || it->is_null())
return maybe_not<T>;
return std::nullopt;
bool right_type = std::is_same_v<T, bool> ? it->is_boolean()
: std::is_unsigned_v<T> || is_timestamp<T> ? it->is_number_unsigned()
@ -117,10 +103,10 @@ namespace {
// current state of the bt_dict_consumer to just after the given field and so this
// *must* be called in sorted key order.
template <typename T>
maybe_type<T> parse_field(bt_dict_consumer& params, const char* name) {
std::optional<T> parse_field(bt_dict_consumer& params, const char* name) {
static_assert(is_parseable_v<T>);
if (!params.skip_until(name))
return maybe_not<T>;
return std::nullopt;
try {
if constexpr (std::is_same_v<T, std::string_view>)
@ -153,7 +139,7 @@ namespace {
// Backwards compat code for fields like ttl and timestamp that are accepted either
// as integer *or* stringified integer.
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
maybe_type<T> parse_stringified(const json& params, const char* name) {
std::optional<T> parse_stringified(const json& params, const char* name) {
if (auto it = params.find(name); it != params.end() && it->is_string()) {
if (T value; util::parse_int(it->get_ref<const std::string&>(), value))
return value;
@ -182,7 +168,7 @@ namespace {
typename = std::enable_if_t<
sizeof...(T) == sizeof...(Names) &&
(std::is_convertible_v<Names, std::string_view> && ...)>>
std::tuple<maybe_type<T>...> load_fields(Dict& params, const Names&... names) {
std::tuple<std::optional<T>...> load_fields(Dict& params, const Names&... names) {
assert(check_ascending(names...));
return {parse_field<T>(params, names)...};
}
@ -307,10 +293,10 @@ namespace {
void set_variant(bt_dict& dict, const std::string& key, const namespace_var& ns) {
if (auto* id = std::get_if<namespace_id>(&ns))
dict[key] = static_cast<std::underlying_type_t<namespace_id>>(*id);
else if (std::holds_alternative<namespace_all_t>(ns))
else {
assert(std::holds_alternative<namespace_all_t>(ns));
dict[key] = "all";
else
assert(std::holds_alternative<std::monostate>(ns));
}
}
} // namespace
@ -527,7 +513,7 @@ static void load(delete_all& da, Dict& d) {
load_pk_signature(da, d, pubkey, pubkey_ed25519, signature);
require("timestamp", timestamp);
da.msg_namespace = std::move(msgs_ns);
da.msg_namespace = msgs_ns.value_or(namespace_id::Default);
da.timestamp = std::move(*timestamp);
}
void delete_all::load_from(json params) {
@ -560,7 +546,7 @@ static void load(delete_before& db, Dict& d) {
load_pk_signature(db, d, pubkey, pubkey_ed25519, signature);
require("before", before);
db.before = std::move(*before);
db.msg_namespace = std::move(msgs_ns);
db.msg_namespace = msgs_ns.value_or(namespace_id::Default);
}
void delete_before::load_from(json params) {
load(*this, params);
@ -592,7 +578,7 @@ static void load(expire_all& e, Dict& d) {
load_pk_signature(e, d, pubkey, pubkey_ed25519, signature);
require("expiry", expiry);
e.expiry = std::move(*expiry);
e.msg_namespace = std::move(msgs_ns);
e.msg_namespace = msgs_ns.value_or(namespace_id::Default);
}
void expire_all::load_from(json params) {
load(*this, params);

View file

@ -177,8 +177,7 @@ struct store final : recursive {
/// - `namespace` (optional) the integral message namespace from which to retrieve messages. Each
/// namespace forms an independent message storage for the same address. When specified,
/// authentication *must* be provided. Omitting the namespace is equivalent to specifying a
/// namespace of 0. (Note, however, that an explicit namespace of 0 requires authentication,
/// while an implicit namespace of 0 does not during the transition period).
/// namespace of 0.
/// - `last_hash` (optional) retrieve messages stored by this storage server since `last_hash` was
/// stored. Can also be specified as `lastHash`. An empty string (or null) is treated as an
/// omitted value.
@ -277,33 +276,25 @@ struct delete_msgs final : recursive {
struct namespace_all_t {};
inline constexpr namespace_all_t namespace_all{};
// Variant for holding unspecified, integer, or "all" namespace input. Unspecified is generally the
// same as an integer of 0 in effect, but the distinction *does* matter for the signature (which
// matches the given arguments, not the implied value).
using namespace_var = std::variant<std::monostate, namespace_id, namespace_all_t>;
// Variant for holding an integer namespace or "all" namespace input.
using namespace_var = std::variant<namespace_id, namespace_all_t>;
constexpr bool is_all(const namespace_var& ns) {
return std::holds_alternative<namespace_all_t>(ns);
}
constexpr bool is_omitted(const namespace_var& ns) {
return std::holds_alternative<std::monostate>(ns);
}
// Returns the implied namespace from a namespace_var containing either a monostate (implied
// namespace 0) or specific namespace. Should not be called on a variant containing an "all" value.
constexpr namespace_id ns_or_default(const namespace_var& ns) {
if (auto* id = std::get_if<namespace_id>(&ns))
return *id;
return namespace_id::Default;
constexpr bool is_default(const namespace_var& ns) {
auto* n = std::get_if<namespace_id>(&ns);
return n && *n == namespace_id::Default;
}
// Returns the representation of a provided namespace variant that should have been used in a
// request signature, which is:
// - empty string if namespace unspecified
// - empty string if default namespace (either unspecified, or explicitly given as 0)
// - "all" if given as all namespaces
// - "NN" for some explicitly given numeric namespace NN
// - "NN" for some explicitly given non-default numeric namespace NN
inline std::string signature_value(const namespace_var& ns) {
return is_omitted(ns) ? ""s : is_all(ns) ? "all"s : to_string(var::get<namespace_id>(ns));
return is_default(ns) ? ""s : is_all(ns) ? "all"s : to_string(var::get<namespace_id>(ns));
}
/// Deletes all messages owned by the given pubkey on this SN and broadcasts the delete request
@ -323,10 +314,10 @@ inline std::string signature_value(const namespace_var& ns) {
/// epoch. Must be within ±60s of the current time. (For clients it is recommended to retrieve a
/// timestamp via `info` first, to avoid client time sync issues).
/// - signature -- an Ed25519 signature of ( "delete_all" || namespace || timestamp ), where
/// `namespace` is the stringified version of the given namespace parameter (i.e. "0" or "-42" or
/// "all"), or the empty string if namespace was not given. The signature must be signed by the
/// ed25519 pubkey in `pubkey` (omitting the leading prefix). Must be base64 encoded for json
/// requests; binary for OMQ requests.
/// `namespace` is the empty string for the default namespace (whether explicitly specified or
/// not), and otherwise the stringified version of the namespace parameter (i.e. "99" or "-42" or
/// "all"). The signature must be signed by the ed25519 pubkey in `pubkey` (omitting the leading
/// prefix). Must be base64 encoded for json requests; binary for OMQ requests.
///
/// Returns dict of:
/// - "swarms" dict mapping ed25519 pubkeys (in hex) of swarm members to dict values of:
@ -370,8 +361,8 @@ struct delete_all final : recursive {
/// allows it to be <= 60s from now.
/// - signature -- Ed25519 signature of ("delete_before" || namespace || before), signed by
/// `pubkey`. Must be base64 encoded (json) or bytes (OMQ). `namespace` is the stringified
/// version of the given namespace parameter (i.e. "0" or "-42" or "all"), or the empty string if
/// namespace was not given.
/// version of the given non-default namespace parameter (i.e. "-42" or "all"), or the empty
/// string for the default namespace (whether explicitly given or not).
///
/// Returns dict of:
/// - "swarms" dict mapping ed25519 pubkeys (in hex) of swarm members to dict values of:

View file

@ -831,7 +831,7 @@ void RequestHandler::process_client_req(
handle_action_one_ns(
mine,
"deleted",
service_node_.get_db().delete_all(req.pubkey, ns_or_default(req.msg_namespace)),
service_node_.get_db().delete_all(req.pubkey, var::get<namespace_id>(req.msg_namespace)),
req.b64,
ed25519_sk_,
req.pubkey.prefixed_hex(),
@ -933,7 +933,7 @@ void RequestHandler::process_client_req(
mine,
"deleted",
service_node_.get_db().delete_by_timestamp(
req.pubkey, ns_or_default(req.msg_namespace), req.before),
req.pubkey, var::get<namespace_id>(req.msg_namespace), req.before),
req.b64,
ed25519_sk_,
req.pubkey.prefixed_hex(),
@ -995,7 +995,7 @@ void RequestHandler::process_client_req(rpc::expire_all&& req, std::function<voi
mine,
"updated",
service_node_.get_db().update_all_expiries(
req.pubkey, ns_or_default(req.msg_namespace), req.expiry),
req.pubkey, var::get<namespace_id>(req.msg_namespace), req.expiry),
req.b64,
ed25519_sk_,
req.pubkey.prefixed_hex(),