0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-06-16 08:58:20 +02:00

ircd:Ⓜ️ Deduplicate user related in m::user. Update various resource related.

This commit is contained in:
Jason Volk 2017-09-24 20:47:13 -07:00
parent ca1f5bbbe5
commit d4508e157f
13 changed files with 365 additions and 206 deletions

View file

@ -564,7 +564,7 @@ try
moi->quote(q);
break;
}
*/
case hash("password"):
{
if(!moi)
@ -575,20 +575,66 @@ try
const auto args(tokens_after(line, " ", 0));
const params token{args, " ", {"new_password"}};
m::request::password password
{{
{ "new_password", token.at(0) },
{ "auth",
{
{ "session", "abcdef" },
{ "type", "m.login.token" }
}}
}};
static char query[512]; const auto query_len
{
fmt::snprintf(query, sizeof(query), "%s=%s",
"access_token",
moi->access_token)
};
moi->password(password);
m::request request
{
"POST", "_matrix/client/r0/account/password", query, json::members
{
{ "new_password", token.at(0) },
{ "auth", json::members
{
{ "type", "m.login.password" }
}},
}
};
static char buf[4096];
ircd::parse::buffer pb{buf};
const json::object response{(*moi)(pb, request)};
std::cout << string_view{response} << std::endl;
break;
}
*/
case hash("deactivate"):
{
if(!moi)
{
std::cerr << "No current session" << std::endl;
break;
}
const auto args(tokens_after(line, " ", 0));
static char query[512]; const auto query_len
{
fmt::snprintf(query, sizeof(query), "%s=%s",
"access_token",
moi->access_token)
};
m::request request
{
"POST", "_matrix/client/r0/account/deactivate", query, json::members
{
{ "auth", json::members
{
{ "type", "m.login.password" }
}},
}
};
static char buf[4096];
ircd::parse::buffer pb{buf};
const json::object response{(*moi)(pb, request)};
std::cout << string_view{response} << std::endl;
break;
}
case hash("setfilter"):
{
if(!moi)

View file

@ -109,6 +109,8 @@ struct ircd::m::filter
json::property<name::presence, event_filter>
>
{
static room filters;
using super_type::tuple;
using super_type::operator=;
};

View file

@ -28,10 +28,10 @@
namespace ircd::m
{
struct user;
}
namespace ircd::m::users
{
extern user me;
extern room my_room;
extern room filters;
}
struct ircd::m::user
@ -40,10 +40,16 @@ struct ircd::m::user
id user_id;
user(const id &user_id);
};
static room accounts;
inline
ircd::m::user::user(const id &user_id)
:user_id{user_id}
{}
bool is_active() const;
bool is_password(const string_view &password) const;
void password(const string_view &password);
void activate(const json::members &contents = {});
void deactivate(const json::members &contents = {});
user(const id &user_id)
:user_id{user_id}
{}
};

View file

@ -77,6 +77,7 @@ struct ircd::resource::request
const http::request::head &head;
http::request::content &content;
http::query::string query;
m::user::id::buf user_id;
request(const http::request::head &head, http::request::content &content, http::query::string query);
};

View file

@ -227,13 +227,14 @@ namespace ircd
#include "fmt.h"
#include "fs.h"
#include "ctx.h"
#include "resource.h"
#include "logger.h"
#include "db.h"
#include "js.h"
#include "client.h"
#include "mods.h"
#include "listen.h"
#include "m.h"
#include "resource.h"
template<class T>
std::string

View file

