simplify sql model
This commit is contained in:
parent
a5a3893415
commit
b0cd59af12
153
sql/schema.sql
153
sql/schema.sql
@ -80,41 +80,26 @@ BEGIN
|
||||
end $$ -- $$
|
||||
language 'plpgsql'; -- $$
|
||||
|
||||
|
||||
create table "user" (
|
||||
"uid" serial not null,
|
||||
create table "auth_info" (
|
||||
"id" serial not null,
|
||||
"group" int not null default default_users_group_id(),
|
||||
"status" integer DEFAULT 0 NOT NULL,
|
||||
"full_name" text not null,
|
||||
"created" timestamp DEFAULT now() not null,
|
||||
"updated" timestamp,
|
||||
"config" json not null DEFAULT ('{}'),
|
||||
constraint "pk_user_uid" primary key ("uid")
|
||||
);
|
||||
create trigger "update_user_updated" before update on "user" FOR EACH ROW EXECUTE PROCEDURE last_update_column();
|
||||
comment on table "user" is '';
|
||||
comment on column "user"."uid" is '';
|
||||
comment on column "user"."group" is '';
|
||||
comment on column "user"."full_name" is '';
|
||||
comment on column "user"."created" is '';
|
||||
comment on column "user"."config" is '';
|
||||
|
||||
|
||||
create table "auth_info" (
|
||||
"id" serial not null,
|
||||
"user_uid" integer,
|
||||
"password_hash" varchar(100) not null,
|
||||
"password_method" varchar(20) not null,
|
||||
"password_salt" varchar(20) not null,
|
||||
"email" varchar(256) not null,
|
||||
"email_verified" boolean not null default false,
|
||||
"email_token" varchar(64),
|
||||
"email_token_expires" timestamp,
|
||||
"email_token_role" integer,
|
||||
"status" integer DEFAULT 0 NOT NULL,
|
||||
constraint "pk_auth_into_id" primary key("id"),
|
||||
constraint "fk_auth_info_user_uid" foreign key ("user_uid") references "user" ("uid") on delete cascade deferrable initially deferred,
|
||||
constraint "ux_auth_info_email" unique ("email")
|
||||
);
|
||||
create trigger "update_auth_info_updated" before update on "auth_info" FOR EACH ROW EXECUTE PROCEDURE last_update_column();
|
||||
comment on table "auth_info" is '';
|
||||
comment on column "auth_info"."id" is '';
|
||||
comment on column "auth_info"."group" is '';
|
||||
comment on column "auth_info"."created" is '';
|
||||
comment on column "auth_info"."config" is '';
|
||||
|
||||
|
||||
create table "auth_identity" (
|
||||
@ -137,6 +122,7 @@ create table "auth_token" (
|
||||
"auth_info_id" bigint,
|
||||
"value" varchar(64) not null,
|
||||
"expires" timestamp not null,
|
||||
"role" smallint not null,
|
||||
constraint "pk_auth_token_id" primary key("id"),
|
||||
constraint "fk_auth_token_auth_info_id" foreign key ("auth_info_id") references "auth_info" ("id") on delete cascade deferrable initially deferred
|
||||
);
|
||||
@ -148,7 +134,6 @@ comment on column "auth_token"."value" is '';
|
||||
comment on column "auth_token"."expires" is '';
|
||||
|
||||
|
||||
|
||||
create table "user_audit_action" (
|
||||
"id" serial not null,
|
||||
"name" text,
|
||||
@ -163,18 +148,18 @@ insert into "user_audit_action"("name") values ('login'), ('logout');
|
||||
|
||||
create table "user_audit" (
|
||||
"id" serial not null,
|
||||
"user_id" int not null,
|
||||
"auth_info_id" int not null,
|
||||
"action_id" int not null,
|
||||
"data" jsonb,
|
||||
"when_happened" timestamp DEFAULT(now()),
|
||||
constraint "pk_user_history_id" primary key ("id"),
|
||||
constraint "fk_user_history_user_uid" foreign key ("user_id") references "user"("uid") on delete cascade deferrable initially deferred,
|
||||
constraint "fk_user_history_auth_info_id" foreign key ("auth_info_id") references "auth_info"("id") on delete cascade deferrable initially deferred,
|
||||
constraint "fk_user_history_user_action" foreign key ("action_id") references "user_audit_action"("id") on delete cascade deferrable initially deferred
|
||||
);
|
||||
create index "ix_user_history_data" ON "user_audit" ((("data" ->> 'status')::text)) WHERE ("data" ->> 'status') IS not null;
|
||||
comment on table "user_audit" IS 'saves user actions like login/logout';
|
||||
comment on column "user_audit"."id" is '';
|
||||
comment on column "user_audit"."user_id" is '';
|
||||
comment on column "user_audit"."auth_info_id" is '';
|
||||
comment on column "user_audit"."action_id" is '';
|
||||
comment on column "user_audit"."data" is 'data column contains information about taken action (if login was successful? if not, from what ip this action was taken?)';
|
||||
comment on column "user_audit"."when_happened" is '';
|
||||
@ -198,7 +183,7 @@ create table "stat"(
|
||||
"created" timestamp DEFAULT now() not null,
|
||||
"updated" timestamp,
|
||||
constraint "pk_stat" primary key ("id"),
|
||||
constraint "fk_stat_user" foreign key ("owner") references "user" ("uid") on delete cascade deferrable initially deferred,
|
||||
constraint "fk_stat_user" foreign key ("owner") references "auth_info" ("id") on delete cascade deferrable initially deferred,
|
||||
constraint "fk_stat_primary_group" foreign key ("group") references "group" ("gid") on delete cascade deferrable initially deferred
|
||||
);
|
||||
create trigger update_stat_last_update before update on stat FOR EACH ROW EXECUTE PROCEDURE last_update_column();
|
||||
@ -280,7 +265,7 @@ create table "category"(
|
||||
"parent_path" ltree,
|
||||
constraint "pk_category_uid" primary key ("id"),
|
||||
constraint "fk_category_parent_id" foreign key ("parent_id") references "category"("id") on delete cascade deferrable initially deferred,
|
||||
constraint "fk_category_stat_owner" foreign key ("owner") references "user"("uid") deferrable initially immediate
|
||||
constraint "fk_category_stat_owner" foreign key ("owner") references "auth_info"("id") deferrable initially immediate
|
||||
) INHERITS (stat);
|
||||
create index "ix_category_parent_path" on "category" using GIST ("parent_path");
|
||||
create unique index "ux_category_name" on "category" ( "parent_id", "name" );
|
||||
@ -366,7 +351,7 @@ create table "item"(
|
||||
"description" TEXT,
|
||||
constraint "pk_items" primary key ("id"),
|
||||
constraint "fk_item_category" foreign key ("category_id") references "category"("id") on delete cascade deferrable initially deferred,
|
||||
constraint "fk_item_user" foreign key ("owner") REFERENCES "user"("uid") deferrable initially IMMEDIATE
|
||||
constraint "fk_item_auth_info" foreign key ("owner") REFERENCES "auth_info"("id") deferrable initially IMMEDIATE
|
||||
) INHERITS (stat);
|
||||
create index "ix_item_attributes" on "item" USING GIN ("attributes");
|
||||
create unique index "ux_item" on "item"("name", "symbol");
|
||||
@ -384,16 +369,16 @@ comment on column "item"."description" is '';
|
||||
create table "inventory"(
|
||||
"description" TEXT check(length(description)< 100000),
|
||||
constraint "pk_inventory" primary key ("id"),
|
||||
constraint "fk_inventory_owner" foreign key ("owner") REFERENCES "user" ("uid") deferrable initially IMMEDIATE,
|
||||
constraint "fk_inventory_owner" foreign key ("owner") REFERENCES "auth_info" ("id") deferrable initially IMMEDIATE,
|
||||
constraint "chk_inventory_name_length" check (length(name) < 100)
|
||||
) INHERITS (stat);
|
||||
create trigger update_inventory_last_update before update on inventory FOR EACH ROW EXECUTE PROCEDURE last_update_column();
|
||||
|
||||
|
||||
create table "user_inventory"(
|
||||
"user_id" INTEGER not null REFERENCES "user" on DELETE CASCADE,
|
||||
"auth_info_id" INTEGER not null REFERENCES "auth_info" on DELETE CASCADE,
|
||||
"inventory_id" INTEGER not null REFERENCES "inventory" on DELETE CASCADE,
|
||||
constraint user_inventory_pk primary key ("inventory_id", "user_id")
|
||||
constraint user_inventory_pk primary key ("inventory_id", "auth_info_id")
|
||||
);
|
||||
|
||||
|
||||
@ -435,106 +420,6 @@ create table "user_inventory"(
|
||||
-- create trigger update_shelfs_last_update before update on shelfs FOR EACH ROW EXECUTE PROCEDURE last_update_column();
|
||||
-- create trigger update_inventory_operations_lats_update before update on inventory_operations FOR EACH ROW EXECUTE PROCEDURE last_update_column();
|
||||
|
||||
|
||||
create OR REPLACE function objects_with_action (m_tab VARCHAR, m_action varchar, userid int)
|
||||
RETURNS setof int AS $$
|
||||
|
||||
DECLARE r int;
|
||||
DECLARE usergroups INT;
|
||||
DECLARE groupsroot INT;
|
||||
DECLARE tablename VARCHAR(255);
|
||||
|
||||
BEGIN
|
||||
SELECT "group"
|
||||
FROM "user"
|
||||
WHERE uid = userid
|
||||
INTO usergroups;
|
||||
|
||||
groupsroot := 1;
|
||||
|
||||
FOR
|
||||
r IN
|
||||
execute 'select distinct obj.uid
|
||||
from ' || m_tab ||' as obj
|
||||
inner join implemented_action as ia
|
||||
on ia.table_name ='''|| m_tab || '''
|
||||
and ia.action = '''|| m_action ||'''
|
||||
and ((ia.status = 0) or (ia.status & obj.status <> 0))
|
||||
inner join action as ac
|
||||
on ac.title = '''|| m_action ||'''
|
||||
left outer join privilege as pr
|
||||
on pr.related_table_name = '''|| m_tab || '''
|
||||
and pr.action = '''|| m_action ||'''
|
||||
and (
|
||||
(pr.type = ''object'' and pr.related_object_uid = obj.uid)
|
||||
or pr.type = ''global''
|
||||
or (pr.role = ''self'' and ' || userid || ' = obj.uid and '''|| m_tab || ''' = ''users''))
|
||||
WHERE ac.apply_object
|
||||
AND (
|
||||
(' || usergroups || ' & ' || groupsroot || ' <> 0)
|
||||
OR (
|
||||
ac.title = ''read''
|
||||
AND (
|
||||
(obj.unixperms & 4 <> 0)
|
||||
OR (
|
||||
(obj.unixperms & 256 <> 0)
|
||||
AND obj.owner = ' || userid || '
|
||||
)
|
||||
OR (
|
||||
(obj.unixperms & 32 <> 0)
|
||||
AND (' || usergroups || ' & obj.group <> 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
OR (
|
||||
ac.title = ''write''
|
||||
AND (
|
||||
(obj.unixperms & 2 <> 0)
|
||||
OR (
|
||||
(obj.unixperms & 128 <> 0)
|
||||
AND obj.owner = ' || userid || '
|
||||
)
|
||||
OR (
|
||||
(obj.unixperms & 16 <> 0)
|
||||
AND (' || usergroups || ' & obj.group <> 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
OR (
|
||||
ac.title = ''delete''
|
||||
AND (
|
||||
(obj.unixperms & 1 <> 0)
|
||||
OR ((obj.unixperms & 64 <> 0) AND obj.owner = ' || userid || ')
|
||||
OR ((obj.unixperms & 8 <> 0) AND (' || usergroups || ' & obj.group <> 0))
|
||||
)
|
||||
)
|
||||
OR (
|
||||
pr.role = ''user''
|
||||
AND pr.who = ' || userid || '
|
||||
)
|
||||
OR (
|
||||
pr.role = ''owner''
|
||||
AND obj.owner = ' || userid || '
|
||||
)
|
||||
OR (
|
||||
pr.role = ''owner_group''
|
||||
AND (obj.group & ' || usergroups || ' <> 0)
|
||||
)
|
||||
OR (
|
||||
pr.role = ''group''
|
||||
AND (pr.who & ' || usergroups || ' <> 0)
|
||||
)
|
||||
)
|
||||
OR pr.role = ''self'' '
|
||||
|
||||
LOOP
|
||||
RETURN NEXT r;
|
||||
end loop;
|
||||
END $$
|
||||
LANGUAGE plpgsql ;
|
||||
|
||||
-- insert needed data into dataase
|
||||
|
||||
DO
|
||||
$$
|
||||
DECLARE lastid int;
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#include <eedb/auth/Services.hpp>
|
||||
#include <eedb/data/Users.hpp>
|
||||
#include <eedb/data/AuthIdentity.hpp>
|
||||
#include <eedb/data/AuthToken.hpp>
|
||||
|
||||
#include <Wt/Auth/AuthService.h>
|
||||
#include <Wt/Auth/Dbo/AuthInfo.h>
|
||||
@ -41,13 +42,37 @@ struct TransactionGuard : public Wt::Auth::AbstractUserDatabase::Transaction {
|
||||
|
||||
namespace eedb::auth {
|
||||
|
||||
PgUserAuth::PgUserAuth(std::unique_ptr<eedb::Users> users, const Wt::WEnvironment & env) : _env{env} {
|
||||
struct PgUserAuth::UserAuthPriv {
|
||||
UserAuthPriv(std::unique_ptr< eedb::Users > users, const Wt::WEnvironment & env) : _users{std::move(users)}, _env{env} {
|
||||
this->setAuthService(eedb::auth::Services::authService());
|
||||
}
|
||||
|
||||
void setAuthService(Wt::Auth::AuthService * s) {
|
||||
_authService = s;
|
||||
}
|
||||
|
||||
std::unique_ptr< eedb::Users > _users;
|
||||
|
||||
std::optional< std::string > _email;
|
||||
std::optional< std::string > _unverifiedEmail;
|
||||
std::optional< std::string > _emailToken;
|
||||
std::optional< std::chrono::time_point<std::chrono::system_clock, std::chrono::microseconds > > _emailTokenExpire;
|
||||
std::optional< Wt::Auth::PasswordHash > _password;
|
||||
std::optional< std::string > _identity;
|
||||
std::optional< int > _emailTokenRole;
|
||||
|
||||
const Wt::WEnvironment & _env;
|
||||
const Wt::Auth::AuthService * _authService;
|
||||
int _in_transaction{0};
|
||||
bool _registrationStarted{false};
|
||||
};
|
||||
|
||||
PgUserAuth::PgUserAuth(std::unique_ptr< eedb::Users > users, const Wt::WEnvironment & env)
|
||||
: _priv{spimpl::make_unique_impl< PgUserAuth::UserAuthPriv >(std::move(users), env)} {}
|
||||
|
||||
Wt::Auth::User PgUserAuth::findWithId(const std::string & id) const {
|
||||
spdlog::get("default")->debug("searching user by id: '{}'...", id);
|
||||
auto duser = users->findWith(std::atoi(id.c_str()));
|
||||
auto duser = _priv->_users->findWith(std::atoi(id.c_str()));
|
||||
|
||||
if(duser) {
|
||||
spdlog::get("default")->trace("\tuser exists");
|
||||
@ -60,11 +85,11 @@ Wt::Auth::User PgUserAuth::findWithId(const std::string & id) const {
|
||||
Wt::Auth::User PgUserAuth::findWithIdentity(const std::string & provider, const Wt::WString & identity) const {
|
||||
spdlog::get("default")->debug("searching user by identity/provider: '{}'/'{}'...", identity.toUTF8(), provider);
|
||||
auto _identity = identity.toUTF8();
|
||||
if(_authService && _authService->identityPolicy() == Wt::Auth::IdentityPolicy::EmailAddress) {
|
||||
if(_priv->_authService && _priv->_authService->identityPolicy() == Wt::Auth::IdentityPolicy::EmailAddress) {
|
||||
std::transform(_identity.begin(), _identity.end(), _identity.begin(), ::tolower);
|
||||
}
|
||||
|
||||
auto duser = users->findWith(eedb::ConstAuthIdentity{_identity, provider});
|
||||
auto duser = _priv->_users->findWith(eedb::ConstAuthIdentity{_identity, provider});
|
||||
if(duser) {
|
||||
spdlog::get("default")->trace("\tuser exists");
|
||||
return Wt::Auth::User(std::to_string(duser->uid()), *this);
|
||||
@ -76,7 +101,7 @@ Wt::Auth::User PgUserAuth::findWithIdentity(const std::string & provider, const
|
||||
Wt::Auth::User PgUserAuth::findWithEmailToken(const std::string & hash) const {
|
||||
spdlog::get("default")->debug("searching user by email token: '{}'...", hash);
|
||||
|
||||
auto duser = users->findWith(eedb::EmailToken{hash});
|
||||
auto duser = _priv->_users->findWith(eedb::EmailToken{hash});
|
||||
if(duser) {
|
||||
spdlog::get("default")->trace("\tuser exists");
|
||||
return Wt::Auth::User(std::to_string(duser->uid()), *this);
|
||||
@ -88,7 +113,7 @@ Wt::Auth::User PgUserAuth::findWithEmailToken(const std::string & hash) const {
|
||||
Wt::Auth::User PgUserAuth::findWithEmail(const std::string & address) const {
|
||||
spdlog::get("default")->debug("searching user by email: '{}'...", address);
|
||||
|
||||
auto duser = users->findWith(eedb::Email{address});
|
||||
auto duser = _priv->_users->findWith(eedb::Email{address});
|
||||
if(duser) {
|
||||
spdlog::get("default")->trace("\tuser exists");
|
||||
return Wt::Auth::User(std::to_string(duser->uid()), *this);
|
||||
@ -100,7 +125,7 @@ Wt::Auth::User PgUserAuth::findWithEmail(const std::string & address) const {
|
||||
// @registration
|
||||
void PgUserAuth::addIdentity(const Wt::Auth::User & user, const std::string & provider, const Wt::WString & identity) {
|
||||
spdlog::get("default")->debug("addind identity/provider: '{}'/'{}' for user_id'{}'...", identity.toUTF8(), provider, user.id());
|
||||
auto u = users->findWith(std::atoi(user.id().c_str()));
|
||||
auto u = _priv->_users->findWith(std::atoi(user.id().c_str()));
|
||||
u->authIdentities().addIdentity(std::make_unique< eedb::ConstAuthIdentity >(identity.toUTF8(), provider));
|
||||
}
|
||||
|
||||
@ -111,7 +136,7 @@ void PgUserAuth::addIdentity(const Wt::Auth::User & user, const std::string & pr
|
||||
|
||||
Wt::WString PgUserAuth::identity(const Wt::Auth::User & user, const std::string & provider) const {
|
||||
spdlog::get("default")->trace("user{} identity: '{}'", provider);
|
||||
auto opt_identity = users->findWith(std::atoi(user.id().c_str()))->authIdentities().findByProvider(provider);
|
||||
auto opt_identity = _priv->_users->findWith(std::atoi(user.id().c_str()))->authIdentities().byProvider(provider);
|
||||
|
||||
if(opt_identity)
|
||||
return Wt::WString::fromUTF8(std::string{opt_identity.value()->identity()});
|
||||
@ -120,16 +145,18 @@ Wt::WString PgUserAuth::identity(const Wt::Auth::User & user, const std::string
|
||||
|
||||
void PgUserAuth::removeIdentity(const Wt::Auth::User & user, const std::string & provider) {
|
||||
spdlog::get("default")->trace("user{} remove provider: '{}'", user.id(), provider);
|
||||
users->findWith(std::atoi(user.id().c_str()))->authIdentities().removeProvider(provider);
|
||||
_priv->_users->findWith(std::atoi(user.id().c_str()))->authIdentities().removeProvider(provider);
|
||||
}
|
||||
|
||||
Wt::Auth::User PgUserAuth::registerNew() {
|
||||
spdlog::get("default")->debug("registering new user");
|
||||
_priv->_registrationStarted = true;
|
||||
}
|
||||
|
||||
void PgUserAuth::deleteUser(const Wt::Auth::User & user) {
|
||||
spdlog::get("default")->debug("delete user {}", user.id());
|
||||
throw std::runtime_error("not implemented void PgUserAuth::deleteUser(const Wt::Auth::User & user)");
|
||||
// _priv->_users->removeUser(eedb::ConstAuthIdentity);
|
||||
// _userAuth.deleteUser({user.id()});
|
||||
}
|
||||
|
||||
@ -153,48 +180,57 @@ void PgUserAuth::deleteUser(const Wt::Auth::User & user) {
|
||||
// @registration
|
||||
void PgUserAuth::setPassword(const Wt::Auth::User & user, const PasswordHash & password) {
|
||||
spdlog::get("default")->trace("user{} set password", user.id());
|
||||
_password = password;
|
||||
_priv->_password = password;
|
||||
}
|
||||
|
||||
PasswordHash PgUserAuth::password(const Wt::Auth::User & user) const {
|
||||
spdlog::get("default")->trace("user{} get password", user.id());
|
||||
auto password = users->findWith(std::atoi(user.id().c_str()))->authInfo().password();
|
||||
auto password = _priv->_users->findWith(std::atoi(user.id().c_str()))->authInfo().password();
|
||||
|
||||
return {password.function(), password.salt(), password.value()};
|
||||
}
|
||||
|
||||
bool PgUserAuth::setEmail(const Wt::Auth::User & user, const std::string & address) {
|
||||
// spdlog::get("default")->trace("user{} set email {}", user.id(), address);
|
||||
// return _userAuth.setEmail({user.id()}, address);
|
||||
spdlog::get("default")->trace("user{} set email {}", user.id(), address);
|
||||
if(_priv->_users->findWith(eedb::Email{address}))
|
||||
return false; //user with given email exists
|
||||
|
||||
_priv->_email = address;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string PgUserAuth::email(const Wt::Auth::User & user) const {
|
||||
// spdlog::get("default")->trace("user{} get email", user.id());
|
||||
// return _userAuth.email({user.id()});
|
||||
spdlog::get("default")->trace("user{} get email", user.id());
|
||||
return std::string{_priv->_users->findWith(std::atoi(user.id().c_str()))->authInfo().email().address()};
|
||||
}
|
||||
|
||||
// @registration
|
||||
void PgUserAuth::setUnverifiedEmail(const Wt::Auth::User & user, const std::string & address) {
|
||||
// spdlog::get("default")->trace("user{} set unverified email {}", user.id(), address);
|
||||
// _userAuth.setUnverifiedEmail({user.id()}, address);
|
||||
spdlog::get("default")->trace("user{} set unverified email {}", user.id(), address);
|
||||
_priv->_unverifiedEmail = address;
|
||||
}
|
||||
|
||||
// @registration
|
||||
void PgUserAuth::setEmailToken(const Wt::Auth::User & user, const Token & token, EmailTokenRole role) {
|
||||
// spdlog::get("default")->trace("user{} set email token {}", user.id(), token.hash());
|
||||
// auto exp = ::date::floor<::std::chrono::milliseconds >(std::chrono::system_clock::from_time_t(token.expirationTime().toTime_t()));
|
||||
// _userAuth.setEmailToken({user.id()}, {token.hash(), exp}, static_cast< int >(role));
|
||||
using namespace std::chrono;
|
||||
spdlog::get("default")->trace("user{} set email token {}", user.id(), token.hash());
|
||||
auto exp = time_point_cast< microseconds >(system_clock::from_time_t(token.expirationTime().toTime_t()));
|
||||
_priv->_emailToken = token.hash();
|
||||
_priv->_emailTokenExpire = exp;
|
||||
_priv->_emailTokenRole = static_cast< int >(role);
|
||||
}
|
||||
|
||||
// @registration
|
||||
std::string PgUserAuth::unverifiedEmail(const Wt::Auth::User & user) const {
|
||||
// spdlog::get("default")->trace("user{} get unverified email", user.id());
|
||||
// return _userAuth.unverifiedEmail({user.id()});
|
||||
spdlog::get("default")->trace("user{} get unverified email", user.id());
|
||||
assert(_priv->_unverifiedEmail.has_value());
|
||||
return _priv->_unverifiedEmail.value();
|
||||
}
|
||||
|
||||
Token PgUserAuth::emailToken(const Wt::Auth::User & user) const {
|
||||
// spdlog::get("default")->trace("user{} get email token", user.id());
|
||||
// auto tok = _userAuth.emailToken({user.id()});
|
||||
spdlog::get("default")->trace("user{} get email token", user.id());
|
||||
auto tok = _priv->_users->findWith(std::atoi(user.id().c_str()))->authTokens().find(AuthTokenRole::EmailToken);
|
||||
// .emailToken({user.id()});
|
||||
// if(!tok)
|
||||
// return {};
|
||||
// auto exp = Wt::WDateTime();
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
|
||||
#include <Wt/Auth/AbstractUserDatabase.h>
|
||||
|
||||
#include <utils/spimpl.hpp>
|
||||
|
||||
namespace eedb {
|
||||
class Users;
|
||||
}
|
||||
@ -18,10 +20,6 @@ namespace eedb::auth {
|
||||
|
||||
class PgUserAuth : public Wt::Auth::AbstractUserDatabase {
|
||||
public:
|
||||
void setAuthService(Wt::Auth::AuthService * s) {
|
||||
_authService = s;
|
||||
}
|
||||
|
||||
PgUserAuth(std::unique_ptr< eedb::Users > users, const Wt::WEnvironment & env);
|
||||
|
||||
Transaction * startTransaction() override;
|
||||
@ -64,15 +62,7 @@ class PgUserAuth : public Wt::Auth::AbstractUserDatabase {
|
||||
void logout(const Wt::Auth::User & user);
|
||||
|
||||
private:
|
||||
std::unique_ptr< eedb::Users > users;
|
||||
|
||||
mutable std::optional< std::string > _email;
|
||||
mutable std::optional< std::string > _emailToken;
|
||||
mutable std::optional< Wt::Auth::PasswordHash > _password;
|
||||
mutable std::optional< std::string > _identity;
|
||||
|
||||
const Wt::WEnvironment & _env;
|
||||
const Wt::Auth::AuthService * _authService;
|
||||
int _in_transaction{0};
|
||||
struct UserAuthPriv;
|
||||
spimpl::unique_impl_ptr< UserAuthPriv > _priv;
|
||||
};
|
||||
} // namespace eedb::auth
|
||||
|
||||
@ -50,7 +50,7 @@ class AuthIdentities {
|
||||
*/
|
||||
virtual AuthIdentity * addIdentity(std::unique_ptr<AuthIdentity>identity);
|
||||
|
||||
virtual std::optional< AuthIdentity * > findByProvider(std::string_view provider) const = 0;
|
||||
virtual std::optional< AuthIdentity * > byProvider(std::string_view provider) const = 0;
|
||||
|
||||
virtual void removeProvider(std::string_view provider) const = 0;
|
||||
};
|
||||
|
||||
@ -1,9 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
namespace eedb {
|
||||
enum class AuthTokenRole : uint8_t { //
|
||||
Auth,
|
||||
EmailToken,
|
||||
PasswordReset
|
||||
};
|
||||
|
||||
class AuthToken {
|
||||
public:
|
||||
virtual ~AuthToken() = default;
|
||||
@ -19,10 +26,10 @@ class AuthTokens {
|
||||
public:
|
||||
virtual ~AuthTokens() = default;
|
||||
|
||||
virtual std::optional< AuthToken * > find(std::string_view token) const = 0;
|
||||
virtual AuthToken * find(std::variant< std::string_view, AuthTokenRole > token) const = 0;
|
||||
|
||||
virtual void removeToken(std::string_view token) = 0;
|
||||
|
||||
virtual AuthToken * addToken(std::string token) = 0;
|
||||
virtual AuthToken * addToken(std::string token, AuthTokenRole role) = 0;
|
||||
};
|
||||
} // namespace eedb
|
||||
|
||||
@ -29,5 +29,8 @@ class Users {
|
||||
|
||||
// adds new user into users
|
||||
virtual unique_ptr< User > addUser(unique_ptr< AuthInfo >, unique_ptr< AuthIdentity >) = 0;
|
||||
|
||||
// remove user from database
|
||||
virtual void removeUser(std::unique_ptr< AuthInfo > user) const = 0;
|
||||
};
|
||||
} // namespace eedb
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
set(SOURCE
|
||||
data/PgCategory.cpp
|
||||
data/PgUser.cpp
|
||||
data/PgUserAuth.cpp
|
||||
data/PgUsers.cpp
|
||||
data/PgAuthToken.cpp
|
||||
|
||||
|
||||
@ -5,6 +5,8 @@
|
||||
#include <eedb/db/connection.hpp>
|
||||
#include <eedb/db/model/auth_token.h>
|
||||
|
||||
#include <utils/Visitor.hpp>
|
||||
|
||||
#include <sqlpp11/sqlpp11.h>
|
||||
|
||||
#include <map>
|
||||
@ -19,8 +21,10 @@ struct PgAuthToken::PgAuthTokenPriv {
|
||||
int64_t id;
|
||||
sqlpp::chrono::microsecond_point expire;
|
||||
std::string hash;
|
||||
bool updated{false};
|
||||
|
||||
void update() {
|
||||
if(!updated) {
|
||||
auto & result = db(select(t_auth_token.expires, t_auth_token.value) //
|
||||
.from(t_auth_token) //
|
||||
.where(t_auth_token.id == id))
|
||||
@ -28,15 +32,12 @@ struct PgAuthToken::PgAuthTokenPriv {
|
||||
expire = result.expires;
|
||||
hash = std::move(result.value);
|
||||
}
|
||||
updated = true;
|
||||
}
|
||||
};
|
||||
|
||||
PgAuthToken::PgAuthToken(db::PgConnection & con, int64_t uid) : _priv{spimpl::make_unique_impl< PgAuthTokenPriv >(con)} {
|
||||
auto & result = con(select(sqlpp::all_of(t_auth_token)) //
|
||||
.from(t_auth_token) //
|
||||
.where(t_auth_token.id == uid))
|
||||
.front();
|
||||
_priv->id = uid;
|
||||
_priv->update();
|
||||
}
|
||||
|
||||
PgAuthToken::PgAuthToken(db::PgConnection & con,
|
||||
@ -47,13 +48,16 @@ PgAuthToken::PgAuthToken(db::PgConnection & con,
|
||||
_priv->id = uid;
|
||||
_priv->expire = expirationtime;
|
||||
_priv->hash = std::move(hash);
|
||||
_priv->updated = true;
|
||||
}
|
||||
|
||||
std::string_view PgAuthToken::token() const {
|
||||
_priv->update();
|
||||
return _priv->hash;
|
||||
}
|
||||
|
||||
bool PgAuthToken::expired() const {
|
||||
_priv->update();
|
||||
return _priv->expire < std::chrono::system_clock::now();
|
||||
}
|
||||
|
||||
@ -74,15 +78,19 @@ struct PgAuthTokens::PgAuthTokensPriv {
|
||||
const User * _owner;
|
||||
|
||||
std::map< std::string, std::unique_ptr< AuthToken > > _cache;
|
||||
std::map< int, std::unique_ptr< AuthToken > > _emailTokensCache;
|
||||
|
||||
AuthToken * find(std::string token) {
|
||||
AuthToken * findAuthToken(std::string token) {
|
||||
if(auto tok = _cache.find(token); tok != _cache.end()) {
|
||||
return tok->second.get();
|
||||
}
|
||||
|
||||
auto token_data = _db(select(sqlpp::all_of(t_auth_token)) //
|
||||
.from(t_auth_token) //.
|
||||
.where(t_auth_token.value == token));
|
||||
.where( //
|
||||
t_auth_token.value == token and //
|
||||
t_auth_token.role == static_cast< int >(AuthTokenRole::Auth) and //
|
||||
t_auth_token.auth_info_id == _owner->uid()));
|
||||
|
||||
if(token_data.empty())
|
||||
return nullptr;
|
||||
@ -94,7 +102,29 @@ struct PgAuthTokens::PgAuthTokensPriv {
|
||||
return iterator->second.get();
|
||||
}
|
||||
|
||||
AuthToken * findEmailToken(int role) {
|
||||
if(auto tok = _emailTokensCache.find(role); tok != _emailTokensCache.end()) {
|
||||
return tok->second.get();
|
||||
}
|
||||
|
||||
auto token_data = _db(select(sqlpp::all_of(t_auth_token)) //
|
||||
.from(t_auth_token) //.
|
||||
.where( //
|
||||
t_auth_token.role == role and //
|
||||
t_auth_token.auth_info_id == _owner->uid()));
|
||||
|
||||
if(token_data.empty())
|
||||
return nullptr;
|
||||
|
||||
auto & data = token_data.front();
|
||||
const auto & iterator =
|
||||
_emailTokensCache.emplace(role, std::make_unique< PgAuthToken >(_db, data.id, std::move(data.value), data.expires)).first;
|
||||
|
||||
return iterator->second.get();
|
||||
}
|
||||
|
||||
AuthToken * add(std::string token) {
|
||||
/// FIXME role
|
||||
using namespace std::chrono;
|
||||
|
||||
auto exp = time_point_cast< microseconds >(system_clock::now() + hours{14 * 24});
|
||||
@ -102,7 +132,8 @@ struct PgAuthTokens::PgAuthTokensPriv {
|
||||
.set( //
|
||||
t_auth_token.value = token, //
|
||||
t_auth_token.auth_info_id = _owner->uid(), //
|
||||
t_auth_token.expires = exp) //
|
||||
t_auth_token.expires = exp,
|
||||
t_auth_token.role = 0) //
|
||||
.returning(t_auth_token.id))
|
||||
.front()
|
||||
.id;
|
||||
@ -119,18 +150,22 @@ struct PgAuthTokens::PgAuthTokensPriv {
|
||||
|
||||
PgAuthTokens::PgAuthTokens(db::PgConnection & con, const User * owner) : _priv{spimpl::make_unique_impl< PgAuthTokensPriv >(con, owner)} {}
|
||||
|
||||
std::optional< AuthToken * > PgAuthTokens::find(std::string_view token) const {
|
||||
auto ptr = _priv->find(std::string{token});
|
||||
if(ptr)
|
||||
return std::make_optional(ptr);
|
||||
return {};
|
||||
AuthToken * PgAuthTokens::find(std::variant< std::string_view, AuthTokenRole > token) const {
|
||||
auto authTokenVisitor = [impl = _priv.get()](std::string_view token) {
|
||||
return impl->findAuthToken(std::string{token});
|
||||
};
|
||||
auto emailRoleVisitor = [impl = _priv.get()](AuthTokenRole role) {
|
||||
return impl->findEmailToken(static_cast< int >(role));
|
||||
};
|
||||
|
||||
return std::visit(Visitor{authTokenVisitor, emailRoleVisitor}, token);
|
||||
}
|
||||
|
||||
void PgAuthTokens::removeToken(std::string_view token) {
|
||||
_priv->remove(std::string{token});
|
||||
}
|
||||
|
||||
AuthToken * PgAuthTokens::addToken(std::string hash) {
|
||||
AuthToken * PgAuthTokens::addToken(std::string hash, AuthTokenRole role) {
|
||||
return _priv->add(std::move(hash));
|
||||
}
|
||||
} // namespace eedb
|
||||
|
||||
@ -37,11 +37,11 @@ class PgAuthTokens final : public AuthTokens {
|
||||
public:
|
||||
PgAuthTokens(db::PgConnection & con, const User * owner);
|
||||
|
||||
std::optional< AuthToken * > find(std::string_view token) const override;
|
||||
AuthToken * find(std::variant< std::string_view, AuthTokenRole > token) const override;
|
||||
|
||||
void removeToken(std::string_view token) override;
|
||||
|
||||
AuthToken * addToken(std::string hash) override;
|
||||
AuthToken * addToken(std::string hash, AuthTokenRole role) override;
|
||||
|
||||
private:
|
||||
struct PgAuthTokensPriv;
|
||||
|
||||
@ -10,7 +10,6 @@
|
||||
#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_audit.h>
|
||||
|
||||
#include <Wt/WString.h>
|
||||
@ -23,16 +22,12 @@ namespace {
|
||||
SQLPP_ALIAS_PROVIDER(fk_auth_info_id);
|
||||
SQLPP_ALIAS_PROVIDER(user_status);
|
||||
|
||||
constexpr eedb::user t_user;
|
||||
constexpr eedb::auth_identity t_identity;
|
||||
constexpr eedb::auth_info t_info;
|
||||
constexpr eedb::auth_token authToken;
|
||||
|
||||
auto auth = t_user.join(t_info).on(t_user.uid == t_info.user_uid).join(t_identity).on(t_info.id == t_identity.auth_info_id);
|
||||
constexpr eedb::auth_token t_auth_token;
|
||||
|
||||
auto auth_info_identity = t_info.join(t_identity).on(t_info.id == t_identity.auth_info_id);
|
||||
auto user_auth_info = t_user.join(t_info).on(t_info.user_uid == t_user.uid);
|
||||
auto auth_token_info = user_auth_info.join(authToken).on(authToken.auth_info_id == t_info.id);
|
||||
auto auth_token_info = t_info.join(t_auth_token).on(t_auth_token.auth_info_id == t_info.id);
|
||||
} // namespace
|
||||
|
||||
namespace eedb {
|
||||
|
||||
@ -1,453 +0,0 @@
|
||||
#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 <spdlog/spdlog.h>
|
||||
#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 static_cast< int >(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
|
||||
@ -1,125 +0,0 @@
|
||||
#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[[deprecated]] 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
|
||||
@ -8,11 +8,9 @@
|
||||
#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 <sqlpp11/sqlpp11.h>
|
||||
|
||||
constexpr eedb::user t_user;
|
||||
constexpr eedb::auth_identity t_auth_identity;
|
||||
constexpr eedb::auth_info t_auth_info;
|
||||
constexpr eedb::auth_token t_auth_token;
|
||||
@ -37,26 +35,26 @@ unique_ptr< User > PgUsers::findWith(const AuthIdentity & identity) const {
|
||||
const auto provider_eq = t_auth_identity.provider == std::string{identity.provider()};
|
||||
|
||||
auto & db = *(_priv->_db.native());
|
||||
auto result = db(select(t_auth_info.user_uid) //
|
||||
auto result = db(select(t_auth_info.id) //
|
||||
.from(t_auth_info.inner_join(t_auth_identity).on(t_auth_info.id == t_auth_identity.auth_info_id)) //
|
||||
.where(provider_eq and identity_eq));
|
||||
|
||||
if(result.empty()) {
|
||||
return {};
|
||||
}
|
||||
return std::make_unique< PgUser >(_priv->_db, result.front().user_uid);
|
||||
return std::make_unique< PgUser >(_priv->_db, result.front().id);
|
||||
}
|
||||
|
||||
unique_ptr< User > PgUsers::findWith(const Email & email) const {
|
||||
auto & db = *(_priv->_db.native());
|
||||
auto result = db(select(t_auth_info.user_uid) //
|
||||
auto result = db(select(t_auth_info.id) //
|
||||
.from(t_auth_info) //
|
||||
.where(t_auth_info.email == std::string{email.address()}));
|
||||
|
||||
if(result.empty()) {
|
||||
return {};
|
||||
}
|
||||
return std::make_unique< PgUser >(_priv->_db, result.front().user_uid);
|
||||
return std::make_unique< PgUser >(_priv->_db, result.front().id);
|
||||
}
|
||||
|
||||
unique_ptr< User > PgUsers::findWith(const EmailToken & token) const {}
|
||||
@ -71,14 +69,6 @@ unique_ptr< User > PgUsers::addUser(unique_ptr< AuthInfo > authInfo, unique_ptr<
|
||||
|
||||
auto & db = *(_priv->_db.native());
|
||||
|
||||
auto user_id = db(sqlpp::postgresql::insert_into(t_user) //
|
||||
.set( //
|
||||
t_user.full_name = "none", //
|
||||
t_user.status = 0)
|
||||
.returning(t_user.uid))
|
||||
.front()
|
||||
.uid;
|
||||
|
||||
const auto & pass = authInfo->password();
|
||||
const auto & email = authInfo->email();
|
||||
|
||||
@ -88,8 +78,8 @@ unique_ptr< User > PgUsers::addUser(unique_ptr< AuthInfo > authInfo, unique_ptr<
|
||||
t_auth_info.password_hash = pass.value(), //
|
||||
t_auth_info.password_method = pass.function(), //
|
||||
t_auth_info.password_salt = pass.salt(), //
|
||||
t_auth_info.user_uid = user_id, //
|
||||
t_auth_info.email = std::string{email.address()})
|
||||
t_auth_info.email = std::string{email.address()},
|
||||
t_auth_info.status = 0) ///TODO status->UserWaitingForVerification
|
||||
.returning(t_auth_info.id))
|
||||
.front()
|
||||
.id;
|
||||
@ -100,7 +90,7 @@ unique_ptr< User > PgUsers::addUser(unique_ptr< AuthInfo > authInfo, unique_ptr<
|
||||
t_auth_identity.identity = std::string{authIdentity->identity()},
|
||||
t_auth_identity.provider = std::string{authIdentity->provider()}));
|
||||
|
||||
return std::make_unique< eedb::PgUser >(_priv->_db, user_id);
|
||||
return std::make_unique< eedb::PgUser >(_priv->_db, auth_info_id);
|
||||
}
|
||||
|
||||
} // namespace eedb
|
||||
|
||||
@ -21,6 +21,7 @@ class PgUsers : public Users {
|
||||
// unique_ptr< User > findWith(const AuthToken & token) const override;
|
||||
|
||||
unique_ptr< User > addUser(unique_ptr< AuthInfo >, unique_ptr< AuthIdentity >) override;
|
||||
void removeUser(std::unique_ptr< AuthInfo > user) const override{}
|
||||
|
||||
private:
|
||||
struct PgUsersPriv;
|
||||
|
||||
@ -24,19 +24,64 @@ namespace eedb {
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::integer, sqlpp::tag::must_not_insert, sqlpp::tag::must_not_update>;
|
||||
};
|
||||
|
||||
struct User_uid {
|
||||
struct Group {
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("user_uid")";
|
||||
static constexpr const char _literal[] = R"("group")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T user_uid;
|
||||
T &operator()() { return user_uid; }
|
||||
const T &operator()() const { return user_uid; }
|
||||
T group;
|
||||
T &operator()() { return group; }
|
||||
const T &operator()() const { return group; }
|
||||
};
|
||||
};
|
||||
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::integer, sqlpp::tag::can_be_null>;
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::integer>;
|
||||
};
|
||||
|
||||
struct Created {
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("created")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T created;
|
||||
T &operator()() { return created; }
|
||||
const T &operator()() const { return created; }
|
||||
};
|
||||
};
|
||||
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::time_point>;
|
||||
};
|
||||
|
||||
struct Updated {
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("updated")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T updated;
|
||||
T &operator()() { return updated; }
|
||||
const T &operator()() const { return updated; }
|
||||
};
|
||||
};
|
||||
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::time_point, sqlpp::tag::can_be_null>;
|
||||
};
|
||||
|
||||
struct Config {
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("config")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T config;
|
||||
T &operator()() { return config; }
|
||||
const T &operator()() const { return config; }
|
||||
};
|
||||
};
|
||||
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::text>;
|
||||
};
|
||||
|
||||
struct Password_hash {
|
||||
@ -99,78 +144,33 @@ namespace eedb {
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::varchar, sqlpp::tag::require_insert>;
|
||||
};
|
||||
|
||||
struct Email_verified {
|
||||
struct Status {
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("email_verified")";
|
||||
static constexpr const char _literal[] = R"("status")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T email_verified;
|
||||
T &operator()() { return email_verified; }
|
||||
const T &operator()() const { return email_verified; }
|
||||
T status;
|
||||
T &operator()() { return status; }
|
||||
const T &operator()() const { return status; }
|
||||
};
|
||||
};
|
||||
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::boolean>;
|
||||
};
|
||||
|
||||
struct Email_token {
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("email_token")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T email_token;
|
||||
T &operator()() { return email_token; }
|
||||
const T &operator()() const { return email_token; }
|
||||
};
|
||||
};
|
||||
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::varchar, sqlpp::tag::can_be_null>;
|
||||
};
|
||||
|
||||
struct Email_token_expires {
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("email_token_expires")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T email_token_expires;
|
||||
T &operator()() { return email_token_expires; }
|
||||
const T &operator()() const { return email_token_expires; }
|
||||
};
|
||||
};
|
||||
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::time_point, sqlpp::tag::can_be_null>;
|
||||
};
|
||||
|
||||
struct Email_token_role {
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("email_token_role")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T email_token_role;
|
||||
T &operator()() { return email_token_role; }
|
||||
const T &operator()() const { return email_token_role; }
|
||||
};
|
||||
};
|
||||
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::integer, sqlpp::tag::can_be_null>;
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::integer>;
|
||||
};
|
||||
}
|
||||
|
||||
struct auth_info : sqlpp::table_t<auth_info,
|
||||
auth_info_::Id,
|
||||
auth_info_::User_uid,
|
||||
auth_info_::Group,
|
||||
auth_info_::Created,
|
||||
auth_info_::Updated,
|
||||
auth_info_::Config,
|
||||
auth_info_::Password_hash,
|
||||
auth_info_::Password_method,
|
||||
auth_info_::Password_salt,
|
||||
auth_info_::Email,
|
||||
auth_info_::Email_verified,
|
||||
auth_info_::Email_token,
|
||||
auth_info_::Email_token_expires,
|
||||
auth_info_::Email_token_role> {
|
||||
auth_info_::Status> {
|
||||
using _value_type = sqlpp::no_value_t;
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("auth_info")";
|
||||
|
||||
@ -36,7 +36,7 @@ namespace eedb {
|
||||
};
|
||||
};
|
||||
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::bigint, sqlpp::tag::require_insert >;
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::bigint, sqlpp::tag::can_be_null>;
|
||||
};
|
||||
|
||||
struct Value {
|
||||
@ -68,13 +68,29 @@ namespace eedb {
|
||||
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::time_point, sqlpp::tag::require_insert>;
|
||||
};
|
||||
|
||||
struct Role {
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("role")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T role;
|
||||
T &operator()() { return role; }
|
||||
const T &operator()() const { return role; }
|
||||
};
|
||||
};
|
||||
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::smallint, sqlpp::tag::require_insert>;
|
||||
};
|
||||
}
|
||||
|
||||
struct auth_token : sqlpp::table_t<auth_token,
|
||||
auth_token_::Id,
|
||||
auth_token_::Auth_info_id,
|
||||
auth_token_::Value,
|
||||
auth_token_::Expires> {
|
||||
auth_token_::Expires,
|
||||
auth_token_::Role> {
|
||||
using _value_type = sqlpp::no_value_t;
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("auth_token")";
|
||||
|
||||
92
src/eedb/db/model/measurands.h
Normal file
92
src/eedb/db/model/measurands.h
Normal file
@ -0,0 +1,92 @@
|
||||
#ifndef EEDB_MEASURANDS_H
|
||||
#define EEDB_MEASURANDS_H
|
||||
|
||||
#include <sqlpp11/table.h>
|
||||
#include <sqlpp11/char_sequence.h>
|
||||
#include <sqlpp11/column_types.h>
|
||||
|
||||
namespace eedb {
|
||||
|
||||
namespace measurands_ {
|
||||
|
||||
struct Id {
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("id")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T id;
|
||||
T &operator()() { return id; }
|
||||
const T &operator()() const { return id; }
|
||||
};
|
||||
};
|
||||
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::integer, sqlpp::tag::must_not_insert, sqlpp::tag::must_not_update>;
|
||||
};
|
||||
|
||||
struct Name {
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("name")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T name;
|
||||
T &operator()() { return name; }
|
||||
const T &operator()() const { return name; }
|
||||
};
|
||||
};
|
||||
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::text, sqlpp::tag::require_insert>;
|
||||
};
|
||||
|
||||
struct Description {
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("description")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T description;
|
||||
T &operator()() { return description; }
|
||||
const T &operator()() const { return description; }
|
||||
};
|
||||
};
|
||||
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::text, sqlpp::tag::can_be_null>;
|
||||
};
|
||||
|
||||
struct Dimension_symbol {
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("dimension_symbol")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T dimension_symbol;
|
||||
T &operator()() { return dimension_symbol; }
|
||||
const T &operator()() const { return dimension_symbol; }
|
||||
};
|
||||
};
|
||||
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::text, sqlpp::tag::can_be_null>;
|
||||
};
|
||||
}
|
||||
|
||||
struct measurands : sqlpp::table_t<measurands,
|
||||
measurands_::Id,
|
||||
measurands_::Name,
|
||||
measurands_::Description,
|
||||
measurands_::Dimension_symbol> {
|
||||
using _value_type = sqlpp::no_value_t;
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("measurands")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T measurands;
|
||||
T &operator()() { return measurands; }
|
||||
const T &operator()() const { return measurands; }
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
76
src/eedb/db/model/metric_systems.h
Normal file
76
src/eedb/db/model/metric_systems.h
Normal file
@ -0,0 +1,76 @@
|
||||
#ifndef EEDB_METRIC_SYSTEMS_H
|
||||
#define EEDB_METRIC_SYSTEMS_H
|
||||
|
||||
#include <sqlpp11/table.h>
|
||||
#include <sqlpp11/char_sequence.h>
|
||||
#include <sqlpp11/column_types.h>
|
||||
|
||||
namespace eedb {
|
||||
|
||||
namespace metric_systems_ {
|
||||
|
||||
struct Id {
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("id")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T id;
|
||||
T &operator()() { return id; }
|
||||
const T &operator()() const { return id; }
|
||||
};
|
||||
};
|
||||
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::integer, sqlpp::tag::must_not_insert, sqlpp::tag::must_not_update>;
|
||||
};
|
||||
|
||||
struct Name {
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("name")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T name;
|
||||
T &operator()() { return name; }
|
||||
const T &operator()() const { return name; }
|
||||
};
|
||||
};
|
||||
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::varchar, sqlpp::tag::require_insert>;
|
||||
};
|
||||
|
||||
struct Description {
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("description")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T description;
|
||||
T &operator()() { return description; }
|
||||
const T &operator()() const { return description; }
|
||||
};
|
||||
};
|
||||
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::text, sqlpp::tag::can_be_null>;
|
||||
};
|
||||
}
|
||||
|
||||
struct metric_systems : sqlpp::table_t<metric_systems,
|
||||
metric_systems_::Id,
|
||||
metric_systems_::Name,
|
||||
metric_systems_::Description> {
|
||||
using _value_type = sqlpp::no_value_t;
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("metric_systems")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T metric_systems;
|
||||
T &operator()() { return metric_systems; }
|
||||
const T &operator()() const { return metric_systems; }
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -1,140 +0,0 @@
|
||||
#ifndef EEDB_USER_H
|
||||
#define EEDB_USER_H
|
||||
|
||||
#include <sqlpp11/table.h>
|
||||
#include <sqlpp11/char_sequence.h>
|
||||
#include <sqlpp11/column_types.h>
|
||||
|
||||
namespace eedb {
|
||||
|
||||
namespace user_ {
|
||||
|
||||
struct Uid {
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("uid")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T uid;
|
||||
T &operator()() { return uid; }
|
||||
const T &operator()() const { return uid; }
|
||||
};
|
||||
};
|
||||
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::integer, sqlpp::tag::must_not_insert, sqlpp::tag::must_not_update>;
|
||||
};
|
||||
|
||||
struct Group {
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("group")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T group;
|
||||
T &operator()() { return group; }
|
||||
const T &operator()() const { return group; }
|
||||
};
|
||||
};
|
||||
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::integer>;
|
||||
};
|
||||
|
||||
struct Status {
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("status")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T status;
|
||||
T &operator()() { return status; }
|
||||
const T &operator()() const { return status; }
|
||||
};
|
||||
};
|
||||
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::integer>;
|
||||
};
|
||||
|
||||
struct Full_name {
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("full_name")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T full_name;
|
||||
T &operator()() { return full_name; }
|
||||
const T &operator()() const { return full_name; }
|
||||
};
|
||||
};
|
||||
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::text, sqlpp::tag::require_insert>;
|
||||
};
|
||||
|
||||
struct Created {
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("created")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T created;
|
||||
T &operator()() { return created; }
|
||||
const T &operator()() const { return created; }
|
||||
};
|
||||
};
|
||||
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::time_point>;
|
||||
};
|
||||
|
||||
struct Updated {
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("updated")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T updated;
|
||||
T &operator()() { return updated; }
|
||||
const T &operator()() const { return updated; }
|
||||
};
|
||||
};
|
||||
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::time_point, sqlpp::tag::can_be_null>;
|
||||
};
|
||||
|
||||
struct Config {
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("config")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T config;
|
||||
T &operator()() { return config; }
|
||||
const T &operator()() const { return config; }
|
||||
};
|
||||
};
|
||||
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::text>;
|
||||
};
|
||||
}
|
||||
|
||||
struct user : sqlpp::table_t<user,
|
||||
user_::Uid,
|
||||
user_::Group,
|
||||
user_::Status,
|
||||
user_::Full_name,
|
||||
user_::Created,
|
||||
user_::Updated,
|
||||
user_::Config> {
|
||||
using _value_type = sqlpp::no_value_t;
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("user")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T user;
|
||||
T &operator()() { return user; }
|
||||
const T &operator()() const { return user; }
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -24,15 +24,15 @@ namespace eedb {
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::integer, sqlpp::tag::must_not_insert, sqlpp::tag::must_not_update>;
|
||||
};
|
||||
|
||||
struct User_id {
|
||||
struct Auth_info_id {
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("user_id")";
|
||||
static constexpr const char _literal[] = R"("auth_info_id")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T user_id;
|
||||
T &operator()() { return user_id; }
|
||||
const T &operator()() const { return user_id; }
|
||||
T auth_info_id;
|
||||
T &operator()() { return auth_info_id; }
|
||||
const T &operator()() const { return auth_info_id; }
|
||||
};
|
||||
};
|
||||
|
||||
@ -69,15 +69,15 @@ namespace eedb {
|
||||
using _traits = ::sqlpp::make_traits<::sqlpp::text, sqlpp::tag::can_be_null>;
|
||||
};
|
||||
|
||||
struct When {
|
||||
struct When_happened {
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("when")";
|
||||
static constexpr const char _literal[] = R"("when_happened")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T _when;
|
||||
T &operator()() { return _when; }
|
||||
const T &operator()() const { return _when; }
|
||||
T when_happened;
|
||||
T &operator()() { return when_happened; }
|
||||
const T &operator()() const { return when_happened; }
|
||||
};
|
||||
};
|
||||
|
||||
@ -87,10 +87,10 @@ namespace eedb {
|
||||
|
||||
struct user_audit : sqlpp::table_t<user_audit,
|
||||
user_audit_::Id,
|
||||
user_audit_::User_id,
|
||||
user_audit_::Auth_info_id,
|
||||
user_audit_::Action_id,
|
||||
user_audit_::Data,
|
||||
user_audit_::When> {
|
||||
user_audit_::When_happened> {
|
||||
using _value_type = sqlpp::no_value_t;
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("user_audit")";
|
||||
|
||||
@ -9,15 +9,15 @@ namespace eedb {
|
||||
|
||||
namespace user_inventory_ {
|
||||
|
||||
struct User_id {
|
||||
struct Auth_info_id {
|
||||
struct _alias_t {
|
||||
static constexpr const char _literal[] = R"("user_id")";
|
||||
static constexpr const char _literal[] = R"("auth_info_id")";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template<typename T>
|
||||
struct _member_t {
|
||||
T user_id;
|
||||
T &operator()() { return user_id; }
|
||||
const T &operator()() const { return user_id; }
|
||||
T auth_info_id;
|
||||
T &operator()() { return auth_info_id; }
|
||||
const T &operator()() const { return auth_info_id; }
|
||||
};
|
||||
};
|
||||
|
||||
@ -41,7 +41,7 @@ namespace eedb {
|
||||
}
|
||||
|
||||
struct user_inventory : sqlpp::table_t<user_inventory,
|
||||
user_inventory_::User_id,
|
||||
user_inventory_::Auth_info_id,
|
||||
user_inventory_::Inventory_id> {
|
||||
using _value_type = sqlpp::no_value_t;
|
||||
struct _alias_t {
|
||||
|
||||
8
src/utils/Visitor.hpp
Normal file
8
src/utils/Visitor.hpp
Normal file
@ -0,0 +1,8 @@
|
||||
#include <utility>
|
||||
|
||||
template < class... Ts >
|
||||
struct Visitor : Ts... {
|
||||
using Ts::operator()...;
|
||||
};
|
||||
template < class... Ts >
|
||||
Visitor(Ts...)->Visitor< std::decay_t< Ts >... >;
|
||||
@ -8,93 +8,51 @@
|
||||
#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 <sqlpp11/sqlpp11.h>
|
||||
|
||||
#include <utils/DbTestBase.hpp>
|
||||
|
||||
constexpr eedb::user t_user;
|
||||
constexpr eedb::user_audit t_user_audit;
|
||||
constexpr eedb::user_action t_user_action;
|
||||
constexpr eedb::auth_identity t_auth_identity;
|
||||
constexpr eedb::auth_info t_auth_info;
|
||||
constexpr eedb::auth_token t_auth_token;
|
||||
|
||||
class UserMock : public eedb::User {
|
||||
public:
|
||||
MOCK_CONST_METHOD0(uid, int());
|
||||
const eedb::UserConfig & config() const override {}
|
||||
eedb::AuthTokens & authTokens() const override {}
|
||||
void logout() override {}
|
||||
eedb::AuthIdentities & authIdentities() const {}
|
||||
int64_t _uid;
|
||||
|
||||
// User interface
|
||||
public:
|
||||
eedb::AuthInfo &authInfo() const override{}
|
||||
};
|
||||
|
||||
class PgAuthTokenTest : public DbTestBase< PgAuthTokenTest > {
|
||||
public:
|
||||
PgAuthTokenTest() {
|
||||
using namespace testing;
|
||||
db().native()->start_transaction();
|
||||
|
||||
authInfoId = db()(sqlpp::postgresql::insert_into(t_auth_info)
|
||||
.set( //
|
||||
t_auth_info.password_hash = "$2y$07$RyytUhDhLDbAPjf0b0r2Y.dsg.FlQ7L.xzWHMmoelI81u0MfBrW7q",
|
||||
t_auth_info.password_method = "bcrypt",
|
||||
t_auth_info.password_salt = "OM/Z1c4WBFXvwkxh",
|
||||
t_auth_info.email = "none@eedb.pl", //
|
||||
t_auth_info.status = 0)
|
||||
.returning(t_auth_info.id))
|
||||
.front()
|
||||
.id;
|
||||
|
||||
sutId = db()(sqlpp::postgresql::insert_into(t_auth_token) //
|
||||
.set( //
|
||||
t_auth_token.auth_info_id = authInfoId, //
|
||||
t_auth_token.expires = std::chrono::system_clock::now(), //
|
||||
t_auth_token.role = static_cast< int >(eedb::AuthTokenRole::Auth), //
|
||||
t_auth_token.value = "empty") //
|
||||
.returning(t_auth_token.id))
|
||||
.front()
|
||||
.id;
|
||||
|
||||
sut = std::make_unique< eedb::PgAuthToken >(db(), sutId);
|
||||
}
|
||||
|
||||
void createExpiredToken() {
|
||||
auto sql = R"sql(
|
||||
WITH user_ AS ( INSERT INTO "user" ("full_name","status")
|
||||
VALUES('none',0)
|
||||
RETURNING "user"."uid" AS user_id )
|
||||
, auth_info_ AS (
|
||||
INSERT INTO "auth_info" ("password_hash","password_method","password_salt","user_uid","email")
|
||||
SELECT '$2y$07$RyytUhDhLDbAPjf0b0r2Y.dsg.FlQ7L.xzWHMmoelI81u0MfBrW7q','bcrypt','OM/Z1c4WBFXvwkxh',user_id,'none@eedb.pl'
|
||||
FROM user_
|
||||
RETURNING "auth_info"."id" AS auth_info_id )
|
||||
, auth_identity_ AS(
|
||||
INSERT INTO "auth_identity" ("auth_info_id","identity","provider")
|
||||
SELECT auth_info_id,'username','loginname'
|
||||
FROM auth_info_ )
|
||||
INSERT into auth_token ("auth_info_id","value","expires")
|
||||
SELECT auth_info_id, 'hash', now() - interval '1 hour'
|
||||
FROM auth_info_
|
||||
)sql";
|
||||
|
||||
db().native()->execute(sql);
|
||||
|
||||
auto auth_info_id = db()(select(t_auth_info.id).from(t_auth_info).where(t_auth_info.email == "none@eedb.pl")).front().id;
|
||||
auto _uid = db()(select(t_auth_token.id).from(t_auth_token).where(t_auth_token.auth_info_id == auth_info_id)).front().id;
|
||||
|
||||
sut = std::make_unique< eedb::PgAuthToken >(db(), _uid);
|
||||
}
|
||||
|
||||
void createValidToken() {
|
||||
auto sql = R"sql(
|
||||
WITH user_ AS ( INSERT INTO "user" ("full_name","status")
|
||||
VALUES('none',0)
|
||||
RETURNING "user"."uid" AS user_id )
|
||||
, auth_info_ AS (
|
||||
INSERT INTO "auth_info" ("password_hash","password_method","password_salt","user_uid","email")
|
||||
SELECT '$2y$07$RyytUhDhLDbAPjf0b0r2Y.dsg.FlQ7L.xzWHMmoelI81u0MfBrW7q','bcrypt','OM/Z1c4WBFXvwkxh',user_id,'none@eedb.pl'
|
||||
FROM user_
|
||||
RETURNING "auth_info"."id" AS auth_info_id )
|
||||
, auth_identity_ AS(
|
||||
INSERT INTO "auth_identity" ("auth_info_id","identity","provider")
|
||||
SELECT auth_info_id,'username','loginname'
|
||||
FROM auth_info_ )
|
||||
INSERT into auth_token ("auth_info_id","value","expires")
|
||||
SELECT auth_info_id, 'hash', now() + interval '1 hour'
|
||||
FROM auth_info_
|
||||
)sql";
|
||||
|
||||
db().native()->execute(sql);
|
||||
|
||||
auto auth_info_id = db()(select(t_auth_info.id).from(t_auth_info).where(t_auth_info.email == "none@eedb.pl")).front().id;
|
||||
auto _uid = db()(select(t_auth_token.id).from(t_auth_token).where(t_auth_token.auth_info_id == auth_info_id)).front().id;
|
||||
|
||||
sut = std::make_unique< eedb::PgAuthToken >(db(), _uid);
|
||||
void setExpireDate(std::chrono::system_clock::time_point exp) {
|
||||
using namespace std::chrono;
|
||||
db()(update(t_auth_token).set(t_auth_token.expires = time_point_cast< microseconds >(exp)).where(t_auth_token.id == sutId));
|
||||
}
|
||||
|
||||
~PgAuthTokenTest() {
|
||||
@ -102,7 +60,8 @@ class PgAuthTokenTest : public DbTestBase< PgAuthTokenTest > {
|
||||
}
|
||||
|
||||
protected:
|
||||
testing::NiceMock< UserMock > user;
|
||||
int64_t authInfoId;
|
||||
int64_t sutId;
|
||||
std::unique_ptr< eedb::PgAuthToken > sut;
|
||||
};
|
||||
|
||||
@ -110,24 +69,23 @@ template <>
|
||||
std::unique_ptr< PgTestDatabasePrepare > DbTestBase< PgAuthTokenTest >::_test_db = {};
|
||||
|
||||
using namespace sqlpp;
|
||||
using namespace std::chrono;
|
||||
|
||||
TEST_F(PgAuthTokenTest, isExpired) {
|
||||
createExpiredToken();
|
||||
setExpireDate(system_clock::now() - hours{1});
|
||||
EXPECT_TRUE(sut->expired());
|
||||
}
|
||||
|
||||
TEST_F(PgAuthTokenTest, isNotExpired) {
|
||||
createValidToken();
|
||||
setExpireDate(system_clock::now() + hours{1});
|
||||
EXPECT_FALSE(sut->expired());
|
||||
}
|
||||
|
||||
TEST_F(PgAuthTokenTest, validValue) {
|
||||
createValidToken();
|
||||
EXPECT_EQ(sut->token(), "hash");
|
||||
EXPECT_EQ(sut->token(), "empty");
|
||||
}
|
||||
|
||||
TEST_F(PgAuthTokenTest, updateValue) {
|
||||
createValidToken();
|
||||
sut->update("NEW");
|
||||
EXPECT_EQ(sut->token(), "NEW");
|
||||
}
|
||||
|
||||
@ -2,20 +2,18 @@
|
||||
|
||||
#include <eedb/db/data/PgAuthToken.hpp>
|
||||
|
||||
#include <eedb/data/Users.hpp>
|
||||
#include <eedb/data/AuthIdentity.hpp>
|
||||
#include <eedb/data/Users.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 <sqlpp11/sqlpp11.h>
|
||||
|
||||
#include <utils/DbTestBase.hpp>
|
||||
|
||||
constexpr eedb::user t_user;
|
||||
constexpr eedb::user_audit t_user_audit;
|
||||
constexpr eedb::user_action t_user_action;
|
||||
constexpr eedb::auth_identity t_auth_identity;
|
||||
@ -43,36 +41,29 @@ class PgAuthTokensTest : public DbTestBase< PgAuthTokensTest > {
|
||||
PgAuthTokensTest() {
|
||||
using namespace testing;
|
||||
db().native()->start_transaction();
|
||||
|
||||
authInfoId = db()(sqlpp::postgresql::insert_into(t_auth_info)
|
||||
.set( //
|
||||
t_auth_info.password_hash = "$2y$07$RyytUhDhLDbAPjf0b0r2Y.dsg.FlQ7L.xzWHMmoelI81u0MfBrW7q",
|
||||
t_auth_info.password_method = "bcrypt",
|
||||
t_auth_info.password_salt = "OM/Z1c4WBFXvwkxh",
|
||||
t_auth_info.email = "none@eedb.pl", //
|
||||
t_auth_info.status = 0)
|
||||
.returning(t_auth_info.id))
|
||||
.front()
|
||||
.id;
|
||||
|
||||
ON_CALL(user, uid()).WillByDefault(testing::Return(authInfoId));
|
||||
sut = std::make_unique< eedb::PgAuthTokens >(db(), &user);
|
||||
}
|
||||
|
||||
void createToken() {
|
||||
auto sql = R"sql(
|
||||
WITH user_ AS ( INSERT INTO "user" ("full_name","status")
|
||||
VALUES('none',0)
|
||||
RETURNING "user"."uid" AS user_id )
|
||||
, auth_info_ AS (
|
||||
INSERT INTO "auth_info" ("password_hash","password_method","password_salt","user_uid","email")
|
||||
SELECT '$2y$07$RyytUhDhLDbAPjf0b0r2Y.dsg.FlQ7L.xzWHMmoelI81u0MfBrW7q','bcrypt','OM/Z1c4WBFXvwkxh',user_id,'none@eedb.pl'
|
||||
FROM user_
|
||||
RETURNING "auth_info"."id" AS auth_info_id )
|
||||
, auth_identity_ AS(
|
||||
INSERT INTO "auth_identity" ("auth_info_id","identity","provider")
|
||||
SELECT auth_info_id,'username','loginname'
|
||||
FROM auth_info_ )
|
||||
INSERT into auth_token ("auth_info_id","value","expires")
|
||||
SELECT auth_info_id, 'hash', now() + interval '1 hour'
|
||||
FROM auth_info_
|
||||
)sql";
|
||||
|
||||
db().native()->execute(sql);
|
||||
|
||||
auto auth_info_id = db()(select(t_auth_info.id).from(t_auth_info).where(t_auth_info.email == "none@eedb.pl")).front().id;
|
||||
// auto _uid = db()(select(t_auth_token.id).from(t_auth_token).where(t_auth_token.auth_info_id ==
|
||||
// auth_info_id)).front().id;
|
||||
|
||||
ON_CALL(user, uid()).WillByDefault(testing::Return(auth_info_id));
|
||||
|
||||
sut = std::make_unique< eedb::PgAuthTokens >(db(), &user);
|
||||
void createToken(std::string hash, eedb::AuthTokenRole role) {
|
||||
db()(sqlpp::postgresql::insert_into(t_auth_token) //
|
||||
.set( //
|
||||
t_auth_token.value = hash, //
|
||||
t_auth_token.auth_info_id = authInfoId, //
|
||||
t_auth_token.role = static_cast< int16_t >(role), //
|
||||
t_auth_token.expires = std::chrono::system_clock::now() + std::chrono::hours{1}));
|
||||
}
|
||||
|
||||
~PgAuthTokensTest() {
|
||||
@ -81,6 +72,7 @@ class PgAuthTokensTest : public DbTestBase< PgAuthTokensTest > {
|
||||
|
||||
protected:
|
||||
UserMock user;
|
||||
int64_t authInfoId{0};
|
||||
std::unique_ptr< eedb::PgAuthTokens > sut;
|
||||
};
|
||||
|
||||
@ -88,33 +80,34 @@ template <>
|
||||
std::unique_ptr< PgTestDatabasePrepare > DbTestBase< PgAuthTokensTest >::_test_db = {};
|
||||
|
||||
using namespace sqlpp;
|
||||
using namespace eedb;
|
||||
|
||||
TEST_F(PgAuthTokensTest, tokenFound) {
|
||||
createToken();
|
||||
EXPECT_TRUE(sut->find("hash"));
|
||||
TEST_F(PgAuthTokensTest, authTokenFound) {
|
||||
createToken("token", AuthTokenRole::Auth);
|
||||
EXPECT_TRUE(sut->find("token"));
|
||||
}
|
||||
|
||||
TEST_F(PgAuthTokensTest, emailTokenFound) {
|
||||
createToken("emailtoken", AuthTokenRole::EmailToken);
|
||||
EXPECT_TRUE(sut->find(AuthTokenRole::EmailToken));
|
||||
}
|
||||
|
||||
TEST_F(PgAuthTokensTest, tokenNotFound) {
|
||||
createToken();
|
||||
EXPECT_FALSE(sut->find("bad hash"));
|
||||
EXPECT_FALSE(sut->find("token"));
|
||||
}
|
||||
|
||||
TEST_F(PgAuthTokensTest, readFromCache) {
|
||||
createToken();
|
||||
EXPECT_TRUE(sut->find("hash"));
|
||||
EXPECT_TRUE(sut->find("hash"));
|
||||
}
|
||||
TEST_F(PgAuthTokensTest, addMultipleAuthTokens) {
|
||||
sut->addToken("authtoken1", eedb::AuthTokenRole::Auth);
|
||||
sut->addToken("authtoken2", eedb::AuthTokenRole::Auth);
|
||||
|
||||
TEST_F(PgAuthTokensTest, addToken) {
|
||||
createToken();
|
||||
sut->addToken("hash2");
|
||||
EXPECT_TRUE(sut->find("hash"));
|
||||
EXPECT_TRUE(sut->find("hash2"));
|
||||
EXPECT_TRUE(sut->find("authtoken1"));
|
||||
EXPECT_TRUE(sut->find("authtoken2"));
|
||||
}
|
||||
|
||||
TEST_F(PgAuthTokensTest, removeToken) {
|
||||
createToken();
|
||||
EXPECT_TRUE(sut->find("hash"));
|
||||
sut->removeToken("hash");
|
||||
EXPECT_FALSE(sut->find("hash"));
|
||||
sut->addToken("authtoken1", eedb::AuthTokenRole::Auth);
|
||||
sut->addToken("authtoken2", eedb::AuthTokenRole::Auth);
|
||||
|
||||
sut->removeToken("authtoken1");
|
||||
EXPECT_FALSE(sut->find("authtoken1"));
|
||||
}
|
||||
|
||||
@ -5,14 +5,12 @@
|
||||
#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 <sqlpp11/sqlpp11.h>
|
||||
|
||||
#include <utils/DbTestBase.hpp>
|
||||
|
||||
constexpr eedb::user t_user;
|
||||
constexpr eedb::user_audit t_user_audit;
|
||||
constexpr eedb::user_action t_user_action;
|
||||
constexpr eedb::auth_identity t_auth_identity;
|
||||
@ -47,18 +45,16 @@ class PgUsersTest : public DbTestBase< PgUsersTest > {
|
||||
}
|
||||
|
||||
auto idMap() {
|
||||
std::map< std::string, uint64_t > _userIdMap;
|
||||
std::map< std::string, int64_t > _userIdMap;
|
||||
auto ids = db()( //
|
||||
select(t_user.uid, t_auth_identity.identity)
|
||||
.from(t_user //
|
||||
.inner_join(t_auth_info)
|
||||
.on(t_auth_info.user_uid == t_user.uid)
|
||||
select(t_auth_info.id, t_auth_identity.identity)
|
||||
.from(t_auth_info //
|
||||
.inner_join(t_auth_identity)
|
||||
.on(t_auth_identity.auth_info_id == t_auth_info.id))
|
||||
.unconditionally());
|
||||
|
||||
for(const auto & row : ids) {
|
||||
_userIdMap[row.identity] = row.uid;
|
||||
_userIdMap[row.identity] = row.id;
|
||||
}
|
||||
|
||||
return _userIdMap;
|
||||
@ -82,18 +78,15 @@ TEST_F(PgUsersTest, createValidUser) {
|
||||
auto new_user = sut->addUser(std::move(info_), std::move(identity_));
|
||||
ASSERT_TRUE(new_user);
|
||||
|
||||
auto info = db()(select(sqlpp::all_of(t_auth_info)).from(t_auth_info).where(t_auth_info.user_uid == new_user->uid()));
|
||||
auto info = db()(select(sqlpp::all_of(t_auth_info)).from(t_auth_info).where(t_auth_info.id == new_user->uid()));
|
||||
EXPECT_FALSE(info.empty());
|
||||
|
||||
EXPECT_EQ(info.front().password_hash, "$2y$07$RyytUhDhLDbAPjf0b0r2Y.dsg.FlQ7L.xzWHMmoelI81u0MfBrW7q");
|
||||
EXPECT_EQ(info.front().password_method, "bcrypt");
|
||||
EXPECT_EQ(info.front().password_salt, "OM/Z1c4WBFXvwkxh");
|
||||
EXPECT_EQ(info.front().email, "none@eedb.pl");
|
||||
EXPECT_EQ(info.front().email_verified, false);
|
||||
EXPECT_EQ(info.front().user_uid.value(), new_user->uid());
|
||||
|
||||
auto user = db()(select(sqlpp::all_of(t_user)).from(t_user).where(t_user.uid == new_user->uid()));
|
||||
/// TODO check username structure
|
||||
EXPECT_EQ(info.front().status.value(), 0); /// ODO status:Waiting for verification
|
||||
EXPECT_EQ(info.front().id.value(), new_user->uid());
|
||||
|
||||
auto identity = db()(select(t_auth_identity.provider, t_auth_identity.identity)
|
||||
.from(t_auth_identity)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user