diff --git a/src/libs/services/auth/impl/AuthServiceBase.cpp b/src/libs/services/auth/impl/AuthServiceBase.cpp index 2a1334a2..eed258c5 100644 --- a/src/libs/services/auth/impl/AuthServiceBase.cpp +++ b/src/libs/services/auth/impl/AuthServiceBase.cpp @@ -19,6 +19,7 @@ #include "AuthServiceBase.hpp" +#include #include "database/Db.hpp" #include "database/Session.hpp" #include "database/User.hpp" @@ -26,46 +27,57 @@ namespace Auth { - using namespace Database; + using namespace Database; - AuthServiceBase::AuthServiceBase(Db& db) - : _db {db} - {} + AuthServiceBase::AuthServiceBase(Db& db) + : _db{ db } + {} - UserId - AuthServiceBase::getOrCreateUser(std::string_view loginName) - { - Session& session {getDbSession()}; - auto transaction {session.createWriteTransaction()}; + UserId AuthServiceBase::getOrCreateUser(std::string_view loginName) + { + Session& session{ getDbSession() }; + auto transaction{ session.createWriteTransaction() }; - User::pointer user {User::find(session, loginName)}; - if (!user) - { - const UserType type {User::getCount(session) == 0 ? UserType::ADMIN : UserType::REGULAR}; + User::pointer user{ User::find(session, loginName) }; + if (!user) + { + const UserType type{ User::getCount(session) == 0 ? UserType::ADMIN : UserType::REGULAR }; - LMS_LOG(AUTH, DEBUG, "Creating user '" << loginName << "', admin = " << (type == UserType::ADMIN)); + LMS_LOG(AUTH, DEBUG, "Creating user '" << loginName << "', admin = " << (type == UserType::ADMIN)); - user = session.create(loginName); - user.modify()->setType(type); - } + user = session.create(loginName); + user.modify()->setType(type); + } - return user->getId(); - } + return user->getId(); + } - void - AuthServiceBase::onUserAuthenticated(UserId userId) - { - Session& session {getDbSession()}; - auto transaction {session.createWriteTransaction()}; + void AuthServiceBase::onUserAuthenticated(UserId userId) + { + Session& session{ getDbSession() }; - User::pointer user {User::find(session, userId)}; - if (user) - user.modify()->setLastLogin(Wt::WDateTime::currentDateTime()); - } + // Update last login only if relevant (avoid hammering write accesses to the database) + { + auto transaction{ session.createReadTransaction() }; - Session& - AuthServiceBase::getDbSession() - { - return _db.getTLSSession(); - } + const User::pointer user{ User::find(session, userId) }; + if (!user) + return; + + if (std::abs(Wt::WDateTime::currentDateTime().secsTo(user->getLastLogin())) < 60) + return; + } + + { + auto transaction{ session.createWriteTransaction() }; + + if (User::pointer user{ User::find(session, userId) }) + user.modify()->setLastLogin(Wt::WDateTime::currentDateTime()); + } + } + + Session& AuthServiceBase::getDbSession() + { + return _db.getTLSSession(); + } } diff --git a/src/libs/services/auth/impl/AuthServiceBase.hpp b/src/libs/services/auth/impl/AuthServiceBase.hpp index ee372c7b..4c0d2ae6 100644 --- a/src/libs/services/auth/impl/AuthServiceBase.hpp +++ b/src/libs/services/auth/impl/AuthServiceBase.hpp @@ -24,23 +24,23 @@ namespace Database { - class Db; - class Session; + class Db; + class Session; } namespace Auth { - class AuthServiceBase - { - protected: - AuthServiceBase(Database::Db& db); + class AuthServiceBase + { + protected: + AuthServiceBase(Database::Db& db); - Database::UserId getOrCreateUser(std::string_view loginName); - void onUserAuthenticated(Database::UserId userId); + Database::UserId getOrCreateUser(std::string_view loginName); + void onUserAuthenticated(Database::UserId userId); - Database::Session& getDbSession(); + Database::Session& getDbSession(); - private: - Database::Db& _db; - }; + private: + Database::Db& _db; + }; } diff --git a/src/libs/services/auth/impl/internal/InternalPasswordService.cpp b/src/libs/services/auth/impl/internal/InternalPasswordService.cpp index 961aafe6..4304aae2 100644 --- a/src/libs/services/auth/impl/internal/InternalPasswordService.cpp +++ b/src/libs/services/auth/impl/internal/InternalPasswordService.cpp @@ -29,110 +29,105 @@ namespace Auth { - InternalPasswordService::InternalPasswordService(Database::Db& db, std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService) - : PasswordServiceBase {db, maxThrottlerEntries, authTokenService} - { - _validator.setMinimumLength(Wt::Auth::PasswordStrengthType::OneCharClass, 4); - _validator.setMinimumLength(Wt::Auth::PasswordStrengthType::TwoCharClass, 4); - _validator.setMinimumLength(Wt::Auth::PasswordStrengthType::PassPhrase, 4); - _validator.setMinimumLength(Wt::Auth::PasswordStrengthType::ThreeCharClass, 4); - _validator.setMinimumLength(Wt::Auth::PasswordStrengthType::FourCharClass, 4); - _validator.setMinimumPassPhraseWords(1); - _validator.setMinimumMatchLength(3); - } + InternalPasswordService::InternalPasswordService(Database::Db& db, std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService) + : PasswordServiceBase{ db, maxThrottlerEntries, authTokenService } + { + _validator.setMinimumLength(Wt::Auth::PasswordStrengthType::OneCharClass, 4); + _validator.setMinimumLength(Wt::Auth::PasswordStrengthType::TwoCharClass, 4); + _validator.setMinimumLength(Wt::Auth::PasswordStrengthType::PassPhrase, 4); + _validator.setMinimumLength(Wt::Auth::PasswordStrengthType::ThreeCharClass, 4); + _validator.setMinimumLength(Wt::Auth::PasswordStrengthType::FourCharClass, 4); + _validator.setMinimumPassPhraseWords(1); + _validator.setMinimumMatchLength(3); + } - bool - InternalPasswordService::checkUserPassword(std::string_view loginName, std::string_view password) - { - LMS_LOG(AUTH, DEBUG, "Checking internal password for user '" << loginName << "'"); + bool InternalPasswordService::checkUserPassword(std::string_view loginName, std::string_view password) + { + LMS_LOG(AUTH, DEBUG, "Checking internal password for user '" << loginName << "'"); - Database::User::PasswordHash passwordHash; - { - Database::Session& session {getDbSession()}; - auto transaction {session.createReadTransaction()}; + Database::User::PasswordHash passwordHash; + { + Database::Session& session{ getDbSession() }; + auto transaction{ session.createReadTransaction() }; - const Database::User::pointer user {Database::User::find(session, loginName)}; - if (!user) - { - LMS_LOG(AUTH, DEBUG, "hashing random stuff"); - // hash random stuff here to waste some time - hashRandomPassword(); - return false; - } + const Database::User::pointer user{ Database::User::find(session, loginName) }; + if (!user) + { + LMS_LOG(AUTH, DEBUG, "hashing random stuff"); + // hash random stuff here to waste some time + hashRandomPassword(); + return false; + } - // Don't allow users being created or coming from other backends - passwordHash = user->getPasswordHash(); - if (passwordHash.salt.empty() || passwordHash.hash.empty()) - { - // hash random stuff here to waste some time - hashRandomPassword(); - return false; - } - } + // Don't allow users being created or coming from other backends + passwordHash = user->getPasswordHash(); + if (passwordHash.salt.empty() || passwordHash.hash.empty()) + { + // hash random stuff here to waste some time + hashRandomPassword(); + return false; + } + } - return _hashFunc.verify(std::string {password}, std::string {passwordHash.salt}, std::string {passwordHash.hash}); - } + return _hashFunc.verify(std::string{ password }, std::string{ passwordHash.salt }, std::string{ passwordHash.hash }); + } - bool - InternalPasswordService::canSetPasswords() const - { - return true; - } + bool InternalPasswordService::canSetPasswords() const + { + return true; + } - IPasswordService::PasswordAcceptabilityResult - InternalPasswordService::checkPasswordAcceptability(std::string_view password, const PasswordValidationContext& context) const - { - switch (context.userType) - { - case Database::UserType::ADMIN: - case Database::UserType::REGULAR: - return _validator.evaluateStrength(std::string {password}, context.loginName, "").isValid() ? PasswordAcceptabilityResult::OK : PasswordAcceptabilityResult::TooWeak; - case Database::UserType::DEMO: - return password == context.loginName ? PasswordAcceptabilityResult::OK : PasswordAcceptabilityResult::MustMatchLoginName; - } + IPasswordService::PasswordAcceptabilityResult InternalPasswordService::checkPasswordAcceptability(std::string_view password, const PasswordValidationContext& context) const + { + switch (context.userType) + { + case Database::UserType::ADMIN: + case Database::UserType::REGULAR: + return _validator.evaluateStrength(std::string{ password }, context.loginName, "").isValid() ? PasswordAcceptabilityResult::OK : PasswordAcceptabilityResult::TooWeak; + case Database::UserType::DEMO: + return password == context.loginName ? PasswordAcceptabilityResult::OK : PasswordAcceptabilityResult::MustMatchLoginName; + } - throw NotImplementedException {}; - } + throw NotImplementedException{}; + } - void - InternalPasswordService::setPassword(Database::UserId userId, std::string_view newPassword) - { - const Database::User::PasswordHash passwordHash {hashPassword(newPassword)}; + void InternalPasswordService::setPassword(Database::UserId userId, std::string_view newPassword) + { + const Database::User::PasswordHash passwordHash{ hashPassword(newPassword) }; - Database::Session& session {getDbSession()}; - auto transaction {session.createWriteTransaction()}; + Database::Session& session{ getDbSession() }; + auto transaction{ session.createWriteTransaction() }; - Database::User::pointer user {Database::User::find(session, userId)}; - if (!user) - throw Exception {"User not found!"}; + Database::User::pointer user{ Database::User::find(session, userId) }; + if (!user) + throw Exception{ "User not found!" }; - switch (checkPasswordAcceptability(newPassword, PasswordValidationContext {user->getLoginName(), user->getType()})) - { - case PasswordAcceptabilityResult::OK: - break; - case PasswordAcceptabilityResult::TooWeak: - throw PasswordTooWeakException {}; - case PasswordAcceptabilityResult::MustMatchLoginName: - throw PasswordMustMatchLoginNameException {}; - } + switch (checkPasswordAcceptability(newPassword, PasswordValidationContext{ user->getLoginName(), user->getType() })) + { + case PasswordAcceptabilityResult::OK: + break; + case PasswordAcceptabilityResult::TooWeak: + throw PasswordTooWeakException{}; + case PasswordAcceptabilityResult::MustMatchLoginName: + throw PasswordMustMatchLoginNameException{}; + } - user.modify()->setPasswordHash(passwordHash); - getAuthTokenService().clearAuthTokens(userId); - } + user.modify()->setPasswordHash(passwordHash); + getAuthTokenService().clearAuthTokens(userId); + } - Database::User::PasswordHash - InternalPasswordService::hashPassword(std::string_view password) const - { - const std::string salt {Wt::WRandom::generateId(32)}; + Database::User::PasswordHash InternalPasswordService::hashPassword(std::string_view password) const + { + const std::string salt{ Wt::WRandom::generateId(32) }; - return {salt, _hashFunc.compute(std::string {password}, salt)}; - } + return { salt, _hashFunc.compute(std::string {password}, salt) }; + } - void - InternalPasswordService::hashRandomPassword() const - { - hashPassword(Wt::WRandom::generateId(32)); - } + void + InternalPasswordService::hashRandomPassword() const + { + hashPassword(Wt::WRandom::generateId(32)); + } } // namespace Auth diff --git a/src/libs/services/auth/impl/internal/InternalPasswordService.hpp b/src/libs/services/auth/impl/internal/InternalPasswordService.hpp index 6c241f31..3cd9e2e4 100644 --- a/src/libs/services/auth/impl/internal/InternalPasswordService.hpp +++ b/src/libs/services/auth/impl/internal/InternalPasswordService.hpp @@ -28,25 +28,25 @@ namespace Auth { - class IAuthTokenService; + class IAuthTokenService; - class InternalPasswordService : public PasswordServiceBase - { - public: - InternalPasswordService(Database::Db& db, std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService); + class InternalPasswordService : public PasswordServiceBase + { + public: + InternalPasswordService(Database::Db& db, std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService); - private: - bool checkUserPassword(std::string_view loginName, std::string_view password) override; + private: + bool checkUserPassword(std::string_view loginName, std::string_view password) override; - bool canSetPasswords() const override; - PasswordAcceptabilityResult checkPasswordAcceptability(std::string_view loginName, const PasswordValidationContext& context) const override; - void setPassword(Database::UserId userId, std::string_view newPassword) override; + bool canSetPasswords() const override; + PasswordAcceptabilityResult checkPasswordAcceptability(std::string_view loginName, const PasswordValidationContext& context) const override; + void setPassword(Database::UserId userId, std::string_view newPassword) override; - Database::User::PasswordHash hashPassword(std::string_view password) const; - void hashRandomPassword() const; + Database::User::PasswordHash hashPassword(std::string_view password) const; + void hashRandomPassword() const; - const Wt::Auth::BCryptHashFunction _hashFunc {7}; // TODO parametrize this - Wt::Auth::PasswordStrengthValidator _validator; - }; + const Wt::Auth::BCryptHashFunction _hashFunc{ 7 }; // TODO parametrize this + Wt::Auth::PasswordStrengthValidator _validator; + }; }