simplify sql model

This commit is contained in:
Bartosz Wieczorek 2018-02-05 14:36:42 +01:00
parent a5a3893415
commit b0cd59af12
25 changed files with 540 additions and 1181 deletions

View File

@ -80,41 +80,26 @@ BEGIN
end $$ -- $$ end $$ -- $$
language 'plpgsql'; -- $$ language 'plpgsql'; -- $$
create table "auth_info" (
create table "user" ( "id" serial not null,
"uid" serial not null,
"group" int not null default default_users_group_id(), "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, "created" timestamp DEFAULT now() not null,
"updated" timestamp, "updated" timestamp,
"config" json not null DEFAULT ('{}'), "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_hash" varchar(100) not null,
"password_method" varchar(20) not null, "password_method" varchar(20) not null,
"password_salt" varchar(20) not null, "password_salt" varchar(20) not null,
"email" varchar(256) not null, "email" varchar(256) not null,
"email_verified" boolean not null default false, "status" integer DEFAULT 0 NOT NULL,
"email_token" varchar(64),
"email_token_expires" timestamp,
"email_token_role" integer,
constraint "pk_auth_into_id" primary key("id"), 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") 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" ( create table "auth_identity" (
@ -137,6 +122,7 @@ create table "auth_token" (
"auth_info_id" bigint, "auth_info_id" bigint,
"value" varchar(64) not null, "value" varchar(64) not null,
"expires" timestamp not null, "expires" timestamp not null,
"role" smallint not null,
constraint "pk_auth_token_id" primary key("id"), 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 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 ''; comment on column "auth_token"."expires" is '';
create table "user_audit_action" ( create table "user_audit_action" (
"id" serial not null, "id" serial not null,
"name" text, "name" text,
@ -163,18 +148,18 @@ insert into "user_audit_action"("name") values ('login'), ('logout');
create table "user_audit" ( create table "user_audit" (
"id" serial not null, "id" serial not null,
"user_id" int not null, "auth_info_id" int not null,
"action_id" int not null, "action_id" int not null,
"data" jsonb, "data" jsonb,
"when_happened" timestamp DEFAULT(now()), "when_happened" timestamp DEFAULT(now()),
constraint "pk_user_history_id" primary key ("id"), 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 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; 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 table "user_audit" IS 'saves user actions like login/logout';
comment on column "user_audit"."id" is ''; 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"."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"."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 ''; comment on column "user_audit"."when_happened" is '';
@ -198,7 +183,7 @@ create table "stat"(
"created" timestamp DEFAULT now() not null, "created" timestamp DEFAULT now() not null,
"updated" timestamp, "updated" timestamp,
constraint "pk_stat" primary key ("id"), 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 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(); 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, "parent_path" ltree,
constraint "pk_category_uid" primary key ("id"), 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_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); ) INHERITS (stat);
create index "ix_category_parent_path" on "category" using GIST ("parent_path"); create index "ix_category_parent_path" on "category" using GIST ("parent_path");
create unique index "ux_category_name" on "category" ( "parent_id", "name" ); create unique index "ux_category_name" on "category" ( "parent_id", "name" );
@ -366,7 +351,7 @@ create table "item"(
"description" TEXT, "description" TEXT,
constraint "pk_items" primary key ("id"), 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_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); ) INHERITS (stat);
create index "ix_item_attributes" on "item" USING GIN ("attributes"); create index "ix_item_attributes" on "item" USING GIN ("attributes");
create unique index "ux_item" on "item"("name", "symbol"); create unique index "ux_item" on "item"("name", "symbol");
@ -384,16 +369,16 @@ comment on column "item"."description" is '';
create table "inventory"( create table "inventory"(
"description" TEXT check(length(description)< 100000), "description" TEXT check(length(description)< 100000),
constraint "pk_inventory" primary key ("id"), 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) constraint "chk_inventory_name_length" check (length(name) < 100)
) INHERITS (stat); ) INHERITS (stat);
create trigger update_inventory_last_update before update on inventory FOR EACH ROW EXECUTE PROCEDURE last_update_column(); create trigger update_inventory_last_update before update on inventory FOR EACH ROW EXECUTE PROCEDURE last_update_column();
create table "user_inventory"( 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, "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_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 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 DO
$$ $$
DECLARE lastid int; DECLARE lastid int;

View File

@ -3,6 +3,7 @@
#include <eedb/auth/Services.hpp> #include <eedb/auth/Services.hpp>
#include <eedb/data/Users.hpp> #include <eedb/data/Users.hpp>
#include <eedb/data/AuthIdentity.hpp> #include <eedb/data/AuthIdentity.hpp>
#include <eedb/data/AuthToken.hpp>
#include <Wt/Auth/AuthService.h> #include <Wt/Auth/AuthService.h>
#include <Wt/Auth/Dbo/AuthInfo.h> #include <Wt/Auth/Dbo/AuthInfo.h>
@ -41,13 +42,37 @@ struct TransactionGuard : public Wt::Auth::AbstractUserDatabase::Transaction {
namespace eedb::auth { 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()); 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 { Wt::Auth::User PgUserAuth::findWithId(const std::string & id) const {
spdlog::get("default")->debug("searching user by id: '{}'...", id); 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) { if(duser) {
spdlog::get("default")->trace("\tuser exists"); 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 { 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); spdlog::get("default")->debug("searching user by identity/provider: '{}'/'{}'...", identity.toUTF8(), provider);
auto _identity = identity.toUTF8(); 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); 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) { if(duser) {
spdlog::get("default")->trace("\tuser exists"); spdlog::get("default")->trace("\tuser exists");
return Wt::Auth::User(std::to_string(duser->uid()), *this); 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 { Wt::Auth::User PgUserAuth::findWithEmailToken(const std::string & hash) const {
spdlog::get("default")->debug("searching user by email token: '{}'...", hash); 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) { if(duser) {
spdlog::get("default")->trace("\tuser exists"); spdlog::get("default")->trace("\tuser exists");
return Wt::Auth::User(std::to_string(duser->uid()), *this); 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 { Wt::Auth::User PgUserAuth::findWithEmail(const std::string & address) const {
spdlog::get("default")->debug("searching user by email: '{}'...", address); 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) { if(duser) {
spdlog::get("default")->trace("\tuser exists"); spdlog::get("default")->trace("\tuser exists");
return Wt::Auth::User(std::to_string(duser->uid()), *this); 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 // @registration
void PgUserAuth::addIdentity(const Wt::Auth::User & user, const std::string & provider, const Wt::WString & identity) { 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()); 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)); 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 { Wt::WString PgUserAuth::identity(const Wt::Auth::User & user, const std::string & provider) const {
spdlog::get("default")->trace("user{} identity: '{}'", provider); 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) if(opt_identity)
return Wt::WString::fromUTF8(std::string{opt_identity.value()->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) { void PgUserAuth::removeIdentity(const Wt::Auth::User & user, const std::string & provider) {
spdlog::get("default")->trace("user{} remove provider: '{}'", user.id(), 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() { Wt::Auth::User PgUserAuth::registerNew() {
spdlog::get("default")->debug("registering new user"); spdlog::get("default")->debug("registering new user");
_priv->_registrationStarted = true;
} }
void PgUserAuth::deleteUser(const Wt::Auth::User & user) { void PgUserAuth::deleteUser(const Wt::Auth::User & user) {
spdlog::get("default")->debug("delete user {}", user.id()); spdlog::get("default")->debug("delete user {}", user.id());
throw std::runtime_error("not implemented void PgUserAuth::deleteUser(const Wt::Auth::User & user)"); throw std::runtime_error("not implemented void PgUserAuth::deleteUser(const Wt::Auth::User & user)");
// _priv->_users->removeUser(eedb::ConstAuthIdentity);
// _userAuth.deleteUser({user.id()}); // _userAuth.deleteUser({user.id()});
} }
@ -153,48 +180,57 @@ void PgUserAuth::deleteUser(const Wt::Auth::User & user) {
// @registration // @registration
void PgUserAuth::setPassword(const Wt::Auth::User & user, const PasswordHash & password) { void PgUserAuth::setPassword(const Wt::Auth::User & user, const PasswordHash & password) {
spdlog::get("default")->trace("user{} set password", user.id()); spdlog::get("default")->trace("user{} set password", user.id());
_password = password; _priv->_password = password;
} }
PasswordHash PgUserAuth::password(const Wt::Auth::User & user) const { PasswordHash PgUserAuth::password(const Wt::Auth::User & user) const {
spdlog::get("default")->trace("user{} get password", user.id()); 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()}; return {password.function(), password.salt(), password.value()};
} }
bool PgUserAuth::setEmail(const Wt::Auth::User & user, const std::string & address) { bool PgUserAuth::setEmail(const Wt::Auth::User & user, const std::string & address) {
// spdlog::get("default")->trace("user{} set email {}", user.id(), address); spdlog::get("default")->trace("user{} set email {}", user.id(), address);
// return _userAuth.setEmail({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 { std::string PgUserAuth::email(const Wt::Auth::User & user) const {
// spdlog::get("default")->trace("user{} get email", user.id()); spdlog::get("default")->trace("user{} get email", user.id());
// return _userAuth.email({user.id()}); return std::string{_priv->_users->findWith(std::atoi(user.id().c_str()))->authInfo().email().address()};
} }
// @registration // @registration
void PgUserAuth::setUnverifiedEmail(const Wt::Auth::User & user, const std::string & address) { void PgUserAuth::setUnverifiedEmail(const Wt::Auth::User & user, const std::string & address) {
// spdlog::get("default")->trace("user{} set unverified email {}", user.id(), address); spdlog::get("default")->trace("user{} set unverified email {}", user.id(), address);
// _userAuth.setUnverifiedEmail({user.id()}, address); _priv->_unverifiedEmail = address;
} }
// @registration // @registration
void PgUserAuth::setEmailToken(const Wt::Auth::User & user, const Token & token, EmailTokenRole role) { 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()); using namespace std::chrono;
// auto exp = ::date::floor<::std::chrono::milliseconds >(std::chrono::system_clock::from_time_t(token.expirationTime().toTime_t())); spdlog::get("default")->trace("user{} set email token {}", user.id(), token.hash());
// _userAuth.setEmailToken({user.id()}, {token.hash(), exp}, static_cast< int >(role)); 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 // @registration
std::string PgUserAuth::unverifiedEmail(const Wt::Auth::User & user) const { std::string PgUserAuth::unverifiedEmail(const Wt::Auth::User & user) const {
// spdlog::get("default")->trace("user{} get unverified email", user.id()); spdlog::get("default")->trace("user{} get unverified email", user.id());
// return _userAuth.unverifiedEmail({user.id()}); assert(_priv->_unverifiedEmail.has_value());
return _priv->_unverifiedEmail.value();
} }
Token PgUserAuth::emailToken(const Wt::Auth::User & user) const { Token PgUserAuth::emailToken(const Wt::Auth::User & user) const {
// spdlog::get("default")->trace("user{} get email token", user.id()); spdlog::get("default")->trace("user{} get email token", user.id());
// auto tok = _userAuth.emailToken({user.id()}); auto tok = _priv->_users->findWith(std::atoi(user.id().c_str()))->authTokens().find(AuthTokenRole::EmailToken);
// .emailToken({user.id()});
// if(!tok) // if(!tok)
// return {}; // return {};
// auto exp = Wt::WDateTime(); // auto exp = Wt::WDateTime();

View File

@ -2,6 +2,8 @@
#include <Wt/Auth/AbstractUserDatabase.h> #include <Wt/Auth/AbstractUserDatabase.h>
#include <utils/spimpl.hpp>
namespace eedb { namespace eedb {
class Users; class Users;
} }
@ -18,10 +20,6 @@ namespace eedb::auth {
class PgUserAuth : public Wt::Auth::AbstractUserDatabase { class PgUserAuth : public Wt::Auth::AbstractUserDatabase {
public: public:
void setAuthService(Wt::Auth::AuthService * s) {
_authService = s;
}
PgUserAuth(std::unique_ptr< eedb::Users > users, const Wt::WEnvironment & env); PgUserAuth(std::unique_ptr< eedb::Users > users, const Wt::WEnvironment & env);
Transaction * startTransaction() override; Transaction * startTransaction() override;
@ -64,15 +62,7 @@ class PgUserAuth : public Wt::Auth::AbstractUserDatabase {
void logout(const Wt::Auth::User & user); void logout(const Wt::Auth::User & user);
private: private:
std::unique_ptr< eedb::Users > users; struct UserAuthPriv;
spimpl::unique_impl_ptr< UserAuthPriv > _priv;
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};
}; };
} // namespace eedb::auth } // namespace eedb::auth

View File

@ -50,7 +50,7 @@ class AuthIdentities {
*/ */
virtual AuthIdentity * addIdentity(std::unique_ptr<AuthIdentity>identity); 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; virtual void removeProvider(std::string_view provider) const = 0;
}; };

View File

@ -1,9 +1,16 @@
#pragma once #pragma once
#include <string>
#include <optional> #include <optional>
#include <string>
#include <variant>
namespace eedb { namespace eedb {
enum class AuthTokenRole : uint8_t { //
Auth,
EmailToken,
PasswordReset
};
class AuthToken { class AuthToken {
public: public:
virtual ~AuthToken() = default; virtual ~AuthToken() = default;
@ -19,10 +26,10 @@ class AuthTokens {
public: public:
virtual ~AuthTokens() = default; 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 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 } // namespace eedb

View File

@ -29,5 +29,8 @@ class Users {
// adds new user into users // adds new user into users
virtual unique_ptr< User > addUser(unique_ptr< AuthInfo >, unique_ptr< AuthIdentity >) = 0; 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 } // namespace eedb

View File

@ -1,7 +1,6 @@
set(SOURCE set(SOURCE
data/PgCategory.cpp data/PgCategory.cpp
data/PgUser.cpp data/PgUser.cpp
data/PgUserAuth.cpp
data/PgUsers.cpp data/PgUsers.cpp
data/PgAuthToken.cpp data/PgAuthToken.cpp

View File

@ -5,6 +5,8 @@
#include <eedb/db/connection.hpp> #include <eedb/db/connection.hpp>
#include <eedb/db/model/auth_token.h> #include <eedb/db/model/auth_token.h>
#include <utils/Visitor.hpp>
#include <sqlpp11/sqlpp11.h> #include <sqlpp11/sqlpp11.h>
#include <map> #include <map>
@ -19,8 +21,10 @@ struct PgAuthToken::PgAuthTokenPriv {
int64_t id; int64_t id;
sqlpp::chrono::microsecond_point expire; sqlpp::chrono::microsecond_point expire;
std::string hash; std::string hash;
bool updated{false};
void update() { void update() {
if(!updated) {
auto & result = db(select(t_auth_token.expires, t_auth_token.value) // auto & result = db(select(t_auth_token.expires, t_auth_token.value) //
.from(t_auth_token) // .from(t_auth_token) //
.where(t_auth_token.id == id)) .where(t_auth_token.id == id))
@ -28,15 +32,12 @@ struct PgAuthToken::PgAuthTokenPriv {
expire = result.expires; expire = result.expires;
hash = std::move(result.value); hash = std::move(result.value);
} }
updated = true;
}
}; };
PgAuthToken::PgAuthToken(db::PgConnection & con, int64_t uid) : _priv{spimpl::make_unique_impl< PgAuthTokenPriv >(con)} { 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->id = uid;
_priv->update();
} }
PgAuthToken::PgAuthToken(db::PgConnection & con, PgAuthToken::PgAuthToken(db::PgConnection & con,
@ -47,13 +48,16 @@ PgAuthToken::PgAuthToken(db::PgConnection & con,
_priv->id = uid; _priv->id = uid;
_priv->expire = expirationtime; _priv->expire = expirationtime;
_priv->hash = std::move(hash); _priv->hash = std::move(hash);
_priv->updated = true;
} }
std::string_view PgAuthToken::token() const { std::string_view PgAuthToken::token() const {
_priv->update();
return _priv->hash; return _priv->hash;
} }
bool PgAuthToken::expired() const { bool PgAuthToken::expired() const {
_priv->update();
return _priv->expire < std::chrono::system_clock::now(); return _priv->expire < std::chrono::system_clock::now();
} }
@ -74,15 +78,19 @@ struct PgAuthTokens::PgAuthTokensPriv {
const User * _owner; const User * _owner;
std::map< std::string, std::unique_ptr< AuthToken > > _cache; 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()) { if(auto tok = _cache.find(token); tok != _cache.end()) {
return tok->second.get(); return tok->second.get();
} }
auto token_data = _db(select(sqlpp::all_of(t_auth_token)) // auto token_data = _db(select(sqlpp::all_of(t_auth_token)) //
.from(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()) if(token_data.empty())
return nullptr; return nullptr;
@ -94,7 +102,29 @@ struct PgAuthTokens::PgAuthTokensPriv {
return iterator->second.get(); 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) { AuthToken * add(std::string token) {
/// FIXME role
using namespace std::chrono; using namespace std::chrono;
auto exp = time_point_cast< microseconds >(system_clock::now() + hours{14 * 24}); auto exp = time_point_cast< microseconds >(system_clock::now() + hours{14 * 24});
@ -102,7 +132,8 @@ struct PgAuthTokens::PgAuthTokensPriv {
.set( // .set( //
t_auth_token.value = token, // t_auth_token.value = token, //
t_auth_token.auth_info_id = _owner->uid(), // 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)) .returning(t_auth_token.id))
.front() .front()
.id; .id;
@ -119,18 +150,22 @@ struct PgAuthTokens::PgAuthTokensPriv {
PgAuthTokens::PgAuthTokens(db::PgConnection & con, const User * owner) : _priv{spimpl::make_unique_impl< PgAuthTokensPriv >(con, owner)} {} 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 { AuthToken * PgAuthTokens::find(std::variant< std::string_view, AuthTokenRole > token) const {
auto ptr = _priv->find(std::string{token}); auto authTokenVisitor = [impl = _priv.get()](std::string_view token) {
if(ptr) return impl->findAuthToken(std::string{token});
return std::make_optional(ptr); };
return {}; 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) { void PgAuthTokens::removeToken(std::string_view token) {
_priv->remove(std::string{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)); return _priv->add(std::move(hash));
} }
} // namespace eedb } // namespace eedb

View File

@ -37,11 +37,11 @@ class PgAuthTokens final : public AuthTokens {
public: public:
PgAuthTokens(db::PgConnection & con, const User * owner); 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; void removeToken(std::string_view token) override;
AuthToken * addToken(std::string hash) override; AuthToken * addToken(std::string hash, AuthTokenRole role) override;
private: private:
struct PgAuthTokensPriv; struct PgAuthTokensPriv;

View File

@ -10,7 +10,6 @@
#include <eedb/db/model/auth_identity.h> #include <eedb/db/model/auth_identity.h>
#include <eedb/db/model/auth_info.h> #include <eedb/db/model/auth_info.h>
#include <eedb/db/model/auth_token.h> #include <eedb/db/model/auth_token.h>
#include <eedb/db/model/user.h>
#include <eedb/db/model/user_audit.h> #include <eedb/db/model/user_audit.h>
#include <Wt/WString.h> #include <Wt/WString.h>
@ -23,16 +22,12 @@ namespace {
SQLPP_ALIAS_PROVIDER(fk_auth_info_id); SQLPP_ALIAS_PROVIDER(fk_auth_info_id);
SQLPP_ALIAS_PROVIDER(user_status); SQLPP_ALIAS_PROVIDER(user_status);
constexpr eedb::user t_user;
constexpr eedb::auth_identity t_identity; constexpr eedb::auth_identity t_identity;
constexpr eedb::auth_info t_info; constexpr eedb::auth_info t_info;
constexpr eedb::auth_token authToken; constexpr eedb::auth_token t_auth_token;
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);
auto auth_info_identity = t_info.join(t_identity).on(t_info.id == t_identity.auth_info_id); 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 = t_info.join(t_auth_token).on(t_auth_token.auth_info_id == t_info.id);
auto auth_token_info = user_auth_info.join(authToken).on(authToken.auth_info_id == t_info.id);
} // namespace } // namespace
namespace eedb { namespace eedb {

View File

@ -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

View File

@ -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

View File

@ -8,11 +8,9 @@
#include <eedb/db/model/auth_identity.h> #include <eedb/db/model/auth_identity.h>
#include <eedb/db/model/auth_info.h> #include <eedb/db/model/auth_info.h>
#include <eedb/db/model/auth_token.h> #include <eedb/db/model/auth_token.h>
#include <eedb/db/model/user.h>
#include <sqlpp11/sqlpp11.h> #include <sqlpp11/sqlpp11.h>
constexpr eedb::user t_user;
constexpr eedb::auth_identity t_auth_identity; constexpr eedb::auth_identity t_auth_identity;
constexpr eedb::auth_info t_auth_info; constexpr eedb::auth_info t_auth_info;
constexpr eedb::auth_token t_auth_token; 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()}; const auto provider_eq = t_auth_identity.provider == std::string{identity.provider()};
auto & db = *(_priv->_db.native()); 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)) // .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)); .where(provider_eq and identity_eq));
if(result.empty()) { if(result.empty()) {
return {}; 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 { unique_ptr< User > PgUsers::findWith(const Email & email) const {
auto & db = *(_priv->_db.native()); 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) // .from(t_auth_info) //
.where(t_auth_info.email == std::string{email.address()})); .where(t_auth_info.email == std::string{email.address()}));
if(result.empty()) { if(result.empty()) {
return {}; 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 {} 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 & 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 & pass = authInfo->password();
const auto & email = authInfo->email(); 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_hash = pass.value(), //
t_auth_info.password_method = pass.function(), // t_auth_info.password_method = pass.function(), //
t_auth_info.password_salt = pass.salt(), // 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)) .returning(t_auth_info.id))
.front() .front()
.id; .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.identity = std::string{authIdentity->identity()},
t_auth_identity.provider = std::string{authIdentity->provider()})); 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 } // namespace eedb

View File

@ -21,6 +21,7 @@ class PgUsers : public Users {
// unique_ptr< User > findWith(const AuthToken & token) const override; // unique_ptr< User > findWith(const AuthToken & token) const override;
unique_ptr< User > addUser(unique_ptr< AuthInfo >, unique_ptr< AuthIdentity >) override; unique_ptr< User > addUser(unique_ptr< AuthInfo >, unique_ptr< AuthIdentity >) override;
void removeUser(std::unique_ptr< AuthInfo > user) const override{}
private: private:
struct PgUsersPriv; struct PgUsersPriv;

View File

@ -24,19 +24,64 @@ namespace eedb {
using _traits = ::sqlpp::make_traits<::sqlpp::integer, sqlpp::tag::must_not_insert, sqlpp::tag::must_not_update>; 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 { 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>; using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template<typename T> template<typename T>
struct _member_t { struct _member_t {
T user_uid; T group;
T &operator()() { return user_uid; } T &operator()() { return group; }
const T &operator()() const { return user_uid; } 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 { struct Password_hash {
@ -99,78 +144,33 @@ namespace eedb {
using _traits = ::sqlpp::make_traits<::sqlpp::varchar, sqlpp::tag::require_insert>; using _traits = ::sqlpp::make_traits<::sqlpp::varchar, sqlpp::tag::require_insert>;
}; };
struct Email_verified { struct Status {
struct _alias_t { 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>; using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template<typename T> template<typename T>
struct _member_t { struct _member_t {
T email_verified; T status;
T &operator()() { return email_verified; } T &operator()() { return status; }
const T &operator()() const { return email_verified; } const T &operator()() const { return status; }
}; };
}; };
using _traits = ::sqlpp::make_traits<::sqlpp::boolean>; using _traits = ::sqlpp::make_traits<::sqlpp::integer>;
};
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>;
}; };
} }
struct auth_info : sqlpp::table_t<auth_info, struct auth_info : sqlpp::table_t<auth_info,
auth_info_::Id, 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_hash,
auth_info_::Password_method, auth_info_::Password_method,
auth_info_::Password_salt, auth_info_::Password_salt,
auth_info_::Email, auth_info_::Email,
auth_info_::Email_verified, auth_info_::Status> {
auth_info_::Email_token,
auth_info_::Email_token_expires,
auth_info_::Email_token_role> {
using _value_type = sqlpp::no_value_t; using _value_type = sqlpp::no_value_t;
struct _alias_t { struct _alias_t {
static constexpr const char _literal[] = R"("auth_info")"; static constexpr const char _literal[] = R"("auth_info")";

View File

@ -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 { struct Value {
@ -68,13 +68,29 @@ namespace eedb {
using _traits = ::sqlpp::make_traits<::sqlpp::time_point, sqlpp::tag::require_insert>; 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, struct auth_token : sqlpp::table_t<auth_token,
auth_token_::Id, auth_token_::Id,
auth_token_::Auth_info_id, auth_token_::Auth_info_id,
auth_token_::Value, auth_token_::Value,
auth_token_::Expires> { auth_token_::Expires,
auth_token_::Role> {
using _value_type = sqlpp::no_value_t; using _value_type = sqlpp::no_value_t;
struct _alias_t { struct _alias_t {
static constexpr const char _literal[] = R"("auth_token")"; static constexpr const char _literal[] = R"("auth_token")";

View 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

View 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

View File

@ -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

View File

@ -24,15 +24,15 @@ namespace eedb {
using _traits = ::sqlpp::make_traits<::sqlpp::integer, sqlpp::tag::must_not_insert, sqlpp::tag::must_not_update>; 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 { 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>; using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template<typename T> template<typename T>
struct _member_t { struct _member_t {
T user_id; T auth_info_id;
T &operator()() { return user_id; } T &operator()() { return auth_info_id; }
const T &operator()() const { return user_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>; using _traits = ::sqlpp::make_traits<::sqlpp::text, sqlpp::tag::can_be_null>;
}; };
struct When { struct When_happened {
struct _alias_t { 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>; using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template<typename T> template<typename T>
struct _member_t { struct _member_t {
T _when; T when_happened;
T &operator()() { return _when; } T &operator()() { return when_happened; }
const T &operator()() const { return _when; } const T &operator()() const { return when_happened; }
}; };
}; };
@ -87,10 +87,10 @@ namespace eedb {
struct user_audit : sqlpp::table_t<user_audit, struct user_audit : sqlpp::table_t<user_audit,
user_audit_::Id, user_audit_::Id,
user_audit_::User_id, user_audit_::Auth_info_id,
user_audit_::Action_id, user_audit_::Action_id,
user_audit_::Data, user_audit_::Data,
user_audit_::When> { user_audit_::When_happened> {
using _value_type = sqlpp::no_value_t; using _value_type = sqlpp::no_value_t;
struct _alias_t { struct _alias_t {
static constexpr const char _literal[] = R"("user_audit")"; static constexpr const char _literal[] = R"("user_audit")";

View File

@ -9,15 +9,15 @@ namespace eedb {
namespace user_inventory_ { namespace user_inventory_ {
struct User_id { struct Auth_info_id {
struct _alias_t { 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>; using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template<typename T> template<typename T>
struct _member_t { struct _member_t {
T user_id; T auth_info_id;
T &operator()() { return user_id; } T &operator()() { return auth_info_id; }
const T &operator()() const { return user_id; } const T &operator()() const { return auth_info_id; }
}; };
}; };
@ -41,7 +41,7 @@ namespace eedb {
} }
struct user_inventory : sqlpp::table_t<user_inventory, struct user_inventory : sqlpp::table_t<user_inventory,
user_inventory_::User_id, user_inventory_::Auth_info_id,
user_inventory_::Inventory_id> { user_inventory_::Inventory_id> {
using _value_type = sqlpp::no_value_t; using _value_type = sqlpp::no_value_t;
struct _alias_t { struct _alias_t {

8
src/utils/Visitor.hpp Normal file
View 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 >... >;

View File

@ -8,93 +8,51 @@
#include <eedb/db/model/auth_identity.h> #include <eedb/db/model/auth_identity.h>
#include <eedb/db/model/auth_info.h> #include <eedb/db/model/auth_info.h>
#include <eedb/db/model/auth_token.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_action.h>
#include <eedb/db/model/user_audit.h> #include <eedb/db/model/user_audit.h>
#include <sqlpp11/sqlpp11.h> #include <sqlpp11/sqlpp11.h>
#include <utils/DbTestBase.hpp> #include <utils/DbTestBase.hpp>
constexpr eedb::user t_user;
constexpr eedb::user_audit t_user_audit; constexpr eedb::user_audit t_user_audit;
constexpr eedb::user_action t_user_action; constexpr eedb::user_action t_user_action;
constexpr eedb::auth_identity t_auth_identity; constexpr eedb::auth_identity t_auth_identity;
constexpr eedb::auth_info t_auth_info; constexpr eedb::auth_info t_auth_info;
constexpr eedb::auth_token t_auth_token; 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 > { class PgAuthTokenTest : public DbTestBase< PgAuthTokenTest > {
public: public:
PgAuthTokenTest() { PgAuthTokenTest() {
using namespace testing; using namespace testing;
db().native()->start_transaction(); 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() { void setExpireDate(std::chrono::system_clock::time_point exp) {
auto sql = R"sql( using namespace std::chrono;
WITH user_ AS ( INSERT INTO "user" ("full_name","status") db()(update(t_auth_token).set(t_auth_token.expires = time_point_cast< microseconds >(exp)).where(t_auth_token.id == sutId));
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);
} }
~PgAuthTokenTest() { ~PgAuthTokenTest() {
@ -102,7 +60,8 @@ class PgAuthTokenTest : public DbTestBase< PgAuthTokenTest > {
} }
protected: protected:
testing::NiceMock< UserMock > user; int64_t authInfoId;
int64_t sutId;
std::unique_ptr< eedb::PgAuthToken > sut; std::unique_ptr< eedb::PgAuthToken > sut;
}; };
@ -110,24 +69,23 @@ template <>
std::unique_ptr< PgTestDatabasePrepare > DbTestBase< PgAuthTokenTest >::_test_db = {}; std::unique_ptr< PgTestDatabasePrepare > DbTestBase< PgAuthTokenTest >::_test_db = {};
using namespace sqlpp; using namespace sqlpp;
using namespace std::chrono;
TEST_F(PgAuthTokenTest, isExpired) { TEST_F(PgAuthTokenTest, isExpired) {
createExpiredToken(); setExpireDate(system_clock::now() - hours{1});
EXPECT_TRUE(sut->expired()); EXPECT_TRUE(sut->expired());
} }
TEST_F(PgAuthTokenTest, isNotExpired) { TEST_F(PgAuthTokenTest, isNotExpired) {
createValidToken(); setExpireDate(system_clock::now() + hours{1});
EXPECT_FALSE(sut->expired()); EXPECT_FALSE(sut->expired());
} }
TEST_F(PgAuthTokenTest, validValue) { TEST_F(PgAuthTokenTest, validValue) {
createValidToken(); EXPECT_EQ(sut->token(), "empty");
EXPECT_EQ(sut->token(), "hash");
} }
TEST_F(PgAuthTokenTest, updateValue) { TEST_F(PgAuthTokenTest, updateValue) {
createValidToken();
sut->update("NEW"); sut->update("NEW");
EXPECT_EQ(sut->token(), "NEW"); EXPECT_EQ(sut->token(), "NEW");
} }

View File

@ -2,20 +2,18 @@
#include <eedb/db/data/PgAuthToken.hpp> #include <eedb/db/data/PgAuthToken.hpp>
#include <eedb/data/Users.hpp>
#include <eedb/data/AuthIdentity.hpp> #include <eedb/data/AuthIdentity.hpp>
#include <eedb/data/Users.hpp>
#include <eedb/db/model/auth_identity.h> #include <eedb/db/model/auth_identity.h>
#include <eedb/db/model/auth_info.h> #include <eedb/db/model/auth_info.h>
#include <eedb/db/model/auth_token.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_action.h>
#include <eedb/db/model/user_audit.h> #include <eedb/db/model/user_audit.h>
#include <sqlpp11/sqlpp11.h> #include <sqlpp11/sqlpp11.h>
#include <utils/DbTestBase.hpp> #include <utils/DbTestBase.hpp>
constexpr eedb::user t_user;
constexpr eedb::user_audit t_user_audit; constexpr eedb::user_audit t_user_audit;
constexpr eedb::user_action t_user_action; constexpr eedb::user_action t_user_action;
constexpr eedb::auth_identity t_auth_identity; constexpr eedb::auth_identity t_auth_identity;
@ -33,8 +31,8 @@ class UserMock : public eedb::User {
int64_t _uid; int64_t _uid;
// User interface // User interface
public: public:
eedb::AuthInfo &authInfo() const override{} eedb::AuthInfo & authInfo() const override {}
}; };
} // namespace } // namespace
@ -43,36 +41,29 @@ class PgAuthTokensTest : public DbTestBase< PgAuthTokensTest > {
PgAuthTokensTest() { PgAuthTokensTest() {
using namespace testing; using namespace testing;
db().native()->start_transaction(); 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() { void createToken(std::string hash, eedb::AuthTokenRole role) {
auto sql = R"sql( db()(sqlpp::postgresql::insert_into(t_auth_token) //
WITH user_ AS ( INSERT INTO "user" ("full_name","status") .set( //
VALUES('none',0) t_auth_token.value = hash, //
RETURNING "user"."uid" AS user_id ) t_auth_token.auth_info_id = authInfoId, //
, auth_info_ AS ( t_auth_token.role = static_cast< int16_t >(role), //
INSERT INTO "auth_info" ("password_hash","password_method","password_salt","user_uid","email") t_auth_token.expires = std::chrono::system_clock::now() + std::chrono::hours{1}));
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);
} }
~PgAuthTokensTest() { ~PgAuthTokensTest() {
@ -81,6 +72,7 @@ class PgAuthTokensTest : public DbTestBase< PgAuthTokensTest > {
protected: protected:
UserMock user; UserMock user;
int64_t authInfoId{0};
std::unique_ptr< eedb::PgAuthTokens > sut; std::unique_ptr< eedb::PgAuthTokens > sut;
}; };
@ -88,33 +80,34 @@ template <>
std::unique_ptr< PgTestDatabasePrepare > DbTestBase< PgAuthTokensTest >::_test_db = {}; std::unique_ptr< PgTestDatabasePrepare > DbTestBase< PgAuthTokensTest >::_test_db = {};
using namespace sqlpp; using namespace sqlpp;
using namespace eedb;
TEST_F(PgAuthTokensTest, tokenFound) { TEST_F(PgAuthTokensTest, authTokenFound) {
createToken(); createToken("token", AuthTokenRole::Auth);
EXPECT_TRUE(sut->find("hash")); EXPECT_TRUE(sut->find("token"));
}
TEST_F(PgAuthTokensTest, emailTokenFound) {
createToken("emailtoken", AuthTokenRole::EmailToken);
EXPECT_TRUE(sut->find(AuthTokenRole::EmailToken));
} }
TEST_F(PgAuthTokensTest, tokenNotFound) { TEST_F(PgAuthTokensTest, tokenNotFound) {
createToken(); EXPECT_FALSE(sut->find("token"));
EXPECT_FALSE(sut->find("bad hash"));
} }
TEST_F(PgAuthTokensTest, readFromCache) { TEST_F(PgAuthTokensTest, addMultipleAuthTokens) {
createToken(); sut->addToken("authtoken1", eedb::AuthTokenRole::Auth);
EXPECT_TRUE(sut->find("hash")); sut->addToken("authtoken2", eedb::AuthTokenRole::Auth);
EXPECT_TRUE(sut->find("hash"));
}
TEST_F(PgAuthTokensTest, addToken) { EXPECT_TRUE(sut->find("authtoken1"));
createToken(); EXPECT_TRUE(sut->find("authtoken2"));
sut->addToken("hash2");
EXPECT_TRUE(sut->find("hash"));
EXPECT_TRUE(sut->find("hash2"));
} }
TEST_F(PgAuthTokensTest, removeToken) { TEST_F(PgAuthTokensTest, removeToken) {
createToken(); sut->addToken("authtoken1", eedb::AuthTokenRole::Auth);
EXPECT_TRUE(sut->find("hash")); sut->addToken("authtoken2", eedb::AuthTokenRole::Auth);
sut->removeToken("hash");
EXPECT_FALSE(sut->find("hash")); sut->removeToken("authtoken1");
EXPECT_FALSE(sut->find("authtoken1"));
} }

View File

@ -5,14 +5,12 @@
#include <eedb/db/model/auth_identity.h> #include <eedb/db/model/auth_identity.h>
#include <eedb/db/model/auth_info.h> #include <eedb/db/model/auth_info.h>
#include <eedb/db/model/auth_token.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_action.h>
#include <eedb/db/model/user_audit.h> #include <eedb/db/model/user_audit.h>
#include <sqlpp11/sqlpp11.h> #include <sqlpp11/sqlpp11.h>
#include <utils/DbTestBase.hpp> #include <utils/DbTestBase.hpp>
constexpr eedb::user t_user;
constexpr eedb::user_audit t_user_audit; constexpr eedb::user_audit t_user_audit;
constexpr eedb::user_action t_user_action; constexpr eedb::user_action t_user_action;
constexpr eedb::auth_identity t_auth_identity; constexpr eedb::auth_identity t_auth_identity;
@ -47,18 +45,16 @@ class PgUsersTest : public DbTestBase< PgUsersTest > {
} }
auto idMap() { auto idMap() {
std::map< std::string, uint64_t > _userIdMap; std::map< std::string, int64_t > _userIdMap;
auto ids = db()( // auto ids = db()( //
select(t_user.uid, t_auth_identity.identity) select(t_auth_info.id, t_auth_identity.identity)
.from(t_user // .from(t_auth_info //
.inner_join(t_auth_info)
.on(t_auth_info.user_uid == t_user.uid)
.inner_join(t_auth_identity) .inner_join(t_auth_identity)
.on(t_auth_identity.auth_info_id == t_auth_info.id)) .on(t_auth_identity.auth_info_id == t_auth_info.id))
.unconditionally()); .unconditionally());
for(const auto & row : ids) { for(const auto & row : ids) {
_userIdMap[row.identity] = row.uid; _userIdMap[row.identity] = row.id;
} }
return _userIdMap; return _userIdMap;
@ -82,18 +78,15 @@ TEST_F(PgUsersTest, createValidUser) {
auto new_user = sut->addUser(std::move(info_), std::move(identity_)); auto new_user = sut->addUser(std::move(info_), std::move(identity_));
ASSERT_TRUE(new_user); 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_FALSE(info.empty());
EXPECT_EQ(info.front().password_hash, "$2y$07$RyytUhDhLDbAPjf0b0r2Y.dsg.FlQ7L.xzWHMmoelI81u0MfBrW7q"); EXPECT_EQ(info.front().password_hash, "$2y$07$RyytUhDhLDbAPjf0b0r2Y.dsg.FlQ7L.xzWHMmoelI81u0MfBrW7q");
EXPECT_EQ(info.front().password_method, "bcrypt"); EXPECT_EQ(info.front().password_method, "bcrypt");
EXPECT_EQ(info.front().password_salt, "OM/Z1c4WBFXvwkxh"); EXPECT_EQ(info.front().password_salt, "OM/Z1c4WBFXvwkxh");
EXPECT_EQ(info.front().email, "none@eedb.pl"); EXPECT_EQ(info.front().email, "none@eedb.pl");
EXPECT_EQ(info.front().email_verified, false); EXPECT_EQ(info.front().status.value(), 0); /// ODO status:Waiting for verification
EXPECT_EQ(info.front().user_uid.value(), new_user->uid()); EXPECT_EQ(info.front().id.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
auto identity = db()(select(t_auth_identity.provider, t_auth_identity.identity) auto identity = db()(select(t_auth_identity.provider, t_auth_identity.identity)
.from(t_auth_identity) .from(t_auth_identity)
@ -113,7 +106,7 @@ TEST_F(PgUsersTest, createWithSameEmailMustFail) {
EXPECT_THROW(createUser("_test_user_2", "test_email@eedb.pl"), sqlpp::postgresql::integrity_constraint_violation); EXPECT_THROW(createUser("_test_user_2", "test_email@eedb.pl"), sqlpp::postgresql::integrity_constraint_violation);
} }
//TEST_F(PgUsersTest, findById) { // TEST_F(PgUsersTest, findById) {
// createTestUsers(); // createTestUsers();
// auto u = sut->findWith(); // auto u = sut->findWith();