diff --git a/src/libs/auth/include/eedb/auth/PgUserAuth.hpp b/src/libs/auth/include/eedb/auth/PgUserAuth.hpp index a16ce37..89df187 100644 --- a/src/libs/auth/include/eedb/auth/PgUserAuth.hpp +++ b/src/libs/auth/include/eedb/auth/PgUserAuth.hpp @@ -5,6 +5,7 @@ #include namespace eedb { +class User; class Users; class Session; } @@ -22,6 +23,12 @@ class UserDatabase : public Wt::Auth::AbstractUserDatabase { Transaction * startTransaction() override; + // signal will be emmited when a failed login has accured + virtual void registerOnFailedLogin(std::function< void(Wt::Auth::User) >); + + std::unique_ptr findEedbUser(const std::string & provider, const Wt::WString & identity) const; + std::unique_ptr findEedbUser(const Wt::Auth::User & user) const; + Wt::Auth::User findWithId(const std::string & id) const override; Wt::Auth::User findWithIdentity(const std::string & provider, const Wt::WString & identity) const override; Wt::Auth::User findWithEmailToken(const std::string & hash) const override; diff --git a/src/libs/auth/src/PgUserAuth.cpp b/src/libs/auth/src/PgUserAuth.cpp index c1ed46a..96c3f4c 100644 --- a/src/libs/auth/src/PgUserAuth.cpp +++ b/src/libs/auth/src/PgUserAuth.cpp @@ -12,8 +12,6 @@ #include #include -#include -#include #include #include @@ -66,65 +64,7 @@ struct UserDatabase::UserAuthPriv { return _users->findWith(extractEmail(user)); } - auto dumpEnvironment() const { - const auto & env = Wt::WApplication::instance()->environment(); - auto getEnvironment = [](const Wt::WEnvironment & env) { - auto getContentType = [](const Wt::WEnvironment & env) { - switch(env.contentType()) { - case Wt::HtmlContentType::XHTML1: - return "XHTML1"; - case Wt::HtmlContentType::HTML4: - return "HTML4"; - case Wt::HtmlContentType::HTML5: - return "HTML5"; - } - }; - auto getCookies = [](const Wt::WEnvironment & env) { - nlohmann::json cookies; - for(const auto & [ name, data ] : env.cookies()) { - cookies.push_back({name, data}); - } - return cookies; - }; - - auto getScreenSize = [](const Wt::WEnvironment & env) { - return nlohmann::json{{"width", env.screenWidth()}, {"height", env.screenHeight()}}; - }; - - return nlohmann::json{// - {"has_ajax", env.ajax()}, - {"has_webgl", env.webGL()}, - {"has_js", env.javaScript()}, - {"has_css_animations", env.supportsCss3Animations()}, - {"supports_cookies", env.supportsCookies()}, - {"client_address", env.clientAddress()}, - {"content_type", getContentType(env)}, - {"dpi_scale", env.dpiScale()}, - {"locale", env.locale().name()}, - {"screen_size", getScreenSize(env)}, - {"user_agent", env.userAgent()}, - - {"cookies", getCookies(env)}}; - }; - -// auto getLoginState = [](const Wt::Auth::Login & login) { -// switch(login.state()) { -// case Wt::Auth::LoginState::Disabled: -// return "Disabled"; -// case Wt::Auth::LoginState::LoggedOut: -// return "LoggedOut"; -// case Wt::Auth::LoginState::Strong: -// return "Strong"; -// case Wt::Auth::LoginState::Weak: -// return "Weak"; -// } -// }; - - return nlohmann::json{// -// {"login", getLoginState(login())}, - {"environment", getEnvironment(env)}}; - } std::unique_ptr< eedb::Users > _users; std::optional< std::string > _email; @@ -136,6 +76,9 @@ struct UserDatabase::UserAuthPriv { std::optional< std::string > _identity; std::optional< std::string > provider; std::optional< int > _emailTokenRole; + + std::function _failedLoginCallback; + bool _registration; const Wt::Auth::AuthService * _authService; @@ -225,6 +168,7 @@ void UserDatabase::addIdentity(const Wt::Auth::User & user, const std::string & // spdlog::get("default")->debug("setting identity/provider: '{}' for user_id'{}'...", identity.toUTF8(), provider, user.id()); //// _userAuth.setIdentity({user.id()}, provider, identity.toUTF8()); //} + // @registration Wt::WString UserDatabase::identity(const Wt::Auth::User & user, const std::string & provider) const { spdlog::get("default")->trace("user{} identity: '{}'", user.id(), provider); @@ -412,8 +356,7 @@ void UserDatabase::setFailedLoginAttempts(const Wt::Auth::User & user, int count spdlog::get("default")->trace("user{} faild login attempts updated to {}", user.id(), count); // _userAuth.setFailedLoginAttempts({user.id()}, count); if(count != 0) { - auto payload = _priv->dumpEnvironment(); - _priv->_session->loginAttempt(_priv->user(user).get(), payload); + _priv->_failedLoginCallback(user); } } @@ -424,7 +367,6 @@ int UserDatabase::failedLoginAttempts(const Wt::Auth::User & user) const { void UserDatabase::setLastLoginAttempt(const Wt::Auth::User & user, const Wt::WDateTime & date) { spdlog::get("default")->trace("user{} set last login attempt to {}", user.id(), date.toString().toUTF8()); - } Wt::WDateTime UserDatabase::lastLoginAttempt(const Wt::Auth::User & user) const { @@ -461,4 +403,35 @@ AbstractUserDatabase::Transaction * UserDatabase::startTransaction() { }; return new TransactionGuard(_priv->_in_transaction, std::move(commit_create_user)); } + +void UserDatabase::registerOnFailedLogin(std::function< void(Wt::Auth::User) > call) { + _priv->_failedLoginCallback = call; +} + +std::unique_ptr< eedb::User > UserDatabase::findEedbUser(const std::string & provider, const Wt::WString & identity) const { + spdlog::get("default")->debug("searching eedb user by identity/provider: '{}'/'{}'...", identity.toUTF8(), provider); + auto _identity = identity.toUTF8(); + if(_priv->_authService && _priv->_authService->identityPolicy() == Wt::Auth::IdentityPolicy::EmailAddress) { + std::transform(_identity.begin(), _identity.end(), _identity.begin(), ::tolower); + } + + auto duser = _priv->_users->findWith(eedb::AuthIdentityConst{_identity, provider}); + if(duser) { + spdlog::get("default")->trace("\tuser exists"); + return duser; + } + spdlog::get("default")->trace("\tuser not found"); + return {}; +} + +std::unique_ptr UserDatabase::findEedbUser(const Wt::Auth::User &user) const +{ + spdlog::get("default")->debug("searching eedb user by wt user {}'...", user.id()); + if(user.isValid()){ + return _priv->user(user); + } + else{ + return {}; + } +} } // namespace eedb::auth diff --git a/src/libs/webapp/include/eedb/WebApplication.hpp b/src/libs/webapp/include/eedb/WebApplication.hpp index 9330a97..ef0c807 100644 --- a/src/libs/webapp/include/eedb/WebApplication.hpp +++ b/src/libs/webapp/include/eedb/WebApplication.hpp @@ -16,7 +16,7 @@ class Session; class Factory; using AuthPageFactory = std::function< std::unique_ptr< AuthPage >() >; -using HomePageFactory = std::function< std::unique_ptr< HomePage >(Wt::Auth::AuthWidget &) >; +using HomePageFactory = std::function< std::unique_ptr< HomePage >(Wt::WInteractWidget &) >; class WebApplication : public Wt::WApplication { public: diff --git a/src/libs/webapp/include/widget/AuthPage.hpp b/src/libs/webapp/include/widget/AuthPage.hpp index 417c46c..5669b67 100644 --- a/src/libs/webapp/include/widget/AuthPage.hpp +++ b/src/libs/webapp/include/widget/AuthPage.hpp @@ -3,6 +3,8 @@ #include #include +#include + #include namespace Wt { @@ -11,21 +13,28 @@ class WContainerWidget; namespace eedb { +class User; + class AuthPage : public Wt::Auth::AuthWidget { - using _base = Wt::Auth::AuthWidget; + using base = Wt::Auth::AuthWidget; public: AuthPage() = delete; - using _base::_base; + + using base::base; virtual ~AuthPage() = default; virtual void registerOnNeedVerification(std::function< void() > f) = 0; - virtual void registerOnUserWeakLogin(std::function< void() > f) = 0; + virtual void registerOnUserWeakLogin(std::function< void(User *, nlohmann::json) > f) = 0; - virtual void registerOnUserStrongLogin(std::function< void() > f) = 0; + virtual void registerOnLoginAttempt(std::function< void(User *, nlohmann::json) > f) = 0; + + virtual void registerOnUserStrongLogin(std::function< void(User *, nlohmann::json) > f) = 0; virtual void registerOnUserLogout(std::function< void() > f) = 0; + + virtual void processEnvironment() = 0; }; } // namespace eedb diff --git a/src/libs/webapp/include/widget/DefaultAuthPage.hpp b/src/libs/webapp/include/widget/DefaultAuthPage.hpp index da1af3b..33436d5 100644 --- a/src/libs/webapp/include/widget/DefaultAuthPage.hpp +++ b/src/libs/webapp/include/widget/DefaultAuthPage.hpp @@ -1,10 +1,11 @@ #pragma once -#include #include +#include + +#include namespace Wt::Auth { -class AbstractUserDatabase; class Login; } // namespace Wt::Auth @@ -14,19 +15,23 @@ class WEnvironment; namespace eedb::auth { class Services; +class UserDatabase; } // namespace eedb::auth namespace eedb { -class DefaultAuthPage final : public AuthPage { +class DefaultAuthPage : public AuthPage { public: - DefaultAuthPage(// + DefaultAuthPage( // const auth::Services & baseAuth, - std::unique_ptr< Wt::Auth::AbstractUserDatabase > userDatabase, + std::unique_ptr< eedb::auth::UserDatabase > userDatabase, Wt::Auth::Login & login); + void create() override; + void registerOnNeedVerification(std::function< void() > f) override; - void registerOnUserWeakLogin(std::function< void() > f) override; - void registerOnUserStrongLogin(std::function< void() > f) override; + void registerOnLoginAttempt(std::function f) override; + void registerOnUserWeakLogin(std::function< void(User *, nlohmann::json) > f) override; + void registerOnUserStrongLogin(std::function< void(User *, nlohmann::json) > f) override; void registerOnUserLogout(std::function< void() > f) override; void processEnvironment() override; diff --git a/src/libs/webapp/include/widget/DefaultNavigationBar.hpp b/src/libs/webapp/include/widget/DefaultNavigationBar.hpp index 860f24e..6a94926 100644 --- a/src/libs/webapp/include/widget/DefaultNavigationBar.hpp +++ b/src/libs/webapp/include/widget/DefaultNavigationBar.hpp @@ -4,15 +4,10 @@ #include -namespace Wt::Auth { -class AbstractUserDatabase; -class Login; -} // namespace Wt::Auth - namespace eedb { class DefaultNavigationBar final : public NavigationBar { public: - DefaultNavigationBar(Wt::Auth::AuthWidget & auth); + DefaultNavigationBar(Wt::WInteractWidget &auth); void registerLogoutAction(std::function< void() > f) override; }; diff --git a/src/libs/webapp/include/widget/NavigationBar.hpp b/src/libs/webapp/include/widget/NavigationBar.hpp index c6f9d67..b1695d5 100644 --- a/src/libs/webapp/include/widget/NavigationBar.hpp +++ b/src/libs/webapp/include/widget/NavigationBar.hpp @@ -5,10 +5,6 @@ #include #include -namespace Wt { -class WContainerWidget; -} - namespace eedb { class NavigationBar : public Wt::WNavigationBar { public: diff --git a/src/libs/webapp/mock/include/eedb/mock/AuthPageMock.hpp b/src/libs/webapp/mock/include/eedb/mock/AuthPageMock.hpp index 7106f47..5ba9e79 100644 --- a/src/libs/webapp/mock/include/eedb/mock/AuthPageMock.hpp +++ b/src/libs/webapp/mock/include/eedb/mock/AuthPageMock.hpp @@ -11,20 +11,19 @@ namespace eedb { class AuthPageMock : public AuthPage { public: - AuthPageMock(Wt::Auth::Login & login) : AuthPage(login) { - } + AuthPageMock(Wt::Auth::Login & login) : AuthPage(login) {} using AuthPage::AuthPage; MOCK_METHOD1(registerOnNeedVerification, void(std::function< void() >)); - MOCK_METHOD1(registerOnUserWeakLogin, void(std::function< void() >)); - MOCK_METHOD1(registerOnUserStrongLogin, void(std::function< void() >)); + MOCK_METHOD1(registerOnUserWeakLogin, void(std::function< void(User *, nlohmann::json) >)); + MOCK_METHOD1(registerOnLoginAttempt, void(std::function< void(User *, nlohmann::json) >)); + MOCK_METHOD1(registerOnUserStrongLogin, void(std::function< void(User *, nlohmann::json) >)); MOCK_METHOD1(registerOnUserLogout, void(std::function< void() >)); MOCK_METHOD0(processEnvironment, void()); private: - }; } // namespace eedb diff --git a/src/libs/webapp/mock/include/eedb/mock/SessionMock.hpp b/src/libs/webapp/mock/include/eedb/mock/SessionMock.hpp index b1f1ff5..06167c8 100644 --- a/src/libs/webapp/mock/include/eedb/mock/SessionMock.hpp +++ b/src/libs/webapp/mock/include/eedb/mock/SessionMock.hpp @@ -14,10 +14,10 @@ namespace eedb { class SessionMock : public Session { public: virtual ~SessionMock() = default; - void login(const User * user, const nlohmann::json & payload) override {} - void loginAttempt(const User * user, const nlohmann::json & payload) override {} - uint failedLogins(const User * user) const override{} - void logout() override {} + MOCK_METHOD2(login, void(const User * user, const nlohmann::json & payload)); + MOCK_METHOD2(loginAttempt, void(const User * user, const nlohmann::json & payload)); + MOCK_CONST_METHOD1(failedLogins, uint(const User * user)); + MOCK_METHOD0(logout, void()); }; } // namespace eedb diff --git a/src/libs/webapp/src/DefaultAuthPage.cpp b/src/libs/webapp/src/DefaultAuthPage.cpp index 18f4e54..1f6b7c1 100644 --- a/src/libs/webapp/src/DefaultAuthPage.cpp +++ b/src/libs/webapp/src/DefaultAuthPage.cpp @@ -1,37 +1,162 @@ #include -#include +#include #include +#include + +#include +#include +#include + +#include + +#include namespace eedb { struct DefaultAuthPage::DefaultAuthPagePriv { - DefaultAuthPagePriv(std::unique_ptr< Wt::Auth::AbstractUserDatabase > userDatabase) : _userDatabase{std::move(userDatabase)} {} + + auto dumpEnvironment() const { + const auto & env = Wt::WApplication::instance()->environment(); + auto getEnvironment = [](const Wt::WEnvironment & env) { + auto getContentType = [](const Wt::WEnvironment & env) { + switch(env.contentType()) { + case Wt::HtmlContentType::XHTML1: + return "XHTML1"; + case Wt::HtmlContentType::HTML4: + return "HTML4"; + case Wt::HtmlContentType::HTML5: + return "HTML5"; + } + }; + + auto getCookies = [](const Wt::WEnvironment & env) { + nlohmann::json cookies; + for(const auto & [ name, data ] : env.cookies()) { + cookies.push_back({name, data}); + } + return cookies; + }; + + auto getScreenSize = [](const Wt::WEnvironment & env) { + return nlohmann::json{{"width", env.screenWidth()}, {"height", env.screenHeight()}}; + }; + + auto getTech = [](const Wt::WEnvironment & env) { + return nlohmann::json{{"ajax", env.ajax()}, + {"webgl", env.webGL()}, + {"js", env.javaScript()}, + {"css_animations", env.supportsCss3Animations()}, + {"cookies", env.supportsCookies()}}; + }; + + auto getParameters = [](const Wt::WEnvironment & env) { + auto serializeValues = [](const auto & values) { + nlohmann::json serialized{}; + for(const auto & value : values) { + if(not value.empty()) + serialized.push_back(value); + } + return serialized; + }; + + nlohmann::json map{}; + + for(const auto & [name, values ]: env.getParameterMap()){ + if(not name.empty()) + map[name] = serializeValues(values); + } + + return map; + }; + + auto getHeaders = [](const Wt::WEnvironment & env) { + using namespace std::string_literals; + const auto headers = std::experimental::make_array( // + "Accept", + "Accept-Language", + "Access-Control-Request-Headers", + "Authorization", + "Accept-Encoding", + "Cache-Control", + "Connection", + "Cookie" + "Content-Length", + "Content-type", + "QUERY_STRING", + "Pragma", + "Range", + "Referer", + "Redirect-Secret", + "Sec-WebSocket-Version", + "Sec-WebSocket-Extensions", + "SSL-Client-Certificates", + "Upgrade", + "X-Forwarded-Host", + "X-Forwarded-For", + "X-Forwarded-Proto"); + + nlohmann::json h; + + for(const auto header : headers) { + auto value = env.headerValue(header); + if(not value.empty()) { + h[header]=value; + } + } + + return h; + }; + + return nlohmann::json{// + {"supported_technologies", getTech(env)}, + {"headers", getHeaders(env)}, + {"parameters", getParameters(env)}, + {"client_address", env.clientAddress()}, + {"content_type", getContentType(env)}, + {"dpi_scale", env.dpiScale()}, + {"locale", env.locale().name()}, + {"screen_size", getScreenSize(env)}, + {"user_agent", env.userAgent()}, + {"cookies", getCookies(env)}}; + }; + + return nlohmann::json{// + // {"login", getLoginState(login())}, + {"environment", getEnvironment(env)}}; + } + + DefaultAuthPagePriv(std::unique_ptr< eedb::auth::UserDatabase > userDatabase) : _userDatabase{std::move(userDatabase)} { + _userDatabase->registerOnFailedLogin([this](Wt::Auth::User user){ + std::cout << dumpEnvironment().dump(2); + _onLoginAttempt(nullptr, dumpEnvironment() ); + }); + } Wt::Signal<> _onNeedEmailVerification; - Wt::Signal<> _onUserWeakLogin; - Wt::Signal<> _onUserStrongLogin; + Wt::Signal< User *, nlohmann::json > _onUserWeakLogin; + Wt::Signal< User *, nlohmann::json > _onLoginAttempt; + Wt::Signal< User *, nlohmann::json > _onUserStrongLogin; Wt::Signal<> _onUserLogout; - std::unique_ptr< Wt::Auth::AbstractUserDatabase > _userDatabase; + std::unique_ptr< eedb::auth::UserDatabase > _userDatabase; }; DefaultAuthPage::DefaultAuthPage(const auth::Services & baseAuth, - std::unique_ptr< Wt::Auth::AbstractUserDatabase > userDatabase, + std::unique_ptr< eedb::auth::UserDatabase > userDatabase, Wt::Auth::Login & _login) : AuthPage(*baseAuth.authService(), *userDatabase, _login), _priv{spimpl::make_unique_impl< DefaultAuthPagePriv >(std::move(userDatabase))} { model()->addPasswordAuth(eedb::auth::Services::passwordService()); model()->addOAuth(eedb::auth::Services::oAuthServices()); setRegistrationEnabled(true); - setId("eedb.authWidget"); + Wt::Auth::AuthWidget::setId("eedb.authWidget"); login().changed().connect([this]() { - if(login().state() == Wt::Auth::LoginState::Strong) { - _priv->_onUserStrongLogin.emit(); + _priv->_onUserStrongLogin.emit(_priv->_userDatabase->findEedbUser(login().user()).get(), _priv->dumpEnvironment()); } else if(login().state() == Wt::Auth::LoginState::Weak) { - _priv->_onUserWeakLogin.emit(); + _priv->_onUserWeakLogin.emit(_priv->_userDatabase->findEedbUser(login().user()).get(), _priv->dumpEnvironment()); } else if(login().state() == Wt::Auth::LoginState::Disabled) { _priv->_onNeedEmailVerification.emit(); } else { @@ -40,15 +165,23 @@ DefaultAuthPage::DefaultAuthPage(const auth::Services & baseAuth, }); } +void DefaultAuthPage::create() { + AuthWidget::create(); +} + void DefaultAuthPage::registerOnNeedVerification(std::function< void() > f) { _priv->_onNeedEmailVerification.connect(f); } -void DefaultAuthPage::registerOnUserWeakLogin(std::function< void() > f) { +void DefaultAuthPage::registerOnLoginAttempt(std::function< void(User *, nlohmann::json) > f) { + _priv->_onLoginAttempt.connect(f); +} + +void DefaultAuthPage::registerOnUserWeakLogin(std::function< void(User *, nlohmann::json) > f) { _priv->_onUserWeakLogin.connect(f); } -void DefaultAuthPage::registerOnUserStrongLogin(std::function< void() > f) { +void DefaultAuthPage::registerOnUserStrongLogin(std::function< void(User *, nlohmann::json) > f) { _priv->_onUserStrongLogin.connect(f); } diff --git a/src/libs/webapp/src/DefaultNavigationBar.cpp b/src/libs/webapp/src/DefaultNavigationBar.cpp index 31b9254..2061fe6 100644 --- a/src/libs/webapp/src/DefaultNavigationBar.cpp +++ b/src/libs/webapp/src/DefaultNavigationBar.cpp @@ -8,7 +8,7 @@ namespace eedb { -DefaultNavigationBar::DefaultNavigationBar(Wt::Auth::AuthWidget & auth) { +DefaultNavigationBar::DefaultNavigationBar(Wt::WInteractWidget & auth) { // Setup a Left-aligned menu. setObjectName("navigation_bar"); @@ -29,7 +29,7 @@ DefaultNavigationBar::DefaultNavigationBar(Wt::Auth::AuthWidget & auth) { sessionMenuPopup->setObjectName("navigation_bar.session_menu_popup"); auto authItem = sessionMenuPopup->addItem("Logout"); - authItem->triggered().connect([p = &auth](Wt::WMenuItem *) { p->login().logout(); }); + authItem->triggered().connect([p = &auth](Wt::WMenuItem *) { p->clicked(); }); { auto sessionItem = std::make_unique< Wt::WMenuItem >("Session"); sessionItem->setMenu(std::move(sessionMenuPopup)); @@ -40,18 +40,9 @@ DefaultNavigationBar::DefaultNavigationBar(Wt::Auth::AuthWidget & auth) { } } -// void DefaultNavigationBar::attachTo(Wt::WContainerWidget * container) { -// _priv->create(); -// container->addWidget(std::move(_priv->_bar)); -//} +void DefaultNavigationBar::registerLogoutAction(std::function f) +{ -// void DefaultNavigationBar::detach() { -// auto parent = dynamic_cast< Wt::WContainerWidget * >(_priv->_bar->parent()); -// if(parent) -// parent->removeWidget(_priv->_bar.get()); -//} - -void DefaultNavigationBar::registerLogoutAction(std::function< void() > f) { - // _priv->onLogin.connect([f]() { f(); }); } + } // namespace eedb diff --git a/src/libs/webapp/src/WebApplication.cpp b/src/libs/webapp/src/WebApplication.cpp index 65fda27..cedc519 100644 --- a/src/libs/webapp/src/WebApplication.cpp +++ b/src/libs/webapp/src/WebApplication.cpp @@ -59,10 +59,29 @@ WebApplication::WebApplication( // auto authPage = authPageFactory(); _savedIds[authWidgetNameTag] = authPage->id(); + authPage->registerOnNeedVerification([] {}); - authPage->registerOnUserWeakLogin([=] { authEventLogin(LoginState::weak); }); - authPage->registerOnUserStrongLogin([=] { authEventLogin(LoginState::strong); }); - authPage->registerOnUserLogout([=] { authEventLogout(); }); + + authPage->registerOnLoginAttempt([session = _session.get()](User * user, nlohmann::json payload) { // + session->loginAttempt(user, payload); + }); + + authPage->registerOnUserWeakLogin([ =, session = _session.get() ](User * user, nlohmann::json payload) { + session->login(user, payload); + authEventLogin(LoginState::weak); + }); + + authPage->registerOnUserStrongLogin([ =, session = _session.get() ](User * user, nlohmann::json payload) { + session->login(user, payload); + spdlog::get("default")->info("User strong login payloag: {}", payload.dump(2)); + authEventLogin(LoginState::strong); + }); + + authPage->registerOnUserLogout([ =, session = _session.get() ] { + session->logout(); + authEventLogout(); + }); + root()->addWidget(std::move(authPage))->processEnvironment(); } @@ -86,14 +105,8 @@ void WebApplication::authEventLogout() { } void WebApplication::notify(const Wt::WEvent & event) { - // Grab resources for during request handling - // try { measure m; WApplication::notify(event); - // } catch(MyException & exception) { - // handle this exception in a central place - // } - // Free resources used during request handling } } // namespace eedb diff --git a/src/libs/webapp/src/WebApplicationFactory.cpp b/src/libs/webapp/src/WebApplicationFactory.cpp index a431638..f497054 100644 --- a/src/libs/webapp/src/WebApplicationFactory.cpp +++ b/src/libs/webapp/src/WebApplicationFactory.cpp @@ -41,8 +41,10 @@ std::unique_ptr< Wt::WApplication > WebApplicationFactory::create( // }; auto homePageFactory = - [ repository = repository.get(), session = session.get() ](Wt::Auth::AuthWidget & authWidget)->std::unique_ptr< eedb::HomePage > { - auto navigationBarFactory = [](Wt::Auth::AuthWidget & w) { return std::make_unique< eedb::DefaultNavigationBar >(w); }; + [ repository = repository.get(), session = session.get() ](Wt::WInteractWidget & authWidget)->std::unique_ptr< eedb::HomePage > { + auto navigationBarFactory = [](Wt::WInteractWidget & w) { // + return std::make_unique< eedb::DefaultNavigationBar >(w); + }; auto categoriesTreeFactory = [repository]() { return std::make_unique< eedb::DefaultCategoriesTree >(repository->categoriesRepository()); }; @@ -51,7 +53,8 @@ std::unique_ptr< Wt::WApplication > WebApplicationFactory::create( // }; return make_unique< eedb::WebApplication >( - env, std::move(session), std::move(login), std::move(authPageFactory), std::move(homePageFactory), std::move(repository)); + env, std::move(session), std::move(login), std::move(authPageFactory), + std::move(homePageFactory), std::move(repository)); } } // namespace eedb diff --git a/src/libs/webapp/test/test_WebApplication.cpp b/src/libs/webapp/test/test_WebApplication.cpp index fee95ea..61895ea 100644 --- a/src/libs/webapp/test/test_WebApplication.cpp +++ b/src/libs/webapp/test/test_WebApplication.cpp @@ -4,6 +4,8 @@ #include +#include + #include #include #include @@ -13,7 +15,7 @@ using namespace testing; class HomeFactoryMock { public: - MOCK_CONST_METHOD1(impl, std::unique_ptr< eedb::HomePage >(Wt::Auth::AuthWidget &)); + MOCK_CONST_METHOD1(impl, std::unique_ptr< eedb::HomePage >(Wt::WWidget &)); }; class WebApplicationTest : public testing::Test { @@ -27,6 +29,7 @@ class WebApplicationTest : public testing::Test { EXPECT_CALL(*authPage, registerOnNeedVerification(_)).WillOnce(SaveArg< 0 >(&_needVerificationCB)); EXPECT_CALL(*authPage, registerOnUserWeakLogin(_)).WillOnce(SaveArg< 0 >(&_weakLoginCB)); + EXPECT_CALL(*authPage, registerOnLoginAttempt(_)).WillOnce(SaveArg< 0 >(&_onLoginAttempt)); EXPECT_CALL(*authPage, registerOnUserStrongLogin(_)).WillOnce(SaveArg< 0 >(&_strongLoginCB)); EXPECT_CALL(*authPage, registerOnUserLogout(_)).WillOnce(SaveArg< 0 >(&_userLogoutCB)); @@ -47,8 +50,9 @@ class WebApplicationTest : public testing::Test { UniquePtrMockWrapper< HomeFactoryMock > homePageFactory; UniquePtrMockWrapper< eedb::HomePageMock > homePage; - std::function< void() > _strongLoginCB; - std::function< void() > _weakLoginCB; + std::function< void(eedb::User *, nlohmann::json) > _strongLoginCB; + std::function< void(eedb::User *, nlohmann::json) > _weakLoginCB; + std::function< void(eedb::User *, nlohmann::json) > _onLoginAttempt; std::function< void() > _userLogoutCB; std::function< void() > _needVerificationCB; @@ -57,14 +61,22 @@ class WebApplicationTest : public testing::Test { TEST_F(WebApplicationTest, strongLoginTrigersHomepageCreation) { EXPECT_CALL(*homePageFactory, impl(Ref(*authPage))).WillOnce(Return(ByMove(homePage.getPtr()))); - _strongLoginCB(); + EXPECT_CALL(*session, login(_, _)); + _strongLoginCB(nullptr, {}); } TEST_F(WebApplicationTest, weakLoginTrigersHomepageCreation) { EXPECT_CALL(*homePageFactory, impl(Ref(*authPage))).WillOnce(Return(ByMove(homePage.getPtr()))); - _weakLoginCB(); + EXPECT_CALL(*session, login(_, _)); + _weakLoginCB(nullptr, {}); +} + +TEST_F(WebApplicationTest, loginAttemptIsSaved) { + EXPECT_CALL(*session, loginAttempt(_, _)); + _onLoginAttempt(nullptr, {}); } TEST_F(WebApplicationTest, logout) { + EXPECT_CALL(*session, logout()); _userLogoutCB(); } diff --git a/src/libs/webapp/test/test_eedb_DefaultAuthPage.cpp b/src/libs/webapp/test/test_eedb_DefaultAuthPage.cpp index d5f7f04..128785b 100644 --- a/src/libs/webapp/test/test_eedb_DefaultAuthPage.cpp +++ b/src/libs/webapp/test/test_eedb_DefaultAuthPage.cpp @@ -1,83 +1,153 @@ -//#include +#include -//#include "utils/UniquePtrMockWrapper.hpp" +#include "utils/UniquePtrMockWrapper.hpp" -//#include "mocks/SessionMock.hpp" -//#include "mocks/db/UserDatabaseMock.hpp" -//#include "mocks/widgets/MainPageMock.hpp" +#include -//#include -//#include -//#include +#include +#include +#include -/////TODO change to mocked version -//#include +#include +#include +#include +#include +#include +#include -//#include +#include -//using namespace testing; +class Veryfier : public Wt::Auth::PasswordService::AbstractVerifier { + public: + bool needsUpdate(const Wt::Auth::PasswordHash & hash) const override { + return false; + } + Wt::Auth::PasswordHash hashPassword(const Wt::WString & password) const override { + return {"", "", ""}; + } + MOCK_CONST_METHOD2(verify, bool(const Wt::WString & password, const Wt::Auth::PasswordHash & hash)); +}; -//class DefaultAuthPageTest : public Test { -// public: -// DefaultAuthPageTest() : app(env) { -// createApp(); -// } +class UserDatabaseMock : public Wt::Auth::AbstractUserDatabase { + // AbstractUserDatabase interface + public: + MOCK_CONST_METHOD1(findWithId, Wt::Auth::User(const std::string & id)); + MOCK_CONST_METHOD2(findWithIdentity, Wt::Auth::User(const std::string & provider, const Wt::WString & identity)); + MOCK_METHOD3(addIdentity, void(const Wt::Auth::User & user, const std::string & provider, const Wt::WString & id)); + MOCK_CONST_METHOD2(identity, Wt::WString(const Wt::Auth::User & user, const std::string & provider)); + MOCK_METHOD2(removeIdentity, void(const Wt::Auth::User & user, const std::string & provider)); +}; -// void createApp() { -// auto services = eedb::auth::Services(); -// // auto session = std::make_unique< eedb::SessionMock >(); +using namespace testing; -// // const eedb::auth::Services & baseAuth, -// // std::unique_ptr< Wt::Auth::AbstractUserDatabase > userDatabase, -// // Wt::Auth::Login & session +class DefaultAuthPageTest : public Test { + public: + DefaultAuthPageTest() : app(env) { + createApp(); + } -// // EXPECT_CALL(*session, login()).WillOnce(ReturnRef(login)); + void createApp() { + eedb::auth::Services::configureAuth(); + auto services = eedb::auth::Services(); -// // homePage = std::make_unique< eedb::Home >(*session); -// // sut = dynamic_cast< eedb::Home * >(homePage.get()); + dynamic_cast< Wt::Auth::PasswordService * >(services.passwordService())->setVerifier(passwordVerifier.getPtr()); + services.authService()->setEmailVerificationRequired(false); -// // EXPECT_CALL(*session, enviroment()).WillOnce(ReturnRef(env)); -//// sut = new eedb::AuthPageImpl(services, session.login()); -// sut = std::make_unique< eedb::DefaultAuthPage >(services, std::move(usedDatabaseMock), login); -// } +// sut = std::make_unique< eedb::DefaultAuthPage >(services, this->usedDatabaseMock.getPtr(), login); + sut->model()->setValidator("password", nullptr); + sut->create(); -// protected: -// Wt::Test::WTestEnvironment env; -// Wt::Auth::Login login; + sut->registerOnUserStrongLogin(_strongLoginCallback.AsStdFunction()); + sut->registerOnUserWeakLogin(_weakLoginCallback.AsStdFunction()); + sut->registerOnLoginAttempt(_attemptLoginCallback.AsStdFunction()); + sut->registerOnUserLogout(_logoutCallback.AsStdFunction()); + } -// std::unique_ptr usedDatabaseMock; + Wt::WPushButton * loginBtn() const { + return dynamic_cast< Wt::WPushButton * >(sut->resolveWidget("login")); + } -// std::function< void() > strongLoginCallback; -// Wt::WApplication app; -// std::unique_ptr< eedb::DefaultAuthPage > sut; -//}; + void clickLogin() const { + loginBtn()->clicked().emit(Wt::WMouseEvent{}); + } -//TEST_F(DefaultAuthPageTest, createApp) { -// auto menu = dynamic_cast< Wt::WPopupMenu * >(app.findWidget("home.navigation.session_menu_popup")); -// auto menuItem = dynamic_cast< Wt::WMenuItem * >(app.findWidget("home.navigation.session_menu.logout")); -// // menu->triggered().emit(menuItem); -//} + void setPassword(std::string pass) const { + auto w = dynamic_cast< Wt::WLineEdit * >(sut->resolveWidget("password")); + w->setText(pass); + w->changed().emit(); + } -//// TEST(a, b) { -//// auto session = std::make_unique< eedb::SessionMock >(); -//// EXPECT_CALL(*session, enviroment()).WillOnce(ReturnRef(env)); + void setUsername(std::string user) const { + auto w = dynamic_cast< Wt::WLineEdit * >(sut->resolveWidget("user-name")); + w->setText(user); + w->changed().emit(); + } -//// sut = new eedb::EEDB(std::move(session), [this]() { return authPageFactory(); }, [this]() { return homePageFactory(); }); + void rememberMe() const { + sut->model()->setValue("remember-me", true); + } -//// // Wt::WPushButton * b = dynamic_cast< Wt::WPushButton * >(app.findWidget("startbutton")); + protected: + Wt::Test::WTestEnvironment env; + Wt::Auth::Login login; -//// // Wt::WProgressBar * bar = dynamic_cast< Wt::WProgressBar * >(app.findWidget("progress")); + UniquePtrMockWrapper< Veryfier > passwordVerifier; + UniquePtrMockWrapper< UserDatabaseMock > usedDatabaseMock; -//// // b->clicked().emit(Wt::WMouseEvent()); + MockFunction< void(eedb::User *, nlohmann::json) > _strongLoginCallback; + MockFunction< void(eedb::User *, nlohmann::json) > _weakLoginCallback; + MockFunction< void(eedb::User *, nlohmann::json) > _attemptLoginCallback; + MockFunction< void() > _logoutCallback; -//// // environment.endRequest(); + Wt::WApplication app; + std::unique_ptr< eedb::DefaultAuthPage > sut; +}; -//// // for(;;) { -//// // boost::this_thread::sleep(boost::posix_time::milliseconds(50)); -//// // std::cerr << "Progress: " << bar->value() << std::endl; -//// // if(b->isEnabled()) -//// // break; -//// // } +TEST_F(DefaultAuthPageTest, emitLoginNoUser) { + EXPECT_CALL(*usedDatabaseMock, findWithIdentity("loginname", Wt::WString(""))) + .Times(AtLeast(2)) + .WillRepeatedly(Return(Wt::Auth::User{})); -//// // environment.startRequest(); -////} + clickLogin(); +} + +TEST_F(DefaultAuthPageTest, emitLoginBadUser) { + EXPECT_CALL(*usedDatabaseMock, findWithIdentity("loginname", Wt::WString("username"))) + .Times(AtLeast(2)) + .WillRepeatedly(Return(Wt::Auth::User{})); + + setUsername("username"); + setPassword("password"); + + clickLogin(); +} + +TEST_F(DefaultAuthPageTest, emitLoginGoodUser) { + EXPECT_CALL(*usedDatabaseMock, findWithIdentity("loginname", Wt::WString("username"))) + .Times(AtLeast(2)) + .WillRepeatedly(Return(Wt::Auth::User{"id", *usedDatabaseMock})); + + EXPECT_CALL(*usedDatabaseMock, identity(_, _)).WillOnce(Return("user")); + EXPECT_CALL(*passwordVerifier, verify(Wt::WString("password"), _)).WillOnce(Return(true)); + + EXPECT_CALL(_strongLoginCallback, Call(_, _)); + + setUsername("username"); + setPassword("password"); + + clickLogin(); +} + +TEST_F(DefaultAuthPageTest, loginAttempt) { + EXPECT_CALL(*usedDatabaseMock, findWithIdentity("loginname", Wt::WString("username"))) + .Times(AtLeast(2)) + .WillRepeatedly(Return(Wt::Auth::User{"id", *usedDatabaseMock})); + EXPECT_CALL(*passwordVerifier, verify(Wt::WString("password"), _)).WillOnce(Return(false)); + +// EXPECT_CALL(_attemptLoginCallback, Call(_, _)); + + setUsername("username"); + setPassword("password"); + + clickLogin(); +} diff --git a/src/libs/webapp/test/test_eedb_application.cpp b/src/libs/webapp/test/test_eedb_application.cpp deleted file mode 100644 index b8f687a..0000000 --- a/src/libs/webapp/test/test_eedb_application.cpp +++ /dev/null @@ -1,93 +0,0 @@ -//#include - -//#include "eedb/mock/SessionMock.hpp" -//#include "mocks/widgets/AuthPageMock.hpp" -//#include "mocks/widgets/MainPageMock.hpp" - -//#include "utils/UniquePtrMockWrapper.hpp" -//#include -//#include - -//#include -//#include -//#include -//#include - - -//#include - -//class AuthPageFactory { -// public: -// MOCK_METHOD0(impl, std::unique_ptr< eedb::AuthPage >()); -// auto operator()() { -// return impl(); -// } -//}; - -//class HomePageFactory { -// public: -// MOCK_METHOD0(impl, std::unique_ptr< eedb::HomePage >()); -// auto operator()() { -// return impl(); -// } -//}; - -//using namespace testing; - -//class EedbApplicationTest : public Test { -// public: -// EedbApplicationTest() { -// createApp(); -// } - -// void createApp() { -// expectCreateAuthPage(); - -// auto session = std::make_unique< eedb::SessionMock >(); -// EXPECT_CALL(*session, enviroment()).WillOnce(ReturnRef(env)); - -//// sut = new eedb::EEDB(std::move(session), [this]() { return authPageFactory(); }, [this]() { return homePageFactory(); }); -// } -// void expectCreateAuthPage() { -// EXPECT_CALL(authPageFactory, impl()).WillOnce(Return(ByMove(authPage.getPtr()))); - -// EXPECT_CALL(*authPage, registerOnNeedVerification(_)).WillOnce(SaveArg< 0 >(&_needVerificationCB)); -// EXPECT_CALL(*authPage, registerOnUserWeakLogin(_)).WillOnce(SaveArg< 0 >(&_weakLoginCB)); -// EXPECT_CALL(*authPage, registerOnUserStrongLogin(_)).WillOnce(SaveArg< 0 >(&_stringLoginCB)); -// EXPECT_CALL(*authPage, registerOnUserLogout(_)).WillOnce(SaveArg< 0 >(&_userLogoutCB)); -// } - -// void expectCreateHomePage() { -// EXPECT_CALL(homePageFactory, impl()).WillOnce(Return(ByMove(homePage.getPtr()))); -//// EXPECT_CALL(*authPage, detach()); -// EXPECT_CALL(*homePage, attachTo(_)); -// } - -// protected: -// Wt::Test::WTestEnvironment env; - -// std::function< void() > _stringLoginCB; -// std::function< void() > _weakLoginCB; -// std::function< void() > _userLogoutCB; -// std::function< void() > _needVerificationCB; - -// UniquePtrMockWrapper< eedb::AuthPageMock > authPage; -// AuthPageFactory authPageFactory; - -// UniquePtrMockWrapper< eedb::HomePageMock > homePage; -// HomePageFactory homePageFactory; - -// eedb::EEDB * sut; -//}; - -////TEST_F(EedbApplicationTest, createApp) {} - -////TEST_F(EedbApplicationTest, strongLoginCreatesMainPage) { -//// expectCreateHomePage(); -////// _stringLoginCB(); -////} - -////TEST_F(EedbApplicationTest, weakLoginCreatesMainPage) { -//// expectCreateHomePage(); -////// _weakLoginCB(); -////}