@ -33,8 +33,6 @@
#include <rocksdb/env.h>
#include <rocksdb/slice_transform.h>
#include <ircd/m.h>
namespace ircd::db
{
const auto BLOCKING = rocksdb::ReadTier::kReadAllTier;

View file

@ -24,7 +24,6 @@
*/
#include <ircd/asio.h>
#include <ircd/m.h>
namespace ircd
{

View file

@ -19,8 +19,6 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <ircd/m.h>
///////////////////////////////////////////////////////////////////////////////
//
// m/session.h
@ -87,6 +85,24 @@ namespace ircd::m
void bootstrap();
}
ircd::m::user
ircd::m::me
{
"@ircd:cdc.z"
};
ircd::m::room
ircd::m::my_room
{
ircd::m::room::id{"!ircd:cdc.z"}
};
ircd::m::room
ircd::m::filter::filters
{
ircd::m::room::id{"!filters:cdc.z"}
};
ircd::m::init::init()
try
{
@ -139,12 +155,8 @@ ircd::m::join_ircd_room()
try
{
// ircd.start event
const m::id::room::buf room_id{"ircd", "cdc.z"};
const m::id::user::buf user_id{"ircd", "cdc.z"};
m::room ircd_room{room_id};
json::iov content;
ircd_room.join(user_id, content);
my_room.join(me.user_id, content);
}
catch(const m::ALREADY_MEMBER &e)
{
@ -155,12 +167,8 @@ void
ircd::m::leave_ircd_room()
{
// ircd.start event
const m::id::room::buf room_id{"ircd", "cdc.z"};
const m::id::user::buf user_id{"ircd", "cdc.z"};
m::room ircd_room{room_id};
json::iov content;
ircd_room.leave(user_id, content);
my_room.leave(me.user_id, content);
}
void
@ -175,46 +183,38 @@ ircd::m::bootstrap()
"database is empty. I will be bootstrapping it with initial events now..."
);
const m::id::user::buf user_id{"ircd", "cdc.z"};
const m::id::room::buf ircd_room_id{"ircd", "cdc.z"};
m::room ircd_room{ircd_room_id};
ircd_room.send(
my_room.send(
{
{ "type", "m.room.create" },
{ "sender", user_id },
{ "sender", me.user_id },
{ "state_key", "" },
{ "content", json::members
{
{ "creator", user_id }
{ "creator", me.user_id }
}}
});
const m::id::room::buf accounts_room_id{"accounts", "cdc.z"};
m::room accounts_room{accounts_room_id};
accounts_room.send(
user::accounts.send(
{
{ "type", "m.room.create" },
{ "sender", user_id },
{ "sender", me.user_id },
{ "state_key", "" },
{ "content", json::members
{
{ "creator", user_id }
{ "creator", me.user_id }
}}
});
json::iov content;
accounts_room.join(user_id, content);
const m::id::room::buf filters_room_id{"filters", "cdc.z"};
m::room filters_room{filters_room_id};
filters_room.send(
user::accounts.join(me.user_id, content);
filter::filters.send(
{
{ "type", "m.room.create" },
{ "sender", user_id },
{ "sender", me.user_id },
{ "state_key", "" },
{ "content", json::members
{
{ "creator", user_id }
{ "creator", me.user_id }
}}
});
}
@ -638,6 +638,163 @@ const
return false;
}
///////////////////////////////////////////////////////////////////////////////
//
// m/user.h
//
ircd::m::room
ircd::m::user::accounts
{
ircd::m::room::id{"!accounts:cdc.z"}
};
/// Register the user by joining them to the accounts room.
///
/// The content of the join event may store keys including the registration
/// options. Once this call completes the join was successful and the user is
/// registered, otherwise throws.
void
ircd::m::user::activate(const json::members &contents)
try
{
json::iov content;
json::iov::push members[contents.size()];
size_t i(0);
for(const auto &member : contents)
new (members + i++) json::iov::push(content, member);
accounts.join(user_id, content);
}
catch(const m::ALREADY_MEMBER &e)
{
throw m::error
{
http::CONFLICT, "M_USER_IN_USE", "The desired user ID is already in use."
};
}
void
ircd::m::user::deactivate(const json::members &contents)
{
json::iov content;
json::iov::push members[contents.size()];
size_t i(0);
for(const auto &member : contents)
new (members + i++) json::iov::push(content, member);
accounts.leave(user_id, content);
}
void
ircd::m::user::password(const string_view &password)
try
{
json::iov event;
json::iov::push members[]
{
{ event, json::member { "type", "ircd.password" }},
{ event, json::member { "state_key", user_id }},
{ event, json::member { "sender", user_id }},
{ event, json::member { "content", json::members
{
{ "plaintext", password }
}}},
};
accounts.send(event);
}
catch(const m::ALREADY_MEMBER &e)
{
throw m::error
{
http::CONFLICT, "M_USER_IN_USE", "The desired user ID is already in use."
};
}
bool
ircd::m::user::is_password(const string_view &supplied_password)
const
{
const m::event::where::equal member_event
{
{ "type", "ircd.password" },
{ "state_key", user_id }
};
bool ret{false};
const m::event::where::test correct_password{[&ret, &supplied_password]
(const auto &event)
{
const json::object &content
{
json::at<m::name::content>(event)
};
const auto &correct_password
{
unquote(content.at("plaintext"))
};
ret = supplied_password == correct_password;
return true;
}};
const auto query
{
member_event && correct_password
};
// The query to the database is made here. Know that this ircd::ctx
// may suspend and global state may have changed after this call.
const room::events events
{
accounts
};
events.query(member_event && correct_password);
return ret;
}
bool
ircd::m::user::is_active()
const
{
const m::event::where::equal member_event
{
{ "type", "m.room.member" },
{ "state_key", user_id }
};
bool ret{false};
const m::event::where::test is_joined{[&ret]
(const auto &event)
{
const json::object &content
{
json::val<m::name::content>(event)
};
const auto &membership
{
unquote(content["membership"])
};
ret = membership == "join";
return true;
}};
const room::events events
{
accounts
};
events.query(member_event && is_joined);
return ret;
}
///////////////////////////////////////////////////////////////////////////////
//
// m/event.h

View file

@ -19,8 +19,6 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <ircd/m.h>
namespace ircd {
IRCD_INIT_PRIORITY(STD_CONTAINER)
@ -108,7 +106,7 @@ noexcept
namespace ircd {
const m::room accounts
const m::room::events accounts
{
m::id::room{"!accounts:cdc.z"}
};
@ -119,7 +117,7 @@ authenticate(client &client,
resource::request &request)
try
{
const auto &access_token
const string_view &access_token
{
request.query.at("access_token")
};
@ -131,7 +129,17 @@ try
{ "state_key", access_token }
};
if(!m::room::events{accounts}.any(query))
const bool result
{
accounts.query(query, [&request, &access_token](const m::event &event)
{
assert(at<m::name::state_key>(event) == access_token);
request.user_id = at<m::name::sender>(event);
return true;
})
};
if(!result)
throw m::error
{
// When credentials are required but missing or invalid, the HTTP call will return with

View file

@ -48,47 +48,36 @@ resource::response
account_password(client &client, const resource::request &request)
try
{
const auto new_password
const string_view &new_password
{
request.at("new_password")
unquote(request.at("new_password"))
};
const auto type
const string_view &type
{
unquote(request.at("auth.type"))
unquote(request.at({"auth", "type"}))
};
const auto session
{
request["auth.session"]
};
if(!type.empty() && type != "m.login.token")
{
if(type != "m.login.password")
throw m::error
{
"M_UNSUPPORTED", "Login type is not supported."
};
}
//db::handle hand(account, "foo");
//database::snapshot snap(hand);
/*
account(db::MERGE, "jzk", std::string
const string_view &session
{
json::index
{{
"password",
{
{ "plaintext", new_password }
}
}}
});
*/
request[{"auth", "session"}]
};
m::user user
{
request.user_id
};
user.password(new_password);
return resource::response
{
client
client, http::OK
};
}
catch(const db::not_found &e)
@ -99,9 +88,49 @@ catch(const db::not_found &e)
};
}
resource::method post
resource::method post_password
{
account_resource.password, "POST", account_password
account_resource.password, "POST", account_password,
{
post_password.REQUIRES_AUTH
}
};
resource::response
account_deactivate(client &client, const resource::request &request)
{
const string_view &type
{
request.at({"auth", "type"})
};
const string_view &session
{
request[{"auth", "session"}]
};
m::user user
{
request.user_id
};
user.deactivate();
return resource::response
{
client, json::members
{
{ "goodbye", "Thanks for visiting. Come back soon!" }
}
};
}
resource::method post_deactivate
{
account_resource.deactivate, "POST", account_deactivate,
{
post_deactivate.REQUIRES_AUTH
}
};
mapi::header IRCD_MODULE

View file

@ -56,24 +56,6 @@ struct body
using super_type::tuple;
};
// Generate !accounts:host which is the MXID of the room where the members
// are the actual account registrations for this homeserver.
const m::id::room::buf accounts_room_id
{
"accounts", home_server
};
// Handle to the accounts room
m::room accounts_room
{
accounts_room_id
};
const m::room::events accounts_room_events
{
accounts_room
};
resource::response
post_login_password(client &client,
const resource::request::object<body> &request)
@ -92,56 +74,21 @@ post_login_password(client &client,
const auto &supplied_password
{
at<name::password>(request)
unquote(at<name::password>(request))
};
// Sets up the query to find the user_id in the accounts room
const m::event::where::equal member_event
m::user user
{
{ "type", "m.room.member" },
{ "state_key", user_id }
user_id
};
// Once the query finds the result this closure views the event in the
// database and returns true if the login is authentic.
const m::event::where::test correct_password{[&supplied_password]
(const auto &event)
{
const json::object &content
if(!user.is_password(supplied_password))
throw m::error
{
json::val<m::name::content>(event)
http::FORBIDDEN, "M_FORBIDDEN", "Access denied."
};
const auto &membership
{
unquote(content.at("membership"))
};
if(membership != "join")
return false;
const auto &correct_password
{
content.get("password")
};
if(!correct_password)
return false;
if(supplied_password != correct_password)
return false;
return true;
}};
const auto query
{
member_event && correct_password
};
// The query to the database is made here. Know that this ircd::ctx
// may suspend and global state may have changed after this call.
if(!accounts_room_events.query(query))
if(!user.is_active())
throw m::error
{
http::FORBIDDEN, "M_FORBIDDEN", "Access denied."
@ -159,7 +106,7 @@ post_login_password(client &client,
// Log the user in by issuing an event in the accounts room containing
// the generated token. When this call completes without throwing the
// access_token will be committed and the user will be logged in.
accounts_room.send(
m::user::accounts.send(
{
{ "type", "ircd.access_token" },
{ "sender", user_id },

View file

@ -47,24 +47,8 @@ struct body
using super_type::tuple;
};
// Generate !accounts:host which is the MXID of the room where the members
// are the account registrations on this homeserver.
const m::id::room::buf accounts_room_id
{
"accounts", home_server
};
// This object is a lightweight handle to the accounts room, which is a
// chatroom whose members are the representation of the accounts registered
// to this server itself.
m::room accounts_room
{
accounts_room_id
};
static void validate_user_id(const m::id::user &user_id);
static void validate_password(const string_view &password);
static void join_accounts_room(const m::id::user &user_id, const json::members &contents);
resource::response
handle_post_kind_user(client &client,
@ -123,17 +107,25 @@ handle_post_kind_user(client &client,
// Check if the password is acceptable for this server or throws
validate_password(password);
// Register the user by joining them to the accounts room. The content of
// the join event will store keys from the registration options including
// the password - do not expose this to clients //TODO: store hashed pass
// Once this call completes the join was successful and the user is
// registered, otherwise throws.
join_accounts_room(user_id,
// Represent the user
m::user user
{
user_id
};
// Activate the account. Underneath this will join the user to the room
// !accounts:your.domain. If the user_id is already a member then this
// throws 409 Conflict; otherwise the user is registered after this call.
user.activate(
{
{ "password", password },
{ "bind_email", bind_email },
});
// Set the password for the account. This issues an ircd.password state
// event to the accounts room for the user. If this call completes the
// user will be able to login with m.login.password
user.password(password);
// Send response to user
return resource::response
{
@ -203,28 +195,6 @@ mapi::header IRCD_MODULE
"registers the resource 'client/register' to handle requests"
};
void
join_accounts_room(const m::id::user &user_id,
const json::members &contents)
try
{
json::iov content;
json::iov::push members[contents.size()];
size_t i(0);
for(const auto &member : contents)
new (members + i++) json::iov::push(content, member);
accounts_room.join(user_id, content);
}
catch(const m::ALREADY_MEMBER &e)
{
throw m::error
{
http::CONFLICT, "M_USER_IN_USE", "The desired user ID is already in use."
};
}
void
validate_user_id(const m::id::user &user_id)
{

View file

@ -30,16 +30,6 @@ resource user_resource
}
};
m::room filters_room
{
m::room::id{"!filters:cdc.z"}
};
const m::room::events filters_room_events
{
filters_room
};
resource::response
get_filter(client &client, const resource::request &request)
try
@ -77,6 +67,11 @@ try
return true;
}};
const m::room::events filters_room_events
{
m::filter::filters
};
if(!filters_room_events.any(query, result))
throw m::NOT_FOUND("No matching filter with that ID");
@ -166,7 +161,7 @@ try
{ event, json::member { "content", request.body }}
};
filters_room.send(event);
m::filter::filters.send(event);
return resource::response
{
client, http::CREATED,