diff --git a/CMakeLists.txt b/CMakeLists.txt index 3df9ef9..58ebeee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,9 +4,6 @@ project(eedb) add_definitions( -std=c++17 ) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "/usr/share/cmake/Modules/") - -#set(EEDB_WT_INSTALL_PATH /usr/local/wt_gcc6/) - find_package(Wt REQUIRED) find_package(Sqlpp11 REQUIRED) find_package(PostgreSQL REQUIRED) diff --git a/src/app/main.cpp b/src/app/main.cpp index a905f1a..63ec8a8 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -20,144 +21,151 @@ class AuthApplication : public Wt::WApplication { public: + auto getDbConfig(const Wt::WEnvironment & env) { + auto config = std::make_unique< sqlpp::postgresql::connection_config >(); + + auto readString = [&env](auto && name, auto & val, auto def) { + if(!env.server()->readConfigurationProperty(name, val)) { + val = def; + } + }; + + auto readInt = [&env](auto && name, auto & val, auto def) { + std::string tmp; + if(env.server()->readConfigurationProperty(name, tmp)) { + val = std::atoi(tmp.c_str()); + } else { + val = def; + } + }; + + auto readBool = [&env](auto && name, auto & val, auto def) { + std::string tmp; + if(env.server()->readConfigurationProperty(name, tmp)) { + val = tmp == "true"; + } else { + val = def; + } + }; + + readString("db_host", config->host, "localhost"); + readString("db_user", config->user, "postgres"); + readString("db_password", config->password, "postgres"); + readString("db_name", config->dbname, "eedb"); + readInt("db_port", config->port, 5432); + readBool("db_debug", config->debug, true); + return config; + } + AuthApplication(const Wt::WEnvironment & env) : Wt::WApplication(env) { - login_.changed().connect(this, &AuthApplication::authEvent); + _login.changed().connect(this, &AuthApplication::authEvent); - auto config = std::make_shared< sqlpp::postgresql::connection_config >(); - config->host = "10.154.46.106"; - config->port = 5432; - config->user = "postgres"; - config->password = "postgres"; - config->dbname = "eedb"; - config->debug = true; - - conn_ = std::make_unique< eedb::db::PgConnection >(config); - userDatabase_ = std::make_unique< eedb::auth::PgUserAuth >(*conn_, env); + _dbConnection = std::make_unique< eedb::db::PgConnection >(getDbConfig(env)); + _userDatabase = std::make_unique< eedb::auth::PgUserAuth >(*_dbConnection, env); root()->addStyleClass("container"); useStyleSheet("/resources/style.css"); setTheme(eedb::BootstrapTheme(this).create()); - _authWidget = new eedb::AuthWidget(eedb::auth::Services{}, *userDatabase_, login_); + _authWidget = new eedb::AuthWidget(eedb::auth::Services{}, *_userDatabase, _login); root()->addWidget(_authWidget); } void authEvent() { using namespace Wt; - if(login_.loggedIn()) { + if(_login.loggedIn()) { Wt::WContainerWidget * container = root(); setInternalPath("/app"); root()->removeStyleClass("container"); // Create a navigation bar with a link to a web page. - Wt::WNavigationBar * navigation = new Wt::WNavigationBar(container); + auto navigation = new Wt::WNavigationBar(container); navigation->setTitle("eedb", "http://eedb.pl"); navigation->setResponsive(true); - Wt::WStackedWidget * contentsStack = new Wt::WStackedWidget(container); + auto contentsStack = new Wt::WStackedWidget(container); contentsStack->addStyleClass("contents"); // Setup a Left-aligned menu. - Wt::WMenu * leftMenu = new Wt::WMenu(contentsStack, container); - navigation->addMenu(leftMenu); - - Wt::WText * searchResult = new Wt::WText("Buy or Sell... Bye!"); - - leftMenu->addItem("Home", new Wt::WText("There is no better place!")); - leftMenu->addItem("Sales", searchResult); + Wt::WMenu * mainTabs = new Wt::WMenu(contentsStack, container); + mainTabs->addItem("Home"); + navigation->addMenu(mainTabs); // Setup a Right-aligned menu. - Wt::WMenu * rightMenu = new Wt::WMenu(); - Wt::WMenu * sessionMenu = new Wt::WMenu(); - - navigation->addMenu(rightMenu, Wt::AlignRight); - navigation->addMenu(sessionMenu, Wt::AlignRight); - // Create a popup submenu for the Help menu. - Wt::WPopupMenu * popup = new Wt::WPopupMenu(); + auto popup = new Wt::WPopupMenu(); popup->addItem("Contents"); popup->addItem("Index"); popup->addSeparator(); popup->addItem("About"); - - popup->itemSelected().connect(std::bind( - [=](Wt::WMenuItem * item) { - Wt::WMessageBox * messageBox = new Wt::WMessageBox( - "Help", Wt::WString::fromUTF8("

