This commit is contained in:
parent
69cecf8022
commit
b17b50b751
@ -1,6 +1,7 @@
|
||||
#include "Session.h"
|
||||
#include "BoostLog.h"
|
||||
#include <Wt/Auth/Dbo/UserDatabase.h>
|
||||
#include <Wt/Auth/Identity.h>
|
||||
#include <Wt/Dbo/FixedSqlConnectionPool.h>
|
||||
#include <Wt/Dbo/SqlConnectionPool.h>
|
||||
#include <Wt/Dbo/WtJsonSqlTraits.h>
|
||||
@ -11,7 +12,7 @@ std::unique_ptr<Wt::Dbo::SqlConnectionPool> sqlConnectionPool;
|
||||
bool initialize(const std::string &path) {
|
||||
try {
|
||||
auto connection = std::make_unique<Wt::Dbo::backend::Sqlite3>(path);
|
||||
connection->setProperty("show-queries", "true");
|
||||
// connection->setProperty("show-queries", "true");
|
||||
connection->setDateTimeStorage(Wt::Dbo::SqlDateTimeType::DateTime,
|
||||
Wt::Dbo::backend::DateTimeStorage::PseudoISO8601AsText);
|
||||
sqlConnectionPool = std::make_unique<Wt::Dbo::FixedSqlConnectionPool>(std::move(connection), 10);
|
||||
@ -73,6 +74,10 @@ void JsonSerializer::act(FieldRef<std::chrono::system_clock::time_point> field)
|
||||
} // namespace Dbo
|
||||
} // namespace Wt
|
||||
|
||||
User::User(const Wt::Auth::User &user) {
|
||||
identity = user.identity(Wt::Auth::Identity::LoginName).toUTF8();
|
||||
}
|
||||
|
||||
DBO_INSTANTIATE_TEMPLATES(User)
|
||||
DBO_INSTANTIATE_TEMPLATES(Task)
|
||||
DBO_INSTANTIATE_TEMPLATES(HomeBox::Item)
|
||||
|
@ -8,9 +8,13 @@ using AuthInfo = Wt::Auth::Dbo::AuthInfo<User>;
|
||||
|
||||
class User {
|
||||
public:
|
||||
User() = default;
|
||||
User(const Wt::Auth::User &user);
|
||||
template <class Action>
|
||||
void persist(Action &a) {
|
||||
}
|
||||
|
||||
std::string identity;
|
||||
};
|
||||
DBO_EXTERN_TEMPLATES(User);
|
||||
|
||||
|
@ -119,36 +119,36 @@ Application::~Application() {
|
||||
}
|
||||
|
||||
void Application::authEvent() {
|
||||
auto app = Amass::Singleton<WebToolkit::Server>::instance();
|
||||
auto &service = app->authService();
|
||||
auto token = environment().getCookie(service.authTokenCookieName());
|
||||
if (m_session->login().loggedIn()) {
|
||||
const Wt::Auth::User &u = m_session->login().user();
|
||||
LOG(info) << "User " << u.id() << " (" << u.identity(Wt::Auth::Identity::LoginName) << ")"
|
||||
<< " logged in.";
|
||||
auto app = Amass::Singleton<WebToolkit::Server>::instance();
|
||||
auto &service = app->authService();
|
||||
auto &env = environment();
|
||||
auto token = env.getCookie(service.authTokenCookieName());
|
||||
if (token == nullptr) {
|
||||
Wt::Http::Cookie cookie(service.authTokenCookieName(), service.createAuthToken(u));
|
||||
cookie.setDomain(service.authTokenCookieDomain());
|
||||
cookie.setPath(AuthModel::CookiePath);
|
||||
cookie.setExpires(Wt::WDateTime());
|
||||
setCookie(cookie);
|
||||
app->insertCookie(cookie.value(), u);
|
||||
}
|
||||
if (m_loginPage) {
|
||||
if (m_navigationBar != nullptr) {
|
||||
m_loginPageRef = m_navigationBar->addLoginItem(std::move(m_loginPage));
|
||||
m_loginPageRef->removeStyleClass("bulma-m-auto");
|
||||
m_loginPageRef->removeStyleClass("bulma-container");
|
||||
}
|
||||
} else if (m_loginPageRef != nullptr && m_loginPageRef->parent() == m_root) {
|
||||
m_loginPage = m_loginPageRef->parent()->removeWidget(m_loginPageRef);
|
||||
if (m_navigationBar != nullptr) {
|
||||
m_loginPageRef = m_navigationBar->addLoginItem(std::move(m_loginPage));
|
||||
m_loginPageRef->removeStyleClass("bulma-m-auto");
|
||||
m_loginPageRef->removeStyleClass("bulma-container");
|
||||
}
|
||||
}
|
||||
if (m_loginedRedirectUrl.empty()) {
|
||||
if (m_loginPage) {
|
||||
if (m_navigationBar != nullptr) {
|
||||
m_loginPageRef = m_navigationBar->addLoginItem(std::move(m_loginPage));
|
||||
m_loginPageRef->removeStyleClass("bulma-m-auto");
|
||||
m_loginPageRef->removeStyleClass("bulma-container");
|
||||
}
|
||||
} else if (m_loginPageRef != nullptr && m_loginPageRef->parent() == m_root) {
|
||||
m_loginPage = m_loginPageRef->parent()->removeWidget(m_loginPageRef);
|
||||
if (m_navigationBar != nullptr) {
|
||||
m_loginPageRef = m_navigationBar->addLoginItem(std::move(m_loginPage));
|
||||
m_loginPageRef->removeStyleClass("bulma-m-auto");
|
||||
m_loginPageRef->removeStyleClass("bulma-container");
|
||||
}
|
||||
}
|
||||
setInternalPath("/", true);
|
||||
} else {
|
||||
redirect(m_loginedRedirectUrl);
|
||||
@ -157,6 +157,9 @@ void Application::authEvent() {
|
||||
if (m_navigationBar != nullptr) {
|
||||
m_loginPage = m_navigationBar->removeLoginItem();
|
||||
}
|
||||
if (token != nullptr) {
|
||||
app->removeCookie(*token);
|
||||
}
|
||||
LOG(info) << "user logged out, internal path: " << internalPath();
|
||||
if (internalPath() == "/wt/login") {
|
||||
handlePathChange(internalPath());
|
||||
@ -258,9 +261,23 @@ const Wt::Auth::PasswordService &Server::passwordService() {
|
||||
return *m_passwordService;
|
||||
}
|
||||
|
||||
void Server::insertCookie(const std::string &cookie) {
|
||||
std::optional<User> Server::user(const std::string &cookie) {
|
||||
if (m_cookies.contains(cookie)) {
|
||||
return m_cookies.at(cookie);
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
void Server::insertCookie(const std::string &cookie, const Wt::Auth::User &user) {
|
||||
if (!m_cookies.contains(cookie)) {
|
||||
m_cookies.insert(cookie);
|
||||
m_cookies.insert_or_assign(cookie, user);
|
||||
}
|
||||
}
|
||||
|
||||
void Server::removeCookie(const std::string &cookie) {
|
||||
if (m_cookies.contains(cookie)) {
|
||||
m_cookies.erase(cookie);
|
||||
}
|
||||
}
|
||||
|
||||
@ -281,7 +298,7 @@ Wt::Http::Cookie Server::updateCookie(const std::string &oldCookie, const Wt::Au
|
||||
if (m_cookies.contains(oldCookie)) { // 勾选了记住我
|
||||
m_cookies.erase(oldCookie);
|
||||
cookie.setMaxAge(std::chrono::seconds(result.newTokenValidity()));
|
||||
m_cookies.insert(newToken);
|
||||
m_cookies.insert_or_assign(newToken, result.user());
|
||||
} else { // 只在会话期间有效
|
||||
cookie.setExpires(Wt::WDateTime());
|
||||
}
|
||||
|
@ -4,7 +4,8 @@
|
||||
#include "Singleton.h"
|
||||
#include <Wt/WApplication.h>
|
||||
#include <memory>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include "Database/User.h"
|
||||
|
||||
namespace Wt {
|
||||
class WServer;
|
||||
@ -55,8 +56,10 @@ public:
|
||||
void initializeAuthenticationService();
|
||||
Wt::Auth::AuthService &authService();
|
||||
const Wt::Auth::PasswordService &passwordService();
|
||||
void insertCookie(const std::string &cookie);
|
||||
void insertCookie(const std::string &cookie, const Wt::Auth::User &user);
|
||||
Wt::Http::Cookie updateCookie(const std::string &oldCookie, const Wt::Auth::AuthTokenResult &result, bool secure);
|
||||
std::optional<User> user(const std::string &cookie);
|
||||
void removeCookie(const std::string &cookie);
|
||||
|
||||
protected:
|
||||
Server(uint16_t port, const std::string &applicationRoot, const std::string &documentRoot);
|
||||
@ -68,7 +71,7 @@ private:
|
||||
std::unique_ptr<Wt::Auth::AuthService> m_authService;
|
||||
std::unique_ptr<Wt::Auth::PasswordService> m_passwordService;
|
||||
|
||||
std::unordered_set<std::string> m_cookies;
|
||||
std::unordered_map<std::string, User> m_cookies;
|
||||
};
|
||||
} // namespace WebToolkit
|
||||
#endif // __WEBAPPLICATION_H__
|
@ -26,7 +26,7 @@ std::unique_ptr<Wt::WWidget> LoginPage::createRegistrationView(const Wt::Auth::I
|
||||
}
|
||||
|
||||
void LoginPage::processExternalEnvironment(const std::string &externalPath, Wt::Auth::AuthService &service) {
|
||||
using namespace Wt::Auth;
|
||||
using namespace Wt;
|
||||
std::string emailToken;
|
||||
if (service.emailVerificationEnabled()) {
|
||||
auto redirectPath = service.emailRedirectInternalPath();
|
||||
@ -36,24 +36,24 @@ void LoginPage::processExternalEnvironment(const std::string &externalPath, Wt::
|
||||
}
|
||||
}
|
||||
if (!emailToken.empty()) {
|
||||
EmailTokenResult result = model()->processEmailToken(emailToken);
|
||||
Auth::EmailTokenResult result = model()->processEmailToken(emailToken);
|
||||
switch (result.state()) {
|
||||
case EmailTokenState::Invalid:
|
||||
case Auth::EmailTokenState::Invalid:
|
||||
displayError(tr("Wt.Auth.error-invalid-token"));
|
||||
break;
|
||||
case EmailTokenState::Expired:
|
||||
case Auth::EmailTokenState::Expired:
|
||||
displayError(tr("Wt.Auth.error-token-expired"));
|
||||
break;
|
||||
case EmailTokenState::UpdatePassword:
|
||||
case Auth::EmailTokenState::UpdatePassword:
|
||||
letUpdatePassword(result.user(), false);
|
||||
break;
|
||||
case EmailTokenState::EmailConfirmed:
|
||||
case Auth::EmailTokenState::EmailConfirmed:
|
||||
displayInfo(tr("Wt.Auth.info-email-confirmed"));
|
||||
User user = result.user();
|
||||
Auth::User user = result.user();
|
||||
|
||||
LoginState state = LoginState::Strong;
|
||||
Auth::LoginState state = Auth::LoginState::Strong;
|
||||
if (model()->hasMfaStep(user)) {
|
||||
state = LoginState::RequiresMfa;
|
||||
state = Auth::LoginState::RequiresMfa;
|
||||
}
|
||||
model()->loginUser(login(), user, state);
|
||||
}
|
||||
@ -67,10 +67,10 @@ void LoginPage::processExternalEnvironment(const std::string &externalPath, Wt::
|
||||
return;
|
||||
}
|
||||
|
||||
User user = model()->processAuthToken();
|
||||
LoginState state = LoginState::Weak;
|
||||
Auth::User user = model()->processAuthToken();
|
||||
Auth::LoginState state = Auth::LoginState::Weak;
|
||||
if (model()->hasMfaStep(user)) {
|
||||
state = LoginState::RequiresMfa;
|
||||
state = Auth::LoginState::RequiresMfa;
|
||||
}
|
||||
model()->loginUser(login(), user, state);
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
#include "Restful.h"
|
||||
#include "Application.h"
|
||||
#include "Database/Session.h"
|
||||
#include "Database/User.h"
|
||||
#include "model/AuthModel.h"
|
||||
#include <Wt/Auth/AuthService.h>
|
||||
#include <Wt/Auth/Identity.h>
|
||||
@ -15,6 +16,7 @@
|
||||
DBO_INSTANTIATE_TEMPLATES(MyMessage)
|
||||
|
||||
void AuthenticationResource::handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) {
|
||||
using namespace Wt;
|
||||
auto tag = request.urlParam("tag");
|
||||
// LOG(info) << "path: " << request.path() << ", tag: " << tag << ", server: " << request.hostName();
|
||||
response.setMimeType("application/json");
|
||||
@ -22,30 +24,35 @@ void AuthenticationResource::handleRequest(const Wt::Http::Request &request, Wt:
|
||||
auto app = Amass::Singleton<WebToolkit::Server>::instance();
|
||||
auto &service = app->authService();
|
||||
if (tag == "verify") {
|
||||
auto session = Database::session();
|
||||
auto enabled = service.authTokenUpdateEnabled();
|
||||
boost::scope::scope_exit raii([&enabled, &service] { service.setAuthTokenUpdateEnabled(enabled); });
|
||||
service.setAuthTokenUpdateEnabled(false);
|
||||
Wt::Auth::AuthTokenState state;
|
||||
Wt::Auth::User user;
|
||||
if (service.authTokensEnabled()) {
|
||||
const std::string *token = request.getCookieValue(service.authTokenCookieName());
|
||||
if (token != nullptr) {
|
||||
Wt::Auth::AuthTokenResult result = service.processAuthToken(*token, session->users());
|
||||
state = result.state();
|
||||
if (state == Wt::Auth::AuthTokenState::Valid) {
|
||||
user = result.user();
|
||||
const std::string *token = request.getCookieValue(service.authTokenCookieName());
|
||||
Auth::AuthTokenState state = Auth::AuthTokenState::Invalid;
|
||||
if (token != nullptr) {
|
||||
if (auto u = app->user(*token); u) {
|
||||
state = Auth::AuthTokenState::Valid;
|
||||
message.user = u->identity;
|
||||
} else {
|
||||
Wt::Auth::User user;
|
||||
auto session = Database::session();
|
||||
auto enabled = service.authTokenUpdateEnabled();
|
||||
boost::scope::scope_exit raii([&enabled, &service] { service.setAuthTokenUpdateEnabled(enabled); });
|
||||
service.setAuthTokenUpdateEnabled(false);
|
||||
if (service.authTokensEnabled()) {
|
||||
Auth::AuthTokenResult result = service.processAuthToken(*token, session->users());
|
||||
state = result.state();
|
||||
if (state == Auth::AuthTokenState::Valid) {
|
||||
user = result.user();
|
||||
message.user = user.identity(Auth::Identity::LoginName).toUTF8();
|
||||
}
|
||||
}
|
||||
}
|
||||
// LOG(info) << "state: " << (int)state << " " << message.user;
|
||||
} else {
|
||||
LOG(warning) << "cannot access cookie.";
|
||||
}
|
||||
if (user.isValid()) {
|
||||
message.user = user.identity(Wt::Auth::Identity::LoginName).toUTF8();
|
||||
}
|
||||
// LOG(info) << "state: " << (int)state << " " << message.user;
|
||||
message.message = "Hello, World!";
|
||||
message.status = state == Wt::Auth::AuthTokenState::Valid ? 0 : 404;
|
||||
message.status = state == Auth::AuthTokenState::Valid ? 0 : 404;
|
||||
using namespace boost::beast::http;
|
||||
response.setStatus(static_cast<int>(state == Wt::Auth::AuthTokenState::Valid ? status::ok : status::unauthorized));
|
||||
response.setStatus(static_cast<int>(state == Auth::AuthTokenState::Valid ? status::ok : status::unauthorized));
|
||||
} else { // logout
|
||||
auto domain = request.hostName();
|
||||
if (domain.find("amass.fun") != std::string::npos) {
|
||||
@ -54,7 +61,7 @@ void AuthenticationResource::handleRequest(const Wt::Http::Request &request, Wt:
|
||||
response.addHeader("Set-Cookie", std::format("{}=; path={}; Domain={}; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT",
|
||||
service.authTokenCookieName(), AuthModel::CookiePath, domain));
|
||||
}
|
||||
Wt::Dbo::JsonSerializer writer(response.out());
|
||||
Dbo::JsonSerializer writer(response.out());
|
||||
writer.serialize(message);
|
||||
}
|
||||
|
||||
|
@ -9,22 +9,22 @@ AuthModel::AuthModel(const Wt::Auth::AuthService &baseAuth, Wt::Auth::AbstractUs
|
||||
}
|
||||
|
||||
Wt::Auth::User AuthModel::processAuthToken() {
|
||||
using namespace Wt::Auth;
|
||||
using namespace Wt;
|
||||
if (baseAuth()->authTokensEnabled()) {
|
||||
Wt::WApplication *app = Wt::WApplication::instance();
|
||||
const Wt::WEnvironment &env = app->environment();
|
||||
const std::string *token = env.getCookie(baseAuth()->authTokenCookieName());
|
||||
if (token) {
|
||||
AuthTokenResult result = baseAuth()->processAuthToken(*token, users());
|
||||
Auth::AuthTokenResult result = baseAuth()->processAuthToken(*token, users());
|
||||
auto server = Amass::Singleton<WebToolkit::Server>::instance();
|
||||
auto cookie = server->updateCookie(*token, result, app->environment().urlScheme() == "https");
|
||||
if ((result.state() == AuthTokenState::Invalid) || !cookie.value().empty()) {
|
||||
if ((result.state() == Auth::AuthTokenState::Invalid) || !cookie.value().empty()) {
|
||||
app->setCookie(cookie);
|
||||
}
|
||||
return result.state() == AuthTokenState::Valid ? result.user() : User();
|
||||
return result.state() == Auth::AuthTokenState::Valid ? result.user() : Auth::User();
|
||||
}
|
||||
}
|
||||
return User();
|
||||
return Auth::User();
|
||||
}
|
||||
|
||||
void AuthModel::setRememberMeCookie(const Wt::Auth::User &user) {
|
||||
@ -39,6 +39,6 @@ void AuthModel::setRememberMeCookie(const Wt::Auth::User &user) {
|
||||
cookie.setSecure(app->environment().urlScheme() == "https");
|
||||
|
||||
auto server = Amass::Singleton<WebToolkit::Server>::instance();
|
||||
server->insertCookie(cookie.value());
|
||||
server->insertCookie(cookie.value(), user);
|
||||
app->setCookie(cookie);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user