construct/matrix/user.cc

410 lines
6.9 KiB
C++

// Matrix Construct
//
// Copyright (C) Matrix Construct Developers, Authors & Contributors
// Copyright (C) 2016-2018 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.
namespace ircd::m
{
static string_view gen_password_hash(const mutable_buffer &, const string_view &);
static room create_user_room(const user::id &, const room::id &, const json::members &contents);
}
ircd::m::user::reading::reading(const user &user)
{
if(!(room_id = viewing(user)))
return;
const user::room user_room
{
user
};
const auto last_event_idx
{
user_room.get(std::nothrow, "ircd.read", room_id)
};
const bool last_content_prefetched
{
prefetch(last_event_idx, "content")
};
time_t last_ots {0};
get<time_t>(last_event_idx, "origin_server_ts", last_ots);
this->last_ots = milliseconds(last_ots) / 1000;
get(std::nothrow, last_event_idx, "content", [this]
(const json::object &content)
{
this->last_ts = content.get<milliseconds>("ts");
this->last = json::string
{
content["event_id"]
};
});
const user::room_account_data room_account_data
{
user, room_id
};
room_account_data.get(std::nothrow, "m.fully_read", [this]
(const string_view &key, const json::object &content)
{
this->full = json::string
{
content["event_id"]
};
});
//TODO: XXX
// full_ots
presence::get(std::nothrow, user, [this]
(const json::object &event)
{
this->currently_active = event.get<bool>("currently_active", false);
});
}
ircd::m::room::id::buf
ircd::m::viewing(const user &user,
size_t i)
{
const m::breadcrumbs breadcrumbs
{
user
};
room::id::buf ret;
breadcrumbs.for_each([&ret, &i]
(const auto &room_id)
{
if(i-- > 0)
return true;
ret = room_id;
return false;
});
return ret;
}
bool
ircd::m::is_oper(const user &user)
{
const m::room::id::buf control_room_id
{
"!control", my_host()
};
return m::membership(control_room_id, user, "join");
}
bool
ircd::m::active(const user &user)
{
const m::user::room user_room
{
user.user_id
};
const m::event::idx &event_idx
{
user_room.get(std::nothrow, "ircd.account", "active")
};
return m::query(std::nothrow, event_idx, "content", []
(const json::object &content)
{
return content.get<bool>("value", false);
});
}
bool
ircd::m::exists(const user &user)
{
return exists(user.user_id);
}
bool
ircd::m::exists(const user::id &user)
{
// The way we know a user exists is testing if their room exists.
const user::room user_room
{
user
};
return m::exists(user_room);
}
bool
ircd::m::my(const user &user)
{
return my(user.user_id);
}
ircd::m::user
ircd::m::create(const m::user::id &user_id,
const json::members &contents)
{
const m::user user
{
user_id
};
const m::room::id::buf room_id
{
user.room_id()
};
const m::room room
{
create_user_room(user_id, room_id, contents)
};
return user;
}
ircd::m::room
ircd::m::create_user_room(const user::id &user_id,
const room::id &room_id,
const json::members &contents)
try
{
return create(room_id, me(), "user");
}
catch(const std::exception &e)
{
if(m::exists(room_id))
return room_id;
log::error
{
log, "Failed to create user %s room %s :%s",
string_view{user_id},
string_view{room_id},
e.what()
};
throw;
}
//
// user::user
//
/// Generates a user-room ID into buffer; see room_id() overload.
ircd::m::id::room::buf
ircd::m::user::room_id()
const
{
ircd::m::id::room::buf buf;
return buf.assigned(room_id(buf));
}
/// This generates a room mxid for the "user's room" essentially serving as
/// a database mechanism for this specific user. This room_id is a hash of
/// the user's full mxid.
///
ircd::m::id::room
ircd::m::user::room_id(const mutable_buffer &buf)
const
{
assert(!empty(user_id));
const ripemd160::buf hash
{
ripemd160{user_id}
};
char b58[size(hash) * 2];
return
{
buf, b58::encode(b58, hash), origin(my())
};
}
ircd::m::event::id::buf
ircd::m::user::oper()
{
const m::room::id::buf control_room_id
{
"!control", origin(my())
};
const m::room control_room
{
control_room_id
};
return m::join(control_room, user_id);
}
ircd::m::event::id::buf
ircd::m::user::deoper()
{
const m::room::id::buf control_room_id
{
"!control", origin(my())
};
const m::room control_room
{
control_room_id
};
return m::leave(control_room, user_id);
}
ircd::m::event::id::buf
ircd::m::user::activate()
{
const m::user::room user_room
{
user_id
};
return send(user_room, m::me(), "ircd.account", "active", json::members
{
{ "value", true }
});
}
ircd::m::event::id::buf
ircd::m::user::deactivate()
{
const m::user::room user_room
{
user_id
};
return send(user_room, m::me(), "ircd.account", "active", json::members
{
{ "value", false }
});
}
ircd::m::event::id::buf
ircd::m::user::password(const string_view &password)
{
char buf[64];
const auto supplied
{
gen_password_hash(buf, password)
};
const m::user::room user_room
{
user_id
};
return send(user_room, user_id, "ircd.password", user_id,
{
{ "sha256", supplied }
});
}
bool
ircd::m::user::is_password(const string_view &password)
const try
{
char buf[64];
const auto supplied
{
gen_password_hash(buf, password)
};
bool ret{false};
const m::user::room user_room
{
user_id
};
const ctx::uninterruptible::nothrow ui;
user_room.get("ircd.password", user_id, [&supplied, &ret]
(const m::event &event)
{
const json::object &content
{
json::at<"content"_>(event)
};
const auto &correct
{
unquote(content.at("sha256"))
};
ret = supplied == correct;
});
return ret;
}
catch(const m::NOT_FOUND &e)
{
return false;
}
catch(const std::exception &e)
{
log::critical
{
"is_password__user() :%s %s",
string_view{user_id},
e.what()
};
return false;
}
ircd::string_view
ircd::m::gen_password_hash(const mutable_buffer &out,
const string_view &supplied_password)
{
//TODO: ADD SALT
const sha256::buf hash
{
sha256{supplied_password}
};
return b64::encode_unpadded(out, hash);
}
//
// user::room
//
ircd::m::user::room::room(const m::user::id &user_id,
const vm::copts *const &copts,
const event::fetch::opts *const &fopts)
:room
{
m::user{user_id}, copts, fopts
}
{
}
ircd::m::user::room::room(const m::user &user,
const vm::copts *const &copts,
const event::fetch::opts *const &fopts)
:user{user}
,room_id{user.room_id()}
{
static_cast<m::room &>(*this) =
{
room_id, copts, fopts
};
}
bool
ircd::m::user::room::is(const room::id &room_id,
const user::id &user_id)
{
const user::room user_room{user_id};
return user_room.room_id == room_id;
}