simplify sql model
This commit is contained in:
parent
a5a3893415
commit
b0cd59af12
153
sql/schema.sql
153
sql/schema.sql
@ -80,41 +80,26 @@ BEGIN
|
|||||||
end $$ -- $$
|
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;
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -1,453 +0,0 @@
|
|||||||
#include <eedb/db/data/PgUserAuth.hpp>
|
|
||||||
|
|
||||||
#include <eedb/db/connection.hpp>
|
|
||||||
#include <eedb/db/model/auth_identity.h>
|
|
||||||
#include <eedb/db/model/auth_info.h>
|
|
||||||
#include <eedb/db/model/auth_token.h>
|
|
||||||
#include <eedb/db/model/user.h>
|
|
||||||
#include <eedb/db/model/user_action.h>
|
|
||||||
#include <eedb/db/model/user_audit.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cctype>
|
|
||||||
#include <experimental/string_view>
|
|
||||||
#include <functional>
|
|
||||||
#include <iostream>
|
|
||||||
#include <random>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
||||||
#include <boost/locale.hpp>
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
#include <spdlog/spdlog.h>
|
|
||||||
#include <sqlpp11/sqlpp11.h>
|
|
||||||
|
|
||||||
using User = eedb::db::details::User;
|
|
||||||
|
|
||||||
constexpr const char eedb::user_audit_::Data::_alias_t::_literal[];
|
|
||||||
constexpr const char eedb::user_audit::_alias_t::_literal[];
|
|
||||||
|
|
||||||
// enum LoginActions { Login, Logout };
|
|
||||||
// static std::array< std::string_view, 2 > UserActionNames = {{"login", "logout"}};
|
|
||||||
|
|
||||||
std::string RandomString(uint len) {
|
|
||||||
using namespace std;
|
|
||||||
srand(static_cast< unsigned int >(time(nullptr)));
|
|
||||||
experimental::string_view str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
||||||
string newstr;
|
|
||||||
std::size_t pos;
|
|
||||||
while(newstr.size() != len) {
|
|
||||||
pos = static_cast< std::size_t >((rand() % (static_cast< int >(str.size()) - 1)));
|
|
||||||
newstr += str.substr(pos, 1).to_string();
|
|
||||||
}
|
|
||||||
return newstr;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace eedb::db {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
constexpr eedb::user t_user;
|
|
||||||
constexpr eedb::user_audit t_user_history;
|
|
||||||
constexpr eedb::user_action t_user_action;
|
|
||||||
constexpr eedb::auth_identity t_identity;
|
|
||||||
constexpr eedb::auth_info t_info;
|
|
||||||
constexpr eedb::auth_token t_auth_token;
|
|
||||||
|
|
||||||
static const auto auth_info_identity = t_info.join(t_identity).on(t_info.id == t_identity.auth_info_id);
|
|
||||||
static const auto user_auth_info = t_user.join(t_info).on(t_info.user_uid == t_user.uid);
|
|
||||||
static const auto auth_token_info = user_auth_info.join(t_auth_token).on(t_auth_token.auth_info_id == t_info.id);
|
|
||||||
|
|
||||||
static const auto select_login_action_id = select(t_user_action.id) //
|
|
||||||
.from(t_user_action) //
|
|
||||||
.where(t_user_action.name == "login") //
|
|
||||||
.limit(1u);
|
|
||||||
static const auto select_logout_action_id = select(t_user_action.id) //
|
|
||||||
.from(t_user_action) //
|
|
||||||
.where(t_user_action.name == "logout") //
|
|
||||||
.limit(1u);
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
PgUserAuth::PgUserAuth(PgConnection & _db) : db{_db} {}
|
|
||||||
|
|
||||||
User PgUserAuth::findWithId(const std::string & id) const {
|
|
||||||
const auto uid_eq = t_user.uid == std::atoi(id.c_str());
|
|
||||||
|
|
||||||
if(db(select(exists(select(t_user.uid) //
|
|
||||||
.from(t_user) //
|
|
||||||
.where(uid_eq))))
|
|
||||||
.front()
|
|
||||||
.exists) {
|
|
||||||
return User(id);
|
|
||||||
}
|
|
||||||
return User();
|
|
||||||
}
|
|
||||||
|
|
||||||
User PgUserAuth::findWithIdentity(const std::string & provider, const std::string & identity) const {
|
|
||||||
const auto identity_eq = t_identity.identity == identity;
|
|
||||||
const auto provider_eq = t_identity.provider == provider;
|
|
||||||
|
|
||||||
const auto result = db(select(t_info.user_uid) //
|
|
||||||
.from(auth_info_identity) //
|
|
||||||
.where(provider_eq and identity_eq));
|
|
||||||
|
|
||||||
if(!result.empty()) {
|
|
||||||
return User(std::to_string(result.front().user_uid));
|
|
||||||
}
|
|
||||||
return User();
|
|
||||||
}
|
|
||||||
|
|
||||||
User PgUserAuth::findWithEmailToken(const std::string & hash) const {
|
|
||||||
auto ret = db(select(t_user.uid) //
|
|
||||||
.from(user_auth_info) //
|
|
||||||
.where(t_info.email_token == hash));
|
|
||||||
|
|
||||||
if(ret.empty())
|
|
||||||
return {};
|
|
||||||
return {std::to_string(ret.front().uid)};
|
|
||||||
}
|
|
||||||
|
|
||||||
User PgUserAuth::findWithEmail(const std::string & address) const {
|
|
||||||
auto ret = db(select(t_info.user_uid) //
|
|
||||||
.from(t_info) //
|
|
||||||
.where(t_info.email == address and t_info.email_verified == true));
|
|
||||||
|
|
||||||
if(ret.empty())
|
|
||||||
return {};
|
|
||||||
return {std::to_string(ret.front().user_uid)};
|
|
||||||
}
|
|
||||||
|
|
||||||
void PgUserAuth::addIdentity(const User & user, const std::string & provider, const std::string & identity) {
|
|
||||||
const auto uid_eq = t_info.user_uid == std::atoi(user.id().c_str());
|
|
||||||
const auto identity_eq = t_identity.identity == identity;
|
|
||||||
const auto provider_eq = t_identity.provider == provider;
|
|
||||||
|
|
||||||
auto res = db(select(t_info.user_uid, t_info.id) //
|
|
||||||
.from(auth_info_identity)
|
|
||||||
.where(identity_eq and provider_eq and uid_eq));
|
|
||||||
|
|
||||||
if(!res.empty()) {
|
|
||||||
// Wt::log("error") << "cannot add identity " << provider << ":'" << identity << "': already exists";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto auth_info_id = db(select(t_info.id)
|
|
||||||
.from(t_info) //
|
|
||||||
.where(uid_eq));
|
|
||||||
|
|
||||||
if(auth_info_id.empty()) {
|
|
||||||
throw std::exception();
|
|
||||||
}
|
|
||||||
|
|
||||||
db(insert_into(t_identity)
|
|
||||||
.set(t_identity.identity = identity, //
|
|
||||||
t_identity.provider = provider, //
|
|
||||||
t_identity.auth_info_id = auth_info_id.front().id));
|
|
||||||
}
|
|
||||||
|
|
||||||
void PgUserAuth::setIdentity(const User & user, const std::string & provider, const std::string & identity) {
|
|
||||||
const auto uid = std::atoi(user.id().c_str());
|
|
||||||
const auto provider_eq = t_identity.provider == provider;
|
|
||||||
|
|
||||||
db(update(t_identity) //
|
|
||||||
.set(t_identity.identity = identity) //
|
|
||||||
.where(t_identity.auth_info_id == select(t_info.id).from(t_info).where(t_info.user_uid == uid) and provider_eq));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::wstring PgUserAuth::identity(const User & user, const std::string & provider) const {
|
|
||||||
const int uid = std::atoi(user.id().c_str());
|
|
||||||
const auto id_eq = t_info.user_uid == uid;
|
|
||||||
const auto provider_eq = t_identity.provider == provider;
|
|
||||||
|
|
||||||
auto res = db(select(t_identity.identity) //
|
|
||||||
.from(auth_info_identity) //
|
|
||||||
.where(id_eq and provider_eq));
|
|
||||||
|
|
||||||
return boost::locale::conv::utf_to_utf< wchar_t >(res.front().identity.text);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PgUserAuth::removeIdentity(const User & user, const std::string & provider) {
|
|
||||||
const auto id_eq = t_identity.id == std::atoi(user.id().c_str());
|
|
||||||
const auto provider_eq = t_identity.provider == provider;
|
|
||||||
|
|
||||||
db(remove_from(t_identity).where(id_eq and provider_eq));
|
|
||||||
}
|
|
||||||
|
|
||||||
User PgUserAuth::registerNew() {
|
|
||||||
auto user_id = db(sqlpp::postgresql::insert_into(t_user) //
|
|
||||||
.set( //
|
|
||||||
t_user.full_name = RandomString(256), //
|
|
||||||
t_user.status = -1)
|
|
||||||
.returning(t_user.uid))
|
|
||||||
.front()
|
|
||||||
.uid;
|
|
||||||
|
|
||||||
db(insert_into(t_info).set( //
|
|
||||||
t_info.password_hash = "NONE", //
|
|
||||||
t_info.password_method = "NONE", //
|
|
||||||
t_info.password_salt = "NONE", //
|
|
||||||
t_info.user_uid = user_id, //
|
|
||||||
t_info.email = RandomString(20) + "@NONE.org"));
|
|
||||||
|
|
||||||
return User{std::to_string(user_id)};
|
|
||||||
}
|
|
||||||
|
|
||||||
void PgUserAuth::deleteUser(const User & user) {
|
|
||||||
db(remove_from(t_info) //
|
|
||||||
.where(t_info.id == std::atoi(user.id().c_str())));
|
|
||||||
}
|
|
||||||
|
|
||||||
// User::Status PgUserAuth::status(const User & user) const {
|
|
||||||
// const auto id_eq = auth_identity.id == std::atoi(user.id().c_str());
|
|
||||||
|
|
||||||
// auto status = db(select(auth_info.status.as(user_status)) //
|
|
||||||
// .from(join) //
|
|
||||||
// .where(id_eq))
|
|
||||||
// .front()
|
|
||||||
// .user_status;
|
|
||||||
// return status.value() == User::Status::Normal ? User::Status::Normal : User::Status::Disabled;
|
|
||||||
//}
|
|
||||||
|
|
||||||
// void PgUserAuth::setStatus(const User & user, User::Status status) {
|
|
||||||
// db(update(auth_info) //
|
|
||||||
// .set(auth_info.status = static_cast< int >(status)) //
|
|
||||||
// .where(auth_info.id == std::atoi(user.id().c_str())));
|
|
||||||
//}
|
|
||||||
|
|
||||||
void PgUserAuth::setPassword(const User & user, details::PasswordHash password) {
|
|
||||||
db(update(t_info)
|
|
||||||
.set( //
|
|
||||||
t_info.password_hash = password.mvalue(), //
|
|
||||||
t_info.password_method = password.mfunction(), //
|
|
||||||
t_info.password_salt = password.msalt())
|
|
||||||
.where(t_info.user_uid == std::atoi(user.id().c_str())));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<details::PasswordHash> PgUserAuth::password(const User & user) const {
|
|
||||||
const auto id_eq = t_info.user_uid == std::atoi(user.id().c_str());
|
|
||||||
|
|
||||||
auto row = db(select(t_info.password_hash, t_info.password_method, t_info.password_salt)
|
|
||||||
.from(t_info) //
|
|
||||||
.where(id_eq));
|
|
||||||
if(row.empty())
|
|
||||||
return {};
|
|
||||||
auto & front = row.front();
|
|
||||||
return details::PasswordHash{std::move(front.password_method), std::move(front.password_salt), std::move(front.password_hash)};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PgUserAuth::setEmail(const User & user, const std::string & address) {
|
|
||||||
const auto uid_eq = t_info.user_uid == std::atoi(user.id().c_str());
|
|
||||||
|
|
||||||
db(update(t_info) //
|
|
||||||
.set( //
|
|
||||||
t_info.email = address,
|
|
||||||
t_info.email_verified = true) //
|
|
||||||
.where(uid_eq));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string PgUserAuth::email(const User & user) const {
|
|
||||||
auto ret = db(select(t_info.email) //
|
|
||||||
.from(t_info) //
|
|
||||||
.where(t_info.user_uid == std::atoi(user.id().c_str()) and t_info.email_verified == true));
|
|
||||||
if(ret.empty())
|
|
||||||
return {};
|
|
||||||
return std::move(ret.front().email);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PgUserAuth::setUnverifiedEmail(const User & user, const std::string & address) {
|
|
||||||
// orginal implementation of UserDatabase (provided by WT team) sets veryfied and unverified emails in
|
|
||||||
// different fields in database. So in order to verify email, they just copy the unverified_email to email and then
|
|
||||||
// set unverified_email to empty string (db don't allow empty strings as email*)
|
|
||||||
if(address.empty())
|
|
||||||
db(update(t_info) //
|
|
||||||
.set(t_info.email_verified = true) //
|
|
||||||
.where(t_info.user_uid == std::atoi(user.id().c_str())));
|
|
||||||
else
|
|
||||||
db(update(t_info) //
|
|
||||||
.set( //
|
|
||||||
t_info.email = address, //
|
|
||||||
t_info.email_verified = false) //
|
|
||||||
.where(t_info.user_uid == std::atoi(user.id().c_str())));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string PgUserAuth::unverifiedEmail(const User & user) const {
|
|
||||||
auto ret = db(select(t_info.email) //
|
|
||||||
.from(t_info) //
|
|
||||||
.where(t_info.user_uid == std::atoi(user.id().c_str()) and t_info.email_verified == false));
|
|
||||||
if(ret.empty())
|
|
||||||
return {};
|
|
||||||
return std::move(ret.front().email);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PgUserAuth::setEmailToken(const User & user, details::Token token, int role) {
|
|
||||||
db(update(t_info) //
|
|
||||||
.set( //
|
|
||||||
t_info.email_token = token.mhash(), //
|
|
||||||
t_info.email_token_expires = token.expirationTime(),
|
|
||||||
t_info.email_token_role = static_cast< int >(role)) //
|
|
||||||
.where(t_info.user_uid == std::atoi(user.id().c_str())));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional< details::Token > PgUserAuth::emailToken(const User & user) const {
|
|
||||||
auto ret = db(select(t_info.email_token, t_info.email_token_expires) //
|
|
||||||
.from(t_info) //
|
|
||||||
.where(t_info.user_uid == std::atoi(user.id().c_str())));
|
|
||||||
if(ret.empty())
|
|
||||||
return {};
|
|
||||||
auto time = ret.front().email_token_expires.value();
|
|
||||||
return details::Token{ret.front().email_token, time};
|
|
||||||
}
|
|
||||||
|
|
||||||
int PgUserAuth::emailTokenRole(const User & user) const {
|
|
||||||
auto ret = db(select(t_info.email_token_role) //
|
|
||||||
.from(t_info) //
|
|
||||||
.where(t_info.user_uid == std::atoi(user.id().c_str())));
|
|
||||||
if(ret.empty())
|
|
||||||
throw std::exception();
|
|
||||||
auto val = ret.front().email_token_role;
|
|
||||||
return static_cast< int >(val.value());
|
|
||||||
}
|
|
||||||
|
|
||||||
void PgUserAuth::addAuthToken(const User & user, details::Token token) {
|
|
||||||
auto select_identity_id = select(t_info.id) //
|
|
||||||
.from(t_info)
|
|
||||||
.where(t_info.user_uid == std::atoi(user.id().c_str()))
|
|
||||||
.limit(1u);
|
|
||||||
db(insert_into(t_auth_token) //
|
|
||||||
.set( //
|
|
||||||
t_auth_token.auth_info_id = select_identity_id, //
|
|
||||||
t_auth_token.expires = std::chrono::system_clock::now() + ::sqlpp::chrono::days{14}, //
|
|
||||||
t_auth_token.value = token.mhash()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void PgUserAuth::removeAuthToken(const User &, const std::string & hash) {
|
|
||||||
db(remove_from(t_auth_token).where(t_auth_token.value == hash));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<details::User> PgUserAuth::findWithAuthToken(const std::string & hash) const {
|
|
||||||
auto fullUser = t_auth_token //
|
|
||||||
.join(t_info)
|
|
||||||
.on(t_info.id == t_auth_token.auth_info_id);
|
|
||||||
|
|
||||||
auto ret = db(select(t_info.user_uid) //
|
|
||||||
.from(fullUser) //
|
|
||||||
.where(t_auth_token.value == hash and t_auth_token.expires > std::chrono::system_clock::now()));
|
|
||||||
|
|
||||||
if(ret.empty())
|
|
||||||
return {};
|
|
||||||
return details::User{std::to_string(ret.front().user_uid)};
|
|
||||||
}
|
|
||||||
|
|
||||||
int PgUserAuth::updateAuthToken(const User & user, const std::string & oldhash, const std::string & newhash) {
|
|
||||||
// method called only after successful login
|
|
||||||
using namespace std::chrono;
|
|
||||||
using namespace std::string_literals;
|
|
||||||
const auto identity_id = db(select(t_info.id) //
|
|
||||||
.from(t_info) //
|
|
||||||
.where(t_info.user_uid == std::atoi(user.id().c_str())));
|
|
||||||
if(identity_id.empty())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
const auto expires = db(sqlpp::postgresql::update(t_auth_token) //
|
|
||||||
.set(t_auth_token.value = newhash)
|
|
||||||
.where(t_auth_token.value == oldhash)
|
|
||||||
.returning(t_auth_token.expires));
|
|
||||||
|
|
||||||
if(expires.empty())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
const nlohmann::json data = {
|
|
||||||
{"status"s, "success"}, //
|
|
||||||
{"method"s, "token"}, //
|
|
||||||
// {"user_address"s, _env.clientAddress()}, //
|
|
||||||
// {"user_agent"s, _env.userAgent()}, //
|
|
||||||
// {"user_locale"s, _env.locale().name()}, //
|
|
||||||
// {"referer", _env.referer()}, //
|
|
||||||
// {"url_scheme", _env.urlScheme()} //
|
|
||||||
};
|
|
||||||
|
|
||||||
db(insert_into(t_user_history)
|
|
||||||
.set(t_user_history.user_id = std::atoi(user.id().c_str()),
|
|
||||||
t_user_history.action_id = select_login_action_id, //
|
|
||||||
t_user_history.data = data.dump()));
|
|
||||||
|
|
||||||
const auto now = system_clock::now();
|
|
||||||
const auto diff = expires.front().expires.value() - now;
|
|
||||||
return duration_cast< duration< int > >(diff).count();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PgUserAuth::setFailedLoginAttempts(const User & user, int count) {
|
|
||||||
using namespace std::string_literals;
|
|
||||||
const auto getStatus = [count]() { return count ? "failed"s : "success"s; };
|
|
||||||
|
|
||||||
const nlohmann::json data = {
|
|
||||||
{"status"s, getStatus()}, //
|
|
||||||
{"method"s, "password"}, //
|
|
||||||
// {"user_address"s, _env.clientAddress()}, //
|
|
||||||
// {"user_agent"s, _env.userAgent()}, //
|
|
||||||
// {"user_locale"s, _env.locale().name()}, //
|
|
||||||
// {"referer", _env.referer()}, //
|
|
||||||
// {"url_scheme", _env.urlScheme()} //
|
|
||||||
};
|
|
||||||
|
|
||||||
db(insert_into(t_user_history)
|
|
||||||
.set(t_user_history.user_id = std::atoi(user.id().c_str()),
|
|
||||||
t_user_history.action_id = select_login_action_id, //
|
|
||||||
t_user_history.data = data.dump()));
|
|
||||||
}
|
|
||||||
|
|
||||||
template < typename T >
|
|
||||||
inline std::string name_of(const T &) {
|
|
||||||
static_assert(sqlpp::is_column_t< T >::value, "T should by a calumn");
|
|
||||||
return std::string{T::_table::_alias_t::_literal} + '.' + T::_alias_t::_literal;
|
|
||||||
}
|
|
||||||
|
|
||||||
int PgUserAuth::failedLoginAttempts(const User & user) const {
|
|
||||||
const auto res =
|
|
||||||
db(select(count(t_user_history.id)) //
|
|
||||||
.from(t_user_history) //
|
|
||||||
.where(t_user_history.user_id == std::atoi(user.id().c_str()) and //
|
|
||||||
t_user_history.action_id == select_login_action_id and //
|
|
||||||
sqlpp::verbatim< sqlpp::boolean >(name_of(t_user_history.data) + "->>'status' = 'failed'") and //
|
|
||||||
t_user_history.id > select(t_user_history.id) //
|
|
||||||
.from(t_user_history) //
|
|
||||||
.where(t_user_history.user_id == std::atoi(user.id().c_str()) and //
|
|
||||||
t_user_history.action_id == select_login_action_id and //
|
|
||||||
sqlpp::verbatim< sqlpp::boolean >(name_of(t_user_history.data) + "->>'status' = 'success'")) //
|
|
||||||
.limit(1u)));
|
|
||||||
return static_cast< int >(res.front().count);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PgUserAuth::setLastLoginAttempt(const User &, PgUserAuth::time_point) {}
|
|
||||||
|
|
||||||
PgUserAuth::time_point PgUserAuth::lastLoginAttempt(const User & user) const {
|
|
||||||
using namespace std::chrono;
|
|
||||||
const auto res =
|
|
||||||
db(select(t_user_history._when) //
|
|
||||||
.from(t_user_history) //
|
|
||||||
.where(t_user_history.action_id == select_login_action_id and t_user_history.user_id == std::atoi(user.id().c_str())) //
|
|
||||||
.order_by(t_user_history.id.desc()) //
|
|
||||||
.limit(1u));
|
|
||||||
if(res.empty())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
return res.front()._when.value();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PgUserAuth::logout(const User & user) {
|
|
||||||
using namespace std::string_literals;
|
|
||||||
const nlohmann::json data = {{"status"s, "success"}};
|
|
||||||
|
|
||||||
db(insert_into(t_user_history)
|
|
||||||
.set(t_user_history.user_id = std::atoi(user.id().c_str()),
|
|
||||||
t_user_history.action_id = select_logout_action_id, //
|
|
||||||
t_user_history.data = data.dump()));
|
|
||||||
}
|
|
||||||
|
|
||||||
//AbstractUserDatabase::Transaction * PgUserAuth::startTransaction() {
|
|
||||||
// return new TransactionGuard< decltype(db) >(db, _in_transaction);
|
|
||||||
//}
|
|
||||||
} // namespace eedb::db
|
|
||||||
@ -1,125 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <eedb/db/connection.hpp>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace eedb::db {
|
|
||||||
namespace details {
|
|
||||||
class User {
|
|
||||||
public:
|
|
||||||
User(std::string id = {}) : _id{std::move(id)} {}
|
|
||||||
const std::string & id() const {
|
|
||||||
return _id;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string && moveid() {
|
|
||||||
return std::move(_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
operator bool() const {
|
|
||||||
return _id != "";
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string _id;
|
|
||||||
};
|
|
||||||
|
|
||||||
class PasswordHash {
|
|
||||||
public:
|
|
||||||
PasswordHash(std::string function, std::string salt, std::string value)
|
|
||||||
: function_{std::move(function)}, salt_{std::move(salt)}, value_{std::move(value)} {}
|
|
||||||
|
|
||||||
bool empty() const {
|
|
||||||
return value_.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string && mfunction() {
|
|
||||||
return std::move(function_);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string && msalt() {
|
|
||||||
return std::move(salt_);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string && mvalue() {
|
|
||||||
return std::move(value_);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string function_, salt_, value_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Token {
|
|
||||||
public:
|
|
||||||
using time_point = std::chrono::time_point< std::chrono::system_clock, std::chrono::microseconds >;
|
|
||||||
Token(std::string hash, time_point expirationTime) : hash_{std::move(hash)}, expirationTime_{expirationTime} {}
|
|
||||||
|
|
||||||
bool empty() const {
|
|
||||||
return hash_.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string && mhash() {
|
|
||||||
return std::move(hash_);
|
|
||||||
}
|
|
||||||
|
|
||||||
time_point expirationTime() const {
|
|
||||||
return expirationTime_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string hash_;
|
|
||||||
time_point expirationTime_;
|
|
||||||
};
|
|
||||||
} // namespace details
|
|
||||||
|
|
||||||
class[[deprecated]] PgUserAuth {
|
|
||||||
public:
|
|
||||||
using time_point = std::chrono::time_point< std::chrono::system_clock, std::chrono::microseconds >;
|
|
||||||
|
|
||||||
PgUserAuth(PgConnection & db);
|
|
||||||
|
|
||||||
details::User findWithId(const std::string & id) const;
|
|
||||||
details::User findWithIdentity(const std::string & provider, const std::string & identity) const;
|
|
||||||
details::User findWithEmailToken(const std::string & hash) const;
|
|
||||||
details::User findWithEmail(const std::string & address) const;
|
|
||||||
|
|
||||||
void addIdentity(const details::User & user, const std::string & provider, const std::string & id);
|
|
||||||
void setIdentity(const details::User & user, const std::string & provider, const std::string & id);
|
|
||||||
std::wstring identity(const details::User & user, const std::string & provider) const;
|
|
||||||
void removeIdentity(const details::User & user, const std::string & provider);
|
|
||||||
|
|
||||||
details::User registerNew();
|
|
||||||
void deleteUser(const details::User & user);
|
|
||||||
|
|
||||||
void setPassword(const details::User & user, details::PasswordHash password);
|
|
||||||
std::optional< details::PasswordHash > password(const details::User & user) const;
|
|
||||||
|
|
||||||
bool setEmail(const details::User & user, const std::string & address);
|
|
||||||
std::string email(const details::User & user) const;
|
|
||||||
void setUnverifiedEmail(const details::User & user, const std::string & address);
|
|
||||||
std::string unverifiedEmail(const details::User & user) const;
|
|
||||||
|
|
||||||
void setEmailToken(const details::User & user, details::Token token, int role);
|
|
||||||
std::optional<details::Token> emailToken(const details::User & user) const;
|
|
||||||
int emailTokenRole(const details::User & user) const;
|
|
||||||
|
|
||||||
void addAuthToken(const details::User & user, details::Token token);
|
|
||||||
void removeAuthToken(const details::User &user, const std::string & hash);
|
|
||||||
std::optional<details::User> findWithAuthToken(const std::string & hash) const;
|
|
||||||
int updateAuthToken(const details::User & user, const std::string & oldhash, const std::string & newhash);
|
|
||||||
|
|
||||||
void setFailedLoginAttempts(const details::User & user, int count);
|
|
||||||
int failedLoginAttempts(const details::User & user) const;
|
|
||||||
void setLastLoginAttempt(const details::User & user, time_point t);
|
|
||||||
time_point lastLoginAttempt(const details::User & user) const;
|
|
||||||
|
|
||||||
void logout(const details::User & user);
|
|
||||||
|
|
||||||
private:
|
|
||||||
eedb::db::PgConnection & db;
|
|
||||||
// const Wt::WEnvironment & _env;
|
|
||||||
// const Wt::Auth::AuthService * _authService;
|
|
||||||
// int _in_transaction{0};
|
|
||||||
};
|
|
||||||
} // namespace eedb::db
|
|
||||||
@ -8,11 +8,9 @@
|
|||||||
#include <eedb/db/model/auth_identity.h>
|
#include <eedb/db/model/auth_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
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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")";
|
||||||
|
|||||||
@ -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")";
|
||||||
|
|||||||
92
src/eedb/db/model/measurands.h
Normal file
92
src/eedb/db/model/measurands.h
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
#ifndef EEDB_MEASURANDS_H
|
||||||
|
#define EEDB_MEASURANDS_H
|
||||||
|
|
||||||
|
#include <sqlpp11/table.h>
|
||||||
|
#include <sqlpp11/char_sequence.h>
|
||||||
|
#include <sqlpp11/column_types.h>
|
||||||
|
|
||||||
|
namespace eedb {
|
||||||
|
|
||||||
|
namespace measurands_ {
|
||||||
|
|
||||||
|
struct Id {
|
||||||
|
struct _alias_t {
|
||||||
|
static constexpr const char _literal[] = R"("id")";
|
||||||
|
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||||
|
template<typename T>
|
||||||
|
struct _member_t {
|
||||||
|
T id;
|
||||||
|
T &operator()() { return id; }
|
||||||
|
const T &operator()() const { return id; }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
using _traits = ::sqlpp::make_traits<::sqlpp::integer, sqlpp::tag::must_not_insert, sqlpp::tag::must_not_update>;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Name {
|
||||||
|
struct _alias_t {
|
||||||
|
static constexpr const char _literal[] = R"("name")";
|
||||||
|
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||||
|
template<typename T>
|
||||||
|
struct _member_t {
|
||||||
|
T name;
|
||||||
|
T &operator()() { return name; }
|
||||||
|
const T &operator()() const { return name; }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
using _traits = ::sqlpp::make_traits<::sqlpp::text, sqlpp::tag::require_insert>;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Description {
|
||||||
|
struct _alias_t {
|
||||||
|
static constexpr const char _literal[] = R"("description")";
|
||||||
|
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||||
|
template<typename T>
|
||||||
|
struct _member_t {
|
||||||
|
T description;
|
||||||
|
T &operator()() { return description; }
|
||||||
|
const T &operator()() const { return description; }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
using _traits = ::sqlpp::make_traits<::sqlpp::text, sqlpp::tag::can_be_null>;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Dimension_symbol {
|
||||||
|
struct _alias_t {
|
||||||
|
static constexpr const char _literal[] = R"("dimension_symbol")";
|
||||||
|
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||||
|
template<typename T>
|
||||||
|
struct _member_t {
|
||||||
|
T dimension_symbol;
|
||||||
|
T &operator()() { return dimension_symbol; }
|
||||||
|
const T &operator()() const { return dimension_symbol; }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
using _traits = ::sqlpp::make_traits<::sqlpp::text, sqlpp::tag::can_be_null>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct measurands : sqlpp::table_t<measurands,
|
||||||
|
measurands_::Id,
|
||||||
|
measurands_::Name,
|
||||||
|
measurands_::Description,
|
||||||
|
measurands_::Dimension_symbol> {
|
||||||
|
using _value_type = sqlpp::no_value_t;
|
||||||
|
struct _alias_t {
|
||||||
|
static constexpr const char _literal[] = R"("measurands")";
|
||||||
|
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||||
|
template<typename T>
|
||||||
|
struct _member_t {
|
||||||
|
T measurands;
|
||||||
|
T &operator()() { return measurands; }
|
||||||
|
const T &operator()() const { return measurands; }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
76
src/eedb/db/model/metric_systems.h
Normal file
76
src/eedb/db/model/metric_systems.h
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
#ifndef EEDB_METRIC_SYSTEMS_H
|
||||||
|
#define EEDB_METRIC_SYSTEMS_H
|
||||||
|
|
||||||
|
#include <sqlpp11/table.h>
|
||||||
|
#include <sqlpp11/char_sequence.h>
|
||||||
|
#include <sqlpp11/column_types.h>
|
||||||
|
|
||||||
|
namespace eedb {
|
||||||
|
|
||||||
|
namespace metric_systems_ {
|
||||||
|
|
||||||
|
struct Id {
|
||||||
|
struct _alias_t {
|
||||||
|
static constexpr const char _literal[] = R"("id")";
|
||||||
|
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||||
|
template<typename T>
|
||||||
|
struct _member_t {
|
||||||
|
T id;
|
||||||
|
T &operator()() { return id; }
|
||||||
|
const T &operator()() const { return id; }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
using _traits = ::sqlpp::make_traits<::sqlpp::integer, sqlpp::tag::must_not_insert, sqlpp::tag::must_not_update>;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Name {
|
||||||
|
struct _alias_t {
|
||||||
|
static constexpr const char _literal[] = R"("name")";
|
||||||
|
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||||
|
template<typename T>
|
||||||
|
struct _member_t {
|
||||||
|
T name;
|
||||||
|
T &operator()() { return name; }
|
||||||
|
const T &operator()() const { return name; }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
using _traits = ::sqlpp::make_traits<::sqlpp::varchar, sqlpp::tag::require_insert>;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Description {
|
||||||
|
struct _alias_t {
|
||||||
|
static constexpr const char _literal[] = R"("description")";
|
||||||
|
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||||
|
template<typename T>
|
||||||
|
struct _member_t {
|
||||||
|
T description;
|
||||||
|
T &operator()() { return description; }
|
||||||
|
const T &operator()() const { return description; }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
using _traits = ::sqlpp::make_traits<::sqlpp::text, sqlpp::tag::can_be_null>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct metric_systems : sqlpp::table_t<metric_systems,
|
||||||
|
metric_systems_::Id,
|
||||||
|
metric_systems_::Name,
|
||||||
|
metric_systems_::Description> {
|
||||||
|
using _value_type = sqlpp::no_value_t;
|
||||||
|
struct _alias_t {
|
||||||
|
static constexpr const char _literal[] = R"("metric_systems")";
|
||||||
|
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||||
|
template<typename T>
|
||||||
|
struct _member_t {
|
||||||
|
T metric_systems;
|
||||||
|
T &operator()() { return metric_systems; }
|
||||||
|
const T &operator()() const { return metric_systems; }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -1,140 +0,0 @@
|
|||||||
#ifndef EEDB_USER_H
|
|
||||||
#define EEDB_USER_H
|
|
||||||
|
|
||||||
#include <sqlpp11/table.h>
|
|
||||||
#include <sqlpp11/char_sequence.h>
|
|
||||||
#include <sqlpp11/column_types.h>
|
|
||||||
|
|
||||||
namespace eedb {
|
|
||||||
|
|
||||||
namespace user_ {
|
|
||||||
|
|
||||||
struct Uid {
|
|
||||||
struct _alias_t {
|
|
||||||
static constexpr const char _literal[] = R"("uid")";
|
|
||||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
|
||||||
template<typename T>
|
|
||||||
struct _member_t {
|
|
||||||
T uid;
|
|
||||||
T &operator()() { return uid; }
|
|
||||||
const T &operator()() const { return uid; }
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
using _traits = ::sqlpp::make_traits<::sqlpp::integer, sqlpp::tag::must_not_insert, sqlpp::tag::must_not_update>;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Group {
|
|
||||||
struct _alias_t {
|
|
||||||
static constexpr const char _literal[] = R"("group")";
|
|
||||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
|
||||||
template<typename T>
|
|
||||||
struct _member_t {
|
|
||||||
T group;
|
|
||||||
T &operator()() { return group; }
|
|
||||||
const T &operator()() const { return group; }
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
using _traits = ::sqlpp::make_traits<::sqlpp::integer>;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Status {
|
|
||||||
struct _alias_t {
|
|
||||||
static constexpr const char _literal[] = R"("status")";
|
|
||||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
|
||||||
template<typename T>
|
|
||||||
struct _member_t {
|
|
||||||
T status;
|
|
||||||
T &operator()() { return status; }
|
|
||||||
const T &operator()() const { return status; }
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
using _traits = ::sqlpp::make_traits<::sqlpp::integer>;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Full_name {
|
|
||||||
struct _alias_t {
|
|
||||||
static constexpr const char _literal[] = R"("full_name")";
|
|
||||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
|
||||||
template<typename T>
|
|
||||||
struct _member_t {
|
|
||||||
T full_name;
|
|
||||||
T &operator()() { return full_name; }
|
|
||||||
const T &operator()() const { return full_name; }
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
using _traits = ::sqlpp::make_traits<::sqlpp::text, sqlpp::tag::require_insert>;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Created {
|
|
||||||
struct _alias_t {
|
|
||||||
static constexpr const char _literal[] = R"("created")";
|
|
||||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
|
||||||
template<typename T>
|
|
||||||
struct _member_t {
|
|
||||||
T created;
|
|
||||||
T &operator()() { return created; }
|
|
||||||
const T &operator()() const { return created; }
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
using _traits = ::sqlpp::make_traits<::sqlpp::time_point>;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Updated {
|
|
||||||
struct _alias_t {
|
|
||||||
static constexpr const char _literal[] = R"("updated")";
|
|
||||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
|
||||||
template<typename T>
|
|
||||||
struct _member_t {
|
|
||||||
T updated;
|
|
||||||
T &operator()() { return updated; }
|
|
||||||
const T &operator()() const { return updated; }
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
using _traits = ::sqlpp::make_traits<::sqlpp::time_point, sqlpp::tag::can_be_null>;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Config {
|
|
||||||
struct _alias_t {
|
|
||||||
static constexpr const char _literal[] = R"("config")";
|
|
||||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
|
||||||
template<typename T>
|
|
||||||
struct _member_t {
|
|
||||||
T config;
|
|
||||||
T &operator()() { return config; }
|
|
||||||
const T &operator()() const { return config; }
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
using _traits = ::sqlpp::make_traits<::sqlpp::text>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
struct user : sqlpp::table_t<user,
|
|
||||||
user_::Uid,
|
|
||||||
user_::Group,
|
|
||||||
user_::Status,
|
|
||||||
user_::Full_name,
|
|
||||||
user_::Created,
|
|
||||||
user_::Updated,
|
|
||||||
user_::Config> {
|
|
||||||
using _value_type = sqlpp::no_value_t;
|
|
||||||
struct _alias_t {
|
|
||||||
static constexpr const char _literal[] = R"("user")";
|
|
||||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
|
||||||
template<typename T>
|
|
||||||
struct _member_t {
|
|
||||||
T user;
|
|
||||||
T &operator()() { return user; }
|
|
||||||
const T &operator()() const { return user; }
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -24,15 +24,15 @@ namespace eedb {
|
|||||||
using _traits = ::sqlpp::make_traits<::sqlpp::integer, sqlpp::tag::must_not_insert, sqlpp::tag::must_not_update>;
|
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")";
|
||||||
|
|||||||
@ -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
8
src/utils/Visitor.hpp
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#include <utility>
|
||||||
|
|
||||||
|
template < class... Ts >
|
||||||
|
struct Visitor : Ts... {
|
||||||
|
using Ts::operator()...;
|
||||||
|
};
|
||||||
|
template < class... Ts >
|
||||||
|
Visitor(Ts...)->Visitor< std::decay_t< Ts >... >;
|
||||||
@ -8,93 +8,51 @@
|
|||||||
#include <eedb/db/model/auth_identity.h>
|
#include <eedb/db/model/auth_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");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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"));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user