Showing Help: {1}

").arg(item->text()), Wt::Information, Wt::Ok); - - messageBox->buttonClicked().connect(std::bind([=]() { delete messageBox; })); - - messageBox->show(); - }, - std::placeholders::_1)); - - Wt::WMenuItem * help = new Wt::WMenuItem("Help"); + popup->itemSelected().connect([](auto && item, auto...) { + auto messageBox = + new Wt::WMessageBox("Help", Wt::WString::fromUTF8("

Showing Help: {1}

").arg(item->text()), Wt::Information, Wt::Ok); + messageBox->buttonClicked().connect([messageBox = messageBox](auto...) { delete messageBox; }); + messageBox->show(); + }); + auto help = new Wt::WMenuItem("Help"); help->setMenu(popup); + auto rightMenu = new Wt::WMenu(); rightMenu->addItem(help); -// sessionMenu->addItem("session", _authWidget); + navigation->addMenu(rightMenu, Wt::AlignRight); + + // Setup a Right-aligned menu. + // Create a popup submenu for the Help menu. + auto sessionMenuPopup = new Wt::WPopupMenu(); + sessionMenuPopup->addItem("Logout"); + sessionMenuPopup->itemSelected().connect([=](auto...) { + this->_authWidget->login().logout(); + std::cout << "logout"; + }); + + auto sessionItem = new Wt::WMenuItem("Session"); + sessionItem->setMenu(sessionMenuPopup); + + auto sessionMenu = new Wt::WMenu(); + sessionMenu->addItem(sessionItem); + navigation->addMenu(sessionMenu, Wt::AlignRight); // Add a Search control. - Wt::WLineEdit * edit = new Wt::WLineEdit(); - edit->setEmptyText("Enter a search item"); - - edit->enterPressed().connect(std::bind([=]() { - leftMenu->select(2); // is the index of the "Sales" - searchResult->setText(Wt::WString("Nothing found for {1}.").arg(edit->text())); - })); - - navigation->addSearch(edit, Wt::AlignRight); + // navigation->addSearch(edit, Wt::AlignRight); Wt::WBorderLayout * layout = new Wt::WBorderLayout(); container->setLayout(layout); - const char * cell = "{1} item"; Wt::WStackedWidget * contents = new Wt::WStackedWidget(); - auto menu = new Wt::WMenu(contents, container); - menu->setStyleClass("nav nav-pills nav-stacked"); - - // Add menu items using the default lazy loading policy. - menu->addItem("Internal paths", new Wt::WText("Internal paths contents")); - menu->addItem("Anchor", new Wt::WTextArea("Anchor contents")); - menu->addItem("Tab widget", new Wt::WTextArea("Tab widget contents")); - menu->addItem("Menu", new Wt::WTextArea("Menu contents")); layout->addWidget(navigation, Wt::WBorderLayout::North); - Wt::WText * item = new Wt::WText(Wt::WString(cell).arg("North")); - item->setStyleClass("green-box"); - layout->addWidget(menu, Wt::WBorderLayout::West); + layout->addWidget(new Wt::WText("West"), Wt::WBorderLayout::West); - item = new Wt::WText(Wt::WString(cell).arg("East")); + auto item = new Wt::WText(Wt::WString("East")); item->setStyleClass("green-box"); layout->addWidget(item, Wt::WBorderLayout::East); - item = new Wt::WText(Wt::WString(cell).arg("South")); + item = new Wt::WText(Wt::WString("South")); item->setStyleClass("green-box"); layout->addWidget(item, Wt::WBorderLayout::South); - item = new Wt::WText(Wt::WString(cell).arg("Center")); - item->setStyleClass("green-box"); layout->addWidget(contents, Wt::WBorderLayout::Center); } else { - // Wt::Auth::AuthWidget * authWidget = new Wt::Auth::AuthWidget(*eedb::auth::Services::authService(), *userDatabase_, - // login_); - // root()->clear(); - // authWidget->model()->addPasswordAuth(eedb::auth::Services::passwordService()); - // authWidget->model()->addOAuth(eedb::auth::Services::oAuthServices()); - // authWidget->setRegistrationEnabled(true); - // root()->addWidget(authWidget); + Wt::Auth::AuthWidget * authWidget = new Wt::Auth::AuthWidget(*eedb::auth::Services::authService(), *_userDatabase, _login); + root()->clear(); + authWidget->model()->addPasswordAuth(eedb::auth::Services::passwordService()); + authWidget->model()->addOAuth(eedb::auth::Services::oAuthServices()); + authWidget->setRegistrationEnabled(true); + root()->addWidget(authWidget); Wt::log("notice") << "User logged out."; } } private: - std::unique_ptr< eedb::db::PgConnection > conn_; - std::unique_ptr< eedb::auth::PgUserAuth > userDatabase_; - Wt::Auth::Login login_; + std::unique_ptr< eedb::db::PgConnection > _dbConnection; + std::unique_ptr< eedb::auth::PgUserAuth > _userDatabase; + Wt::Auth::Login _login; eedb::AuthWidget * _authWidget; }; diff --git a/src/eedb/auth/PgUserAuth.cpp b/src/eedb/auth/PgUserAuth.cpp index 51ced6d..375a23d 100644 --- a/src/eedb/auth/PgUserAuth.cpp +++ b/src/eedb/auth/PgUserAuth.cpp @@ -1,11 +1,9 @@ #include "PgUserAuth.hpp" -#include - -#include - #include +#include +#include #include #include #include @@ -19,6 +17,7 @@ #include #include #include +//#include #include #include @@ -32,6 +31,9 @@ using namespace sqlpp; using namespace Wt::Auth; using namespace std::string_literals; +// enum LoginActions { Login, Logout }; +// static std::array< std::string_view, 2 > UserActionNames = {{"login", "logout"}}; + std::string RandomString(int len) { using namespace std; srand(time(0)); @@ -65,26 +67,25 @@ struct TransactionGuard : public Wt::Auth::AbstractUserDatabase::Transaction { namespace eedb::auth { namespace { - SQLPP_ALIAS_PROVIDER(fk_auth_info_id); - SQLPP_ALIAS_PROVIDER(user_status); - constexpr eedb::user t_user; constexpr eedb::user_audit t_user_history; constexpr eedb::user_action t_user_action; - constexpr eedb::auth_identity authIdentity; - constexpr eedb::auth_info authInfo; + constexpr eedb::auth_identity t_identity; + constexpr eedb::auth_info t_info; constexpr eedb::auth_token t_auth_token; - auto auth = t_user.join(authInfo).on(t_user.uid == authInfo.user_uid).join(authIdentity).on(authInfo.id == authIdentity.auth_info_id); - - auto auth_info_identity = authInfo.join(authIdentity).on(authInfo.id == authIdentity.auth_info_id); - auto user_auth_info = t_user.join(authInfo).on(authInfo.user_uid == t_user.uid); - auto auth_token_info = user_auth_info.join(t_auth_token).on(t_auth_token.auth_info_id == authInfo.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 = user_auth_info.join(t_auth_token).on(t_auth_token.auth_info_id == t_info.id); const auto select_login_action_id = select(t_user_action.id) // .from(t_user_action) // .where(t_user_action.name == "login") // .limit(1u); + const auto select_logout_action_id = select(t_user_action.id) // + .from(t_user_action) // + .where(t_user_action.name == "logout") // + .limit(1u); } PgUserAuth::~PgUserAuth() {} @@ -112,10 +113,10 @@ User PgUserAuth::findWithIdentity(const std::string & provider, const Wt::WStrin std::transform(_identity.begin(), _identity.end(), _identity.begin(), ::tolower); } - auto identity_eq = authIdentity.identity == _identity; - auto provider_eq = authIdentity.provider == provider; + auto identity_eq = t_identity.identity == _identity; + auto provider_eq = t_identity.provider == provider; - auto result = db(select(authInfo.user_uid) // + auto result = db(select(t_info.user_uid) // .from(auth_info_identity) // .where(provider_eq and identity_eq)); @@ -126,11 +127,11 @@ User PgUserAuth::findWithIdentity(const std::string & provider, const Wt::WStrin } void PgUserAuth::addIdentity(const User & user, const std::string & provider, const Wt::WString & identity) { - const auto uid_eq = authInfo.user_uid == std::atoi(user.id().c_str()); - const auto identity_eq = authIdentity.identity == identity.toUTF8(); - const auto provider_eq = authIdentity.provider == provider; + const auto uid_eq = t_info.user_uid == std::atoi(user.id().c_str()); + const auto identity_eq = t_identity.identity == identity.toUTF8(); + const auto provider_eq = t_identity.provider == provider; - auto res = db(select(authInfo.user_uid, authInfo.id) // + auto res = db(select(t_info.user_uid, t_info.id) // .from(auth_info_identity) .where(identity_eq and provider_eq and uid_eq)); @@ -139,42 +140,42 @@ void PgUserAuth::addIdentity(const User & user, const std::string & provider, co return; } - auto auth_info_id = db(select(authInfo.id) - .from(authInfo) // + 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(authIdentity) - .set(authIdentity.identity = identity.toUTF8(), // - authIdentity.provider = provider, // - authIdentity.auth_info_id = auth_info_id.front().id)); + db(insert_into(t_identity) + .set(t_identity.identity = identity.toUTF8(), // + t_identity.provider = provider, // + t_identity.auth_info_id = auth_info_id.front().id)); Wt::log("info") << "created new identity for user"; } void PgUserAuth::setIdentity(const User & user, const std::string & provider, const Wt::WString & identity) { const auto uid = std::atoi(user.id().c_str()); - const auto provider_eq = authIdentity.provider == provider; + const auto provider_eq = t_identity.provider == provider; - db(update(authIdentity) // - .set(authIdentity.identity = identity.toUTF8()) // - .where(authIdentity.auth_info_id == - select(authInfo.id) // - .from(authInfo) // - .where(authInfo.user_uid == uid) and + db(update(t_identity) // + .set(t_identity.identity = identity.toUTF8()) // + .where(t_identity.auth_info_id == + select(t_info.id) // + .from(t_info) // + .where(t_info.user_uid == uid) and provider_eq)); } Wt::WString PgUserAuth::identity(const User & user, const std::string & provider) const { - const auto uid = std::atoi(user.id().c_str()); - const auto id_eq = authInfo.user_uid == uid; - const auto provider_eq = authIdentity.provider == provider; + 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(authIdentity.identity) // - .from(auth_info_identity) // + auto res = db(select(t_identity.identity) // + .from(auth_info_identity) // .where(id_eq and provider_eq)); if(res.empty()) @@ -183,10 +184,10 @@ Wt::WString PgUserAuth::identity(const User & user, const std::string & provider } void PgUserAuth::removeIdentity(const User & user, const std::string & provider) { - const auto id_eq = authIdentity.id == std::atoi(user.id().c_str()); - const auto provider_eq = authIdentity.provider == 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(authIdentity) // + db(remove_from(t_identity) // .where(id_eq and provider_eq)); } @@ -199,19 +200,19 @@ User PgUserAuth::registerNew() { .front() .uid; - db(insert_into(authInfo).set( // - authInfo.password_hash = "NONE", // - authInfo.password_method = "NONE", // - authInfo.password_salt = "NONE", // - authInfo.user_uid = user_id, // - authInfo.email = RandomString(20) + "@NONE.org")); + db(insert_into(t_info).set( // + t_info.password_hash = "NONE", // + t_info.password_method = "NONE", // + t_info.password_salt = "NONE", // + t_info.user_uid = user_id, // + t_info.email = RandomString(20) + "@NONE.org")); return User{std::to_string(user_id), *this}; } void PgUserAuth::deleteUser(const User & user) { - db(remove_from(authInfo) // - .where(authInfo.id == std::atoi(user.id().c_str()))); + db(remove_from(t_info) // + .where(t_info.id == std::atoi(user.id().c_str()))); } // User::Status PgUserAuth::status(const User & user) const { @@ -232,19 +233,19 @@ void PgUserAuth::deleteUser(const User & user) { //} void PgUserAuth::setPassword(const User & user, const PasswordHash & password) { - db(update(authInfo) - .set( // - authInfo.password_hash = password.value(), // - authInfo.password_method = password.function(), // - authInfo.password_salt = password.salt()) - .where(authInfo.user_uid == std::atoi(user.id().c_str()))); + db(update(t_info) + .set( // + t_info.password_hash = password.value(), // + t_info.password_method = password.function(), // + t_info.password_salt = password.salt()) + .where(t_info.user_uid == std::atoi(user.id().c_str()))); } PasswordHash PgUserAuth::password(const User & user) const { - const auto id_eq = authInfo.user_uid == std::atoi(user.id().c_str()); + const auto id_eq = t_info.user_uid == std::atoi(user.id().c_str()); - auto row = db(select(authInfo.password_hash, authInfo.password_method, authInfo.password_salt) - .from(authInfo) // + 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 PasswordHash(); @@ -253,20 +254,20 @@ PasswordHash PgUserAuth::password(const User & user) const { } bool PgUserAuth::setEmail(const User & user, const std::string & address) { - const auto uid_eq = authInfo.user_uid == std::atoi(user.id().c_str()); + const auto uid_eq = t_info.user_uid == std::atoi(user.id().c_str()); - db(update(authInfo) // - .set( // - authInfo.email = address, - authInfo.email_verified = true) // + 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(authInfo.email) // - .from(authInfo) // - .where(authInfo.user_uid == std::atoi(user.id().c_str()) and authInfo.email_verified == true)); + 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); @@ -277,30 +278,30 @@ void PgUserAuth::setUnverifiedEmail(const User & user, const std::string & addre // 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(authInfo) // - .set(authInfo.email_verified = true) // - .where(authInfo.user_uid == std::atoi(user.id().c_str()))); + db(update(t_info) // + .set(t_info.email_verified = true) // + .where(t_info.user_uid == std::atoi(user.id().c_str()))); else - db(update(authInfo) // - .set( // - authInfo.email = address, // - authInfo.email_verified = false) // - .where(authInfo.user_uid == std::atoi(user.id().c_str()))); + 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(authInfo.email) // - .from(authInfo) // - .where(authInfo.user_uid == std::atoi(user.id().c_str()) and authInfo.email_verified == false)); + auto ret = db(select(t_info.email) // + .from(t_info) // + .where(t_info.user_uid == std::atoi(user.id().c_str()) and t_info.email_verified == false)); if(ret.empty()) return {}; return std::move(ret.front().email); } User PgUserAuth::findWithEmail(const std::string & address) const { - auto ret = db(select(authInfo.user_uid) // - .from(authInfo) // - .where(authInfo.email == address and authInfo.email_verified == true)); + 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 {}; @@ -309,18 +310,18 @@ User PgUserAuth::findWithEmail(const std::string & address) const { void PgUserAuth::setEmailToken(const User & user, const Token & token, User::EmailTokenRole role) { auto exp = ::date::floor<::std::chrono::milliseconds >(std::chrono::system_clock::from_time_t(token.expirationTime().toTime_t())); - db(update(authInfo) // - .set( // - authInfo.email_token = token.hash(), // - authInfo.email_token_expires = exp, - authInfo.email_token_role = static_cast< int >(role)) // - .where(authInfo.user_uid == std::atoi(user.id().c_str()))); + db(update(t_info) // + .set( // + t_info.email_token = token.hash(), // + t_info.email_token_expires = exp, + t_info.email_token_role = static_cast< int >(role)) // + .where(t_info.user_uid == std::atoi(user.id().c_str()))); } Token PgUserAuth::emailToken(const User & user) const { - auto ret = db(select(authInfo.email_token, authInfo.email_token_expires) // - .from(authInfo) // - .where(authInfo.user_uid == std::atoi(user.id().c_str()))); + 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 exp = Wt::WDateTime(); @@ -331,9 +332,9 @@ Token PgUserAuth::emailToken(const User & user) const { } User::EmailTokenRole PgUserAuth::emailTokenRole(const User & user) const { - auto ret = db(select(authInfo.email_token_role) // - .from(authInfo) // - .where(authInfo.user_uid == std::atoi(user.id().c_str()))); + 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; @@ -343,7 +344,7 @@ User::EmailTokenRole PgUserAuth::emailTokenRole(const User & user) const { User PgUserAuth::findWithEmailToken(const std::string & hash) const { auto ret = db(select(t_user.uid) // .from(user_auth_info) // - .where(authInfo.email_token == hash)); + .where(t_info.email_token == hash)); if(ret.empty()) return {}; @@ -351,9 +352,9 @@ User PgUserAuth::findWithEmailToken(const std::string & hash) const { } void PgUserAuth::addAuthToken(const User & user, const Token & token) { - auto select_identity_id = select(authInfo.id) // - .from(authInfo) - .where(authInfo.user_uid == std::atoi(user.id().c_str())) + 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( // @@ -368,11 +369,11 @@ void PgUserAuth::removeAuthToken(const User &, const std::string & hash) { User PgUserAuth::findWithAuthToken(const std::string & hash) const { auto fullUser = t_auth_token // - .join(authInfo) - .on(authInfo.id == t_auth_token.auth_info_id); + .join(t_info) + .on(t_info.id == t_auth_token.auth_info_id); - auto ret = db(select(authInfo.user_uid) // - .from(fullUser) // + 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()) @@ -383,9 +384,9 @@ User PgUserAuth::findWithAuthToken(const std::string & hash) const { int PgUserAuth::updateAuthToken(const User & user, const std::string & oldhash, const std::string & newhash) { // method called only after successful login using namespace std::chrono; - auto identity_id = db(select(authInfo.id) // - .from(authInfo) // - .where(authInfo.user_uid == std::atoi(user.id().c_str()))); + 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; @@ -438,23 +439,24 @@ void PgUserAuth::setFailedLoginAttempts(const User & user, int count) { } template < typename T > -inline std::string name_of(const T&) { +inline std::string name_of(const T &) { static_assert(sqlpp::is_column_t< T >::value, "T should by a calumn"); return std::string{sqlpp::name_of< typename T::_table >::char_ptr()} + '.' + sqlpp::name_of< T >::char_ptr(); } int PgUserAuth::failedLoginAttempts(const User & user) 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))); + 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); } @@ -476,6 +478,15 @@ Wt::WDateTime PgUserAuth::lastLoginAttempt(const User & user) const { return lastLogin; } +void PgUserAuth::logout(const User & user) { + const tao::json::value 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 = tao::json::to_string(data))); +} + AbstractUserDatabase::Transaction * PgUserAuth::startTransaction() { return new TransactionGuard< decltype(db) >(db); } diff --git a/src/eedb/auth/PgUserAuth.hpp b/src/eedb/auth/PgUserAuth.hpp index 168c7bd..262629d 100644 --- a/src/eedb/auth/PgUserAuth.hpp +++ b/src/eedb/auth/PgUserAuth.hpp @@ -61,6 +61,8 @@ class PgUserAuth : public Wt::Auth::AbstractUserDatabase { int failedLoginAttempts(const Wt::Auth::User & user) const override; void setLastLoginAttempt(const Wt::Auth::User & user, const Wt::WDateTime & t) override; Wt::WDateTime lastLoginAttempt(const Wt::Auth::User & user) const override; + + void logout(const Wt::Auth::User & user); private: eedb::db::PgConnection & db; diff --git a/src/eedb/data/PgUser.cpp b/src/eedb/data/PgUser.cpp index db362c1..9122bbf 100644 --- a/src/eedb/data/PgUser.cpp +++ b/src/eedb/data/PgUser.cpp @@ -19,31 +19,31 @@ SQLPP_ALIAS_PROVIDER(fk_auth_info_id); SQLPP_ALIAS_PROVIDER(user_status); constexpr eedb::user t_user; -constexpr eedb::auth_identity authIdentity; -constexpr eedb::auth_info authInfo; +constexpr eedb::auth_identity t_identity; +constexpr eedb::auth_info t_info; constexpr eedb::auth_token authToken; -auto auth = t_user.join(authInfo).on(t_user.uid == authInfo.user_uid).join(authIdentity).on(authInfo.id == authIdentity.auth_info_id); +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 = authInfo.join(authIdentity).on(authInfo.id == authIdentity.auth_info_id); -auto user_auth_info = t_user.join(authInfo).on(authInfo.user_uid == t_user.uid); -auto auth_token_info = user_auth_info.join(authToken).on(authToken.auth_info_id == authInfo.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 = user_auth_info.join(authToken).on(authToken.auth_info_id == t_info.id); } namespace eedb { PgUserIdentity::PgUserIdentity(db::PgConnection & con, int uid, string provider) : _db(con), _provider(std::move(provider)), _email("") { - select(all_of(authIdentity)) // + select(all_of(t_identity)) // .from(auth_info_identity) // - .where(authInfo.user_uid == uid); + .where(t_info.user_uid == uid); } AuthIdentity::string_view PgUserIdentity::identity() const { - const auto id_eq = authInfo.user_uid == 0; - const auto provider_eq = authIdentity.provider == _provider; + const auto id_eq = t_info.user_uid == 0; + const auto provider_eq = t_identity.provider == _provider; - auto res = _db(select(authIdentity.identity) // + auto res = _db(select(t_identity.identity) // .from(auth_info_identity) // .where(id_eq and provider_eq));