0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-06-25 05:18:23 +02:00

ircd:Ⓜ️/modules/client: Add device ID generation; use token/password helpers; various comments/cleanup.

This commit is contained in:
Jason Volk 2018-02-15 13:11:51 -08:00
parent 6ece5db391
commit eab4aef7e6
4 changed files with 128 additions and 63 deletions

View file

@ -14,6 +14,8 @@
namespace ircd::m
{
struct user;
bool exists(const id::user &);
}
struct ircd::m::user

View file

@ -10,6 +10,8 @@
#include <ircd/m/m.h>
/// ID of the room which indexes all users (an instance of the room is
/// provided below).
const ircd::m::room::id::buf
users_room_id
{
@ -27,17 +29,19 @@ ircd::m::user::users
users_room_id
};
/// The tokens room serves as a key-value lookup for various tokens to
/// users, etc. It primarily serves to store access tokens for users. This
/// is a separate room from the users room because in the future it may
/// have an optimized configuration as well as being more easily cleared.
///
/// ID of the room which stores ephemeral tokens (an instance of the room is
/// provided below).
const ircd::m::room::id::buf
tokens_room_id
{
"tokens", ircd::my_host()
};
/// The tokens room serves as a key-value lookup for various tokens to
/// users, etc. It primarily serves to store access tokens for users. This
/// is a separate room from the users room because in the future it may
/// have an optimized configuration as well as being more easily cleared.
///
ircd::m::room
ircd::m::user::tokens
{
@ -92,22 +96,16 @@ void
ircd::m::user::password(const string_view &password)
try
{
//TODO: ADD SALT
const sha256::buf hash
char buf[64];
const auto supplied
{
sha256{password}
gen_password_hash(buf, password)
};
char b64[64];
const auto digest
const user::room user_room{*this};
send(user_room, user_id, "ircd.password", user_id,
{
b64encode_unpadded(b64, hash)
};
const auto room_id{this->room_id()};
send(room_id, user_id, "ircd.password", user_id,
{
{ "sha256", digest }
{ "sha256", supplied }
});
}
catch(const m::ALREADY_MEMBER &e)
@ -119,24 +117,18 @@ catch(const m::ALREADY_MEMBER &e)
}
bool
ircd::m::user::is_password(const string_view &supplied_password)
ircd::m::user::is_password(const string_view &password)
const
{
//TODO: ADD SALT
const sha256::buf hash
char buf[64];
const auto supplied
{
sha256{supplied_password}
};
char b64[64];
const auto supplied_hash
{
b64encode_unpadded(b64, hash)
gen_password_hash(buf, password)
};
bool ret{false};
const auto room_id{this->room_id()};
m::room{room_id}.get("ircd.password", user_id, [&supplied_hash, &ret]
const user::room user_room{*this};
user_room.get("ircd.password", user_id, [&supplied, &ret]
(const m::event &event)
{
const json::object &content
@ -144,12 +136,12 @@ const
json::at<"content"_>(event)
};
const auto &correct_hash
const auto &correct
{
unquote(content.at("sha256"))
};
ret = supplied_hash == correct_hash;
ret = supplied == correct;
});
return ret;

View file

@ -28,29 +28,56 @@ login_resource
namespace { namespace name
{
constexpr const auto password{"password"};
constexpr const auto medium{"medium"};
constexpr const auto type{"type"};
constexpr const auto user{"user"};
constexpr const auto medium{"medium"};
constexpr const auto address{"address"};
constexpr const auto password{"password"};
constexpr const auto token{"token"};
constexpr const auto device_id{"device_id"};
constexpr const auto initial_device_display_name{"initial_device_display_name"};
}}
struct body
:json::tuple
<
json::property<name::password, string_view>,
json::property<name::medium, time_t>,
/// Required. The login type being used. One of: ["m.login.password",
/// "m.login.token"]
json::property<name::type, string_view>,
/// The fully qualified user ID or just local part of the user ID, to
/// log in.
json::property<name::user, string_view>,
json::property<name::address, string_view>
/// When logging in using a third party identifier, the medium of the
/// identifier. Must be 'email'.
json::property<name::medium, string_view>,
/// Third party identifier for the user.
json::property<name::address, string_view>,
/// Required when type is m.login.password. The user's password.
json::property<name::password, string_view>,
/// Required when type is m.login.token. The login token.
json::property<name::token, string_view>,
/// ID of the client device. If this does not correspond to a known client
/// device, a new device will be created. The server will auto-generate a
/// device_id if this is not specified.
json::property<name::device_id, string_view>,
/// A display name to assign to the newly-created device. Ignored if
/// device_id corresponds to a known device.
json::property<name::initial_device_display_name, string_view>
>
{
using super_type::tuple;
};
resource::response
post_login_password(client &client,
const resource::request::object<body> &request)
post__login_password(client &client,
const resource::request::object<body> &request)
{
// Build a canonical MXID from a the user field
const m::id::user::buf user_id
@ -80,13 +107,22 @@ post_login_password(client &client,
"Access denied."
};
// Generate the access token
static constexpr const auto token_len{127};
static const auto token_dict{rand::dict::alpha};
char token_buf[token_len + 1];
const auto requested_device_id
{
unquote(json::get<"device_id"_>(request))
};
const m::id::device device_id
{
requested_device_id?
m::id::device::buf{requested_device_id, my_host()}:
m::id::device::buf{m::id::generate, my_host()}
};
char access_token_buf[32];
const string_view access_token
{
rand::string(token_dict, token_len, token_buf, sizeof(token_buf))
m::user::gen_access_token(access_token_buf)
};
// Log the user in by issuing an event in the tokens room containing
@ -95,49 +131,51 @@ post_login_password(client &client,
m::send(m::user::tokens, user_id, "ircd.access_token", access_token,
{
{ "ip", string(remote(client)) },
{ "device", "unknown" },
{ "device", device_id },
});
// Send response to user
return resource::response
{
client,
client, json::members
{
{ "user_id", user_id },
{ "home_server", my_host() },
{ "access_token", access_token },
{ "device_id", device_id },
}
};
}
resource::response
post_login(client &client, const resource::request::object<body> &request)
post_login(client &client,
const resource::request::object<body> &request)
{
// x.x.x Required. The login type being used.
// Currently only "m.login.password" is supported.
const auto type
const auto &type
{
unquote(at<"type"_>(request))
};
if(type == "m.login.password")
return post_login_password(client, request);
else
throw m::error
{
"M_UNSUPPORTED", "Login type is not supported."
};
return post__login_password(client, request);
throw m::UNSUPPORTED
{
"Login type is not supported."
};
}
resource::method method_post
resource::method
method_post
{
login_resource, "POST", post_login
};
resource::response
get_login(client &client, const resource::request &request)
get_login(client &client,
const resource::request &request)
{
json::member login_password
const json::member login_password
{
"type", "m.login.password"
};

View file

@ -22,6 +22,7 @@ namespace { namespace name
constexpr const auto bind_email {"bind_email"};
constexpr const auto password {"password"};
constexpr const auto auth {"auth"};
constexpr const auto device_id {"device_id"};
}}
struct body
@ -30,7 +31,8 @@ struct body
json::property<name::username, string_view>,
json::property<name::bind_email, bool>,
json::property<name::password, string_view>,
json::property<name::auth, json::object>
json::property<name::auth, json::object>,
json::property<name::device_id, string_view>
>
{
using super_type::tuple;
@ -45,13 +47,13 @@ handle_post_kind_user(client &client,
try
{
// 3.3.1 Additional authentication information for the user-interactive authentication API.
const json::object auth
const json::object &auth
{
json::get<"auth"_>(request)
};
// 3.3.1 Required. The login type that the client is attempting to complete.
const string_view type
// 3.3.1 The login type that the client is attempting to complete.
const string_view &type
{
!empty(auth)? unquote(auth.at("type")) : string_view{}
};
@ -87,6 +89,21 @@ try
unquote(at<"password"_>(request))
};
// (r0.3.0) 3.4.1 ID of the client device. If this does not correspond to a
// known client device, a new device will be created. The server will auto-
// generate a device_id if this is not specified.
const auto requested_device_id
{
unquote(json::get<"device_id"_>(request))
};
const m::id::device device_id
{
requested_device_id?
m::id::device::buf{requested_device_id, my_host()}:
m::id::device::buf{m::id::generate, my_host()}
};
// 3.3.1 If true, the server binds the email used for authentication to the
// Matrix ID with the ID Server. Defaults to false.
const auto &bind_email
@ -117,6 +134,21 @@ try
// m.login.password
user.password(password);
char access_token_buf[32];
const string_view access_token
{
m::user::gen_access_token(access_token_buf)
};
// Log the user in by issuing an event in the tokens room containing
// the generated token. When this call completes without throwing the
// access_token will be committed and the user will be logged in.
m::send(m::user::tokens, user_id, "ircd.access_token", access_token,
{
{ "ip", string(remote(client)) },
{ "device", device_id },
});
// Send response to user
return resource::response
{
@ -124,7 +156,8 @@ try
{
{ "user_id", user_id },
{ "home_server", my_host() },
// { "access_token", access_token },
{ "access_token", access_token },
{ "device_id", device_id },
}
};
}