0
0
Fork 0
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:
Jason Volk 2019-09-24 14:24:16 -07:00
parent 9cb0f46440
commit 4254960ee1
11 changed files with 178 additions and 200 deletions

View file

@ -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);
} }

View file

@ -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

View file

@ -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;

View file

@ -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)

View file

@ -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 = {};
} }

View file

@ -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;

View file

@ -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

View file

@ -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)

View file

@ -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);
} }

View file

@ -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

View file

@ -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;
}