0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-12-25 23:14:13 +01:00

ircd:Ⓜ️ Add user::tokens convenience interface; consolidate various direct room access.

This commit is contained in:
Jason Volk 2020-04-01 17:14:51 -07:00
parent f026110b15
commit d052249573
16 changed files with 352 additions and 242 deletions

View file

@ -123,7 +123,7 @@ struct ircd::m::device
using closure_bool = std::function<bool (const string_view &)>;
// util
static bool access_token_to_id(const string_view &token, const closure &); //nothrow
static id::buf access_token_to_id(std::nothrow_t, const string_view &token);
static id::buf access_token_to_id(const string_view &token);
// primary interface
@ -145,3 +145,16 @@ struct ircd::m::device
using super_type::tuple;
using super_type::operator=;
};
inline ircd::m::device::id::buf
ircd::m::device::access_token_to_id(const string_view &token)
{
return m::user::tokens::device(token);
}
inline ircd::m::device::id::buf
ircd::m::device::access_token_to_id(std::nothrow_t,
const string_view &token)
{
return m::user::tokens::device(std::nothrow, token);
}

View file

@ -0,0 +1,35 @@
// The Construct
//
// Copyright (C) The Construct Developers, Authors & Contributors
// Copyright (C) 2016-2020 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_TOKENS_H
struct ircd::m::user::tokens
{
using closure = std::function<void (const event::idx &, const string_view &)>;
using closure_bool = std::function<bool (const event::idx &, const string_view &)>;
static string_view generate(const mutable_buffer &out);
static id::device::buf device(std::nothrow_t, const string_view &token);
static id::device::buf device(const string_view &token);
static id::user::buf get(std::nothrow_t, const string_view &token);
static id::user::buf get(const string_view &token);
m::user user;
bool for_each(const closure_bool &) const;
bool check(const string_view &token) const;
bool del(const string_view &token, const string_view &reason) const;
size_t del(const string_view &reason) const;
tokens(const m::user &user)
:user{user}
{}
};

View file

@ -45,6 +45,7 @@ struct ircd::m::user
struct pushrules;
struct pushers;
struct notifications;
struct tokens;
using id = m::id::user;
using closure = std::function<void (const user &)>;
@ -54,9 +55,6 @@ struct ircd::m::user
operator const id &() const;
static string_view gen_access_token(const mutable_buffer &out);
static id::device::buf get_device_from_access_token(const string_view &token);
id::room room_id(const mutable_buffer &) const;
id::room::buf room_id() const;
@ -96,3 +94,4 @@ const
#include "pushrules.h"
#include "pushers.h"
#include "notifications.h"
#include "tokens.h"

View file

@ -110,6 +110,7 @@ libircd_matrix_la_SOURCES += user_account_data.cc
libircd_matrix_la_SOURCES += user_events.cc
libircd_matrix_la_SOURCES += user_rooms.cc
libircd_matrix_la_SOURCES += user_filter.cc
libircd_matrix_la_SOURCES += user_ignores.cc
libircd_matrix_la_SOURCES += user_mitsein.cc
libircd_matrix_la_SOURCES += user_notifications.cc
libircd_matrix_la_SOURCES += user_profile.cc
@ -118,7 +119,7 @@ libircd_matrix_la_SOURCES += user_pushrules.cc
libircd_matrix_la_SOURCES += user_register.cc
libircd_matrix_la_SOURCES += user_room_account_data.cc
libircd_matrix_la_SOURCES += user_room_tags.cc
libircd_matrix_la_SOURCES += user_ignores.cc
libircd_matrix_la_SOURCES += user_tokens.cc
libircd_matrix_la_SOURCES += breadcrumb_rooms.cc
libircd_matrix_la_SOURCES += device.cc
libircd_matrix_la_SOURCES += display_name.cc

View file

