mirror of
https://github.com/matrix-construct/construct
synced 2024-11-15 14:31:11 +01:00
ircd:Ⓜ️ Simplify m::visible(); refactor implementation.
This commit is contained in:
parent
9cb0f46440
commit
4254960ee1
11 changed files with 178 additions and 200 deletions
|
@ -17,5 +17,4 @@ namespace ircd::m
|
||||||
// authentication is supplied (m::id cannot be empty because that's
|
// authentication is supplied (m::id cannot be empty because that's
|
||||||
// considered an invalid mxid). In that case the test is for public vis.
|
// considered an invalid mxid). In that case the test is for public vis.
|
||||||
bool visible(const event &, const string_view &mxid);
|
bool visible(const event &, const string_view &mxid);
|
||||||
bool visible(const id::event &, const string_view &mxid);
|
|
||||||
}
|
}
|
||||||
|
|
40
ircd/m.cc
40
ircd/m.cc
|
@ -1673,46 +1673,6 @@ ircd::m::redacted::prefetch(const event::idx &event_idx)
|
||||||
return refs.prefetch(dbs::ref::M_ROOM_REDACTION);
|
return refs.prefetch(dbs::ref::M_ROOM_REDACTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// m/visible.h
|
|
||||||
//
|
|
||||||
|
|
||||||
bool
|
|
||||||
ircd::m::visible(const event::id &event_id,
|
|
||||||
const string_view &mxid)
|
|
||||||
{
|
|
||||||
m::room::id::buf room_id
|
|
||||||
{
|
|
||||||
get(event_id, "room_id", room_id)
|
|
||||||
};
|
|
||||||
|
|
||||||
const m::event event
|
|
||||||
{
|
|
||||||
json::members
|
|
||||||
{
|
|
||||||
{ "event_id", event_id },
|
|
||||||
{ "room_id", room_id },
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return visible(event, mxid);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ircd::m::visible(const event &event,
|
|
||||||
const string_view &mxid)
|
|
||||||
{
|
|
||||||
using prototype = bool (const m::event &, const string_view &);
|
|
||||||
|
|
||||||
static mods::import<prototype> call
|
|
||||||
{
|
|
||||||
"m_room_history_visibility", "ircd::m::visible"
|
|
||||||
};
|
|
||||||
|
|
||||||
return call(event, mxid);
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// m/presence.h
|
// m/presence.h
|
||||||
|
|
|
@ -345,19 +345,28 @@ get_events_from(client &client,
|
||||||
size_t i(0), j(0);
|
size_t i(0), j(0);
|
||||||
for(; it && i < size_t(events_limit); --it, ++i)
|
for(; it && i < size_t(events_limit); --it, ++i)
|
||||||
{
|
{
|
||||||
if(!visible(it.event_id(), request.user_id))
|
const m::event &event{*it};
|
||||||
|
if(!visible(event, request.user_id))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
j += append_event(chunk, *it, it.event_idx(), room_depth, user_room);
|
const auto &event_idx(it.event_idx());
|
||||||
|
j += append_event(chunk, event, event_idx, room_depth, user_room);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!j)
|
if(!j)
|
||||||
return j;
|
return j;
|
||||||
|
|
||||||
chunk.~array();
|
chunk.~array();
|
||||||
|
const m::event::id::buf end_token
|
||||||
|
{
|
||||||
|
it?
|
||||||
|
m::event_id(it.event_idx()):
|
||||||
|
room_head
|
||||||
|
};
|
||||||
|
|
||||||
json::stack::member
|
json::stack::member
|
||||||
{
|
{
|
||||||
out, "end", it? it.event_id() : room_head
|
out, "end", end_token
|
||||||
};
|
};
|
||||||
|
|
||||||
return j;
|
return j;
|
||||||
|
|
|
@ -551,13 +551,17 @@ initialsync_room_timeline_events(client &client,
|
||||||
// event_id on the way down in case of renewing the iterator for the
|
// event_id on the way down in case of renewing the iterator for the
|
||||||
// way back. This is not a big deal but rocksdb should fix their shit.
|
// way back. This is not a big deal but rocksdb should fix their shit.
|
||||||
ssize_t i(0);
|
ssize_t i(0);
|
||||||
|
m::event::idx event_idx{0};
|
||||||
m::event::id::buf event_id;
|
m::event::id::buf event_id;
|
||||||
m::room::events it{room};
|
m::room::events it{room};
|
||||||
for(; it && i < 10; --it, ++i)
|
for(; it && i < 10; --it, ++i)
|
||||||
event_id = it.event_id();
|
{
|
||||||
|
event_idx = it.event_idx();
|
||||||
|
event_id = m::event_id(event_idx);
|
||||||
|
}
|
||||||
|
|
||||||
if(i > 0 && !it)
|
if(i > 0 && !it)
|
||||||
it.seek(event_id);
|
it.seek(event_idx);
|
||||||
|
|
||||||
if(i > 0)
|
if(i > 0)
|
||||||
for(; it && i > -1; ++it, --i)
|
for(; it && i > -1; ++it, --i)
|
||||||
|
|
|
@ -183,7 +183,7 @@ get__context(client &client,
|
||||||
--before;
|
--before;
|
||||||
|
|
||||||
if(before)
|
if(before)
|
||||||
start = before.event_id();
|
start = m::event_id(before.event_idx());
|
||||||
else
|
else
|
||||||
start = {};
|
start = {};
|
||||||
}
|
}
|
||||||
|
@ -223,7 +223,7 @@ get__context(client &client,
|
||||||
++after;
|
++after;
|
||||||
|
|
||||||
if(after)
|
if(after)
|
||||||
end = after.event_id();
|
end = m::event_id(after.event_idx());
|
||||||
else
|
else
|
||||||
end = {};
|
end = {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -192,9 +192,6 @@ get__initialsync_local(client &client,
|
||||||
room_state.for_each(m::event::id::closure_bool{[&]
|
room_state.for_each(m::event::id::closure_bool{[&]
|
||||||
(const m::event::id &event_id)
|
(const m::event::id &event_id)
|
||||||
{
|
{
|
||||||
if(!visible(event_id, user.user_id))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
const m::event::fetch event
|
const m::event::fetch event
|
||||||
{
|
{
|
||||||
event_id, std::nothrow
|
event_id, std::nothrow
|
||||||
|
@ -203,6 +200,9 @@ get__initialsync_local(client &client,
|
||||||
if(!event.valid)
|
if(!event.valid)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if(!visible(event, user.user_id))
|
||||||
|
return true;
|
||||||
|
|
||||||
m::event::append::opts opts;
|
m::event::append::opts opts;
|
||||||
opts.event_idx = &event.event_idx;
|
opts.event_idx = &event.event_idx;
|
||||||
opts.user_id = &user.user_id;
|
opts.user_id = &user.user_id;
|
||||||
|
@ -223,7 +223,7 @@ get__initialsync_local(client &client,
|
||||||
if(it)
|
if(it)
|
||||||
json::stack::member
|
json::stack::member
|
||||||
{
|
{
|
||||||
messages, "start", it.event_id()
|
messages, "start", m::event_id(it.event_idx())
|
||||||
};
|
};
|
||||||
|
|
||||||
// seek down first to give events in chronological order.
|
// seek down first to give events in chronological order.
|
||||||
|
@ -231,7 +231,7 @@ get__initialsync_local(client &client,
|
||||||
if(it)
|
if(it)
|
||||||
json::stack::member
|
json::stack::member
|
||||||
{
|
{
|
||||||
messages, "end", it.event_id()
|
messages, "end", m::event_id(it.event_idx())
|
||||||
};
|
};
|
||||||
|
|
||||||
json::stack::array chunk
|
json::stack::array chunk
|
||||||
|
@ -241,12 +241,10 @@ get__initialsync_local(client &client,
|
||||||
|
|
||||||
for(; it; ++it)
|
for(; it; ++it)
|
||||||
{
|
{
|
||||||
const auto &event_id(it.event_id());
|
|
||||||
if(!visible(event_id, user.user_id))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const m::event &event(*it);
|
|
||||||
const auto &event_idx(it.event_idx());
|
const auto &event_idx(it.event_idx());
|
||||||
|
const m::event &event(*it);
|
||||||
|
if(!visible(event, user.user_id))
|
||||||
|
continue;
|
||||||
|
|
||||||
m::event::append::opts opts;
|
m::event::append::opts opts;
|
||||||
opts.event_idx = &event_idx;
|
opts.event_idx = &event_idx;
|
||||||
|
|
|
@ -7406,12 +7406,17 @@ console_cmd__event__visible(opt &out, const string_view &line)
|
||||||
param[1]
|
param[1]
|
||||||
};
|
};
|
||||||
|
|
||||||
const bool visible
|
const m::event event
|
||||||
{
|
{
|
||||||
m::visible(event_id, mxid)
|
event_id
|
||||||
};
|
};
|
||||||
|
|
||||||
out << event_id << " is "
|
const bool visible
|
||||||
|
{
|
||||||
|
m::visible(event, mxid)
|
||||||
|
};
|
||||||
|
|
||||||
|
out << event.event_id << " is "
|
||||||
<< (visible? "VISIBLE" : "NOT VISIBLE")
|
<< (visible? "VISIBLE" : "NOT VISIBLE")
|
||||||
<< (mxid? " to " : "")
|
<< (mxid? " to " : "")
|
||||||
<< mxid
|
<< mxid
|
||||||
|
|
|
@ -91,12 +91,6 @@ get__backfill(client &client,
|
||||||
m::head(room_id)
|
m::head(room_id)
|
||||||
};
|
};
|
||||||
|
|
||||||
if(!visible(event_id, request.node_id))
|
|
||||||
throw m::ACCESS_DENIED
|
|
||||||
{
|
|
||||||
"You are not permitted to view the room at this event."
|
|
||||||
};
|
|
||||||
|
|
||||||
const size_t limit
|
const size_t limit
|
||||||
{
|
{
|
||||||
calc_limit(request)
|
calc_limit(request)
|
||||||
|
|
|
@ -127,9 +127,10 @@ get__backfill_ids(client &client,
|
||||||
size_t count{0};
|
size_t count{0};
|
||||||
for(; it && count < limit; ++count, --it)
|
for(; it && count < limit; ++count, --it)
|
||||||
{
|
{
|
||||||
const auto &event_id(it.event_id());
|
const auto event_id
|
||||||
if(!visible(event_id, request.node_id))
|
{
|
||||||
continue;
|
m::event_id(it.event_idx())
|
||||||
|
};
|
||||||
|
|
||||||
pdus.append(event_id);
|
pdus.append(event_id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,17 +44,17 @@ handle_get(client &client,
|
||||||
url::decode(event_id, request.parv[0])
|
url::decode(event_id, request.parv[0])
|
||||||
};
|
};
|
||||||
|
|
||||||
if(!visible(event_id, request.node_id))
|
|
||||||
throw m::ACCESS_DENIED
|
|
||||||
{
|
|
||||||
"You are not permitted to view this event"
|
|
||||||
};
|
|
||||||
|
|
||||||
const m::event::fetch event
|
const m::event::fetch event
|
||||||
{
|
{
|
||||||
event_id
|
event_id
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if(!visible(event, request.node_id))
|
||||||
|
throw m::ACCESS_DENIED
|
||||||
|
{
|
||||||
|
"You are not permitted to view this event"
|
||||||
|
};
|
||||||
|
|
||||||
resource::response::chunked response
|
resource::response::chunked response
|
||||||
{
|
{
|
||||||
client, http::OK
|
client, http::OK
|
||||||
|
|
|
@ -8,100 +8,42 @@
|
||||||
// copyright notice and this permission notice is present in all copies. The
|
// copyright notice and this permission notice is present in all copies. The
|
||||||
// full license for this software is available in the LICENSE file.
|
// full license for this software is available in the LICENSE file.
|
||||||
|
|
||||||
using namespace ircd;
|
namespace ircd::m
|
||||||
|
{
|
||||||
|
static bool visible_to_node(const room &, const string_view &node_id, const event &);
|
||||||
|
static bool visible_to_user(const room &, const string_view &history_visibility, const m::user::id &, const event &);
|
||||||
|
|
||||||
mapi::header
|
static void changed_history_visibility(const event &, vm::eval &);
|
||||||
|
extern hookfn<vm::eval &> changed_history_visibility_hookfn;
|
||||||
|
}
|
||||||
|
|
||||||
|
ircd::mapi::header
|
||||||
IRCD_MODULE
|
IRCD_MODULE
|
||||||
{
|
{
|
||||||
"Matrix m.room.history_visibility"
|
"Matrix m.room.history_visibility"
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool
|
decltype(ircd::m::changed_history_visibility_hookfn)
|
||||||
_visible_to_user(const m::event &event,
|
ircd::m::changed_history_visibility_hookfn
|
||||||
const m::user::id &user_id,
|
|
||||||
const m::room &room,
|
|
||||||
const string_view &history_visibility)
|
|
||||||
{
|
{
|
||||||
char membership_buf[m::room::MEMBERSHIP_MAX_SIZE];
|
changed_history_visibility,
|
||||||
string_view membership
|
|
||||||
{
|
{
|
||||||
m::membership(membership_buf, room, user_id)
|
{ "_site", "vm.effect" },
|
||||||
};
|
{ "type", "m.room.history_visibility" },
|
||||||
|
|
||||||
if(membership == "join")
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if(history_visibility == "joined")
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if(history_visibility == "invited")
|
|
||||||
return membership == "invite";
|
|
||||||
|
|
||||||
assert(history_visibility == "shared");
|
|
||||||
if(membership == "invite")
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// If the room is not at the present event then we have to run another
|
|
||||||
// test for membership here. Otherwise the "join" test already failed.
|
|
||||||
if(room.event_id)
|
|
||||||
{
|
|
||||||
const m::room present{room.room_id};
|
|
||||||
membership = m::membership(membership_buf, present, user_id);
|
|
||||||
return membership == "join" || membership == "invite";
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return false;
|
void
|
||||||
}
|
ircd::m::changed_history_visibility(const event &event,
|
||||||
|
vm::eval &)
|
||||||
static bool
|
|
||||||
_visible_to_node(const m::event &event,
|
|
||||||
const string_view &node_id,
|
|
||||||
const m::room &room,
|
|
||||||
const string_view &history_visibility)
|
|
||||||
{
|
{
|
||||||
const m::room::origins origins
|
log::info
|
||||||
{
|
{
|
||||||
room
|
log, "Changed visibility of %s to %s by %s => %s",
|
||||||
};
|
json::get<"room_id"_>(event),
|
||||||
|
json::get<"content"_>(event).get("history_visibility"),
|
||||||
// Allow joined servers
|
json::get<"sender"_>(event),
|
||||||
if(origins.has(node_id))
|
string_view{event.event_id},
|
||||||
return true;
|
|
||||||
|
|
||||||
// Allow auth chain events XXX: this is too broad
|
|
||||||
if(m::room::auth::is_power_event(event))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Allow any event where the state_key string is a user mxid and the server
|
|
||||||
// is the host of that user. Note that applies to any type of event.
|
|
||||||
if(m::valid(m::id::USER, json::get<"state_key"_>(event)))
|
|
||||||
if(m::user::id(at<"state_key"_>(event)).host() == node_id)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
_visible(const m::event &event,
|
|
||||||
const string_view &mxid,
|
|
||||||
const m::room &room,
|
|
||||||
const string_view &history_visibility)
|
|
||||||
{
|
|
||||||
if(history_visibility == "world_readable")
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if(empty(mxid))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if(m::valid(m::id::USER, mxid))
|
|
||||||
return _visible_to_user(event, mxid, room, history_visibility);
|
|
||||||
|
|
||||||
if(rfc3986::valid_remote(std::nothrow, mxid))
|
|
||||||
return _visible_to_node(event, mxid, room, history_visibility);
|
|
||||||
|
|
||||||
throw m::UNSUPPORTED
|
|
||||||
{
|
|
||||||
"Cannot determine visibility for '%s'", mxid
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,61 +57,127 @@ ircd::m::visible(const m::event &event,
|
||||||
at<"room_id"_>(event), event.event_id
|
at<"room_id"_>(event), event.event_id
|
||||||
};
|
};
|
||||||
|
|
||||||
static const m::event::fetch::opts fopts
|
|
||||||
{
|
|
||||||
m::event::keys::include{"content"}
|
|
||||||
};
|
|
||||||
|
|
||||||
const m::room::state state
|
const m::room::state state
|
||||||
{
|
{
|
||||||
room, &fopts
|
room
|
||||||
};
|
};
|
||||||
|
|
||||||
bool ret{false};
|
const event::idx visibility_event_idx
|
||||||
const bool has_state_event
|
|
||||||
{
|
{
|
||||||
state.get(std::nothrow, "m.room.history_visibility", "", [&]
|
state.get(std::nothrow, "m.room.history_visibility", "")
|
||||||
(const m::event &visibility_event)
|
};
|
||||||
|
|
||||||
|
char buf[32];
|
||||||
|
string_view history_visibility{"shared"};
|
||||||
|
m::get(std::nothrow, visibility_event_idx, "content", [&buf, &history_visibility]
|
||||||
|
(const json::object &content)
|
||||||
|
{
|
||||||
|
const json::string &_history_visibility
|
||||||
{
|
{
|
||||||
const json::object &content
|
content.get("history_visibility", "shared")
|
||||||
{
|
};
|
||||||
json::get<"content"_>(visibility_event)
|
|
||||||
};
|
|
||||||
|
|
||||||
const string_view &history_visibility
|
history_visibility = strncpy
|
||||||
{
|
{
|
||||||
unquote(content.get("history_visibility", "shared"))
|
buf, _history_visibility
|
||||||
};
|
};
|
||||||
|
});
|
||||||
|
|
||||||
ret = _visible(event, mxid, room, history_visibility);
|
if(history_visibility == "world_readable")
|
||||||
})
|
return true;
|
||||||
};
|
|
||||||
|
|
||||||
return !has_state_event?
|
if(empty(mxid))
|
||||||
_visible(event, mxid, room, "shared"):
|
return false;
|
||||||
ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
if(m::valid(m::id::USER, mxid))
|
||||||
_changed_visibility(const m::event &event,
|
return visible_to_user(room, history_visibility, mxid, event);
|
||||||
m::vm::eval &)
|
|
||||||
{
|
if(rfc3986::valid_remote(std::nothrow, mxid))
|
||||||
log::info
|
return visible_to_node(room, mxid, event);
|
||||||
|
|
||||||
|
throw m::UNSUPPORTED
|
||||||
{
|
{
|
||||||
m::log, "Changed visibility of %s to %s by %s => %s",
|
"Cannot determine visibility of %s for '%s'",
|
||||||
json::get<"room_id"_>(event),
|
string_view{room.room_id},
|
||||||
json::get<"content"_>(event).get("history_visibility"),
|
mxid,
|
||||||
json::get<"sender"_>(event),
|
|
||||||
string_view{event.event_id}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
m::hookfn<m::vm::eval &>
|
bool
|
||||||
_changed_visibility_hookfn
|
ircd::m::visible_to_user(const m::room &room,
|
||||||
|
const string_view &history_visibility,
|
||||||
|
const m::user::id &user_id,
|
||||||
|
const m::event &event)
|
||||||
{
|
{
|
||||||
_changed_visibility,
|
assert(history_visibility != "world_readable");
|
||||||
|
|
||||||
|
// Allow any member event where the state_key string is a user mxid.
|
||||||
|
if(json::get<"type"_>(event) == "m.room.member")
|
||||||
|
if(at<"state_key"_>(event) == user_id)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Get the membership of the user in the room at the event.
|
||||||
|
char buf[m::room::MEMBERSHIP_MAX_SIZE];
|
||||||
|
const string_view membership
|
||||||
{
|
{
|
||||||
{ "_site", "vm.effect" },
|
m::membership(buf, room, user_id)
|
||||||
{ "type", "m.room.history_visibility" },
|
};
|
||||||
}
|
|
||||||
};
|
if(membership == "join")
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if(history_visibility == "joined")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(membership == "invite")
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if(history_visibility == "invited")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// The history_visibility is now likely "shared"; though we cannot assert
|
||||||
|
// that in case some other string is used for any non-spec customization
|
||||||
|
// or for graceful forward compatibility. We default to "shared" here.
|
||||||
|
//assert(history_visibility == "shared");
|
||||||
|
|
||||||
|
// An m::room instance with no event_id is used to query the room at the
|
||||||
|
// present state.
|
||||||
|
const m::room present
|
||||||
|
{
|
||||||
|
room.room_id
|
||||||
|
};
|
||||||
|
|
||||||
|
// If the room is not at the present event then we have to run another
|
||||||
|
// test for membership here. Otherwise the "join" test already failed.
|
||||||
|
if(!room.event_id)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return m::membership(present, user_id, m::membership_positive); // join || invite
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ircd::m::visible_to_node(const m::room &room,
|
||||||
|
const string_view &node_id,
|
||||||
|
const m::event &event)
|
||||||
|
{
|
||||||
|
// Allow auth chain events XXX: this is too broad
|
||||||
|
if(m::room::auth::is_power_event(event))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Allow any event where the state_key string is a user mxid and the server
|
||||||
|
// is the host of that user. Note that applies to any type of event.
|
||||||
|
if(m::valid(m::id::USER, json::get<"state_key"_>(event)))
|
||||||
|
if(m::user::id(at<"state_key"_>(event)).host() == node_id)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const m::room::origins origins
|
||||||
|
{
|
||||||
|
room
|
||||||
|
};
|
||||||
|
|
||||||
|
// Allow joined servers
|
||||||
|
if(origins.has(node_id))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue