Compare commits
30 Commits
dabc8b4304
...
a7a3275817
Author | SHA1 | Date |
---|---|---|
Jason Volk | a7a3275817 | |
Jason Volk | 82feeb5274 | |
Jason Volk | 8af781c5d7 | |
Jason Volk | 29f3ddfc3e | |
Jason Volk | 0b888b662a | |
Jason Volk | bbed809975 | |
Jason Volk | f9aeae5516 | |
Jason Volk | 9301980f9d | |
Jason Volk | ca80d66e85 | |
Jason Volk | e7ad503f8c | |
Jason Volk | f70d837258 | |
Jason Volk | 6f5121dc6a | |
Jason Volk | 91a8fcbe43 | |
Jason Volk | b4b26484ec | |
Jason Volk | faf796a56b | |
Jason Volk | 48e0755a79 | |
Jason Volk | 5f5be52fa9 | |
Jason Volk | 5f0b98e5d1 | |
Jason Volk | 62a1c850cc | |
Jason Volk | e874e28473 | |
Jason Volk | 55f332c821 | |
Jason Volk | 44a4f33e45 | |
Jason Volk | 04025af961 | |
Jason Volk | d1b0722169 | |
Jason Volk | 0943cfd69c | |
Jason Volk | 8b0cf48578 | |
Jason Volk | 8eb4de920c | |
Giovanni Bottaro | 5a06c0066e | |
Jason Volk | 4ebc9fefaf | |
Jason Volk | 5f3398bf52 |
|
@ -4,18 +4,27 @@
|
|||
name: Docker Images
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
features:
|
||||
type: string
|
||||
description: JSON array of feature-set names to build images for.
|
||||
distros:
|
||||
type: string
|
||||
description: JSON array of operating system distros to build for.
|
||||
machines:
|
||||
type: string
|
||||
description: JSON array of machines to build for.
|
||||
toolchains:
|
||||
type: string
|
||||
description: JSON array of compiler toolchains to build for.
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
ctor_id: ${{ vars.DOCKER_ID }}
|
||||
ctor_id: ${{vars.DOCKER_ID}}
|
||||
ctor_url: https://github.com/${{github.repository}}
|
||||
|
||||
jobs:
|
||||
|
@ -26,8 +35,8 @@ jobs:
|
|||
id: ${{github.env.ctor_id}}
|
||||
url: ${{github.env.ctor_url}}
|
||||
features: '["base"]'
|
||||
distros: ${{vars.DOCKER_DISTROS}}
|
||||
machines: ${{vars.DOCKER_MACHINES}}
|
||||
distros: ${{github.event.inputs.distros || vars.DOCKER_DISTROS}}
|
||||
machines: ${{github.event.inputs.machines || vars.DOCKER_MACHINES}}
|
||||
test: ${{contains(github.events.push.commits[0].message, '[ci test]')}}
|
||||
|
||||
# Build the full-feature intermediate images (cached and not shipped).
|
||||
|
@ -38,8 +47,8 @@ jobs:
|
|||
id: ${{github.env.ctor_id}}
|
||||
url: ${{github.env.ctor_url}}
|
||||
features: '["full"]'
|
||||
distros: ${{vars.DOCKER_DISTROS}}
|
||||
machines: ${{vars.DOCKER_MACHINES}}
|
||||
distros: ${{github.event.inputs.distros || vars.DOCKER_DISTROS}}
|
||||
machines: ${{github.event.inputs.machines || vars.DOCKER_MACHINES}}
|
||||
test: ${{contains(github.events.push.commits[0].message, '[ci test]')}}
|
||||
|
||||
# Build the leaf images (shipped and not cached)
|
||||
|
@ -49,10 +58,10 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
feature: ${{fromJSON(vars.DOCKER_FEATURES)}}
|
||||
distro: ${{fromJSON(vars.DOCKER_DISTROS)}}
|
||||
machine: ${{fromJSON(vars.DOCKER_MACHINES)}}
|
||||
toolchain: ${{fromJSON(vars.DOCKER_TOOLCHAINS)}}
|
||||
feature: ${{fromJSON(github.event.inputs.features || vars.DOCKER_FEATURES)}}
|
||||
distro: ${{fromJSON(github.event.inputs.distros || vars.DOCKER_DISTROS)}}
|
||||
machine: ${{fromJSON(github.event.inputs.machines || vars.DOCKER_MACHINES)}}
|
||||
toolchain: ${{fromJSON(github.event.inputs.toolchains || vars.DOCKER_TOOLCHAINS)}}
|
||||
exclude:
|
||||
- distro: alpine-3.17
|
||||
toolchain: gcc-10 # n/a on distro version
|
||||
|
|
|
@ -46,6 +46,8 @@ struct ircd::ctx::future
|
|||
|
||||
bool valid() const { return !is(state(), future_state::INVALID); }
|
||||
bool operator!() const { return !valid(); }
|
||||
std::exception_ptr eptr() const { return state().eptr; }
|
||||
string_view what() const { return util::what(eptr()); }
|
||||
explicit operator T() { return get(); }
|
||||
|
||||
template<class U, class time_point> friend bool wait_until(const future<U> &, const time_point &, std::nothrow_t);
|
||||
|
@ -75,6 +77,8 @@ struct ircd::ctx::future<void>
|
|||
|
||||
bool valid() const { return !is(state(), future_state::INVALID); }
|
||||
bool operator!() const { return !valid(); }
|
||||
std::exception_ptr eptr() const { return state().eptr; }
|
||||
string_view what() const { return util::what(eptr()); }
|
||||
|
||||
template<class U, class time_point> friend bool wait_until(const future<U> &, const time_point &, std::nothrow_t);
|
||||
template<class U, class time_point> friend void wait_until(const future<U> &, const time_point &);
|
||||
|
|
|
@ -53,11 +53,14 @@ struct ircd::m::signing_key_update
|
|||
/// Required. The user ID whose cross-signing keys have changed.
|
||||
json::property<name::user_id, json::string>,
|
||||
|
||||
/// Cross signing key
|
||||
/// Master signing key
|
||||
json::property<name::master_key, json::object>,
|
||||
|
||||
/// Cross signing key
|
||||
json::property<name::self_signing_key, json::object>
|
||||
/// Self signing key
|
||||
json::property<name::self_signing_key, json::object>,
|
||||
|
||||
/// User signing key (local only)
|
||||
json::property<name::user_signing_key, json::object>
|
||||
>
|
||||
{
|
||||
using super_type::tuple;
|
||||
|
|
|
@ -221,4 +221,5 @@ struct ircd::m::name
|
|||
static constexpr const char *const usage {"usage"};
|
||||
static constexpr const char *const master_key {"master_key"};
|
||||
static constexpr const char *const self_signing_key {"self_signing_key"};
|
||||
static constexpr const char *const user_signing_key {"user_signing_key"};
|
||||
};
|
||||
|
|
|
@ -13,8 +13,10 @@
|
|||
|
||||
struct ircd::m::user::devices
|
||||
{
|
||||
struct send;
|
||||
|
||||
using closure = std::function<void (const event::idx &, const string_view &)>;
|
||||
using closure_bool = std::function<bool (const event::idx &, const string_view &)>;
|
||||
using closure_bool = util::function_bool<const event::idx &, const string_view &>;
|
||||
|
||||
m::user user;
|
||||
|
||||
|
@ -36,9 +38,17 @@ struct ircd::m::user::devices
|
|||
///TODO: XXX junk
|
||||
static std::map<std::string, long> count_one_time_keys(const m::user &, const string_view &);
|
||||
static bool update(const device_list_update &);
|
||||
static bool send(json::iov &content);
|
||||
|
||||
devices(const m::user &user)
|
||||
:user{user}
|
||||
{}
|
||||
};
|
||||
|
||||
/// Broadcast m.device_list_update.
|
||||
///
|
||||
struct ircd::m::user::devices::send
|
||||
{
|
||||
send(const m::user::devices &,
|
||||
const m::id::device &,
|
||||
const string_view = {});
|
||||
};
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
// The Construct
|
||||
//
|
||||
// Copyright (C) The Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2023 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
#define HAVE_IRCD_M_USER_KEYS_H
|
||||
|
||||
struct ircd::m::user::keys
|
||||
{
|
||||
struct send;
|
||||
|
||||
static string_view make_sigs_state_key(const mutable_buffer &, const string_view &tgt, const string_view &src);
|
||||
static std::tuple<string_view, string_view> unmake_sigs_state_key(const string_view &) noexcept;
|
||||
|
||||
m::user::room user_room;
|
||||
|
||||
void attach_sigs(json::stack::object &, const json::object &, const user::id &) const;
|
||||
bool attach_sigs(json::stack::object &, const event::idx &, const user::id &) const;
|
||||
void append_keys(json::stack::object &, const json::object &, const user::id &) const;
|
||||
bool append_keys(json::stack::object &, const event::idx &, const user::id &) const;
|
||||
|
||||
public:
|
||||
bool has_device(const string_view &) const;
|
||||
bool has_cross(const string_view &type) const;
|
||||
bool has_cross_master() const;
|
||||
bool has_cross_self() const;
|
||||
bool has_cross_user() const;
|
||||
|
||||
void device(json::stack::object &, const string_view &device_id) const;
|
||||
void cross(json::stack::object &, const string_view &type) const;
|
||||
void cross_master(json::stack::object &) const;
|
||||
void cross_self(json::stack::object &) const;
|
||||
void cross_user(json::stack::object &) const;
|
||||
|
||||
bool claim(json::stack::object &, const string_view &device_id, const string_view &algo) const;
|
||||
void update(const m::signing_key_update &) const;
|
||||
|
||||
keys(const m::user &user)
|
||||
:user_room{user}
|
||||
{}
|
||||
};
|
||||
|
||||
struct ircd::m::user::keys::send
|
||||
{
|
||||
send(const m::user::keys &,
|
||||
const string_view = {});
|
||||
};
|
||||
|
||||
inline void
|
||||
ircd::m::user::keys::cross_user(json::stack::object &out)
|
||||
const
|
||||
{
|
||||
return cross(out, "ircd.cross_signing.user");
|
||||
}
|
||||
|
||||
inline void
|
||||
ircd::m::user::keys::cross_self(json::stack::object &out)
|
||||
const
|
||||
{
|
||||
return cross(out, "ircd.cross_signing.self");
|
||||
}
|
||||
|
||||
inline void
|
||||
ircd::m::user::keys::cross_master(json::stack::object &out)
|
||||
const
|
||||
{
|
||||
return cross(out, "ircd.cross_signing.master");
|
||||
}
|
||||
|
||||
inline void
|
||||
ircd::m::user::keys::cross(json::stack::object &out,
|
||||
const string_view &type)
|
||||
const
|
||||
{
|
||||
const auto event_idx
|
||||
{
|
||||
user_room.get(std::nothrow, type, "")
|
||||
};
|
||||
|
||||
append_keys(out, event_idx, user_room.user.user_id);
|
||||
}
|
||||
|
||||
inline bool
|
||||
ircd::m::user::keys::has_cross_user()
|
||||
const
|
||||
{
|
||||
return has_cross("ircd.cross_signing.user");
|
||||
}
|
||||
|
||||
inline bool
|
||||
ircd::m::user::keys::has_cross_self()
|
||||
const
|
||||
{
|
||||
return has_cross("ircd.cross_signing.self");
|
||||
}
|
||||
|
||||
inline bool
|
||||
ircd::m::user::keys::has_cross_master()
|
||||
const
|
||||
{
|
||||
return has_cross("ircd.cross_signing.master");
|
||||
}
|
||||
|
||||
inline bool
|
||||
ircd::m::user::keys::has_cross(const string_view &type)
|
||||
const
|
||||
{
|
||||
return user_room.has(type, "");
|
||||
}
|
||||
|
||||
inline bool
|
||||
ircd::m::user::keys::has_device(const string_view &device_id)
|
||||
const
|
||||
{
|
||||
const m::user::devices devices
|
||||
{
|
||||
user_room.user
|
||||
};
|
||||
|
||||
return devices.has(device_id, "keys");
|
||||
}
|
|
@ -52,6 +52,7 @@ struct ircd::m::user
|
|||
struct tokens;
|
||||
struct devices;
|
||||
struct reading;
|
||||
struct keys;
|
||||
|
||||
using id = m::id::user;
|
||||
using closure = std::function<void (const user &)>;
|
||||
|
@ -105,3 +106,4 @@ const
|
|||
#include "tokens.h"
|
||||
#include "devices.h"
|
||||
#include "reading.h"
|
||||
#include "keys.h"
|
||||
|
|
|
@ -40,6 +40,7 @@ struct ircd::rest::get
|
|||
:request
|
||||
{
|
||||
get(const mutable_buffer &out, const rfc3986::uri &, opts);
|
||||
get(const mutable_buffer &out, const rfc3986::uri &);
|
||||
get(const rfc3986::uri &, opts);
|
||||
};
|
||||
|
||||
|
@ -181,6 +182,15 @@ ircd::rest::get::get(const rfc3986::uri &uri,
|
|||
}
|
||||
{}
|
||||
|
||||
inline
|
||||
ircd::rest::get::get(const mutable_buffer &out,
|
||||
const rfc3986::uri &uri)
|
||||
:get
|
||||
{
|
||||
out, uri, opts{}
|
||||
}
|
||||
{}
|
||||
|
||||
inline
|
||||
ircd::rest::get::get(const mutable_buffer &out,
|
||||
const rfc3986::uri &uri,
|
||||
|
|
|
@ -191,10 +191,10 @@ bool
|
|||
ircd::net::dns::cache::operator==(const waiter &a, const waiter &b)
|
||||
noexcept
|
||||
{
|
||||
return
|
||||
a.opts.qtype == b.opts.qtype &&
|
||||
a.key && b.key &&
|
||||
a.key == b.key;
|
||||
return true
|
||||
&& a.opts.qtype == b.opts.qtype
|
||||
&& a.key && b.key
|
||||
&& a.key == b.key;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -227,7 +227,7 @@ ircd::net::dns::cache::waiter::waiter(const hostport &hp,
|
|||
{
|
||||
opts.qtype == 33?
|
||||
make_SRV_key(keybuf, hp, opts):
|
||||
strlcpy(keybuf, host(hp))
|
||||
tolower(keybuf, host(hp))
|
||||
}
|
||||
{
|
||||
this->opts.srv = {};
|
||||
|
|
|
@ -137,6 +137,7 @@ libircd_matrix_la_SOURCES += user_devices.cc
|
|||
libircd_matrix_la_SOURCES += user_events.cc
|
||||
libircd_matrix_la_SOURCES += user_filter.cc
|
||||
libircd_matrix_la_SOURCES += user_ignores.cc
|
||||
libircd_matrix_la_SOURCES += user_keys.cc
|
||||
libircd_matrix_la_SOURCES += user_mitsein.cc
|
||||
libircd_matrix_la_SOURCES += user_notifications.cc
|
||||
libircd_matrix_la_SOURCES += user_profile.cc
|
||||
|
|
|
@ -200,3 +200,4 @@ constexpr const char *const ircd::m::name::m_in_reply_to;
|
|||
constexpr const char *const ircd::m::name::usage;
|
||||
constexpr const char *const ircd::m::name::master_key;
|
||||
constexpr const char *const ircd::m::name::self_signing_key;
|
||||
constexpr const char *const ircd::m::name::user_signing_key;
|
||||
|
|
|
@ -365,8 +365,8 @@ ircd::m::pretty_stateline(std::ostream &out,
|
|||
<< std::right << " [ "
|
||||
<< std::setw(30) << type
|
||||
<< std::left << " | "
|
||||
<< std::setw(50) << state_key
|
||||
<< std::left << " ]" << flags << " "
|
||||
<< std::setw(50) << trunc(state_key, 50)
|
||||
<< std::left << "]" << flags << " "
|
||||
<< std::setw(10) << event_idx
|
||||
<< std::left << " "
|
||||
<< std::setw(72) << string_view{event.event_id}
|
||||
|
@ -382,10 +382,10 @@ ircd::m::pretty_stateline(std::ostream &out,
|
|||
<< std::right << " "
|
||||
<< std::setw(9) << json::get<"depth"_>(event)
|
||||
<< std::right << " [ "
|
||||
<< std::setw(40) << type
|
||||
<< std::setw(50) << type
|
||||
<< std::left << " | "
|
||||
<< std::setw(56) << state_key
|
||||
<< std::left << " ]" << flags << " "
|
||||
<< std::setw(60) << trunc(state_key, 60)
|
||||
<< std::left << "]" << flags << " "
|
||||
<< std::setw(10) << event_idx
|
||||
<< ' '
|
||||
<< std::left << trunc(content, 80)
|
||||
|
|
|
@ -8,26 +8,81 @@
|
|||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
bool
|
||||
ircd::m::user::devices::send(json::iov &content)
|
||||
ircd::m::user::devices::send::send(const m::user::devices &devices,
|
||||
const m::id::device &device_id,
|
||||
const string_view room_id)
|
||||
try
|
||||
{
|
||||
assert(content.has("user_id"));
|
||||
assert(content.has("device_id"));
|
||||
assert(device_id);
|
||||
|
||||
const auto &user_id
|
||||
{
|
||||
devices.user.user_id
|
||||
};
|
||||
|
||||
const bool deleted
|
||||
{
|
||||
devices.has(device_id)
|
||||
};
|
||||
|
||||
const m::user::keys user_keys
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
const bool has_keys
|
||||
{
|
||||
!deleted && user_keys.has_device(device_id)
|
||||
};
|
||||
|
||||
const unique_mutable_buffer keys_buf
|
||||
{
|
||||
has_keys? 4_KiB: 0_KiB
|
||||
};
|
||||
|
||||
json::stack keys{keys_buf};
|
||||
if(has_keys)
|
||||
{
|
||||
json::stack::object top{keys};
|
||||
user_keys.device(top, device_id);
|
||||
}
|
||||
|
||||
// Triggers a devices request from the remote; also see
|
||||
// modules/federation/user_devices.cc
|
||||
const long &stream_id
|
||||
{
|
||||
1L
|
||||
};
|
||||
static const auto stream_id{1L};
|
||||
|
||||
json::iov event;
|
||||
json::iov event, content;
|
||||
const json::iov::push push[]
|
||||
{
|
||||
{ event, { "type", "m.device_list_update" } },
|
||||
{ event, { "sender", content.at("user_id") } },
|
||||
{ event, { "sender", user_id } },
|
||||
{ content, { "deleted", deleted } },
|
||||
{ content, { "device_id", device_id } },
|
||||
{ content, { "stream_id", stream_id } },
|
||||
{ content, { "user_id", user_id } },
|
||||
};
|
||||
|
||||
const json::iov::push push_keys
|
||||
{
|
||||
content, has_keys,
|
||||
{
|
||||
"keys", [&keys]
|
||||
{
|
||||
return keys.completed();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// For diagnostic purposes; usually not defined.
|
||||
const json::iov::push push_room_id
|
||||
{
|
||||
event, m::valid(m::id::ROOM, room_id),
|
||||
{
|
||||
"room_id", [&room_id]
|
||||
{
|
||||
return room_id;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
m::vm::copts opts;
|
||||
|
@ -39,8 +94,6 @@ try
|
|||
{
|
||||
event, content, opts
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
catch(const ctx::interrupted &)
|
||||
{
|
||||
|
@ -51,12 +104,10 @@ catch(const std::exception &e)
|
|||
log::error
|
||||
{
|
||||
m::log, "Send m.device_list_update for '%s' belonging to %s :%s",
|
||||
content.at("device_id"),
|
||||
content.at("user_id"),
|
||||
string_view{device_id},
|
||||
string_view{devices.user.user_id},
|
||||
e.what(),
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -185,21 +236,11 @@ const
|
|||
m::redact(user_room, user_room.user, event_id, "deleted")
|
||||
};
|
||||
|
||||
if(!my(user))
|
||||
return true;
|
||||
|
||||
json::iov content;
|
||||
const json::iov::push push[]
|
||||
{
|
||||
{ content, { "user_id", user.user_id } },
|
||||
{ content, { "device_id", id } },
|
||||
{ content, { "deleted", true } },
|
||||
};
|
||||
|
||||
const bool broadcasted
|
||||
{
|
||||
user::devices::send(content)
|
||||
};
|
||||
if(my(user))
|
||||
user::devices::send
|
||||
{
|
||||
*this, id
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,411 @@
|
|||
// The Construct
|
||||
//
|
||||
// Copyright (C) The Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2023 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
ircd::m::user::keys::send::send(const m::user::keys &user_keys,
|
||||
const string_view room_id)
|
||||
try
|
||||
{
|
||||
const auto &user_id
|
||||
{
|
||||
user_keys.user_room.user.user_id
|
||||
};
|
||||
|
||||
const unique_mutable_buffer keys_buf[2]
|
||||
{
|
||||
{ 4_KiB },
|
||||
{ 4_KiB },
|
||||
};
|
||||
|
||||
json::stack keys[2]
|
||||
{
|
||||
{ keys_buf[0] },
|
||||
{ keys_buf[1] },
|
||||
};
|
||||
|
||||
// master
|
||||
{
|
||||
json::stack::object object{keys[0]};
|
||||
user_keys.cross_master(object);
|
||||
}
|
||||
|
||||
// self
|
||||
{
|
||||
json::stack::object object{keys[1]};
|
||||
user_keys.cross_self(object);
|
||||
}
|
||||
|
||||
json::iov event, content;
|
||||
const json::iov::push push[]
|
||||
{
|
||||
{ event, { "type", "m.signing_key_update" } },
|
||||
{ event, { "sender", user_id } },
|
||||
{ content, { "master_key", keys[0].completed() } },
|
||||
{ content, { "self_signing_key", keys[1].completed() } },
|
||||
{ content, { "user_id", user_id } },
|
||||
};
|
||||
|
||||
// For diagnostic purposes; usually not defined.
|
||||
const json::iov::push push_room_id
|
||||
{
|
||||
event, m::valid(m::id::ROOM, room_id),
|
||||
{
|
||||
"room_id", [&room_id]
|
||||
{
|
||||
return room_id;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
m::vm::copts opts;
|
||||
opts.edu = true;
|
||||
opts.prop_mask.reset();
|
||||
opts.prop_mask.set("origin");
|
||||
opts.notify_clients = false;
|
||||
m::vm::eval
|
||||
{
|
||||
event, content, opts
|
||||
};
|
||||
}
|
||||
catch(const ctx::interrupted &)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
log::error
|
||||
{
|
||||
m::log, "Sending m.signing_key_update for %s :%s",
|
||||
string_view{user_keys.user_room.user.user_id},
|
||||
e.what(),
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::user::keys::update(const m::signing_key_update &sku)
|
||||
const
|
||||
{
|
||||
const m::user::id &user_id
|
||||
{
|
||||
json::get<"user_id"_>(sku)
|
||||
};
|
||||
|
||||
const m::user::room room
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
const json::object &msk
|
||||
{
|
||||
json::get<"master_key"_>(sku)
|
||||
};
|
||||
|
||||
const auto cross_master_id
|
||||
{
|
||||
json::get<"master_key"_>(sku)?
|
||||
m::send(room, user_id, "ircd.cross_signing.master", "", msk):
|
||||
m::event::id::buf{}
|
||||
};
|
||||
|
||||
const json::object &ssk
|
||||
{
|
||||
json::get<"self_signing_key"_>(sku)
|
||||
};
|
||||
|
||||
const auto cross_self_id
|
||||
{
|
||||
ssk?
|
||||
m::send(room, user_id, "ircd.cross_signing.self", "", ssk):
|
||||
m::event::id::buf{}
|
||||
};
|
||||
|
||||
const json::object &usk
|
||||
{
|
||||
json::get<"user_signing_key"_>(sku)
|
||||
};
|
||||
|
||||
const auto cross_user_id
|
||||
{
|
||||
usk && my(user_id)?
|
||||
m::send(room, user_id, "ircd.cross_signing.user", "", usk):
|
||||
m::event::id::buf{}
|
||||
};
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::user::keys::claim(json::stack::object &object,
|
||||
const string_view &device_id,
|
||||
const string_view &algorithm)
|
||||
const
|
||||
{
|
||||
const fmt::bsprintf<m::event::TYPE_MAX_SIZE> type
|
||||
{
|
||||
"ircd.device.one_time_key|%s",
|
||||
algorithm
|
||||
};
|
||||
|
||||
const m::room::type events
|
||||
{
|
||||
user_room, type, { -1UL, -1L }, true
|
||||
};
|
||||
|
||||
return !events.for_each([this, &object, &device_id]
|
||||
(const string_view &type, const auto &, const m::event::idx &event_idx)
|
||||
{
|
||||
if(m::redacted(event_idx))
|
||||
return true;
|
||||
|
||||
const bool match
|
||||
{
|
||||
m::query(std::nothrow, event_idx, "state_key", [&device_id]
|
||||
(const string_view &state_key) noexcept
|
||||
{
|
||||
return state_key == device_id;
|
||||
})
|
||||
};
|
||||
|
||||
if(!match)
|
||||
return true;
|
||||
|
||||
const auto algorithm
|
||||
{
|
||||
split(type, '|').second
|
||||
};
|
||||
|
||||
const bool fetched
|
||||
{
|
||||
m::get(std::nothrow, event_idx, "content", [&object, &algorithm]
|
||||
(const json::object &content)
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
object, algorithm, json::object
|
||||
{
|
||||
content[""] // ircd.device.* quirk
|
||||
}
|
||||
};
|
||||
})
|
||||
};
|
||||
|
||||
if(!fetched)
|
||||
return true;
|
||||
|
||||
const auto event_id
|
||||
{
|
||||
m::event_id(event_idx)
|
||||
};
|
||||
|
||||
const auto redact_id
|
||||
{
|
||||
m::redact(user_room, user_room.user, event_id, "claimed")
|
||||
};
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::user::keys::device(json::stack::object &out,
|
||||
const string_view &device_id)
|
||||
const
|
||||
{
|
||||
const m::user::devices devices
|
||||
{
|
||||
user_room.user
|
||||
};
|
||||
|
||||
devices.get(std::nothrow, device_id, "keys", [this, &out, &device_id]
|
||||
(const auto &, const json::object &device_keys)
|
||||
{
|
||||
const auto &user_id
|
||||
{
|
||||
user_room.user.user_id
|
||||
};
|
||||
|
||||
for(const auto &member : device_keys)
|
||||
if(member.first != "signatures")
|
||||
json::stack::member
|
||||
{
|
||||
out, member
|
||||
};
|
||||
|
||||
json::stack::object sigs
|
||||
{
|
||||
out, "signatures"
|
||||
};
|
||||
|
||||
json::stack::object user_sigs
|
||||
{
|
||||
sigs, user_id
|
||||
};
|
||||
|
||||
attach_sigs(user_sigs, device_keys, user_id);
|
||||
const m::room::state state
|
||||
{
|
||||
user_room
|
||||
};
|
||||
|
||||
state.for_each("ircd.keys.signatures", [this, &user_sigs, &user_id, &device_id]
|
||||
(const string_view &, const string_view &state_key, const auto &event_idx)
|
||||
{
|
||||
const auto &[target, source]
|
||||
{
|
||||
unmake_sigs_state_key(state_key)
|
||||
};
|
||||
|
||||
if(target && target != device_id)
|
||||
return true;
|
||||
|
||||
attach_sigs(user_sigs, event_idx, user_id);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::user::keys::append_keys(json::stack::object &out,
|
||||
const event::idx &event_idx,
|
||||
const user::id &user_id)
|
||||
const
|
||||
{
|
||||
return m::get(std::nothrow, event_idx, "content", [this, &out, &user_id]
|
||||
(const json::object &device_keys)
|
||||
{
|
||||
append_keys(out, device_keys, user_id);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::user::keys::append_keys(json::stack::object &out,
|
||||
const json::object &device_keys,
|
||||
const user::id &user_id)
|
||||
const
|
||||
{
|
||||
for(const auto &member : device_keys)
|
||||
if(member.first != "signatures")
|
||||
json::stack::member
|
||||
{
|
||||
out, member
|
||||
};
|
||||
|
||||
json::stack::object sigs
|
||||
{
|
||||
out, "signatures"
|
||||
};
|
||||
|
||||
json::stack::object user_sigs
|
||||
{
|
||||
sigs, user_id
|
||||
};
|
||||
|
||||
attach_sigs(user_sigs, device_keys, user_id);
|
||||
const json::object device_keys_keys
|
||||
{
|
||||
device_keys["keys"]
|
||||
};
|
||||
|
||||
const m::room::state state
|
||||
{
|
||||
user_room
|
||||
};
|
||||
|
||||
state.for_each("ircd.keys.signatures", [this, &user_sigs, &user_id, &device_keys_keys]
|
||||
(const string_view &, const string_view &state_key, const auto &event_idx)
|
||||
{
|
||||
for(const auto &[key_id_, key] : device_keys_keys)
|
||||
{
|
||||
const auto &key_id
|
||||
{
|
||||
split(key_id_, ':').second
|
||||
};
|
||||
|
||||
const auto &[target, source]
|
||||
{
|
||||
unmake_sigs_state_key(state_key)
|
||||
};
|
||||
|
||||
if(target != key_id)
|
||||
continue;
|
||||
|
||||
attach_sigs(user_sigs, event_idx, user_id);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::user::keys::attach_sigs(json::stack::object &user_sigs,
|
||||
const event::idx &event_idx,
|
||||
const user::id &user_id)
|
||||
const
|
||||
{
|
||||
return m::get(std::nothrow, event_idx, "content", [this, &user_sigs, &user_id]
|
||||
(const json::object &device_sigs)
|
||||
{
|
||||
attach_sigs(user_sigs, device_sigs, user_id);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::user::keys::attach_sigs(json::stack::object &user_sigs,
|
||||
const json::object &device_sigs,
|
||||
const user::id &user_id)
|
||||
const
|
||||
{
|
||||
const json::object device_sigs_sigs
|
||||
{
|
||||
device_sigs["signatures"]
|
||||
};
|
||||
|
||||
const json::object device_sigs_user_sigs
|
||||
{
|
||||
device_sigs_sigs[user_id]
|
||||
};
|
||||
|
||||
for(const auto &member : device_sigs_user_sigs)
|
||||
json::stack::member
|
||||
{
|
||||
user_sigs, member
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// user::keys::sigs
|
||||
//
|
||||
|
||||
std::tuple<ircd::string_view, ircd::string_view>
|
||||
ircd::m::user::keys::unmake_sigs_state_key(const string_view &state_key)
|
||||
noexcept
|
||||
{
|
||||
const auto &[tgt, src]
|
||||
{
|
||||
rsplit(state_key, '%')
|
||||
};
|
||||
|
||||
return std::tuple
|
||||
{
|
||||
tgt, src
|
||||
};
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::m::user::keys::make_sigs_state_key(const mutable_buffer &buf,
|
||||
const string_view &tgt,
|
||||
const string_view &src)
|
||||
{
|
||||
return fmt::sprintf
|
||||
{
|
||||
buf, "%s%s",
|
||||
string_view{tgt},
|
||||
tgt != src && src?
|
||||
string_view{src}:
|
||||
string_view{},
|
||||
};
|
||||
}
|
|
@ -60,7 +60,7 @@ ircd::m::groups::handle_post(client &client,
|
|||
|
||||
return resource::response
|
||||
{
|
||||
client, http::CREATED, json::members
|
||||
client, http::OK, json::members
|
||||
{
|
||||
{ "group_id", group_id }
|
||||
},
|
||||
|
|
|
@ -103,7 +103,7 @@ post__createroom(client &client,
|
|||
top.~object();
|
||||
return m::resource::response
|
||||
{
|
||||
client, http::CREATED, json::object
|
||||
client, http::OK, json::object
|
||||
{
|
||||
out.completed()
|
||||
}
|
||||
|
|
|
@ -42,10 +42,11 @@ recv_response(const string_view &,
|
|||
const system_point &);
|
||||
|
||||
static void
|
||||
recv_responses(query_map &,
|
||||
recv_responses(const host_users_map &,
|
||||
query_map &,
|
||||
failure_map &,
|
||||
json::stack::object &,
|
||||
const milliseconds &);
|
||||
const system_point &);
|
||||
|
||||
static void
|
||||
handle_failures(const failure_map &,
|
||||
|
@ -137,6 +138,11 @@ post__keys_claim(client &client,
|
|||
send_requests(map, buffers, failures)
|
||||
};
|
||||
|
||||
const system_point timedout
|
||||
{
|
||||
ircd::now<system_point>() + timeout
|
||||
};
|
||||
|
||||
m::resource::response::chunked response
|
||||
{
|
||||
client, http::OK
|
||||
|
@ -152,9 +158,9 @@ post__keys_claim(client &client,
|
|||
out
|
||||
};
|
||||
|
||||
recv_responses(queries, failures, top, timeout);
|
||||
recv_responses(map, queries, failures, top, timedout);
|
||||
handle_failures(failures, top);
|
||||
return {};
|
||||
return response;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -174,14 +180,22 @@ handle_failures(const failure_map &failures,
|
|||
}
|
||||
|
||||
void
|
||||
recv_responses(query_map &queries,
|
||||
recv_responses(const host_users_map &map,
|
||||
query_map &queries,
|
||||
failure_map &failures,
|
||||
json::stack::object &out,
|
||||
const milliseconds &timeout)
|
||||
const system_point &timeout)
|
||||
{
|
||||
const system_point timedout
|
||||
static const user_devices_map empty;
|
||||
|
||||
const auto it
|
||||
{
|
||||
ircd::now<system_point>() + timeout
|
||||
map.find(origin(m::my()))
|
||||
};
|
||||
|
||||
const user_devices_map &self
|
||||
{
|
||||
it != end(map)? it->second: empty
|
||||
};
|
||||
|
||||
json::stack::object one_time_keys
|
||||
|
@ -189,13 +203,35 @@ recv_responses(query_map &queries,
|
|||
out, "one_time_keys"
|
||||
};
|
||||
|
||||
// local handle
|
||||
for(const auto &[user_id, reqs] : self)
|
||||
{
|
||||
const m::user::keys keys
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
json::stack::object user_object
|
||||
{
|
||||
one_time_keys, user_id
|
||||
};
|
||||
|
||||
for(const auto &[device_id, algorithm] : json::object(reqs))
|
||||
{
|
||||
json::stack::object device_object
|
||||
{
|
||||
user_object, device_id
|
||||
};
|
||||
|
||||
keys.claim(device_object, device_id, json::string(algorithm));
|
||||
}
|
||||
}
|
||||
|
||||
// remote handle
|
||||
for(auto &[remote, request] : queries)
|
||||
{
|
||||
assert(!failures.count(remote));
|
||||
if(failures.count(remote))
|
||||
continue;
|
||||
|
||||
recv_response(remote, request, failures, one_time_keys, timedout);
|
||||
recv_response(remote, request, failures, one_time_keys, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -223,14 +259,22 @@ try
|
|||
};
|
||||
|
||||
for(const auto &[user_id, keys] : one_time_keys)
|
||||
{
|
||||
if(m::user::id(user_id).host() != remote)
|
||||
continue;
|
||||
|
||||
json::stack::member
|
||||
{
|
||||
object, user_id, json::object{keys}
|
||||
object, user_id, json::object
|
||||
{
|
||||
keys
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
log::error
|
||||
log::derror
|
||||
{
|
||||
m::log, "user keys claim from %s :%s",
|
||||
remote,
|
||||
|
@ -247,7 +291,8 @@ send_requests(const host_users_map &hosts,
|
|||
{
|
||||
query_map ret;
|
||||
for(const auto &[remote, user_devices] : hosts)
|
||||
send_request(remote, user_devices, failures, buffers, ret);
|
||||
if(likely(!my_host(remote)))
|
||||
send_request(remote, user_devices, failures, buffers, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -292,7 +337,7 @@ try
|
|||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
log::error
|
||||
log::derror
|
||||
{
|
||||
m::log, "user keys claim to %s for %zu users :%s",
|
||||
remote,
|
||||
|
|
|
@ -66,52 +66,26 @@ ircd::m::post_keys_device_signing_upload(client &client,
|
|||
auth["password"]
|
||||
};
|
||||
|
||||
const m::user::room room
|
||||
const m::user user
|
||||
{
|
||||
request.user_id
|
||||
};
|
||||
|
||||
if(!room.user.is_password(password))
|
||||
if(!user.is_password(password))
|
||||
throw m::ACCESS_DENIED
|
||||
{
|
||||
"Incorrect password."
|
||||
};
|
||||
|
||||
const json::object &msk
|
||||
const m::user::keys keys
|
||||
{
|
||||
request["master_key"]
|
||||
user
|
||||
};
|
||||
|
||||
const auto master_id
|
||||
{
|
||||
msk?
|
||||
send(room, request.user_id, "ircd.device.signing.master", "", msk):
|
||||
event::id::buf{}
|
||||
};
|
||||
m::signing_key_update sku{request};
|
||||
json::get<"user_id"_>(sku) = request.user_id;
|
||||
|
||||
const json::object &ssk
|
||||
{
|
||||
request["self_signing_key"]
|
||||
};
|
||||
|
||||
const auto self_signing_id
|
||||
{
|
||||
ssk?
|
||||
send(room, request.user_id, "ircd.device.signing.self", "", ssk):
|
||||
event::id::buf{}
|
||||
};
|
||||
|
||||
const json::object &usk
|
||||
{
|
||||
request["user_signing_key"]
|
||||
};
|
||||
|
||||
const auto user_signing_id
|
||||
{
|
||||
usk?
|
||||
send(room, request.user_id, "ircd.device.signing.user", "", usk):
|
||||
event::id::buf{}
|
||||
};
|
||||
keys.update(sku);
|
||||
|
||||
return resource::response
|
||||
{
|
||||
|
|
|
@ -19,8 +19,32 @@ namespace
|
|||
using buffer_list = std::vector<unique_buffer<mutable_buffer>>;
|
||||
}
|
||||
|
||||
static host_users_map
|
||||
parse_user_request(const json::object &device_keys);
|
||||
static void
|
||||
handle_cross_keys(const m::resource::request &,
|
||||
const user_devices_map &,
|
||||
query_map &,
|
||||
failure_map &,
|
||||
json::stack::object &,
|
||||
const string_view &);
|
||||
|
||||
static void
|
||||
handle_device_keys(const m::resource::request &,
|
||||
const user_devices_map &,
|
||||
query_map &,
|
||||
failure_map &,
|
||||
json::stack::object &);
|
||||
|
||||
static void
|
||||
handle_responses(const m::resource::request &,
|
||||
const host_users_map &,
|
||||
query_map &,
|
||||
failure_map &,
|
||||
json::stack::object &);
|
||||
|
||||
static void
|
||||
handle_errors(const m::resource::request &,
|
||||
query_map &,
|
||||
failure_map &);
|
||||
|
||||
static bool
|
||||
send_request(const string_view &,
|
||||
|
@ -34,19 +58,8 @@ send_requests(const host_users_map &,
|
|||
buffer_list &,
|
||||
failure_map &);
|
||||
|
||||
static void
|
||||
recv_response(const m::resource::request &,
|
||||
const string_view &,
|
||||
m::fed::user::keys::query &,
|
||||
failure_map &,
|
||||
json::stack::object &);
|
||||
|
||||
static void
|
||||
recv_responses(const m::resource::request &,
|
||||
query_map &,
|
||||
failure_map &,
|
||||
json::stack::object &,
|
||||
const milliseconds &);
|
||||
static host_users_map
|
||||
parse_user_request(const json::object &device_keys);
|
||||
|
||||
static void
|
||||
handle_failures(const failure_map &,
|
||||
|
@ -148,6 +161,20 @@ post__keys_query(client &client,
|
|||
send_requests(map, buffers, failures)
|
||||
};
|
||||
|
||||
auto responses
|
||||
{
|
||||
ctx::when_all(begin(queries), end(queries), []
|
||||
(auto &it) -> m::fed::user::keys::query &
|
||||
{
|
||||
return it->second;
|
||||
})
|
||||
};
|
||||
|
||||
const bool all_good
|
||||
{
|
||||
responses.wait_until(now<system_point>() + timeout, std::nothrow)
|
||||
};
|
||||
|
||||
m::resource::response::chunked response
|
||||
{
|
||||
client, http::OK
|
||||
|
@ -163,9 +190,9 @@ post__keys_query(client &client,
|
|||
out
|
||||
};
|
||||
|
||||
recv_responses(request, queries, failures, top, timeout);
|
||||
handle_responses(request, map, queries, failures, top);
|
||||
handle_failures(failures, top);
|
||||
return {};
|
||||
return response;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -177,216 +204,39 @@ handle_failures(const failure_map &failures,
|
|||
out, "failures"
|
||||
};
|
||||
|
||||
for(const auto &p : failures)
|
||||
{
|
||||
const string_view &hostname(p.first);
|
||||
const std::exception_ptr &eptr(p.second);
|
||||
for(const auto &[remote, eptr] : failures)
|
||||
json::stack::member
|
||||
{
|
||||
response_failures, hostname, what(eptr)
|
||||
response_failures, remote, what(eptr)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
recv_responses(const m::resource::request &client_request,
|
||||
query_map &queries,
|
||||
failure_map &failures,
|
||||
json::stack::object &out,
|
||||
const milliseconds &timeout)
|
||||
try
|
||||
host_users_map
|
||||
parse_user_request(const json::object &device_keys)
|
||||
{
|
||||
const system_point timedout
|
||||
host_users_map ret;
|
||||
for(const auto &member : device_keys)
|
||||
{
|
||||
ircd::now<system_point>() + timeout
|
||||
};
|
||||
const m::user::id &user_id(member.first);
|
||||
const json::array &device_ids(member.second);
|
||||
const string_view &host(user_id.host());
|
||||
|
||||
while(!queries.empty())
|
||||
{
|
||||
static const auto dereferencer{[]
|
||||
(auto &it) -> m::fed::user::keys::query &
|
||||
auto it(ret.lower_bound(host));
|
||||
if(it == end(ret) || it->first != host)
|
||||
it = ret.emplace_hint(it, host, user_devices_map{});
|
||||
|
||||
user_devices_map &users(it->second);
|
||||
{
|
||||
return it->second;
|
||||
}};
|
||||
auto it(users.lower_bound(user_id));
|
||||
if(it == end(users) || it->first != user_id)
|
||||
it = users.emplace_hint(it, user_id, json::array{});
|
||||
|
||||
auto next
|
||||
{
|
||||
ctx::when_any(begin(queries), end(queries), dereferencer)
|
||||
};
|
||||
|
||||
next.wait_until(timedout); // throws on timeout
|
||||
const auto it{next.get()};
|
||||
const unwind remove{[&queries, &it]
|
||||
{
|
||||
queries.erase(it);
|
||||
}};
|
||||
|
||||
const auto &remote(it->first);
|
||||
auto &request(it->second);
|
||||
|
||||
assert(!failures.count(remote));
|
||||
if(failures.count(remote))
|
||||
continue;
|
||||
|
||||
recv_response(client_request, remote, request, failures, out);
|
||||
}
|
||||
}
|
||||
catch(const std::exception &)
|
||||
{
|
||||
for(const auto &[remote, request] : queries)
|
||||
failures.emplace(remote, std::current_exception());
|
||||
}
|
||||
|
||||
void
|
||||
recv_response(const m::resource::request &client_request,
|
||||
const string_view &remote,
|
||||
m::fed::user::keys::query &request,
|
||||
failure_map &failures,
|
||||
json::stack::object &out)
|
||||
try
|
||||
{
|
||||
const auto code
|
||||
{
|
||||
request.get()
|
||||
};
|
||||
|
||||
const json::object response
|
||||
{
|
||||
request
|
||||
};
|
||||
|
||||
// device_keys
|
||||
{
|
||||
json::stack::object object
|
||||
{
|
||||
out, "device_keys"
|
||||
};
|
||||
|
||||
const json::object &device_keys
|
||||
{
|
||||
response["device_keys"]
|
||||
};
|
||||
|
||||
for(const auto &[_user_id, device_keys] : device_keys)
|
||||
{
|
||||
const m::user::id &user_id
|
||||
{
|
||||
_user_id
|
||||
};
|
||||
|
||||
json::stack::object user_object
|
||||
{
|
||||
object, user_id
|
||||
};
|
||||
|
||||
for(const auto &[device_id, keys] : json::object(device_keys))
|
||||
json::stack::member
|
||||
{
|
||||
user_object, device_id, keys
|
||||
};
|
||||
if(!empty(device_ids))
|
||||
it->second = device_ids;
|
||||
}
|
||||
}
|
||||
|
||||
// master_keys
|
||||
{
|
||||
json::stack::object object
|
||||
{
|
||||
out, "master_keys"
|
||||
};
|
||||
|
||||
const json::object &master_keys
|
||||
{
|
||||
response["master_keys"]
|
||||
};
|
||||
|
||||
for(const auto &[_user_id, master_key] : master_keys)
|
||||
{
|
||||
const m::user::id &user_id
|
||||
{
|
||||
_user_id
|
||||
};
|
||||
|
||||
json::stack::member
|
||||
{
|
||||
object, user_id, json::object
|
||||
{
|
||||
master_key
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// self_signing_keys
|
||||
{
|
||||
json::stack::object object
|
||||
{
|
||||
out, "self_signing_keys"
|
||||
};
|
||||
|
||||
const json::object &self_signing_keys
|
||||
{
|
||||
response["self_signing_keys"]
|
||||
};
|
||||
|
||||
for(const auto &[_user_id, self_signing_key] : self_signing_keys)
|
||||
{
|
||||
const m::user::id &user_id
|
||||
{
|
||||
_user_id
|
||||
};
|
||||
|
||||
json::stack::member
|
||||
{
|
||||
object, user_id, json::object
|
||||
{
|
||||
self_signing_key
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// user_signing_keys
|
||||
{
|
||||
json::stack::object object
|
||||
{
|
||||
out, "user_signing_keys"
|
||||
};
|
||||
|
||||
const json::object &user_signing_keys
|
||||
{
|
||||
response["user_signing_keys"]
|
||||
};
|
||||
|
||||
for(const auto &[_user_id, user_signing_key] : user_signing_keys)
|
||||
{
|
||||
const m::user::id &user_id
|
||||
{
|
||||
_user_id
|
||||
};
|
||||
|
||||
if(client_request.user_id != _user_id)
|
||||
continue;
|
||||
|
||||
json::stack::member
|
||||
{
|
||||
object, user_id, json::object
|
||||
{
|
||||
user_signing_key
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
log::error
|
||||
{
|
||||
m::log, "user keys query from %s :%s",
|
||||
remote,
|
||||
e.what()
|
||||
};
|
||||
|
||||
failures.emplace(remote, std::current_exception());
|
||||
return ret;
|
||||
}
|
||||
|
||||
query_map
|
||||
|
@ -395,12 +245,9 @@ send_requests(const host_users_map &hosts,
|
|||
failure_map &failures)
|
||||
{
|
||||
query_map ret;
|
||||
for(const auto &pair : hosts)
|
||||
{
|
||||
const string_view &remote(pair.first);
|
||||
const user_devices_map &user_devices(pair.second);
|
||||
send_request(remote, user_devices, failures, buffers, ret);
|
||||
}
|
||||
for(const auto &[remote, user_devices] : hosts)
|
||||
if(likely(!my_host(remote)))
|
||||
send_request(remote, user_devices, failures, buffers, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -441,43 +288,281 @@ try
|
|||
|
||||
return true;
|
||||
}
|
||||
catch(const ctx::interrupted &e)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
log::error
|
||||
failures.emplace(remote, std::current_exception());
|
||||
log::derror
|
||||
{
|
||||
m::log, "user keys query to %s :%s",
|
||||
remote,
|
||||
e.what()
|
||||
};
|
||||
|
||||
failures.emplace(remote, std::current_exception());
|
||||
return false;
|
||||
}
|
||||
|
||||
host_users_map
|
||||
parse_user_request(const json::object &device_keys)
|
||||
void
|
||||
handle_responses(const m::resource::request &request,
|
||||
const host_users_map &map,
|
||||
query_map &queries,
|
||||
failure_map &failures,
|
||||
json::stack::object &out)
|
||||
{
|
||||
host_users_map ret;
|
||||
for(const auto &member : device_keys)
|
||||
static const user_devices_map empty;
|
||||
|
||||
const auto it
|
||||
{
|
||||
const m::user::id &user_id(member.first);
|
||||
const json::array &device_ids(member.second);
|
||||
const string_view &host(user_id.host());
|
||||
map.find(origin(m::my()))
|
||||
};
|
||||
|
||||
auto it(ret.lower_bound(host));
|
||||
if(it == end(ret) || it->first != host)
|
||||
it = ret.emplace_hint(it, host, user_devices_map{});
|
||||
const user_devices_map &self
|
||||
{
|
||||
it != end(map)? it->second: empty
|
||||
};
|
||||
|
||||
user_devices_map &users(it->second);
|
||||
handle_errors(request, queries, failures);
|
||||
handle_device_keys(request, self, queries, failures, out);
|
||||
handle_cross_keys(request, self, queries, failures, out, "master_keys");
|
||||
handle_cross_keys(request, self, queries, failures, out, "self_signing_keys");
|
||||
handle_cross_keys(request, self, queries, failures, out, "user_signing_keys");
|
||||
}
|
||||
|
||||
void
|
||||
handle_errors(const m::resource::request &request,
|
||||
query_map &queries,
|
||||
failure_map &failures)
|
||||
{
|
||||
auto it(begin(queries));
|
||||
while(it != end(queries))
|
||||
{
|
||||
const auto &[remote, query] {*it};
|
||||
if(query.eptr())
|
||||
{
|
||||
auto it(users.lower_bound(user_id));
|
||||
if(it == end(users) || it->first != user_id)
|
||||
it = users.emplace_hint(it, user_id, json::array{});
|
||||
failures.emplace(remote, query.eptr());
|
||||
it = queries.erase(it);
|
||||
}
|
||||
else ++it;
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty(device_ids))
|
||||
it->second = device_ids;
|
||||
void
|
||||
handle_device_keys(const m::resource::request &request,
|
||||
const user_devices_map &self,
|
||||
query_map &queries,
|
||||
failure_map &failures,
|
||||
json::stack::object &out)
|
||||
{
|
||||
json::stack::object object
|
||||
{
|
||||
out, "device_keys"
|
||||
};
|
||||
|
||||
// local handle
|
||||
for(const auto &[user_id, device_ids] : self)
|
||||
{
|
||||
const m::user::keys keys
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
json::stack::object user_object
|
||||
{
|
||||
object, user_id
|
||||
};
|
||||
|
||||
if(empty(json::array(device_ids)))
|
||||
{
|
||||
const m::user::devices devices
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
devices.for_each([&user_object, &keys]
|
||||
(const auto &, const string_view &device_id)
|
||||
{
|
||||
json::stack::object device_object
|
||||
{
|
||||
user_object, device_id
|
||||
};
|
||||
|
||||
keys.device(device_object, device_id);
|
||||
});
|
||||
}
|
||||
else for(const json::string device_id : json::array(device_ids))
|
||||
{
|
||||
json::stack::object device_object
|
||||
{
|
||||
user_object, device_id
|
||||
};
|
||||
|
||||
keys.device(device_object, device_id);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
// remote handle
|
||||
for(const auto &[remote, query] : queries) try
|
||||
{
|
||||
const json::object response
|
||||
{
|
||||
query.in.content
|
||||
};
|
||||
|
||||
const json::object &device_keys
|
||||
{
|
||||
response["device_keys"]
|
||||
};
|
||||
|
||||
for(const auto &[user_id, device_keys] : device_keys)
|
||||
{
|
||||
if(m::user::id(user_id).host() != remote)
|
||||
continue;
|
||||
|
||||
json::stack::object user_object
|
||||
{
|
||||
object, user_id
|
||||
};
|
||||
|
||||
for(const auto &[device_id, keys] : json::object(device_keys))
|
||||
json::stack::member
|
||||
{
|
||||
user_object, device_id, keys
|
||||
};
|
||||
}
|
||||
}
|
||||
catch(const ctx::interrupted &)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
failures.emplace(remote, std::current_exception());
|
||||
log::derror
|
||||
{
|
||||
m::log, "Processing device_keys response from '%s' :%s",
|
||||
remote,
|
||||
e.what(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static std::tuple<string_view, bool>
|
||||
translate_cross_type(const string_view &name)
|
||||
{
|
||||
bool match_user;
|
||||
string_view cross_type;
|
||||
switch(match_user = false; hash(name))
|
||||
{
|
||||
case "master_keys"_:
|
||||
cross_type = "ircd.cross_signing.master";
|
||||
break;
|
||||
|
||||
case "self_signing_keys"_:
|
||||
cross_type = "ircd.cross_signing.self";
|
||||
break;
|
||||
|
||||
case "user_signing_keys"_:
|
||||
cross_type = "ircd.cross_signing.user";
|
||||
match_user = true;
|
||||
break;
|
||||
};
|
||||
|
||||
assert(cross_type);
|
||||
return
|
||||
{
|
||||
cross_type, match_user
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
handle_cross_keys(const m::resource::request &request,
|
||||
const user_devices_map &self,
|
||||
query_map &queries,
|
||||
failure_map &failures,
|
||||
json::stack::object &out_,
|
||||
const string_view &name)
|
||||
{
|
||||
const auto &[cross_type, match_user]
|
||||
{
|
||||
translate_cross_type(name)
|
||||
};
|
||||
|
||||
json::stack::object out
|
||||
{
|
||||
out_, name
|
||||
};
|
||||
|
||||
// local handle
|
||||
for(const auto &[user_id, device_ids] : self)
|
||||
{
|
||||
if(match_user && request.user_id != user_id)
|
||||
continue;
|
||||
|
||||
const m::user::keys keys
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
if(!keys.has_cross(cross_type))
|
||||
continue;
|
||||
|
||||
json::stack::object user_object
|
||||
{
|
||||
out, user_id
|
||||
};
|
||||
|
||||
keys.cross(user_object, cross_type);
|
||||
}
|
||||
|
||||
// remote handle
|
||||
for(auto &[remote, query] : queries) try
|
||||
{
|
||||
if(match_user && request.user_id.host() != remote)
|
||||
continue;
|
||||
|
||||
const json::object response
|
||||
{
|
||||
query.in.content
|
||||
};
|
||||
|
||||
const json::object &object
|
||||
{
|
||||
response[name]
|
||||
};
|
||||
|
||||
for(const auto &[user_id, keys] : object)
|
||||
{
|
||||
if(m::user::id(user_id).host() != remote)
|
||||
continue;
|
||||
|
||||
if(match_user && request.user_id != user_id)
|
||||
continue;
|
||||
|
||||
json::stack::member
|
||||
{
|
||||
out, user_id, json::object
|
||||
{
|
||||
keys
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
catch(const ctx::interrupted &)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
failures.emplace(remote, std::current_exception());
|
||||
log::derror
|
||||
{
|
||||
m::log, "Processing %s response from '%s' :%s",
|
||||
name,
|
||||
remote,
|
||||
e.what(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,42 +43,35 @@ ircd::m::resource::response
|
|||
ircd::m::post_keys_signatures_upload(client &client,
|
||||
const resource::request &request)
|
||||
{
|
||||
for(const auto &[_user_id, devices_keys_] : request)
|
||||
const auto src_dev
|
||||
{
|
||||
if(!valid(m::id::USER, _user_id))
|
||||
user::tokens::device(std::nothrow, request.access_token)
|
||||
};
|
||||
|
||||
for(const auto &[user_id, device_keys_] : request)
|
||||
{
|
||||
if(!valid(m::id::USER, user_id))
|
||||
continue;
|
||||
|
||||
const m::user::id user_id
|
||||
const json::object device_keys
|
||||
{
|
||||
_user_id
|
||||
device_keys_
|
||||
};
|
||||
|
||||
const m::user::room user_room
|
||||
const user::room user_room
|
||||
{
|
||||
user_id
|
||||
user::id{user_id}
|
||||
};
|
||||
|
||||
const m::user::devices devices
|
||||
for(const auto &[tgt_id, keys] : device_keys)
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
const json::object &devices_keys
|
||||
{
|
||||
devices_keys_
|
||||
};
|
||||
|
||||
for(const auto &[_device_id, device_keys_] : devices_keys)
|
||||
{
|
||||
const m::device_keys device_keys
|
||||
char state_key_buf[512];
|
||||
const string_view state_key
|
||||
{
|
||||
device_keys_
|
||||
user::keys::make_sigs_state_key(state_key_buf, tgt_id, src_dev)
|
||||
};
|
||||
|
||||
const bool set
|
||||
{
|
||||
devices.set(_device_id, "signatures", device_keys_)
|
||||
};
|
||||
send(user_room, user_id, "ircd.keys.signatures", state_key, keys);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -148,7 +148,7 @@ try
|
|||
// Send response to user
|
||||
return m::resource::response
|
||||
{
|
||||
client, http::CREATED, response
|
||||
client, http::OK, response
|
||||
};
|
||||
}
|
||||
catch(const m::INVALID_MXID &e)
|
||||
|
@ -191,7 +191,7 @@ post__register_guest(client &client,
|
|||
|
||||
return m::resource::response
|
||||
{
|
||||
client, http::CREATED,
|
||||
client, http::OK,
|
||||
{
|
||||
{ "user_id", user_id },
|
||||
{ "home_server", my_host() },
|
||||
|
@ -243,7 +243,7 @@ try
|
|||
// Send response to user
|
||||
return m::resource::response
|
||||
{
|
||||
client, http::CREATED, response
|
||||
client, http::OK, response
|
||||
};
|
||||
}
|
||||
catch(const m::INVALID_MXID &e)
|
||||
|
|
|
@ -60,9 +60,11 @@ get__relations(client &client,
|
|||
"relation rel_type path parameter required"
|
||||
};
|
||||
|
||||
const string_view &rel_type
|
||||
const string_view rel_type
|
||||
{
|
||||
url::decode(rel_type_buf, request.parv[3])
|
||||
request.parv.size() > 3?
|
||||
url::decode(rel_type_buf, request.parv[3]):
|
||||
string_view{}
|
||||
};
|
||||
|
||||
// Get the alleged type path parameter.
|
||||
|
@ -75,9 +77,11 @@ get__relations(client &client,
|
|||
"relation ?type? path parameter required"
|
||||
};
|
||||
|
||||
const string_view &type
|
||||
const string_view type
|
||||
{
|
||||
url::decode(type_buf, request.parv[4])
|
||||
request.parv.size() > 4?
|
||||
url::decode(type_buf, request.parv[4]):
|
||||
string_view{}
|
||||
};
|
||||
|
||||
const auto event_idx
|
||||
|
|
|
@ -38,13 +38,21 @@ ircd::m::sync::device_lists_linear(data &data)
|
|||
|
||||
assert(data.event);
|
||||
const m::event &event{*data.event};
|
||||
if(!startswith(json::get<"type"_>(event), "ircd.device"))
|
||||
return false;
|
||||
|
||||
if(startswith(json::get<"type"_>(event), "ircd.device.signing"))
|
||||
return false;
|
||||
const bool including
|
||||
{
|
||||
false
|
||||
|| startswith(json::get<"type"_>(event), "ircd.device")
|
||||
|| startswith(json::get<"type"_>(event), "ircd.keys.signatures")
|
||||
};
|
||||
|
||||
if(startswith(json::get<"type"_>(event), "ircd.device.one_time_key"))
|
||||
const bool excluding
|
||||
{
|
||||
false
|
||||
|| startswith(json::get<"type"_>(event), "ircd.device.one_time_key")
|
||||
};
|
||||
|
||||
if(!including || excluding)
|
||||
return false;
|
||||
|
||||
const m::user sender
|
||||
|
|
|
@ -122,7 +122,7 @@ post__filter(client &client,
|
|||
|
||||
return m::resource::response
|
||||
{
|
||||
client, http::CREATED,
|
||||
client, http::OK,
|
||||
{
|
||||
{ "filter_id", filter_id }
|
||||
}
|
||||
|
|
|
@ -14449,7 +14449,7 @@ console_cmd__user__devices__update(opt &out, const string_view &line)
|
|||
{
|
||||
const params param{line, " ",
|
||||
{
|
||||
"user_id", "device_id", "deleted"
|
||||
"user_id", "device_id", "room_id"
|
||||
}};
|
||||
|
||||
const m::user::id &user_id
|
||||
|
@ -14459,12 +14459,14 @@ console_cmd__user__devices__update(opt &out, const string_view &line)
|
|||
|
||||
const string_view &device_id
|
||||
{
|
||||
param.at("device_id")
|
||||
param.at("device_id", "*"_sv)
|
||||
};
|
||||
|
||||
const bool deleted
|
||||
const m::room::id::buf room_id
|
||||
{
|
||||
param["deleted"] == "deleted"
|
||||
m::valid(m::id::ROOM, param["room_id"])?
|
||||
m::room_id(param["room_id"]):
|
||||
m::room::id::buf{}
|
||||
};
|
||||
|
||||
const m::user::devices devices
|
||||
|
@ -14472,20 +14474,36 @@ console_cmd__user__devices__update(opt &out, const string_view &line)
|
|||
user_id
|
||||
};
|
||||
|
||||
json::iov content;
|
||||
const json::iov::push push[]
|
||||
const auto update{[&out, &devices, &user_id, &room_id]
|
||||
(const auto &device_id)
|
||||
{
|
||||
{ content, { "user_id", user_id } },
|
||||
{ content, { "device_id", device_id } },
|
||||
{ content, { "deleted", deleted } },
|
||||
m::user::devices::send
|
||||
{
|
||||
devices, device_id, room_id
|
||||
};
|
||||
|
||||
out
|
||||
<< "broadcast: "
|
||||
<< device_id
|
||||
<< std::endl;
|
||||
}};
|
||||
|
||||
const bool found
|
||||
{
|
||||
!devices.for_each([&update, &device_id]
|
||||
(const auto &, const string_view &_device_id)
|
||||
{
|
||||
if(device_id != "*" && _device_id != device_id)
|
||||
return true;
|
||||
|
||||
update(_device_id);
|
||||
return _device_id != device_id; // false to break
|
||||
})
|
||||
};
|
||||
|
||||
const bool broadcasted
|
||||
{
|
||||
m::user::devices::send(content)
|
||||
};
|
||||
if(device_id != "*" && !found)
|
||||
update(device_id);
|
||||
|
||||
out << "done" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -14497,6 +14515,43 @@ console_id__device(opt &out,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
console_cmd__user__keys__update(opt &out, const string_view &line)
|
||||
{
|
||||
const params param{line, " ",
|
||||
{
|
||||
"user_id", "room_id"
|
||||
}};
|
||||
|
||||
const m::user::id &user_id
|
||||
{
|
||||
param.at("user_id")
|
||||
};
|
||||
|
||||
const m::room::id::buf room_id
|
||||
{
|
||||
m::valid(m::id::ROOM, param["room_id"])?
|
||||
m::room_id(param["room_id"]):
|
||||
m::room::id::buf{}
|
||||
};
|
||||
|
||||
const m::user::keys keys
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
m::user::keys::send
|
||||
{
|
||||
keys, room_id
|
||||
};
|
||||
|
||||
out
|
||||
<< "broadcast: "
|
||||
<< user_id
|
||||
<< std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
console_cmd__user__ignores(opt &out, const string_view &line)
|
||||
{
|
||||
|
@ -17205,7 +17260,7 @@ console_cmd__fed__user__devices(opt &out, const string_view &line)
|
|||
{
|
||||
const params param{line, " ",
|
||||
{
|
||||
"user_id", "remote"
|
||||
"user_id", "remote", "op"
|
||||
}};
|
||||
|
||||
const m::user::id &user_id
|
||||
|
@ -17218,6 +17273,11 @@ console_cmd__fed__user__devices(opt &out, const string_view &line)
|
|||
param.at("remote", user_id.host())
|
||||
};
|
||||
|
||||
const bool raw
|
||||
{
|
||||
has(param["op"], "raw")
|
||||
};
|
||||
|
||||
m::fed::user::devices::opts opts;
|
||||
opts.remote = remote;
|
||||
|
||||
|
@ -17242,6 +17302,14 @@ console_cmd__fed__user__devices(opt &out, const string_view &line)
|
|||
request
|
||||
};
|
||||
|
||||
if(raw)
|
||||
{
|
||||
out
|
||||
<< string_view{response}
|
||||
<< std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
const string_view stream_id
|
||||
{
|
||||
unquote(response["stream_id"])
|
||||
|
@ -17272,16 +17340,23 @@ console_cmd__fed__user__keys__query(opt &out, const string_view &line)
|
|||
param.at("user_id")
|
||||
};
|
||||
|
||||
const string_view &device_id
|
||||
{
|
||||
param.at("device_id", string_view{})
|
||||
};
|
||||
|
||||
const string_view remote
|
||||
{
|
||||
param.at("remote", user_id.host())
|
||||
};
|
||||
|
||||
const bool raw
|
||||
{
|
||||
param["device_id"] == "raw"
|
||||
};
|
||||
|
||||
const string_view device_id
|
||||
{
|
||||
!raw?
|
||||
param.at("device_id", string_view{}):
|
||||
string_view{}
|
||||
};
|
||||
|
||||
m::fed::user::opts opts;
|
||||
opts.remote = remote;
|
||||
|
||||
|
@ -17306,6 +17381,14 @@ console_cmd__fed__user__keys__query(opt &out, const string_view &line)
|
|||
request
|
||||
};
|
||||
|
||||
if(raw)
|
||||
{
|
||||
out
|
||||
<< string_view{response}
|
||||
<< std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
const json::object &device_keys
|
||||
{
|
||||
response["device_keys"]
|
||||
|
@ -18448,6 +18531,35 @@ console_cmd__well_known__matrix__server(opt &out, const string_view &line)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
console_cmd__well_known__matrix__client(opt &out, const string_view &line)
|
||||
{
|
||||
const params param{line, " ",
|
||||
{
|
||||
"remote"
|
||||
}};
|
||||
|
||||
const fmt::bsprintf<512> url
|
||||
{
|
||||
"https://%s/.well-known/matrix/client",
|
||||
param.at("remote"),
|
||||
};
|
||||
|
||||
char buf[1024];
|
||||
const json::object response
|
||||
{
|
||||
rest::get
|
||||
{
|
||||
buf, string_view{url}
|
||||
}
|
||||
};
|
||||
|
||||
out
|
||||
<< string_view{response}
|
||||
<< std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// bridge
|
||||
//
|
||||
|
|
|
@ -64,6 +64,11 @@ get__user_devices(client &client,
|
|||
user_id
|
||||
};
|
||||
|
||||
const m::user::keys user_keys
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
m::resource::response::chunked::json response
|
||||
{
|
||||
client, http::OK
|
||||
|
@ -82,49 +87,34 @@ get__user_devices(client &client,
|
|||
}
|
||||
};
|
||||
|
||||
const auto master_event_idx
|
||||
if(user_keys.has_cross_master())
|
||||
{
|
||||
user_room.get(std::nothrow, "ircd.device.signing.master", "")
|
||||
};
|
||||
|
||||
m::get(std::nothrow, master_event_idx, "content", [&response]
|
||||
(const json::object &content)
|
||||
{
|
||||
json::stack::member
|
||||
json::stack::object object
|
||||
{
|
||||
response, "master_key", content
|
||||
};
|
||||
});
|
||||
|
||||
const auto self_event_idx
|
||||
{
|
||||
user_room.get(std::nothrow, "ircd.device.signing.self", "")
|
||||
};
|
||||
|
||||
m::get(std::nothrow, self_event_idx, "content", [&response]
|
||||
(const json::object &content)
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
response, "self_signing_key", content
|
||||
};
|
||||
});
|
||||
|
||||
if(my_host(request.node_id))
|
||||
{
|
||||
const auto user_event_idx
|
||||
{
|
||||
user_room.get(std::nothrow, "ircd.device.signing.user", "")
|
||||
response, "master_key"
|
||||
};
|
||||
|
||||
m::get(std::nothrow, user_event_idx, "content", [&response]
|
||||
(const json::object &content)
|
||||
user_keys.cross_master(object);
|
||||
}
|
||||
|
||||
if(user_keys.has_cross_self())
|
||||
{
|
||||
json::stack::object object
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
response, "user_signing_key", content
|
||||
};
|
||||
});
|
||||
response, "self_signing_key"
|
||||
};
|
||||
|
||||
user_keys.cross_self(object);
|
||||
}
|
||||
|
||||
if(my_host(request.node_id) && user_keys.has_cross_user())
|
||||
{
|
||||
json::stack::object object
|
||||
{
|
||||
response, "user_signing_key"
|
||||
};
|
||||
|
||||
user_keys.cross_user(object);
|
||||
}
|
||||
|
||||
json::stack::array devices
|
||||
|
@ -132,7 +122,7 @@ get__user_devices(client &client,
|
|||
response, "devices"
|
||||
};
|
||||
|
||||
user_devices.for_each([&user_devices, &devices]
|
||||
user_devices.for_each([&user_devices, &devices, &user_keys]
|
||||
(const auto &, const string_view &device_id)
|
||||
{
|
||||
json::stack::object device
|
||||
|
@ -145,6 +135,16 @@ get__user_devices(client &client,
|
|||
device, "device_id", device_id
|
||||
};
|
||||
|
||||
if(user_keys.has_device(device_id))
|
||||
{
|
||||
json::stack::object keys
|
||||
{
|
||||
device, "keys"
|
||||
};
|
||||
|
||||
user_keys.device(keys, device_id);
|
||||
}
|
||||
|
||||
// The property name difference here is on purpose, probably one of
|
||||
// those so-called spec "thinkos"
|
||||
user_devices.get(std::nothrow, device_id, "display_name", [&device]
|
||||
|
@ -156,15 +156,6 @@ get__user_devices(client &client,
|
|||
};
|
||||
});
|
||||
|
||||
user_devices.get(std::nothrow, device_id, "keys", [&device]
|
||||
(const auto &, const json::object &value)
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
device, "keys", value
|
||||
};
|
||||
});
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
|
|
|
@ -47,29 +47,27 @@ post__user_keys_claim(client &client,
|
|||
request["one_time_keys"]
|
||||
};
|
||||
|
||||
m::resource::response::chunked response
|
||||
m::resource::response::chunked::json response
|
||||
{
|
||||
client, http::OK
|
||||
};
|
||||
|
||||
json::stack out
|
||||
{
|
||||
response.buf, response.flusher()
|
||||
};
|
||||
|
||||
json::stack::object top
|
||||
{
|
||||
out
|
||||
};
|
||||
|
||||
json::stack::object response_keys
|
||||
{
|
||||
top, "one_time_keys"
|
||||
response, "one_time_keys"
|
||||
};
|
||||
|
||||
for(const auto &[user_id, devices] : one_time_keys)
|
||||
for(const auto &[user_id_, devices] : one_time_keys)
|
||||
{
|
||||
const m::user::room user_room
|
||||
const m::user::id user_id
|
||||
{
|
||||
user_id_
|
||||
};
|
||||
|
||||
if(!m::exists(user_id))
|
||||
continue;
|
||||
|
||||
const m::user::keys keys
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
@ -79,26 +77,11 @@ post__user_keys_claim(client &client,
|
|||
response_keys, user_id
|
||||
};
|
||||
|
||||
for(const auto &[device_id_, algorithm_] : json::object(devices))
|
||||
for(const auto &[device_id, algorithm_] : json::object(devices))
|
||||
{
|
||||
const json::string &algorithm{algorithm_};
|
||||
const json::string &device_id{device_id_};
|
||||
const auto match{[&device_id]
|
||||
(const string_view &state_key) noexcept
|
||||
const json::string algorithm
|
||||
{
|
||||
return state_key == device_id;
|
||||
}};
|
||||
|
||||
char buf[m::event::TYPE_MAX_SIZE];
|
||||
const string_view type{fmt::sprintf
|
||||
{
|
||||
buf, "ircd.device.one_time_key|%s",
|
||||
algorithm
|
||||
}};
|
||||
|
||||
const m::room::type events
|
||||
{
|
||||
user_room, type, { -1UL, -1L }, true
|
||||
algorithm_
|
||||
};
|
||||
|
||||
json::stack::object response_device
|
||||
|
@ -106,33 +89,9 @@ post__user_keys_claim(client &client,
|
|||
response_user, device_id
|
||||
};
|
||||
|
||||
events.for_each([&response_device, &match]
|
||||
(const string_view &type, const auto &, const m::event::idx &event_idx)
|
||||
{
|
||||
if(!m::query(std::nothrow, event_idx, "state_key", match))
|
||||
return true;
|
||||
|
||||
m::get(std::nothrow, event_idx, "content", [&response_device, type]
|
||||
(const json::object &content)
|
||||
{
|
||||
const auto algorithm
|
||||
{
|
||||
split(type, '|').second
|
||||
};
|
||||
|
||||
json::stack::member
|
||||
{
|
||||
response_device, algorithm, json::object
|
||||
{
|
||||
content[""] // device quirk
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
keys.claim(response_device, device_id, algorithm);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
return response;
|
||||
}
|
||||
|
|
|
@ -155,24 +155,20 @@ _query_master_keys(client &client,
|
|||
user_id_
|
||||
};
|
||||
|
||||
const m::user::room room
|
||||
const m::user::keys keys
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
const auto event_idx
|
||||
if(!keys.has_cross_master())
|
||||
continue;
|
||||
|
||||
json::stack::object object
|
||||
{
|
||||
room.get(std::nothrow, "ircd.device.signing.master", "")
|
||||
response_keys, user_id
|
||||
};
|
||||
|
||||
m::get(std::nothrow, event_idx, "content", [&response_keys, &user_id]
|
||||
(const json::object &content)
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
response_keys, user_id, content
|
||||
};
|
||||
});
|
||||
keys.cross_master(object);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -198,24 +194,20 @@ _query_self_keys(client &client,
|
|||
user_id_
|
||||
};
|
||||
|
||||
const m::user::room room
|
||||
const m::user::keys keys
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
const auto event_idx
|
||||
if(!keys.has_cross_self())
|
||||
continue;
|
||||
|
||||
json::stack::object object
|
||||
{
|
||||
room.get(std::nothrow, "ircd.device.signing.self", "")
|
||||
response_keys, user_id
|
||||
};
|
||||
|
||||
m::get(std::nothrow, event_idx, "content", [&response_keys, &user_id]
|
||||
(const json::object &content)
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
response_keys, user_id, content
|
||||
};
|
||||
});
|
||||
keys.cross_self(object);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,24 +233,20 @@ _query_user_keys(client &client,
|
|||
user_id_
|
||||
};
|
||||
|
||||
const m::user::room room
|
||||
const m::user::keys keys
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
const auto event_idx
|
||||
if(!keys.has_cross_user())
|
||||
continue;
|
||||
|
||||
json::stack::object object
|
||||
{
|
||||
room.get(std::nothrow, "ircd.device.signing.user", "")
|
||||
response_keys, user_id
|
||||
};
|
||||
|
||||
m::get(std::nothrow, event_idx, "content", [&response_keys, &user_id]
|
||||
(const json::object &content)
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
response_keys, user_id, content
|
||||
};
|
||||
});
|
||||
keys.cross_user(object);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -269,7 +257,12 @@ _query_user_device(client &client,
|
|||
const string_view &device_id,
|
||||
json::stack::object &out)
|
||||
{
|
||||
if(!devices.has(device_id, "keys"))
|
||||
const m::user::keys keys
|
||||
{
|
||||
devices.user
|
||||
};
|
||||
|
||||
if(!keys.has_device(device_id))
|
||||
return;
|
||||
|
||||
json::stack::object object
|
||||
|
@ -277,67 +270,7 @@ _query_user_device(client &client,
|
|||
out, device_id
|
||||
};
|
||||
|
||||
devices.get(std::nothrow, device_id, "keys", [&devices, &device_id, &object]
|
||||
(const auto &event_idx, const json::object &device_keys)
|
||||
{
|
||||
const auto &user_id
|
||||
{
|
||||
devices.user.user_id
|
||||
};
|
||||
|
||||
for(const auto &member : device_keys)
|
||||
if(member.first != "signatures")
|
||||
json::stack::member
|
||||
{
|
||||
object, member
|
||||
};
|
||||
|
||||
json::stack::object sigs
|
||||
{
|
||||
object, "signatures"
|
||||
};
|
||||
|
||||
json::stack::object user_sigs
|
||||
{
|
||||
sigs, user_id
|
||||
};
|
||||
|
||||
const json::object device_keys_sigs
|
||||
{
|
||||
device_keys["signatures"]
|
||||
};
|
||||
|
||||
const json::object device_keys_user_sigs
|
||||
{
|
||||
device_keys_sigs[user_id]
|
||||
};
|
||||
|
||||
for(const auto &member : device_keys_user_sigs)
|
||||
json::stack::member
|
||||
{
|
||||
user_sigs, member
|
||||
};
|
||||
|
||||
devices.get(std::nothrow, device_id, "signatures", [&user_id, &user_sigs]
|
||||
(const auto &event_idx, const json::object &device_sigs)
|
||||
{
|
||||
const json::object device_sigs_sigs
|
||||
{
|
||||
device_sigs["signatures"]
|
||||
};
|
||||
|
||||
const json::object device_sigs_user_sigs
|
||||
{
|
||||
device_sigs_sigs[user_id]
|
||||
};
|
||||
|
||||
for(const auto &member : device_sigs_user_sigs)
|
||||
json::stack::member
|
||||
{
|
||||
user_sigs, member
|
||||
};
|
||||
});
|
||||
});
|
||||
keys.device(object, device_id);
|
||||
|
||||
devices.get(std::nothrow, device_id, "display_name", [&device_id, &object]
|
||||
(const auto &event_idx, const string_view &display_name)
|
||||
|
|
|
@ -56,53 +56,29 @@ try
|
|||
if(user_id.host() != at<"origin"_>(event))
|
||||
return;
|
||||
|
||||
const json::object &msk
|
||||
{
|
||||
json::get<"master_key"_>(update)
|
||||
};
|
||||
|
||||
const m::user::room room
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
if(!exists(room))
|
||||
if(!exists(user_id))
|
||||
{
|
||||
log::derror
|
||||
{
|
||||
m::log, "Refusing signing key update for unknown %s",
|
||||
json::get<"user_id"_>(update),
|
||||
string_view{user_id},
|
||||
};
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const auto master_id
|
||||
const m::user::keys keys
|
||||
{
|
||||
msk?
|
||||
send(room, user_id, "ircd.device.signing.master", "", msk):
|
||||
m::event::id::buf{}
|
||||
user_id
|
||||
};
|
||||
|
||||
const json::object &ssk
|
||||
{
|
||||
json::get<"self_signing_key"_>(update)
|
||||
};
|
||||
|
||||
const auto self_id
|
||||
{
|
||||
ssk?
|
||||
send(room, user_id, "ircd.device.signing.self", "", ssk):
|
||||
m::event::id::buf{}
|
||||
};
|
||||
keys.update(update);
|
||||
|
||||
log::info
|
||||
{
|
||||
m::log, "Signing key update from :%s by %s master:%s self:%s",
|
||||
m::log, "Signing key update from '%s' for %s",
|
||||
json::get<"origin"_>(event),
|
||||
json::get<"user_id"_>(update),
|
||||
string_view{master_id},
|
||||
string_view{self_id},
|
||||
};
|
||||
}
|
||||
catch(const ctx::interrupted &e)
|
||||
|
@ -113,7 +89,7 @@ catch(const std::exception &e)
|
|||
{
|
||||
log::derror
|
||||
{
|
||||
m::log, "m.signing_key_update from %s :%s",
|
||||
m::log, "m.signing_key_update from '%s' :%s",
|
||||
json::get<"origin"_>(event),
|
||||
e.what(),
|
||||
};
|
||||
|
|
|
@ -96,7 +96,7 @@ post__upload(client &client,
|
|||
|
||||
return m::resource::response
|
||||
{
|
||||
client, http::CREATED, json::members
|
||||
client, http::OK, json::members
|
||||
{
|
||||
{ "content_uri", content_uri }
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ ircd::net::dns::cache::put(const hostport &hp,
|
|||
{
|
||||
opts.qtype == 33?
|
||||
make_SRV_key(state_key_buf, hp, opts):
|
||||
host(hp)
|
||||
tolower(state_key_buf, host(hp))
|
||||
};
|
||||
|
||||
return put(type, state_key, code, msg);
|
||||
|
@ -117,7 +117,7 @@ ircd::net::dns::cache::put(const hostport &hp,
|
|||
{
|
||||
opts.qtype == 33?
|
||||
make_SRV_key(state_key_buf, hp, opts):
|
||||
host(hp)
|
||||
tolower(state_key_buf, host(hp))
|
||||
};
|
||||
|
||||
return put(type, state_key, rrs);
|
||||
|
@ -369,7 +369,7 @@ ircd::net::dns::cache::get(const hostport &hp,
|
|||
{
|
||||
opts.qtype == 33?
|
||||
make_SRV_key(state_key_buf, hp, opts):
|
||||
host(hp)
|
||||
tolower(state_key_buf, host(hp))
|
||||
};
|
||||
|
||||
const m::room::state state
|
||||
|
@ -431,7 +431,7 @@ ircd::net::dns::cache::for_each(const hostport &hp,
|
|||
{
|
||||
opts.qtype == 33?
|
||||
make_SRV_key(state_key_buf, hp, opts):
|
||||
host(hp)
|
||||
tolower(state_key_buf, host(hp))
|
||||
};
|
||||
|
||||
const m::room::state state
|
||||
|
|
|
@ -620,7 +620,7 @@ find_reaction_id(const m::room &room,
|
|||
}
|
||||
|
||||
static ircd::m::event::id::buf
|
||||
find_reaction_id_endswith(const m::room &room,
|
||||
find_reaction_id_contains(const m::room &room,
|
||||
const m::user::id &user_id,
|
||||
const m::event::id &event_id,
|
||||
const string_view &label)
|
||||
|
@ -633,7 +633,7 @@ find_reaction_id_endswith(const m::room &room,
|
|||
relates["key"]
|
||||
};
|
||||
|
||||
return endswith(key, label);
|
||||
return has(key, label);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -656,14 +656,14 @@ clear_reaction(const m::room &room,
|
|||
}
|
||||
|
||||
static bool
|
||||
clear_reaction_endswith(const m::room &room,
|
||||
clear_reaction_contains(const m::room &room,
|
||||
const m::user::id &user_id,
|
||||
const m::event::id &event_id,
|
||||
const string_view &label)
|
||||
{
|
||||
const auto reaction_id
|
||||
{
|
||||
find_reaction_id_endswith(room, user_id, event_id, label)
|
||||
find_reaction_id_contains(room, user_id, event_id, label)
|
||||
};
|
||||
|
||||
if(!reaction_id)
|
||||
|
@ -1058,15 +1058,8 @@ github_handle__workflow_run(std::ostream &out,
|
|||
annote = ircd::strlcat(buf, " "_sv);
|
||||
annote = ircd::strlcat(buf, name);
|
||||
|
||||
const auto reaction_id
|
||||
{
|
||||
push_event_id && action != "requested"? // skip search on first action
|
||||
find_reaction_id_endswith(_webhook_room, _webhook_user, push_event_id, name):
|
||||
m::event::id::buf{}
|
||||
};
|
||||
|
||||
if(reaction_id)
|
||||
m::redact(_webhook_room, _webhook_user, reaction_id, "status change");
|
||||
if(push_event_id && action != "requested") // skip search on first action
|
||||
while(clear_reaction_contains(_webhook_room, _webhook_user, push_event_id, name));
|
||||
|
||||
m::annotate(_webhook_room, _webhook_user, push_event_id, annote);
|
||||
|
||||
|
|
Loading…
Reference in New Issue