@ -335,63 +335,6 @@ ircd::m::device::for_each(const m::user &user,
});
}
ircd::m::device::id::buf
ircd::m::device::access_token_to_id(const string_view &token)
{
id::buf ret;
access_token_to_id(token, [&ret]
(const string_view &device_id)
{
ret = device_id;
});
return ret;
}
bool
ircd::m::device::access_token_to_id(const string_view &token,
const closure &closure)
{
const m::room::id::buf tokens
{
"tokens", origin(my())
};
const m::room::state &state
{
tokens
};
const m::event::idx &event_idx
{
state.get(std::nothrow, "ircd.access_token", token)
};
bool ret{false};
const auto device_id{[&closure, &ret]
(const json::object &content)
{
const json::string &device_id
{
content["device_id"]
};
if(likely(device_id))
{
closure(device_id);
ret = true;
}
}};
if(!event_idx)
return ret;
if(!m::get(std::nothrow, event_idx, "content", device_id))
return ret;
return ret;
}
bool
ircd::m::device_list_update::send(json::iov &content)
try

View file

@ -118,51 +118,6 @@ const
};
}
ircd::m::device::id::buf
ircd::m::user::get_device_from_access_token(const string_view &token)
{
const m::room::id::buf tokens_room_id
{
"tokens", origin(my())
};
const m::room tokens
{
tokens_room_id
};
const event::idx event_idx
{
tokens.get("ircd.access_token", token)
};
device::id::buf ret;
m::get(event_idx, "content", [&ret]
(const json::object &content)
{
ret = unquote(content.at("device_id"));
});
return ret;
}
ircd::string_view
ircd::m::user::gen_access_token(const mutable_buffer &buf)
{
static const size_t token_max{32};
static const auto &token_dict
{
rand::dict::alpha
};
const mutable_buffer out
{
data(buf), std::min(token_max, size(buf))
};
return rand::string(token_dict, out);
}
ircd::m::event::id::buf
ircd::m::user::activate()
{

View file

@ -135,7 +135,7 @@ const
const string_view access_token
{
!json::get<"inhibit_login"_>(*this)?
m::user::gen_access_token(access_token_buf):
m::user::tokens::generate(access_token_buf):
string_view{}
};

256
matrix/user_tokens.cc Normal file
View file

@ -0,0 +1,256 @@
// The Construct
//
// Copyright (C) The Construct Developers, Authors & Contributors
// Copyright (C) 2016-2020 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.
size_t
ircd::m::user::tokens::del(const string_view &reason)
const
{
size_t ret(0);
for_each([this, &ret, &reason]
(const event::idx &event_idx, const string_view &token)
{
ret += del(token, reason);
return true;
});
return ret;
}
bool
ircd::m::user::tokens::del(const string_view &token,
const string_view &reason)
const
{
const m::room::id::buf tokens_room_id
{
"tokens", origin(my())
};
const m::room tokens
{
tokens_room_id
};
const auto event_idx
{
tokens.get(std::nothrow, "ircd.access_token", token)
};
const auto match
{
[this](const string_view &sender)
{
return sender == this->user.user_id;
}
};
if(unlikely(!m::query(std::nothrow, event_idx, "sender", match)))
return false;
const auto event_id
{
m::event_id(std::nothrow, event_idx)
};
if(unlikely(!event_id))
return false;
const auto redact_id
{
m::redact(tokens, user.user_id, event_id, reason)
};
return true;
}
bool
ircd::m::user::tokens::check(const string_view &token)
const
{
const m::room::id::buf tokens_room_id
{
"tokens", origin(my())
};
const m::room tokens
{
tokens_room_id
};
const auto event_idx
{
tokens.get(std::nothrow, "ircd.access_token", token)
};
return event_idx && m::query(std::nothrow, event_idx, "sender", [this]
(const string_view &sender)
{
return sender == this->user.user_id;
});
}
bool
ircd::m::user::tokens::for_each(const closure_bool &closure)
const
{
const m::room::id::buf tokens_room_id
{
"tokens", origin(my())
};
const m::room tokens
{
tokens_room_id
};
const m::room::state state
{
tokens
};
return state.for_each("ircd.access_token", [this, &closure]
(const auto &type, const auto &state_key, const auto &event_idx)
{
const auto match
{
[this](const string_view &sender)
{
return sender == this->user.user_id;
}
};
if(!m::query(std::nothrow, event_idx, "sender", match))
return true;
if(!closure(event_idx, state_key))
return false;
return true;
});
}
ircd::m::user::id::buf
ircd::m::user::tokens::get(const string_view &token)
{
const auto ret
{
get(std::nothrow, token)
};
if(!ret)
throw m::error
{
http::UNAUTHORIZED, "M_UNKNOWN_TOKEN",
"Credentials for this method are required but invalid."
};
return ret;
}
ircd::m::user::id::buf
ircd::m::user::tokens::get(std::nothrow_t,
const string_view &token)
{
const m::room::id::buf tokens_room_id
{
"tokens", origin(my())
};
const m::room tokens
{
tokens_room_id
};
const event::idx event_idx
{
tokens.get(std::nothrow, "ircd.access_token", token)
};
m::user::id::buf ret;
m::get(std::nothrow, event_idx, "sender", [&ret]
(const string_view &sender)
{
ret = sender;
});
return ret;
}
ircd::m::device::id::buf
ircd::m::user::tokens::device(const string_view &token)
{
const auto ret
{
device(std::nothrow, token)
};
if(unlikely(!ret))
throw m::NOT_FOUND
{
"No device for this access_token"
};
return ret;
}
ircd::m::device::id::buf
ircd::m::user::tokens::device(std::nothrow_t,
const string_view &token)
{
const m::room::id::buf tokens_room_id
{
"tokens", origin(my())
};
const m::room tokens
{
tokens_room_id
};
const event::idx event_idx
{
tokens.get(std::nothrow, "ircd.access_token", token)
};
device::id::buf ret;
m::get(std::nothrow, event_idx, "content", [&ret]
(const json::object &content)
{
const json::string &device_id
{
content.at("device_id")
};
ret = device_id;
});
return ret;
}
ircd::string_view
ircd::m::user::tokens::generate(const mutable_buffer &buf)
{
static const size_t token_max
{
32
};
static const auto &token_dict
{
rand::dict::alpha
};
const mutable_buffer out
{
data(buf), std::min(token_max, size(buf))
};
return rand::string(token_dict, out);
}

View file

@ -66,7 +66,7 @@ ircd::m::post_keys_device_signing_upload(client &client,
const m::device::id::buf device_id
{
m::user::get_device_from_access_token(request.access_token)
m::user::tokens::device(request.access_token)
};
const m::user::room user_room

View file

@ -45,7 +45,7 @@ ircd::m::post_keys_signatures_upload(client &client,
{
const m::device::id::buf device_id
{
m::user::get_device_from_access_token(request.access_token)
m::user::tokens::device(request.access_token)
};
const m::user::room user_room

View file

@ -86,7 +86,7 @@ post__keys_upload(client &client,
const m::device::id::buf device_id
{
m::user::get_device_from_access_token(request.access_token)
m::user::tokens::device(request.access_token)
};
if(!empty(device_keys))

View file

@ -101,7 +101,7 @@ post__login_password(client &client,
char access_token_buf[32];
const string_view access_token
{
m::user::gen_access_token(access_token_buf)
m::user::tokens::generate(access_token_buf)
};
char remote_buf[96];

View file

@ -10,10 +10,6 @@
using namespace ircd;
static bool
do_logout(const m::user::id &user_id,
const m::event::idx &token_event_idx);
static m::resource::response
post__logout(client &,
const m::resource::request &);
@ -76,23 +72,16 @@ post__logout(client &client,
request.access_token
};
const m::room::id::buf tokens_room_id
const m::user::tokens tokens
{
"tokens", origin(m::my())
request.user_id
};
const m::room::state tokens
const bool deleted
{
tokens_room_id
tokens.del(access_token, "client logout")
};
const auto token_event_idx
{
tokens.get("ircd.access_token", access_token)
};
do_logout(request.user_id, token_event_idx);
return m::resource::response
{
client, http::OK
@ -103,79 +92,21 @@ m::resource::response
post__logout_all(client &client,
const m::resource::request &request)
{
const m::room::id::buf tokens_room_id
const m::user::tokens tokens
{
"tokens", origin(m::my())
request.user_id
};
const m::room::state tokens
const size_t invalidations
{
tokens_room_id
tokens.del("client logout all")
};
long count(0);
tokens.for_each("ircd.access_token", m::event::closure_idx{[&request, &count]
(const m::event::idx &event_idx)
{
bool match(false);
m::get(std::nothrow, event_idx, "sender", [&request, &match]
(const string_view &sender)
{
match = request.user_id == sender;
});
if(match)
{
do_logout(request.user_id, event_idx);
++count;
}
}});
return m::resource::response
{
client, json::members
{
{ "invalidations", count }
{ "invalidations", long(invalidations) }
}
};
}
bool
do_logout(const m::user::id &user_id,
const m::event::idx &token_event_idx)
try
{
static const string_view reason
{
"logout"
};
const auto token_event_id
{
m::event_id(token_event_idx)
};
const m::room::id::buf tokens_room_id
{
"tokens", origin(m::my())
};
const auto redaction_event_id
{
m::redact(tokens_room_id, user_id, token_event_id, reason)
};
return true;
}
catch(const std::exception &e)
{
log::error
{
m::log, "Error logging out user '%s' token event_idx:%lu :%s",
string_view{user_id},
token_event_idx,
e.what()
};
return false;
}

View file

@ -165,10 +165,10 @@ post__register_guest(client &client,
m::generate, my_host()
};
char access_token_buf[32];
char access_token_buf[64];
const string_view access_token
{
m::user::gen_access_token(access_token_buf)
m::user::tokens::generate(access_token_buf)
};
return resource::response

View file

@ -80,7 +80,7 @@ ircd::m::post_room_keys_version(client &client,
const m::device::id::buf device_id
{
m::user::get_device_from_access_token(request.access_token)
m::user::tokens::device(request.access_token)
};
const m::user::room user_room

View file

@ -11547,42 +11547,28 @@ console_cmd__user__tokens(opt &out, const string_view &line)
param["clear"] == "clear"
};
const m::room::id::buf tokens_room
const m::user::tokens tokens
{
"tokens", origin(m::my())
user
};
const m::room::state &tokens
if(clear)
{
tokens_room
};
tokens.for_each("ircd.access_token", m::event::closure_idx{[&out, &user, &clear]
(const m::event::idx &event_idx)
{
bool match(false);
m::get(std::nothrow, event_idx, "sender", [&match, &user]
(const string_view &sender)
const size_t count
{
match = sender == user.user_id;
});
if(!match)
return;
const m::event::fetch event
{
event_idx
tokens.del("Invalidated by administrator console.")
};
const string_view &token
{
at<"state_key"_>(event)
};
out << "Invalidated " << count << std::endl;
return true;
}
tokens.for_each([&out]
(const m::event::idx &event_idx, const string_view &token)
{
const milliseconds ost
{
at<"origin_server_ts"_>(event)
m::get<long>(event_idx, "origin_server_ts")
};
const milliseconds now
@ -11590,31 +11576,22 @@ console_cmd__user__tokens(opt &out, const string_view &line)
time<milliseconds>()
};
out << token
<< " "
<< ost
<< " "
<< pretty(now - ost) << " ago"
<< " "
<< string_view{event.event_id};
if(clear)
const auto event_id
{
const m::room::id::buf tokens_room
{
"tokens", origin(m::my())
};
m::event_id(std::nothrow, event_idx)
};
const auto eid
{
m::redact(tokens_room, user.user_id, event.event_id, "cleared")
};
out << " - cleared by " << eid;
}
out << std::endl;
}});
out
<< token
<< " "
<< ost
<< " "
<< pretty(now - ost) << " ago"
<< " "
<< string_view{event_id}
<< std::endl;
return true;
});
return true;
}