mirror of
https://github.com/oxen-io/oxen-storage-server.git
synced 2023-12-13 21:00:26 +01:00
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:
parent
bc7424d3a8
commit
0264de1daa
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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(),
|
||||
|
|
Loading…
Reference in a new issue