move all db dependent data into db subdir
This commit is contained in:
parent
207e50a880
commit
51e39e5498
@ -10,6 +10,7 @@ file(GLOB SOURCE
|
||||
)
|
||||
|
||||
include_directories( ${PostgreSQL_INCLUDE_DIRS} )
|
||||
|
||||
add_subdirectory(db)
|
||||
|
||||
add_library(auth STATIC ${SOURCE})
|
||||
|
||||
@ -1,21 +1,7 @@
|
||||
#include "PgUserAuth.hpp"
|
||||
|
||||
#include <eedb/db/data/PgUserAuth.hpp>
|
||||
#include <eedb/auth/Services.hpp>
|
||||
#include <eedb/db/connection.hpp>
|
||||
#include <eedb/db/model/auth_identity.h>
|
||||
#include <eedb/db/model/auth_info.h>
|
||||
#include <eedb/db/model/auth_token.h>
|
||||
#include <eedb/db/model/user.h>
|
||||
#include <eedb/db/model/user_action.h>
|
||||
#include <eedb/db/model/user_audit.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <experimental/string_view>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <string>
|
||||
|
||||
#include <Wt/Auth/AuthService.h>
|
||||
#include <Wt/Auth/Dbo/AuthInfo.h>
|
||||
@ -23,29 +9,9 @@
|
||||
#include <Wt/WLogger.h>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <sqlpp11/sqlpp11.h>
|
||||
|
||||
using namespace Wt::Auth;
|
||||
|
||||
constexpr const char eedb::user_audit_::Data::_alias_t::_literal[];
|
||||
constexpr const char eedb::user_audit::_alias_t::_literal[];
|
||||
|
||||
// enum LoginActions { Login, Logout };
|
||||
// static std::array< std::string_view, 2 > UserActionNames = {{"login", "logout"}};
|
||||
|
||||
std::string RandomString(uint len) {
|
||||
using namespace std;
|
||||
srand(static_cast< unsigned int >(time(nullptr)));
|
||||
experimental::string_view str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
string newstr;
|
||||
std::size_t pos;
|
||||
while(newstr.size() != len) {
|
||||
pos = static_cast< std::size_t >((rand() % (static_cast< int >(str.size()) - 1)));
|
||||
newstr += str.substr(pos, 1).to_string();
|
||||
}
|
||||
return newstr;
|
||||
}
|
||||
|
||||
template < typename Connection >
|
||||
struct TransactionGuard : public Wt::Auth::AbstractUserDatabase::Transaction {
|
||||
TransactionGuard(Connection & c, int & transaction_active) : _c(c), _transaction{transaction_active} {
|
||||
@ -72,42 +38,15 @@ struct TransactionGuard : public Wt::Auth::AbstractUserDatabase::Transaction {
|
||||
|
||||
namespace eedb::auth {
|
||||
|
||||
namespace {
|
||||
constexpr eedb::user t_user;
|
||||
constexpr eedb::user_audit t_user_history;
|
||||
constexpr eedb::user_action t_user_action;
|
||||
constexpr eedb::auth_identity t_identity;
|
||||
constexpr eedb::auth_info t_info;
|
||||
constexpr eedb::auth_token t_auth_token;
|
||||
|
||||
static const auto auth_info_identity = t_info.join(t_identity).on(t_info.id == t_identity.auth_info_id);
|
||||
static const auto user_auth_info = t_user.join(t_info).on(t_info.user_uid == t_user.uid);
|
||||
static const auto auth_token_info = user_auth_info.join(t_auth_token).on(t_auth_token.auth_info_id == t_info.id);
|
||||
|
||||
static const auto select_login_action_id = select(t_user_action.id) //
|
||||
.from(t_user_action) //
|
||||
.where(t_user_action.name == "login") //
|
||||
.limit(1u);
|
||||
static const auto select_logout_action_id = select(t_user_action.id) //
|
||||
.from(t_user_action) //
|
||||
.where(t_user_action.name == "logout") //
|
||||
.limit(1u);
|
||||
} // namespace
|
||||
|
||||
PgUserAuth::PgUserAuth(eedb::db::PgConnection & _db, const Wt::WEnvironment & env) : db(_db), _env(env) {
|
||||
PgUserAuth::PgUserAuth(eedb::db::PgConnection & _db, const Wt::WEnvironment & env) : db(_db), _env(env), _userAuth{_db} {
|
||||
this->setAuthService(eedb::auth::Services::authService());
|
||||
}
|
||||
|
||||
User PgUserAuth::findWithId(const std::string & id) const {
|
||||
const auto uid_eq = t_user.uid == std::atoi(id.c_str());
|
||||
auto duser = _userAuth.findWithId(id);
|
||||
|
||||
if(db(select(exists(select(t_user.uid) //
|
||||
.from(t_user) //
|
||||
.where(uid_eq))))
|
||||
.front()
|
||||
.exists) {
|
||||
return User(id, *this);
|
||||
}
|
||||
if(duser)
|
||||
return User(duser.moveid(), *this);
|
||||
return User();
|
||||
}
|
||||
|
||||
@ -117,101 +56,53 @@ User PgUserAuth::findWithIdentity(const std::string & provider, const Wt::WStrin
|
||||
std::transform(_identity.begin(), _identity.end(), _identity.begin(), ::tolower);
|
||||
}
|
||||
|
||||
const auto identity_eq = t_identity.identity == _identity;
|
||||
const auto provider_eq = t_identity.provider == provider;
|
||||
|
||||
const auto result = db(select(t_info.user_uid) //
|
||||
.from(auth_info_identity) //
|
||||
.where(provider_eq and identity_eq));
|
||||
|
||||
if(!result.empty()) {
|
||||
return User(std::to_string(result.front().user_uid), *this);
|
||||
auto duser = _userAuth.findWithIdentity(provider, _identity);
|
||||
if(duser) {
|
||||
return User(duser.moveid(), *this);
|
||||
}
|
||||
return User();
|
||||
}
|
||||
|
||||
User PgUserAuth::findWithEmailToken(const std::string & hash) const {
|
||||
auto duser = _userAuth.findWithEmailToken(hash);
|
||||
if(duser)
|
||||
return {duser.moveid(), *this};
|
||||
return {};
|
||||
}
|
||||
|
||||
User PgUserAuth::findWithEmail(const std::string & address) const {
|
||||
auto duser = _userAuth.findWithEmail(address);
|
||||
if(duser)
|
||||
return {duser.moveid(), *this};
|
||||
return {};
|
||||
}
|
||||
|
||||
void PgUserAuth::addIdentity(const User & user, const std::string & provider, const Wt::WString & identity) {
|
||||
const auto uid_eq = t_info.user_uid == std::atoi(user.id().c_str());
|
||||
const auto identity_eq = t_identity.identity == identity.toUTF8();
|
||||
const auto provider_eq = t_identity.provider == provider;
|
||||
|
||||
auto res = db(select(t_info.user_uid, t_info.id) //
|
||||
.from(auth_info_identity)
|
||||
.where(identity_eq and provider_eq and uid_eq));
|
||||
|
||||
if(!res.empty()) {
|
||||
Wt::log("error") << "cannot add identity " << provider << ":'" << identity << "': already exists";
|
||||
return;
|
||||
}
|
||||
|
||||
auto auth_info_id = db(select(t_info.id)
|
||||
.from(t_info) //
|
||||
.where(uid_eq));
|
||||
|
||||
if(auth_info_id.empty()) {
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
db(insert_into(t_identity)
|
||||
.set(t_identity.identity = identity.toUTF8(), //
|
||||
t_identity.provider = provider, //
|
||||
t_identity.auth_info_id = auth_info_id.front().id));
|
||||
|
||||
Wt::log("info") << "created new identity for user";
|
||||
_userAuth.addIdentity({user.id()}, provider, identity.toUTF8());
|
||||
}
|
||||
|
||||
void PgUserAuth::setIdentity(const User & user, const std::string & provider, const Wt::WString & identity) {
|
||||
const auto uid = std::atoi(user.id().c_str());
|
||||
const auto provider_eq = t_identity.provider == provider;
|
||||
|
||||
db(update(t_identity) //
|
||||
.set(t_identity.identity = identity.toUTF8()) //
|
||||
.where(t_identity.auth_info_id == select(t_info.id).from(t_info).where(t_info.user_uid == uid) and provider_eq));
|
||||
_userAuth.setIdentity({user.id()}, provider, identity.toUTF8());
|
||||
}
|
||||
|
||||
Wt::WString PgUserAuth::identity(const User & user, const std::string & provider) const {
|
||||
const int uid = std::atoi(user.id().c_str());
|
||||
const auto id_eq = t_info.user_uid == uid;
|
||||
const auto provider_eq = t_identity.provider == provider;
|
||||
auto id = _userAuth.identity({user.id()}, provider);
|
||||
|
||||
auto res = db(select(t_identity.identity) //
|
||||
.from(auth_info_identity) //
|
||||
.where(id_eq and provider_eq));
|
||||
|
||||
if(res.empty())
|
||||
if(id.empty())
|
||||
return Wt::WString::Empty;
|
||||
return {res.front().identity};
|
||||
return {id};
|
||||
}
|
||||
|
||||
void PgUserAuth::removeIdentity(const User & user, const std::string & provider) {
|
||||
const auto id_eq = t_identity.id == std::atoi(user.id().c_str());
|
||||
const auto provider_eq = t_identity.provider == provider;
|
||||
|
||||
db(remove_from(t_identity).where(id_eq and provider_eq));
|
||||
_userAuth.removeIdentity({user.id()}, provider);
|
||||
}
|
||||
|
||||
User PgUserAuth::registerNew() {
|
||||
auto user_id = db(sqlpp::postgresql::insert_into(t_user) //
|
||||
.set( //
|
||||
t_user.full_name = RandomString(256), //
|
||||
t_user.status = -1)
|
||||
.returning(t_user.uid))
|
||||
.front()
|
||||
.uid;
|
||||
|
||||
db(insert_into(t_info).set( //
|
||||
t_info.password_hash = "NONE", //
|
||||
t_info.password_method = "NONE", //
|
||||
t_info.password_salt = "NONE", //
|
||||
t_info.user_uid = user_id, //
|
||||
t_info.email = RandomString(20) + "@NONE.org"));
|
||||
|
||||
return User{std::to_string(user_id), *this};
|
||||
return User{_userAuth.registerNew().moveid(), *this};
|
||||
}
|
||||
|
||||
void PgUserAuth::deleteUser(const User & user) {
|
||||
db(remove_from(t_info) //
|
||||
.where(t_info.id == std::atoi(user.id().c_str())));
|
||||
_userAuth.deleteUser({user.id()});
|
||||
}
|
||||
|
||||
// User::Status PgUserAuth::status(const User & user) const {
|
||||
@ -232,265 +123,98 @@ void PgUserAuth::deleteUser(const User & user) {
|
||||
//}
|
||||
|
||||
void PgUserAuth::setPassword(const User & user, const PasswordHash & password) {
|
||||
db(update(t_info)
|
||||
.set( //
|
||||
t_info.password_hash = password.value(), //
|
||||
t_info.password_method = password.function(), //
|
||||
t_info.password_salt = password.salt())
|
||||
.where(t_info.user_uid == std::atoi(user.id().c_str())));
|
||||
_userAuth.setPassword({user.id()}, {password.function(), password.salt(), password.value()});
|
||||
}
|
||||
|
||||
PasswordHash PgUserAuth::password(const User & user) const {
|
||||
const auto id_eq = t_info.user_uid == std::atoi(user.id().c_str());
|
||||
|
||||
auto row = db(select(t_info.password_hash, t_info.password_method, t_info.password_salt)
|
||||
.from(t_info) //
|
||||
.where(id_eq));
|
||||
if(row.empty())
|
||||
auto pass = _userAuth.password({user.id()});
|
||||
if(!pass)
|
||||
return PasswordHash();
|
||||
auto & front = row.front();
|
||||
return {std::move(front.password_method), std::move(front.password_salt), std::move(front.password_hash)};
|
||||
return {pass->mfunction(), pass->msalt(), pass->mvalue()};
|
||||
}
|
||||
|
||||
bool PgUserAuth::setEmail(const User & user, const std::string & address) {
|
||||
const auto uid_eq = t_info.user_uid == std::atoi(user.id().c_str());
|
||||
|
||||
db(update(t_info) //
|
||||
.set( //
|
||||
t_info.email = address,
|
||||
t_info.email_verified = true) //
|
||||
.where(uid_eq));
|
||||
return true;
|
||||
return _userAuth.setEmail({user.id()}, address);
|
||||
}
|
||||
|
||||
std::string PgUserAuth::email(const User & user) const {
|
||||
auto ret = db(select(t_info.email) //
|
||||
.from(t_info) //
|
||||
.where(t_info.user_uid == std::atoi(user.id().c_str()) and t_info.email_verified == true));
|
||||
if(ret.empty())
|
||||
return {};
|
||||
return std::move(ret.front().email);
|
||||
return _userAuth.email({user.id()});
|
||||
}
|
||||
|
||||
void PgUserAuth::setUnverifiedEmail(const User & user, const std::string & address) {
|
||||
// orginal implementation of UserDatabase (provided by WT team) sets veryfied and unverified emails in
|
||||
// different fields in database. So in order to verify email, they just copy the unverified_email to email and then
|
||||
// set unverified_email to empty string (db don't allow empty strings as email*)
|
||||
if(address.empty())
|
||||
db(update(t_info) //
|
||||
.set(t_info.email_verified = true) //
|
||||
.where(t_info.user_uid == std::atoi(user.id().c_str())));
|
||||
else
|
||||
db(update(t_info) //
|
||||
.set( //
|
||||
t_info.email = address, //
|
||||
t_info.email_verified = false) //
|
||||
.where(t_info.user_uid == std::atoi(user.id().c_str())));
|
||||
}
|
||||
|
||||
std::string PgUserAuth::unverifiedEmail(const User & user) const {
|
||||
auto ret = db(select(t_info.email) //
|
||||
.from(t_info) //
|
||||
.where(t_info.user_uid == std::atoi(user.id().c_str()) and t_info.email_verified == false));
|
||||
if(ret.empty())
|
||||
return {};
|
||||
return std::move(ret.front().email);
|
||||
}
|
||||
|
||||
User PgUserAuth::findWithEmail(const std::string & address) const {
|
||||
auto ret = db(select(t_info.user_uid) //
|
||||
.from(t_info) //
|
||||
.where(t_info.email == address and t_info.email_verified == true));
|
||||
|
||||
if(ret.empty())
|
||||
return {};
|
||||
return {std::to_string(ret.front().user_uid), *this};
|
||||
_userAuth.setUnverifiedEmail({user.id()}, address);
|
||||
}
|
||||
|
||||
void PgUserAuth::setEmailToken(const User & user, const Token & token, EmailTokenRole role) {
|
||||
auto exp = ::date::floor<::std::chrono::milliseconds >(std::chrono::system_clock::from_time_t(token.expirationTime().toTime_t()));
|
||||
db(update(t_info) //
|
||||
.set( //
|
||||
t_info.email_token = token.hash(), //
|
||||
t_info.email_token_expires = exp,
|
||||
t_info.email_token_role = static_cast< int >(role)) //
|
||||
.where(t_info.user_uid == std::atoi(user.id().c_str())));
|
||||
_userAuth.setEmailToken({user.id()}, {token.hash(), exp}, static_cast< int >(role) );
|
||||
}
|
||||
|
||||
std::string PgUserAuth::unverifiedEmail(const User & user) const {
|
||||
_userAuth.unverifiedEmail({user.id()});
|
||||
}
|
||||
|
||||
Token PgUserAuth::emailToken(const User & user) const {
|
||||
auto ret = db(select(t_info.email_token, t_info.email_token_expires) //
|
||||
.from(t_info) //
|
||||
.where(t_info.user_uid == std::atoi(user.id().c_str())));
|
||||
if(ret.empty())
|
||||
auto tok = _userAuth.emailToken({user.id()});
|
||||
if(!tok)
|
||||
return {};
|
||||
auto exp = Wt::WDateTime();
|
||||
auto time = ret.front().email_token_expires.value();
|
||||
auto time = tok->expirationTime();
|
||||
auto systime = std::chrono::system_clock::to_time_t(time);
|
||||
exp.setTime_t(systime);
|
||||
return {ret.front().email_token, exp};
|
||||
return {tok->mhash(), exp};
|
||||
}
|
||||
|
||||
EmailTokenRole PgUserAuth::emailTokenRole(const User & user) const {
|
||||
auto ret = db(select(t_info.email_token_role) //
|
||||
.from(t_info) //
|
||||
.where(t_info.user_uid == std::atoi(user.id().c_str())));
|
||||
if(ret.empty())
|
||||
throw std::exception();
|
||||
auto val = ret.front().email_token_role;
|
||||
return static_cast< EmailTokenRole >(val.value());
|
||||
}
|
||||
|
||||
User PgUserAuth::findWithEmailToken(const std::string & hash) const {
|
||||
auto ret = db(select(t_user.uid) //
|
||||
.from(user_auth_info) //
|
||||
.where(t_info.email_token == hash));
|
||||
|
||||
if(ret.empty())
|
||||
return {};
|
||||
return {std::to_string(ret.front().uid), *this};
|
||||
auto val = _userAuth.emailTokenRole({user.id()});
|
||||
return static_cast< EmailTokenRole >(val);
|
||||
}
|
||||
|
||||
void PgUserAuth::addAuthToken(const User & user, const Token & token) {
|
||||
auto select_identity_id = select(t_info.id) //
|
||||
.from(t_info)
|
||||
.where(t_info.user_uid == std::atoi(user.id().c_str()))
|
||||
.limit(1u);
|
||||
db(insert_into(t_auth_token) //
|
||||
.set( //
|
||||
t_auth_token.auth_info_id = select_identity_id, //
|
||||
t_auth_token.expires = std::chrono::system_clock::now() + ::sqlpp::chrono::days{14}, //
|
||||
t_auth_token.value = token.hash()));
|
||||
auto exp = std::chrono::time_point_cast< std::chrono::microseconds >(std::chrono::system_clock::now()) + ::sqlpp::chrono::days{14};
|
||||
auto tok = eedb::db::details::Token{token.hash(), exp};
|
||||
_userAuth.addAuthToken({user.id()}, std::move(tok));
|
||||
}
|
||||
|
||||
void PgUserAuth::removeAuthToken(const User &, const std::string & hash) {
|
||||
db(remove_from(t_auth_token).where(t_auth_token.value == hash));
|
||||
void PgUserAuth::removeAuthToken(const User &user, const std::string & hash) {
|
||||
_userAuth.removeAuthToken({user.id()}, hash);
|
||||
}
|
||||
|
||||
User PgUserAuth::findWithAuthToken(const std::string & hash) const {
|
||||
auto fullUser = t_auth_token //
|
||||
.join(t_info)
|
||||
.on(t_info.id == t_auth_token.auth_info_id);
|
||||
|
||||
auto ret = db(select(t_info.user_uid) //
|
||||
.from(fullUser) //
|
||||
.where(t_auth_token.value == hash and t_auth_token.expires > std::chrono::system_clock::now()));
|
||||
|
||||
if(ret.empty())
|
||||
auto u = _userAuth.findWithAuthToken(hash);
|
||||
if(!u)
|
||||
return {};
|
||||
return {std::to_string(ret.front().user_uid), *this};
|
||||
return {u->moveid(), *this};
|
||||
}
|
||||
|
||||
int PgUserAuth::updateAuthToken(const User & user, const std::string & oldhash, const std::string & newhash) {
|
||||
// method called only after successful login
|
||||
using namespace std::chrono;
|
||||
using namespace std::string_literals;
|
||||
const auto identity_id = db(select(t_info.id) //
|
||||
.from(t_info) //
|
||||
.where(t_info.user_uid == std::atoi(user.id().c_str())));
|
||||
if(identity_id.empty())
|
||||
return 0;
|
||||
|
||||
const auto expires = db(sqlpp::postgresql::update(t_auth_token) //
|
||||
.set(t_auth_token.value = newhash)
|
||||
.where(t_auth_token.value == oldhash)
|
||||
.returning(t_auth_token.expires));
|
||||
|
||||
if(expires.empty())
|
||||
return 0;
|
||||
|
||||
const nlohmann::json data = {
|
||||
{"status"s, "success"}, //
|
||||
{"method"s, "token"}, //
|
||||
{"user_address"s, _env.clientAddress()}, //
|
||||
{"user_agent"s, _env.userAgent()}, //
|
||||
{"user_locale"s, _env.locale().name()}, //
|
||||
{"referer", _env.referer()}, //
|
||||
{"url_scheme", _env.urlScheme()} //
|
||||
};
|
||||
|
||||
db(insert_into(t_user_history)
|
||||
.set(t_user_history.user_id = std::atoi(user.id().c_str()),
|
||||
t_user_history.action_id = select_login_action_id, //
|
||||
t_user_history.data = data.dump()));
|
||||
|
||||
const auto now = system_clock::now();
|
||||
const auto diff = expires.front().expires.value() - now;
|
||||
return duration_cast< duration< int > >(diff).count();
|
||||
return _userAuth.updateAuthToken({user.id()}, oldhash, newhash);
|
||||
}
|
||||
|
||||
void PgUserAuth::setFailedLoginAttempts(const User & user, int count) {
|
||||
using namespace std::string_literals;
|
||||
const auto getStatus = [count]() { return count ? "failed"s : "success"s; };
|
||||
|
||||
const nlohmann::json data = {
|
||||
{"status"s, getStatus()}, //
|
||||
{"method"s, "password"}, //
|
||||
{"user_address"s, _env.clientAddress()}, //
|
||||
{"user_agent"s, _env.userAgent()}, //
|
||||
{"user_locale"s, _env.locale().name()}, //
|
||||
{"referer", _env.referer()}, //
|
||||
{"url_scheme", _env.urlScheme()} //
|
||||
};
|
||||
|
||||
db(insert_into(t_user_history)
|
||||
.set(t_user_history.user_id = std::atoi(user.id().c_str()),
|
||||
t_user_history.action_id = select_login_action_id, //
|
||||
t_user_history.data = data.dump()));
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
inline std::string name_of(const T &) {
|
||||
static_assert(sqlpp::is_column_t< T >::value, "T should by a calumn");
|
||||
return std::string{T::_table::_alias_t::_literal} + '.' + T::_alias_t::_literal;
|
||||
_userAuth.setFailedLoginAttempts({user.id()}, count);
|
||||
}
|
||||
|
||||
int PgUserAuth::failedLoginAttempts(const User & user) const {
|
||||
const auto res =
|
||||
db(select(count(t_user_history.id)) //
|
||||
.from(t_user_history) //
|
||||
.where(t_user_history.user_id == std::atoi(user.id().c_str()) and //
|
||||
t_user_history.action_id == select_login_action_id and //
|
||||
sqlpp::verbatim< sqlpp::boolean >(name_of(t_user_history.data) + "->>'status' = 'failed'") and //
|
||||
t_user_history.id > select(t_user_history.id) //
|
||||
.from(t_user_history) //
|
||||
.where(t_user_history.user_id == std::atoi(user.id().c_str()) and //
|
||||
t_user_history.action_id == select_login_action_id and //
|
||||
sqlpp::verbatim< sqlpp::boolean >(name_of(t_user_history.data) + "->>'status' = 'success'")) //
|
||||
.limit(1u)));
|
||||
return static_cast< int >(res.front().count);
|
||||
return _userAuth.failedLoginAttempts({user.id()});
|
||||
}
|
||||
|
||||
void PgUserAuth::setLastLoginAttempt(const User &, const Wt::WDateTime &) {}
|
||||
|
||||
Wt::WDateTime PgUserAuth::lastLoginAttempt(const User & user) const {
|
||||
using namespace std::chrono;
|
||||
const auto res =
|
||||
db(select(t_user_history._when) //
|
||||
.from(t_user_history) //
|
||||
.where(t_user_history.action_id == select_login_action_id and t_user_history.user_id == std::atoi(user.id().c_str())) //
|
||||
.order_by(t_user_history.id.desc()) //
|
||||
.limit(1u));
|
||||
if(res.empty())
|
||||
return {};
|
||||
|
||||
const auto time = res.front()._when.value();
|
||||
const auto systime = system_clock::to_time_t(time);
|
||||
const auto time = _userAuth.lastLoginAttempt({user.id()});
|
||||
const auto systime = std::chrono::system_clock::to_time_t(time);
|
||||
auto lastLogin = Wt::WDateTime();
|
||||
lastLogin.setTime_t(systime);
|
||||
return lastLogin;
|
||||
}
|
||||
|
||||
void PgUserAuth::logout(const User & user) {
|
||||
using namespace std::string_literals;
|
||||
const nlohmann::json data = {{"status"s, "success"}};
|
||||
|
||||
db(insert_into(t_user_history)
|
||||
.set(t_user_history.user_id = std::atoi(user.id().c_str()),
|
||||
t_user_history.action_id = select_logout_action_id, //
|
||||
t_user_history.data = data.dump()));
|
||||
_userAuth.logout({user.id()});
|
||||
}
|
||||
|
||||
AbstractUserDatabase::Transaction * PgUserAuth::startTransaction() {
|
||||
return new TransactionGuard< decltype(db) >(db, _in_transaction);
|
||||
// return new TransactionGuard< decltype(db) >(db, _in_transaction);
|
||||
}
|
||||
} // namespace eedb::auth
|
||||
|
||||
@ -2,12 +2,15 @@
|
||||
|
||||
#include <Wt/Auth/AbstractUserDatabase.h>
|
||||
|
||||
#include <eedb/db/data/PgUserAuth.hpp>
|
||||
|
||||
namespace eedb::db {
|
||||
class PgConnection;
|
||||
class PgUserAuth;
|
||||
}
|
||||
|
||||
namespace Wt {
|
||||
class WEnviroment;
|
||||
class WEnvironment;
|
||||
}
|
||||
|
||||
namespace Wt::Auth {
|
||||
@ -65,6 +68,7 @@ class PgUserAuth : public Wt::Auth::AbstractUserDatabase {
|
||||
|
||||
private:
|
||||
eedb::db::PgConnection & db;
|
||||
eedb::db::PgUserAuth _userAuth;
|
||||
const Wt::WEnvironment & _env;
|
||||
const Wt::Auth::AuthService * _authService;
|
||||
int _in_transaction{0};
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
set(SOURCE
|
||||
connection.cpp
|
||||
config.cpp
|
||||
)
|
||||
data/PgCategory.cpp
|
||||
data/PgUser.cpp
|
||||
data/PgUserAuth.cpp
|
||||
data/PgUsers.cpp
|
||||
|
||||
INCLUDE_DIRECTORIES( ${PostgreSQL_INCLUDE_DIRS} )
|
||||
connection.cpp
|
||||
config.cpp
|
||||
)
|
||||
|
||||
add_library(eedb_db ${SOURCE})
|
||||
target_link_libraries( eedb_db wt sqlpp-postgresql )
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#include <eedb/data/PgCategory.hpp>
|
||||
#include <eedb/db/data/PgCategory.hpp>
|
||||
|
||||
#include <eedb/db/model/category.h>
|
||||
#include <eedb/db/model/stat.h>
|
||||
@ -1,4 +1,4 @@
|
||||
#include <eedb/data/PgUser.hpp>
|
||||
#include <eedb/db/data/PgUser.hpp>
|
||||
|
||||
#include <eedb/db/connection.hpp>
|
||||
|
||||
452
src/eedb/db/data/PgUserAuth.cpp
Normal file
452
src/eedb/db/data/PgUserAuth.cpp
Normal file
@ -0,0 +1,452 @@
|
||||
#include <eedb/db/data/PgUserAuth.hpp>
|
||||
|
||||
#include <eedb/db/connection.hpp>
|
||||
#include <eedb/db/model/auth_identity.h>
|
||||
#include <eedb/db/model/auth_info.h>
|
||||
#include <eedb/db/model/auth_token.h>
|
||||
#include <eedb/db/model/user.h>
|
||||
#include <eedb/db/model/user_action.h>
|
||||
#include <eedb/db/model/user_audit.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <experimental/string_view>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <string>
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#include <boost/locale.hpp>
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <sqlpp11/sqlpp11.h>
|
||||
|
||||
using User = eedb::db::details::User;
|
||||
|
||||
constexpr const char eedb::user_audit_::Data::_alias_t::_literal[];
|
||||
constexpr const char eedb::user_audit::_alias_t::_literal[];
|
||||
|
||||
// enum LoginActions { Login, Logout };
|
||||
// static std::array< std::string_view, 2 > UserActionNames = {{"login", "logout"}};
|
||||
|
||||
std::string RandomString(uint len) {
|
||||
using namespace std;
|
||||
srand(static_cast< unsigned int >(time(nullptr)));
|
||||
experimental::string_view str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
string newstr;
|
||||
std::size_t pos;
|
||||
while(newstr.size() != len) {
|
||||
pos = static_cast< std::size_t >((rand() % (static_cast< int >(str.size()) - 1)));
|
||||
newstr += str.substr(pos, 1).to_string();
|
||||
}
|
||||
return newstr;
|
||||
}
|
||||
|
||||
namespace eedb::db {
|
||||
|
||||
namespace {
|
||||
constexpr eedb::user t_user;
|
||||
constexpr eedb::user_audit t_user_history;
|
||||
constexpr eedb::user_action t_user_action;
|
||||
constexpr eedb::auth_identity t_identity;
|
||||
constexpr eedb::auth_info t_info;
|
||||
constexpr eedb::auth_token t_auth_token;
|
||||
|
||||
static const auto auth_info_identity = t_info.join(t_identity).on(t_info.id == t_identity.auth_info_id);
|
||||
static const auto user_auth_info = t_user.join(t_info).on(t_info.user_uid == t_user.uid);
|
||||
static const auto auth_token_info = user_auth_info.join(t_auth_token).on(t_auth_token.auth_info_id == t_info.id);
|
||||
|
||||
static const auto select_login_action_id = select(t_user_action.id) //
|
||||
.from(t_user_action) //
|
||||
.where(t_user_action.name == "login") //
|
||||
.limit(1u);
|
||||
static const auto select_logout_action_id = select(t_user_action.id) //
|
||||
.from(t_user_action) //
|
||||
.where(t_user_action.name == "logout") //
|
||||
.limit(1u);
|
||||
} // namespace
|
||||
|
||||
PgUserAuth::PgUserAuth(PgConnection & _db) : db{_db} {}
|
||||
|
||||
User PgUserAuth::findWithId(const std::string & id) const {
|
||||
const auto uid_eq = t_user.uid == std::atoi(id.c_str());
|
||||
|
||||
if(db(select(exists(select(t_user.uid) //
|
||||
.from(t_user) //
|
||||
.where(uid_eq))))
|
||||
.front()
|
||||
.exists) {
|
||||
return User(id);
|
||||
}
|
||||
return User();
|
||||
}
|
||||
|
||||
User PgUserAuth::findWithIdentity(const std::string & provider, const std::string & identity) const {
|
||||
const auto identity_eq = t_identity.identity == identity;
|
||||
const auto provider_eq = t_identity.provider == provider;
|
||||
|
||||
const auto result = db(select(t_info.user_uid) //
|
||||
.from(auth_info_identity) //
|
||||
.where(provider_eq and identity_eq));
|
||||
|
||||
if(!result.empty()) {
|
||||
return User(std::to_string(result.front().user_uid));
|
||||
}
|
||||
return User();
|
||||
}
|
||||
|
||||
User PgUserAuth::findWithEmailToken(const std::string & hash) const {
|
||||
auto ret = db(select(t_user.uid) //
|
||||
.from(user_auth_info) //
|
||||
.where(t_info.email_token == hash));
|
||||
|
||||
if(ret.empty())
|
||||
return {};
|
||||
return {std::to_string(ret.front().uid)};
|
||||
}
|
||||
|
||||
User PgUserAuth::findWithEmail(const std::string & address) const {
|
||||
auto ret = db(select(t_info.user_uid) //
|
||||
.from(t_info) //
|
||||
.where(t_info.email == address and t_info.email_verified == true));
|
||||
|
||||
if(ret.empty())
|
||||
return {};
|
||||
return {std::to_string(ret.front().user_uid)};
|
||||
}
|
||||
|
||||
void PgUserAuth::addIdentity(const User & user, const std::string & provider, const std::string & identity) {
|
||||
const auto uid_eq = t_info.user_uid == std::atoi(user.id().c_str());
|
||||
const auto identity_eq = t_identity.identity == identity;
|
||||
const auto provider_eq = t_identity.provider == provider;
|
||||
|
||||
auto res = db(select(t_info.user_uid, t_info.id) //
|
||||
.from(auth_info_identity)
|
||||
.where(identity_eq and provider_eq and uid_eq));
|
||||
|
||||
if(!res.empty()) {
|
||||
// Wt::log("error") << "cannot add identity " << provider << ":'" << identity << "': already exists";
|
||||
return;
|
||||
}
|
||||
|
||||
auto auth_info_id = db(select(t_info.id)
|
||||
.from(t_info) //
|
||||
.where(uid_eq));
|
||||
|
||||
if(auth_info_id.empty()) {
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
db(insert_into(t_identity)
|
||||
.set(t_identity.identity = identity, //
|
||||
t_identity.provider = provider, //
|
||||
t_identity.auth_info_id = auth_info_id.front().id));
|
||||
}
|
||||
|
||||
void PgUserAuth::setIdentity(const User & user, const std::string & provider, const std::string & identity) {
|
||||
const auto uid = std::atoi(user.id().c_str());
|
||||
const auto provider_eq = t_identity.provider == provider;
|
||||
|
||||
db(update(t_identity) //
|
||||
.set(t_identity.identity = identity) //
|
||||
.where(t_identity.auth_info_id == select(t_info.id).from(t_info).where(t_info.user_uid == uid) and provider_eq));
|
||||
}
|
||||
|
||||
std::wstring PgUserAuth::identity(const User & user, const std::string & provider) const {
|
||||
const int uid = std::atoi(user.id().c_str());
|
||||
const auto id_eq = t_info.user_uid == uid;
|
||||
const auto provider_eq = t_identity.provider == provider;
|
||||
|
||||
auto res = db(select(t_identity.identity) //
|
||||
.from(auth_info_identity) //
|
||||
.where(id_eq and provider_eq));
|
||||
|
||||
return boost::locale::conv::utf_to_utf< wchar_t >(res.front().identity.text);
|
||||
}
|
||||
|
||||
void PgUserAuth::removeIdentity(const User & user, const std::string & provider) {
|
||||
const auto id_eq = t_identity.id == std::atoi(user.id().c_str());
|
||||
const auto provider_eq = t_identity.provider == provider;
|
||||
|
||||
db(remove_from(t_identity).where(id_eq and provider_eq));
|
||||
}
|
||||
|
||||
User PgUserAuth::registerNew() {
|
||||
auto user_id = db(sqlpp::postgresql::insert_into(t_user) //
|
||||
.set( //
|
||||
t_user.full_name = RandomString(256), //
|
||||
t_user.status = -1)
|
||||
.returning(t_user.uid))
|
||||
.front()
|
||||
.uid;
|
||||
|
||||
db(insert_into(t_info).set( //
|
||||
t_info.password_hash = "NONE", //
|
||||
t_info.password_method = "NONE", //
|
||||
t_info.password_salt = "NONE", //
|
||||
t_info.user_uid = user_id, //
|
||||
t_info.email = RandomString(20) + "@NONE.org"));
|
||||
|
||||
return User{std::to_string(user_id)};
|
||||
}
|
||||
|
||||
void PgUserAuth::deleteUser(const User & user) {
|
||||
db(remove_from(t_info) //
|
||||
.where(t_info.id == std::atoi(user.id().c_str())));
|
||||
}
|
||||
|
||||
// User::Status PgUserAuth::status(const User & user) const {
|
||||
// const auto id_eq = auth_identity.id == std::atoi(user.id().c_str());
|
||||
|
||||
// auto status = db(select(auth_info.status.as(user_status)) //
|
||||
// .from(join) //
|
||||
// .where(id_eq))
|
||||
// .front()
|
||||
// .user_status;
|
||||
// return status.value() == User::Status::Normal ? User::Status::Normal : User::Status::Disabled;
|
||||
//}
|
||||
|
||||
// void PgUserAuth::setStatus(const User & user, User::Status status) {
|
||||
// db(update(auth_info) //
|
||||
// .set(auth_info.status = static_cast< int >(status)) //
|
||||
// .where(auth_info.id == std::atoi(user.id().c_str())));
|
||||
//}
|
||||
|
||||
void PgUserAuth::setPassword(const User & user, details::PasswordHash password) {
|
||||
db(update(t_info)
|
||||
.set( //
|
||||
t_info.password_hash = password.mvalue(), //
|
||||
t_info.password_method = password.mfunction(), //
|
||||
t_info.password_salt = password.msalt())
|
||||
.where(t_info.user_uid == std::atoi(user.id().c_str())));
|
||||
}
|
||||
|
||||
std::optional<details::PasswordHash> PgUserAuth::password(const User & user) const {
|
||||
const auto id_eq = t_info.user_uid == std::atoi(user.id().c_str());
|
||||
|
||||
auto row = db(select(t_info.password_hash, t_info.password_method, t_info.password_salt)
|
||||
.from(t_info) //
|
||||
.where(id_eq));
|
||||
if(row.empty())
|
||||
return {};
|
||||
auto & front = row.front();
|
||||
return details::PasswordHash{std::move(front.password_method), std::move(front.password_salt), std::move(front.password_hash)};
|
||||
}
|
||||
|
||||
bool PgUserAuth::setEmail(const User & user, const std::string & address) {
|
||||
const auto uid_eq = t_info.user_uid == std::atoi(user.id().c_str());
|
||||
|
||||
db(update(t_info) //
|
||||
.set( //
|
||||
t_info.email = address,
|
||||
t_info.email_verified = true) //
|
||||
.where(uid_eq));
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string PgUserAuth::email(const User & user) const {
|
||||
auto ret = db(select(t_info.email) //
|
||||
.from(t_info) //
|
||||
.where(t_info.user_uid == std::atoi(user.id().c_str()) and t_info.email_verified == true));
|
||||
if(ret.empty())
|
||||
return {};
|
||||
return std::move(ret.front().email);
|
||||
}
|
||||
|
||||
void PgUserAuth::setUnverifiedEmail(const User & user, const std::string & address) {
|
||||
// orginal implementation of UserDatabase (provided by WT team) sets veryfied and unverified emails in
|
||||
// different fields in database. So in order to verify email, they just copy the unverified_email to email and then
|
||||
// set unverified_email to empty string (db don't allow empty strings as email*)
|
||||
if(address.empty())
|
||||
db(update(t_info) //
|
||||
.set(t_info.email_verified = true) //
|
||||
.where(t_info.user_uid == std::atoi(user.id().c_str())));
|
||||
else
|
||||
db(update(t_info) //
|
||||
.set( //
|
||||
t_info.email = address, //
|
||||
t_info.email_verified = false) //
|
||||
.where(t_info.user_uid == std::atoi(user.id().c_str())));
|
||||
}
|
||||
|
||||
std::string PgUserAuth::unverifiedEmail(const User & user) const {
|
||||
auto ret = db(select(t_info.email) //
|
||||
.from(t_info) //
|
||||
.where(t_info.user_uid == std::atoi(user.id().c_str()) and t_info.email_verified == false));
|
||||
if(ret.empty())
|
||||
return {};
|
||||
return std::move(ret.front().email);
|
||||
}
|
||||
|
||||
void PgUserAuth::setEmailToken(const User & user, details::Token token, int role) {
|
||||
db(update(t_info) //
|
||||
.set( //
|
||||
t_info.email_token = token.mhash(), //
|
||||
t_info.email_token_expires = token.expirationTime(),
|
||||
t_info.email_token_role = static_cast< int >(role)) //
|
||||
.where(t_info.user_uid == std::atoi(user.id().c_str())));
|
||||
}
|
||||
|
||||
std::optional< details::Token > PgUserAuth::emailToken(const User & user) const {
|
||||
auto ret = db(select(t_info.email_token, t_info.email_token_expires) //
|
||||
.from(t_info) //
|
||||
.where(t_info.user_uid == std::atoi(user.id().c_str())));
|
||||
if(ret.empty())
|
||||
return {};
|
||||
auto time = ret.front().email_token_expires.value();
|
||||
return details::Token{ret.front().email_token, time};
|
||||
}
|
||||
|
||||
int PgUserAuth::emailTokenRole(const User & user) const {
|
||||
auto ret = db(select(t_info.email_token_role) //
|
||||
.from(t_info) //
|
||||
.where(t_info.user_uid == std::atoi(user.id().c_str())));
|
||||
if(ret.empty())
|
||||
throw std::exception();
|
||||
auto val = ret.front().email_token_role;
|
||||
return val.value();
|
||||
}
|
||||
|
||||
void PgUserAuth::addAuthToken(const User & user, details::Token token) {
|
||||
auto select_identity_id = select(t_info.id) //
|
||||
.from(t_info)
|
||||
.where(t_info.user_uid == std::atoi(user.id().c_str()))
|
||||
.limit(1u);
|
||||
db(insert_into(t_auth_token) //
|
||||
.set( //
|
||||
t_auth_token.auth_info_id = select_identity_id, //
|
||||
t_auth_token.expires = std::chrono::system_clock::now() + ::sqlpp::chrono::days{14}, //
|
||||
t_auth_token.value = token.mhash()));
|
||||
}
|
||||
|
||||
void PgUserAuth::removeAuthToken(const User &, const std::string & hash) {
|
||||
db(remove_from(t_auth_token).where(t_auth_token.value == hash));
|
||||
}
|
||||
|
||||
std::optional<details::User> PgUserAuth::findWithAuthToken(const std::string & hash) const {
|
||||
auto fullUser = t_auth_token //
|
||||
.join(t_info)
|
||||
.on(t_info.id == t_auth_token.auth_info_id);
|
||||
|
||||
auto ret = db(select(t_info.user_uid) //
|
||||
.from(fullUser) //
|
||||
.where(t_auth_token.value == hash and t_auth_token.expires > std::chrono::system_clock::now()));
|
||||
|
||||
if(ret.empty())
|
||||
return {};
|
||||
return details::User{std::to_string(ret.front().user_uid)};
|
||||
}
|
||||
|
||||
int PgUserAuth::updateAuthToken(const User & user, const std::string & oldhash, const std::string & newhash) {
|
||||
// method called only after successful login
|
||||
using namespace std::chrono;
|
||||
using namespace std::string_literals;
|
||||
const auto identity_id = db(select(t_info.id) //
|
||||
.from(t_info) //
|
||||
.where(t_info.user_uid == std::atoi(user.id().c_str())));
|
||||
if(identity_id.empty())
|
||||
return 0;
|
||||
|
||||
const auto expires = db(sqlpp::postgresql::update(t_auth_token) //
|
||||
.set(t_auth_token.value = newhash)
|
||||
.where(t_auth_token.value == oldhash)
|
||||
.returning(t_auth_token.expires));
|
||||
|
||||
if(expires.empty())
|
||||
return 0;
|
||||
|
||||
const nlohmann::json data = {
|
||||
{"status"s, "success"}, //
|
||||
{"method"s, "token"}, //
|
||||
// {"user_address"s, _env.clientAddress()}, //
|
||||
// {"user_agent"s, _env.userAgent()}, //
|
||||
// {"user_locale"s, _env.locale().name()}, //
|
||||
// {"referer", _env.referer()}, //
|
||||
// {"url_scheme", _env.urlScheme()} //
|
||||
};
|
||||
|
||||
db(insert_into(t_user_history)
|
||||
.set(t_user_history.user_id = std::atoi(user.id().c_str()),
|
||||
t_user_history.action_id = select_login_action_id, //
|
||||
t_user_history.data = data.dump()));
|
||||
|
||||
const auto now = system_clock::now();
|
||||
const auto diff = expires.front().expires.value() - now;
|
||||
return duration_cast< duration< int > >(diff).count();
|
||||
}
|
||||
|
||||
void PgUserAuth::setFailedLoginAttempts(const User & user, int count) {
|
||||
using namespace std::string_literals;
|
||||
const auto getStatus = [count]() { return count ? "failed"s : "success"s; };
|
||||
|
||||
const nlohmann::json data = {
|
||||
{"status"s, getStatus()}, //
|
||||
{"method"s, "password"}, //
|
||||
// {"user_address"s, _env.clientAddress()}, //
|
||||
// {"user_agent"s, _env.userAgent()}, //
|
||||
// {"user_locale"s, _env.locale().name()}, //
|
||||
// {"referer", _env.referer()}, //
|
||||
// {"url_scheme", _env.urlScheme()} //
|
||||
};
|
||||
|
||||
db(insert_into(t_user_history)
|
||||
.set(t_user_history.user_id = std::atoi(user.id().c_str()),
|
||||
t_user_history.action_id = select_login_action_id, //
|
||||
t_user_history.data = data.dump()));
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
inline std::string name_of(const T &) {
|
||||
static_assert(sqlpp::is_column_t< T >::value, "T should by a calumn");
|
||||
return std::string{T::_table::_alias_t::_literal} + '.' + T::_alias_t::_literal;
|
||||
}
|
||||
|
||||
int PgUserAuth::failedLoginAttempts(const User & user) const {
|
||||
const auto res =
|
||||
db(select(count(t_user_history.id)) //
|
||||
.from(t_user_history) //
|
||||
.where(t_user_history.user_id == std::atoi(user.id().c_str()) and //
|
||||
t_user_history.action_id == select_login_action_id and //
|
||||
sqlpp::verbatim< sqlpp::boolean >(name_of(t_user_history.data) + "->>'status' = 'failed'") and //
|
||||
t_user_history.id > select(t_user_history.id) //
|
||||
.from(t_user_history) //
|
||||
.where(t_user_history.user_id == std::atoi(user.id().c_str()) and //
|
||||
t_user_history.action_id == select_login_action_id and //
|
||||
sqlpp::verbatim< sqlpp::boolean >(name_of(t_user_history.data) + "->>'status' = 'success'")) //
|
||||
.limit(1u)));
|
||||
return static_cast< int >(res.front().count);
|
||||
}
|
||||
|
||||
void PgUserAuth::setLastLoginAttempt(const User &, PgUserAuth::time_point) {}
|
||||
|
||||
PgUserAuth::time_point PgUserAuth::lastLoginAttempt(const User & user) const {
|
||||
using namespace std::chrono;
|
||||
const auto res =
|
||||
db(select(t_user_history._when) //
|
||||
.from(t_user_history) //
|
||||
.where(t_user_history.action_id == select_login_action_id and t_user_history.user_id == std::atoi(user.id().c_str())) //
|
||||
.order_by(t_user_history.id.desc()) //
|
||||
.limit(1u));
|
||||
if(res.empty())
|
||||
return {};
|
||||
|
||||
return res.front()._when.value();
|
||||
}
|
||||
|
||||
void PgUserAuth::logout(const User & user) {
|
||||
using namespace std::string_literals;
|
||||
const nlohmann::json data = {{"status"s, "success"}};
|
||||
|
||||
db(insert_into(t_user_history)
|
||||
.set(t_user_history.user_id = std::atoi(user.id().c_str()),
|
||||
t_user_history.action_id = select_logout_action_id, //
|
||||
t_user_history.data = data.dump()));
|
||||
}
|
||||
|
||||
//AbstractUserDatabase::Transaction * PgUserAuth::startTransaction() {
|
||||
// return new TransactionGuard< decltype(db) >(db, _in_transaction);
|
||||
//}
|
||||
} // namespace eedb::db
|
||||
125
src/eedb/db/data/PgUserAuth.hpp
Normal file
125
src/eedb/db/data/PgUserAuth.hpp
Normal file
@ -0,0 +1,125 @@
|
||||
#pragma once
|
||||
|
||||
#include <eedb/db/connection.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace eedb::db {
|
||||
namespace details {
|
||||
class User {
|
||||
public:
|
||||
User(std::string id = {}) : _id{std::move(id)} {}
|
||||
const std::string & id() const {
|
||||
return _id;
|
||||
}
|
||||
|
||||
std::string && moveid() {
|
||||
return std::move(_id);
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return _id != "";
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _id;
|
||||
};
|
||||
|
||||
class PasswordHash {
|
||||
public:
|
||||
PasswordHash(std::string function, std::string salt, std::string value)
|
||||
: function_{std::move(function)}, salt_{std::move(salt)}, value_{std::move(value)} {}
|
||||
|
||||
bool empty() const {
|
||||
return value_.empty();
|
||||
}
|
||||
|
||||
std::string && mfunction() {
|
||||
return std::move(function_);
|
||||
}
|
||||
|
||||
std::string && msalt() {
|
||||
return std::move(salt_);
|
||||
}
|
||||
|
||||
std::string && mvalue() {
|
||||
return std::move(value_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string function_, salt_, value_;
|
||||
};
|
||||
|
||||
class Token {
|
||||
public:
|
||||
using time_point = std::chrono::time_point< std::chrono::system_clock, std::chrono::microseconds >;
|
||||
Token(std::string hash, time_point expirationTime) : hash_{std::move(hash)}, expirationTime_{expirationTime} {}
|
||||
|
||||
bool empty() const {
|
||||
return hash_.empty();
|
||||
}
|
||||
|
||||
std::string && mhash() {
|
||||
return std::move(hash_);
|
||||
}
|
||||
|
||||
time_point expirationTime() const {
|
||||
return expirationTime_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string hash_;
|
||||
time_point expirationTime_;
|
||||
};
|
||||
} // namespace details
|
||||
|
||||
class PgUserAuth {
|
||||
public:
|
||||
using time_point = std::chrono::time_point< std::chrono::system_clock, std::chrono::microseconds >;
|
||||
|
||||
PgUserAuth(PgConnection & db);
|
||||
|
||||
details::User findWithId(const std::string & id) const;
|
||||
details::User findWithIdentity(const std::string & provider, const std::string & identity) const;
|
||||
details::User findWithEmailToken(const std::string & hash) const;
|
||||
details::User findWithEmail(const std::string & address) const;
|
||||
|
||||
void addIdentity(const details::User & user, const std::string & provider, const std::string & id);
|
||||
void setIdentity(const details::User & user, const std::string & provider, const std::string & id);
|
||||
std::wstring identity(const details::User & user, const std::string & provider) const;
|
||||
void removeIdentity(const details::User & user, const std::string & provider);
|
||||
|
||||
details::User registerNew();
|
||||
void deleteUser(const details::User & user);
|
||||
|
||||
void setPassword(const details::User & user, details::PasswordHash password);
|
||||
std::optional< details::PasswordHash > password(const details::User & user) const;
|
||||
|
||||
bool setEmail(const details::User & user, const std::string & address);
|
||||
std::string email(const details::User & user) const;
|
||||
void setUnverifiedEmail(const details::User & user, const std::string & address);
|
||||
std::string unverifiedEmail(const details::User & user) const;
|
||||
|
||||
void setEmailToken(const details::User & user, details::Token token, int role);
|
||||
std::optional<details::Token> emailToken(const details::User & user) const;
|
||||
int emailTokenRole(const details::User & user) const;
|
||||
|
||||
void addAuthToken(const details::User & user, details::Token token);
|
||||
void removeAuthToken(const details::User &user, const std::string & hash);
|
||||
std::optional<details::User> findWithAuthToken(const std::string & hash) const;
|
||||
int updateAuthToken(const details::User & user, const std::string & oldhash, const std::string & newhash);
|
||||
|
||||
void setFailedLoginAttempts(const details::User & user, int count);
|
||||
int failedLoginAttempts(const details::User & user) const;
|
||||
void setLastLoginAttempt(const details::User & user, time_point t);
|
||||
time_point lastLoginAttempt(const details::User & user) const;
|
||||
|
||||
void logout(const details::User & user);
|
||||
|
||||
private:
|
||||
eedb::db::PgConnection & db;
|
||||
// const Wt::WEnvironment & _env;
|
||||
// const Wt::Auth::AuthService * _authService;
|
||||
// int _in_transaction{0};
|
||||
};
|
||||
} // namespace eedb::db
|
||||
@ -1,5 +1,5 @@
|
||||
#include <eedb/data/PgUser.hpp>
|
||||
#include <eedb/data/PgUsers.hpp>
|
||||
#include <eedb/db/data/PgUser.hpp>
|
||||
#include <eedb/db/data/PgUsers.hpp>
|
||||
|
||||
#include <eedb/db/model/auth_identity.h>
|
||||
#include <eedb/db/model/auth_info.h>
|
||||
@ -1,6 +1,6 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <eedb/data/PgCategory.hpp>
|
||||
#include <eedb/db/data/PgCategory.hpp>
|
||||
|
||||
class PgCategoriesTest : public testing::Test {
|
||||
public:
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <eedb/data/PgCategory.hpp>
|
||||
#include <eedb/db/data/PgCategory.hpp>
|
||||
|
||||
#include <eedb/db/config.hpp>
|
||||
#include <eedb/db/connection.hpp>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user