add signal for login attempt

This commit is contained in:
Bartosz Wieczorek 2018-04-04 14:22:59 +02:00
parent e5bb171420
commit 5b8eaed9e0
16 changed files with 402 additions and 289 deletions

View File

@ -5,6 +5,7 @@
#include <utils/spimpl.hpp>
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<eedb::User> findEedbUser(const std::string & provider, const Wt::WString & identity) const;
std::unique_ptr<eedb::User> 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;

View File

@ -12,8 +12,6 @@
#include <Wt/Auth/AuthService.h>
#include <Wt/Auth/Login.h>
#include <Wt/WApplication.h>
#include <Wt/WEnvironment.h>
#include <nlohmann/json.hpp>
#include <spdlog/spdlog.h>
@ -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<void(Wt::Auth::User)> _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<eedb::User> 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

View File

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

View File

@ -3,6 +3,8 @@
#include <functional>
#include <memory>
#include <nlohmann/json.hpp>
#include <Wt/Auth/AuthWidget.h>
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

View File

@ -1,10 +1,11 @@
#pragma once
#include <widget/AuthPage.hpp>
#include <utils/spimpl.hpp>
#include <widget/AuthPage.hpp>
#include <Wt/Auth/AuthWidget.h>
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<void (User *, nlohmann::json)> 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;

View File

@ -4,15 +4,10 @@
#include <utils/spimpl.hpp>
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;
};

View File

@ -5,10 +5,6 @@
#include <functional>
#include <memory>
namespace Wt {
class WContainerWidget;
}
namespace eedb {
class NavigationBar : public Wt::WNavigationBar {
public:

View File

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

View File

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

View File

@ -1,37 +1,162 @@
#include <widget/DefaultAuthPage.hpp>
#include <eedb/auth/Services.hpp>
#include <eedb/User.hpp>
#include <eedb/auth/PgUserAuth.hpp>
#include <eedb/auth/Services.hpp>
#include <Wt/WApplication.h>
#include <Wt/WEnvironment.h>
#include <Wt/WLineEdit.h>
#include <Wt/Http/Request.h>
#include <experimental/array>
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);
}

View File

@ -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<void ()> 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

View File

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

View File

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

View File

@ -4,6 +4,8 @@
#include <utils/UniquePtrMockWrapper.hpp>
#include <eedb/User.hpp>
#include <eedb/mock/AuthPageMock.hpp>
#include <eedb/mock/FactoryMock.hpp>
#include <eedb/mock/HomePageMock.hpp>
@ -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();
}

View File

@ -1,83 +1,153 @@
//#include <gmock/gmock.h>
#include <gmock/gmock.h>
//#include "utils/UniquePtrMockWrapper.hpp"
#include "utils/UniquePtrMockWrapper.hpp"
//#include "mocks/SessionMock.hpp"
//#include "mocks/db/UserDatabaseMock.hpp"
//#include "mocks/widgets/MainPageMock.hpp"
#include <eedb/auth/Services.hpp>
//#include <Wt/Test/WTestEnvironment.h>
//#include <Wt/WApplication.h>
//#include <Wt/WPopupMenu.h>
#include <Wt/Auth/AbstractPasswordService.h>
#include <Wt/Auth/AbstractUserDatabase.h>
#include <Wt/Auth/PasswordService.h>
/////TODO change to mocked version
//#include <eedb/auth/Services.hpp>
#include <Wt/Test/WTestEnvironment.h>
#include <Wt/WApplication.h>
#include <Wt/WFormWidget.h>
#include <Wt/WLineEdit.h>
#include <Wt/WPopupMenu.h>
#include <Wt/WPushButton.h>
//#include <eedb/widgets/DefaultAuthPage.hpp>
#include <widget/DefaultAuthPage.hpp>
//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<AbstracUserDatabaseMock> 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();
}

View File

@ -1,93 +0,0 @@
//#include <gmock/gmock.h>
//#include "eedb/mock/SessionMock.hpp"
//#include "mocks/widgets/AuthPageMock.hpp"
//#include "mocks/widgets/MainPageMock.hpp"
//#include "utils/UniquePtrMockWrapper.hpp"
//#include <Wt/Auth/Login.h>
//#include <Wt/Test/WTestEnvironment.h>
//#include <eedb/EEDB.hpp>
//#include <eedb/Session.hpp>
//#include <eedb/connection.hpp>
//#include <eedb/widgets/Theme.hpp>
//#include <sqlpp11/ppgen.h>
//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();
////}