Removed session parameters from auth services

This commit is contained in:
emeric
2021-10-03 14:35:25 +02:00
parent cbc137529b
commit 617139c2f4
25 changed files with 135 additions and 121 deletions
+24 -8
View File
@@ -19,25 +19,33 @@
#include "AuthServiceBase.hpp" #include "AuthServiceBase.hpp"
#include "database/Db.hpp"
#include "database/Session.hpp" #include "database/Session.hpp"
#include "database/User.hpp" #include "database/User.hpp"
#include "utils/Logger.hpp" #include "utils/Logger.hpp"
namespace Auth namespace Auth
{ {
Database::UserId using namespace Database;
AuthServiceBase::getOrCreateUser(Database::Session& session, std::string_view loginName)
AuthServiceBase::AuthServiceBase(Db& db)
: _db {db}
{}
UserId
AuthServiceBase::getOrCreateUser(std::string_view loginName)
{ {
Session& session {getDbSession()};
auto transaction {session.createUniqueTransaction()}; auto transaction {session.createUniqueTransaction()};
Database::User::pointer user {Database::User::getByLoginName(session, loginName)}; User::pointer user {User::getByLoginName(session, loginName)};
if (!user) if (!user)
{ {
const Database::UserType type {Database::User::getCount(session) == 0 ? Database::UserType::ADMIN : Database::UserType::REGULAR}; const UserType type {User::getCount(session) == 0 ? UserType::ADMIN : UserType::REGULAR};
LMS_LOG(AUTH, DEBUG) << "Creating user '" << loginName << "', admin = " << (type == Database::UserType::ADMIN); LMS_LOG(AUTH, DEBUG) << "Creating user '" << loginName << "', admin = " << (type == UserType::ADMIN);
user = Database::User::create(session, loginName); user = User::create(session, loginName);
user.modify()->setType(type); user.modify()->setType(type);
} }
@@ -45,11 +53,19 @@ namespace Auth
} }
void void
AuthServiceBase::onUserAuthenticated(Database::Session& session, Database::UserId userId) AuthServiceBase::onUserAuthenticated(UserId userId)
{ {
Session& session {getDbSession()};
auto transaction {session.createUniqueTransaction()}; auto transaction {session.createUniqueTransaction()};
Database::User::pointer user {Database::User::getById(session, userId)};
User::pointer user {User::getById(session, userId)};
if (user) if (user)
user.modify()->setLastLogin(Wt::WDateTime::currentDateTime()); user.modify()->setLastLogin(Wt::WDateTime::currentDateTime());
} }
Session&
AuthServiceBase::getDbSession()
{
return _db.getTLSSession();
}
} }
+10 -2
View File
@@ -24,6 +24,7 @@
namespace Database namespace Database
{ {
class Db;
class Session; class Session;
} }
@@ -32,7 +33,14 @@ namespace Auth
class AuthServiceBase class AuthServiceBase
{ {
protected: protected:
Database::UserId getOrCreateUser(Database::Session& session, std::string_view loginName); AuthServiceBase(Database::Db& db);
void onUserAuthenticated(Database::Session& session, Database::UserId userId);
Database::UserId getOrCreateUser(std::string_view loginName);
void onUserAuthenticated(Database::UserId userId);
Database::Session& getDbSession();
private:
Database::Db& _db;
}; };
} }
+16 -11
View File
@@ -32,24 +32,27 @@
namespace Auth namespace Auth
{ {
std::unique_ptr<IAuthTokenService> createAuthTokenService(std::size_t maxThrottlerEntries) std::unique_ptr<IAuthTokenService> createAuthTokenService(Database::Db& db, std::size_t maxThrottlerEntries)
{ {
return std::make_unique<AuthTokenService>(maxThrottlerEntries); return std::make_unique<AuthTokenService>(db, maxThrottlerEntries);
} }
static const Wt::Auth::SHA1HashFunction sha1Function; static const Wt::Auth::SHA1HashFunction sha1Function;
AuthTokenService::AuthTokenService(std::size_t maxThrottlerEntries) AuthTokenService::AuthTokenService(Database::Db& db, std::size_t maxThrottlerEntries)
: _loginThrottler {maxThrottlerEntries} : AuthServiceBase {db}
, _loginThrottler {maxThrottlerEntries}
{ {
} }
std::string std::string
AuthTokenService::createAuthToken(Database::Session& session, Database::UserId userId, const Wt::WDateTime& expiry) AuthTokenService::createAuthToken(Database::UserId userId, const Wt::WDateTime& expiry)
{ {
const std::string secret {Wt::WRandom::generateId(32)}; const std::string secret {Wt::WRandom::generateId(32)};
const std::string secretHash {sha1Function.compute(secret, {})}; const std::string secretHash {sha1Function.compute(secret, {})};
Database::Session& session {getDbSession()};
auto transaction {session.createUniqueTransaction()}; auto transaction {session.createUniqueTransaction()};
Database::User::pointer user {Database::User::getById(session, userId)}; Database::User::pointer user {Database::User::getById(session, userId)};
@@ -66,12 +69,12 @@ namespace Auth
return secret; return secret;
} }
static
std::optional<AuthTokenService::AuthTokenProcessResult::AuthTokenInfo> std::optional<AuthTokenService::AuthTokenProcessResult::AuthTokenInfo>
processAuthToken(Database::Session& session, std::string_view secret) AuthTokenService::processAuthToken(std::string_view secret)
{ {
const std::string secretHash {sha1Function.compute(std::string {secret}, {})}; const std::string secretHash {sha1Function.compute(std::string {secret}, {})};
Database::Session& session {getDbSession()};
auto transaction {session.createUniqueTransaction()}; auto transaction {session.createUniqueTransaction()};
Database::AuthToken::pointer authToken {Database::AuthToken::getByValue(session, secretHash)}; Database::AuthToken::pointer authToken {Database::AuthToken::getByValue(session, secretHash)};
@@ -93,7 +96,7 @@ namespace Auth
} }
AuthTokenService::AuthTokenProcessResult AuthTokenService::AuthTokenProcessResult
AuthTokenService::processAuthToken(Database::Session& session, const boost::asio::ip::address& clientAddress, std::string_view tokenValue) AuthTokenService::processAuthToken(const boost::asio::ip::address& clientAddress, std::string_view tokenValue)
{ {
// Do not waste too much resource on brute force attacks (optim) // Do not waste too much resource on brute force attacks (optim)
{ {
@@ -103,7 +106,7 @@ namespace Auth
return AuthTokenProcessResult {AuthTokenProcessResult::State::Throttled}; return AuthTokenProcessResult {AuthTokenProcessResult::State::Throttled};
} }
auto res {Auth::processAuthToken(session, tokenValue)}; auto res {processAuthToken(tokenValue)};
{ {
std::unique_lock lock {_mutex}; std::unique_lock lock {_mutex};
@@ -117,14 +120,16 @@ namespace Auth
} }
_loginThrottler.onGoodClientAttempt(clientAddress); _loginThrottler.onGoodClientAttempt(clientAddress);
onUserAuthenticated(session, res->userId); onUserAuthenticated(res->userId);
return AuthTokenProcessResult {AuthTokenProcessResult::State::Granted, std::move(*res)}; return AuthTokenProcessResult {AuthTokenProcessResult::State::Granted, std::move(*res)};
} }
} }
void void
AuthTokenService::clearAuthTokens(Database::Session& session, Database::UserId userId) AuthTokenService::clearAuthTokens(Database::UserId userId)
{ {
Database::Session& session {getDbSession()};
auto transaction {session.createUniqueTransaction()}; auto transaction {session.createUniqueTransaction()};
Database::User::pointer user {Database::User::getById(session, userId)}; Database::User::pointer user {Database::User::getById(session, userId)};
+6 -4
View File
@@ -35,7 +35,7 @@ namespace Auth
class AuthTokenService : public IAuthTokenService, public AuthServiceBase class AuthTokenService : public IAuthTokenService, public AuthServiceBase
{ {
public: public:
AuthTokenService(std::size_t maxThrottlerEntries); AuthTokenService(Database::Db& db, std::size_t maxThrottlerEntries);
AuthTokenService(const AuthTokenService&) = delete; AuthTokenService(const AuthTokenService&) = delete;
AuthTokenService& operator=(const AuthTokenService&) = delete; AuthTokenService& operator=(const AuthTokenService&) = delete;
@@ -43,9 +43,11 @@ namespace Auth
AuthTokenService& operator=(AuthTokenService&&) = delete; AuthTokenService& operator=(AuthTokenService&&) = delete;
private: private:
AuthTokenProcessResult processAuthToken(Database::Session& session, const boost::asio::ip::address& clientAddress, std::string_view tokenValue) override; AuthTokenProcessResult processAuthToken(const boost::asio::ip::address& clientAddress, std::string_view tokenValue) override;
std::string createAuthToken(Database::Session& session, Database::UserId userId, const Wt::WDateTime& expiry) override; std::string createAuthToken(Database::UserId userId, const Wt::WDateTime& expiry) override;
void clearAuthTokens(Database::Session& session, Database::UserId userId) override; void clearAuthTokens(Database::UserId userId) override;
std::optional<AuthTokenService::AuthTokenProcessResult::AuthTokenInfo> processAuthToken(std::string_view secret);
std::shared_mutex _mutex; std::shared_mutex _mutex;
LoginThrottler _loginThrottler; LoginThrottler _loginThrottler;
+2 -2
View File
@@ -25,10 +25,10 @@
namespace Auth namespace Auth
{ {
std::unique_ptr<IEnvService> std::unique_ptr<IEnvService>
createEnvService(std::string_view backendName) createEnvService(std::string_view backendName, Database::Db& db)
{ {
if (backendName == "http-headers") if (backendName == "http-headers")
return std::make_unique<HttpHeadersEnvService>(); return std::make_unique<HttpHeadersEnvService>(db);
throw Exception {"Authentication backend '" + std::string {backendName} + "' is not supported!"}; throw Exception {"Authentication backend '" + std::string {backendName} + "' is not supported!"};
} }
+10 -12
View File
@@ -39,29 +39,27 @@ namespace Auth
static const Wt::Auth::SHA1HashFunction sha1Function; static const Wt::Auth::SHA1HashFunction sha1Function;
std::unique_ptr<IPasswordService> std::unique_ptr<IPasswordService>
createPasswordService(std::string_view passwordAuthenticationBackend, std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService) createPasswordService(std::string_view passwordAuthenticationBackend, Database::Db& db, std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService)
{ {
if (passwordAuthenticationBackend == "internal") if (passwordAuthenticationBackend == "internal")
return std::make_unique<InternalPasswordService>(maxThrottlerEntries, authTokenService); return std::make_unique<InternalPasswordService>(db, maxThrottlerEntries, authTokenService);
#ifdef LMS_SUPPORT_PAM #ifdef LMS_SUPPORT_PAM
else if (passwordAuthenticationBackend == "pam") else if (passwordAuthenticationBackend == "pam")
return std::make_unique<PAMPasswordService>(maxThrottlerEntries, authTokenService); return std::make_unique<PAMPasswordService>(db, maxThrottlerEntries, authTokenService);
#endif // LMS_SUPPORT_PAM #endif // LMS_SUPPORT_PAM
throw Exception {"Authentication backend '" + std::string {passwordAuthenticationBackend} + "' is not supported!"}; throw Exception {"Authentication backend '" + std::string {passwordAuthenticationBackend} + "' is not supported!"};
} }
PasswordServiceBase::PasswordServiceBase(std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService) PasswordServiceBase::PasswordServiceBase(Database::Db& db, std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService)
: _loginThrottler {maxThrottlerEntries} : AuthServiceBase {db}
, _loginThrottler {maxThrottlerEntries}
, _authTokenService {authTokenService} , _authTokenService {authTokenService}
{ {
} }
PasswordServiceBase::CheckResult PasswordServiceBase::CheckResult
PasswordServiceBase::checkUserPassword(Database::Session& session, PasswordServiceBase::checkUserPassword(const boost::asio::ip::address& clientAddress, std::string_view loginName, std::string_view password)
const boost::asio::ip::address& clientAddress,
std::string_view loginName,
std::string_view password)
{ {
LMS_LOG(AUTH, DEBUG) << "Checking password for user '" << loginName << "'"; LMS_LOG(AUTH, DEBUG) << "Checking password for user '" << loginName << "'";
@@ -73,7 +71,7 @@ namespace Auth
return {CheckResult::State::Throttled}; return {CheckResult::State::Throttled};
} }
const bool match {checkUserPassword(session, loginName, password)}; const bool match {checkUserPassword(loginName, password)};
{ {
std::unique_lock lock {_mutex}; std::unique_lock lock {_mutex};
@@ -84,8 +82,8 @@ namespace Auth
{ {
_loginThrottler.onGoodClientAttempt(clientAddress); _loginThrottler.onGoodClientAttempt(clientAddress);
const Database::UserId userId {getOrCreateUser(session, loginName)}; const Database::UserId userId {getOrCreateUser(loginName)};
onUserAuthenticated(session, userId); onUserAuthenticated(userId);
return {CheckResult::State::Granted, userId}; return {CheckResult::State::Granted, userId};
} }
else else
+6 -8
View File
@@ -27,6 +27,7 @@
namespace Database namespace Database
{ {
class Db;
class Session; class Session;
} }
@@ -36,7 +37,7 @@ namespace Auth
class PasswordServiceBase : public IPasswordService, public AuthServiceBase class PasswordServiceBase : public IPasswordService, public AuthServiceBase
{ {
public: public:
PasswordServiceBase(std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService); PasswordServiceBase(Database::Db& db, std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService);
PasswordServiceBase(const PasswordServiceBase&) = delete; PasswordServiceBase(const PasswordServiceBase&) = delete;
PasswordServiceBase& operator=(const PasswordServiceBase&) = delete; PasswordServiceBase& operator=(const PasswordServiceBase&) = delete;
@@ -47,14 +48,11 @@ namespace Auth
IAuthTokenService& getAuthTokenService() { return _authTokenService; } IAuthTokenService& getAuthTokenService() { return _authTokenService; }
private: private:
virtual bool checkUserPassword(Database::Session& session, virtual bool checkUserPassword(std::string_view loginName, std::string_view password) = 0;
std::string_view loginName,
std::string_view password) = 0;
CheckResult checkUserPassword(Database::Session& session, CheckResult checkUserPassword(const boost::asio::ip::address& clientAddress,
const boost::asio::ip::address& clientAddress, std::string_view loginName,
std::string_view loginName, std::string_view password) override;
std::string_view password) override;
std::shared_mutex _mutex; std::shared_mutex _mutex;
LoginThrottler _loginThrottler; LoginThrottler _loginThrottler;
@@ -28,14 +28,15 @@
namespace Auth namespace Auth
{ {
HttpHeadersEnvService::HttpHeadersEnvService() HttpHeadersEnvService::HttpHeadersEnvService(Database::Db& db)
: _fieldName {Service<IConfig>::get()->getString("http-headers-login-field", "X-Forwarded-User")} : AuthServiceBase {db}
, _fieldName {Service<IConfig>::get()->getString("http-headers-login-field", "X-Forwarded-User")}
{ {
LMS_LOG(AUTH, INFO) << "Using http header field = '" << _fieldName << "'"; LMS_LOG(AUTH, INFO) << "Using http header field = '" << _fieldName << "'";
} }
HttpHeadersEnvService::CheckResult HttpHeadersEnvService::CheckResult
HttpHeadersEnvService::processEnv(Database::Session& session, const Wt::WEnvironment& env) HttpHeadersEnvService::processEnv(const Wt::WEnvironment& env)
{ {
const std::string loginName {env.headerValue(_fieldName)}; const std::string loginName {env.headerValue(_fieldName)};
if (loginName.empty()) if (loginName.empty())
@@ -43,13 +44,13 @@ namespace Auth
LMS_LOG(AUTH, DEBUG) << "Extracted login name = '" << loginName << "' from HTTP header"; LMS_LOG(AUTH, DEBUG) << "Extracted login name = '" << loginName << "' from HTTP header";
const Database::UserId userId {getOrCreateUser(session, loginName)}; const Database::UserId userId {getOrCreateUser(loginName)};
onUserAuthenticated(session, userId); onUserAuthenticated(userId);
return {CheckResult::State::Granted, userId}; return {CheckResult::State::Granted, userId};
} }
HttpHeadersEnvService::CheckResult HttpHeadersEnvService::CheckResult
HttpHeadersEnvService::processRequest(Database::Session& session, const Wt::Http::Request& request) HttpHeadersEnvService::processRequest(const Wt::Http::Request& request)
{ {
const std::string loginName {request.headerValue(_fieldName)}; const std::string loginName {request.headerValue(_fieldName)};
if (loginName.empty()) if (loginName.empty())
@@ -57,8 +58,8 @@ namespace Auth
LMS_LOG(AUTH, DEBUG) << "Extracted login name = '" << loginName << "' from HTTP header"; LMS_LOG(AUTH, DEBUG) << "Extracted login name = '" << loginName << "' from HTTP header";
const Database::UserId userId {getOrCreateUser(session, loginName)}; const Database::UserId userId {getOrCreateUser(loginName)};
onUserAuthenticated(session, userId); onUserAuthenticated(userId);
return {CheckResult::State::Granted, userId}; return {CheckResult::State::Granted, userId};
} }
@@ -27,11 +27,11 @@ namespace Auth
class HttpHeadersEnvService : public IEnvService, public AuthServiceBase class HttpHeadersEnvService : public IEnvService, public AuthServiceBase
{ {
public: public:
HttpHeadersEnvService(); HttpHeadersEnvService(Database::Db& db);
private: private:
CheckResult processEnv(Database::Session& session, const Wt::WEnvironment& env) override; CheckResult processEnv(const Wt::WEnvironment& env) override;
CheckResult processRequest(Database::Session& session, const Wt::Http::Request& request) override; CheckResult processRequest(const Wt::Http::Request& request) override;
std::string _fieldName; std::string _fieldName;
}; };
@@ -29,8 +29,8 @@
namespace Auth namespace Auth
{ {
InternalPasswordService::InternalPasswordService(std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService) InternalPasswordService::InternalPasswordService(Database::Db& db, std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService)
: PasswordServiceBase {maxThrottlerEntries, authTokenService} : PasswordServiceBase {db, maxThrottlerEntries, authTokenService}
{ {
_validator.setMinimumLength(Wt::Auth::PasswordStrengthType::OneCharClass, 4); _validator.setMinimumLength(Wt::Auth::PasswordStrengthType::OneCharClass, 4);
_validator.setMinimumLength(Wt::Auth::PasswordStrengthType::TwoCharClass, 4); _validator.setMinimumLength(Wt::Auth::PasswordStrengthType::TwoCharClass, 4);
@@ -42,14 +42,13 @@ namespace Auth
} }
bool bool
InternalPasswordService::checkUserPassword(Database::Session& session, InternalPasswordService::checkUserPassword(std::string_view loginName, std::string_view password)
std::string_view loginName,
std::string_view password)
{ {
LMS_LOG(AUTH, DEBUG) << "Checking internal password for user '" << loginName << "'"; LMS_LOG(AUTH, DEBUG) << "Checking internal password for user '" << loginName << "'";
Database::User::PasswordHash passwordHash; Database::User::PasswordHash passwordHash;
{ {
Database::Session& session {getDbSession()};
auto transaction {session.createSharedTransaction()}; auto transaction {session.createSharedTransaction()};
const Database::User::pointer user {Database::User::getByLoginName(session, loginName)}; const Database::User::pointer user {Database::User::getByLoginName(session, loginName)};
@@ -96,10 +95,11 @@ namespace Auth
} }
void void
InternalPasswordService::setPassword(Database::Session& session, Database::UserId userId, std::string_view newPassword) InternalPasswordService::setPassword(Database::UserId userId, std::string_view newPassword)
{ {
const Database::User::PasswordHash passwordHash {hashPassword(newPassword)}; const Database::User::PasswordHash passwordHash {hashPassword(newPassword)};
Database::Session& session {getDbSession()};
auto transaction {session.createUniqueTransaction()}; auto transaction {session.createUniqueTransaction()};
Database::User::pointer user {Database::User::getById(session, userId)}; Database::User::pointer user {Database::User::getById(session, userId)};
@@ -117,7 +117,7 @@ namespace Auth
} }
user.modify()->setPasswordHash(passwordHash); user.modify()->setPasswordHash(passwordHash);
getAuthTokenService().clearAuthTokens(session, userId); getAuthTokenService().clearAuthTokens(userId);
} }
Database::User::PasswordHash Database::User::PasswordHash
@@ -33,16 +33,14 @@ namespace Auth
class InternalPasswordService : public PasswordServiceBase class InternalPasswordService : public PasswordServiceBase
{ {
public: public:
InternalPasswordService(std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService); InternalPasswordService(Database::Db& db, std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService);
private: private:
bool checkUserPassword(Database::Session& session, bool checkUserPassword(std::string_view loginName, std::string_view password) override;
std::string_view loginName,
std::string_view password) override;
bool canSetPasswords() const override; bool canSetPasswords() const override;
PasswordAcceptabilityResult checkPasswordAcceptability(std::string_view loginName, const PasswordValidationContext& context) const override; PasswordAcceptabilityResult checkPasswordAcceptability(std::string_view loginName, const PasswordValidationContext& context) const override;
void setPassword(Database::Session& session, Database::UserId userId, std::string_view newPassword) override; void setPassword(Database::UserId userId, std::string_view newPassword) override;
Database::User::PasswordHash hashPassword(std::string_view password) const; Database::User::PasswordHash hashPassword(std::string_view password) const;
void hashRandomPassword() const; void hashRandomPassword() const;
@@ -161,7 +161,7 @@ namespace Auth
}; };
bool bool
PAMPasswordService::checkUserPassword(Database::Session& /*session*/, std::string_view loginName, std::string_view password) PAMPasswordService::checkUserPassword(std::string_view loginName, std::string_view password)
{ {
try try
{ {
@@ -193,7 +193,7 @@ namespace Auth
} }
void void
PAMPasswordService::setPassword(Database::Session&, Database::UserId, std::string_view) PAMPasswordService::setPassword(Database::UserId, std::string_view)
{ {
throw NotImplementedException {}; throw NotImplementedException {};
} }
@@ -31,14 +31,9 @@ namespace Auth
using PasswordServiceBase::PasswordServiceBase; using PasswordServiceBase::PasswordServiceBase;
private: private:
bool checkUserPassword(Database::Session& session, bool checkUserPassword(std::string_view loginName,std::string_view password) override;
std::string_view loginName,
std::string_view password) override;
bool canSetPasswords() const override; bool canSetPasswords() const override;
PasswordAcceptabilityResult checkPasswordAcceptability(std::string_view loginName, const PasswordValidationContext& context) const override; PasswordAcceptabilityResult checkPasswordAcceptability(std::string_view loginName, const PasswordValidationContext& context) const override;
void setPassword(Database::Session& session, void setPassword(Database::UserId userId, std::string_view newPassword) override;
Database::UserId userId,
std::string_view newPassword) override;
}; };
} }
@@ -31,7 +31,7 @@
namespace Database namespace Database
{ {
class Session; class Db;
class User; class User;
} }
@@ -63,12 +63,12 @@ namespace Auth
}; };
// Provided token is only accepted once // Provided token is only accepted once
virtual AuthTokenProcessResult processAuthToken(Database::Session& session, const boost::asio::ip::address& clientAddress, std::string_view tokenValue) = 0; virtual AuthTokenProcessResult processAuthToken(const boost::asio::ip::address& clientAddress, std::string_view tokenValue) = 0;
// Returns a one time token // Returns a one time token
virtual std::string createAuthToken(Database::Session& session, Database::UserId userid, const Wt::WDateTime& expiry) = 0; virtual std::string createAuthToken(Database::UserId userid, const Wt::WDateTime& expiry) = 0;
virtual void clearAuthTokens(Database::Session& session, Database::UserId userid) = 0; virtual void clearAuthTokens(Database::UserId userid) = 0;
}; };
std::unique_ptr<IAuthTokenService> createAuthTokenService(std::size_t maxThrottlerEntryCount); std::unique_ptr<IAuthTokenService> createAuthTokenService(Database::Db& db, std::size_t maxThrottlerEntryCount);
} }
+4 -3
View File
@@ -26,6 +26,7 @@
namespace Database namespace Database
{ {
class Db;
class Session; class Session;
} }
@@ -60,9 +61,9 @@ namespace Auth
std::optional<Database::UserId> userId {}; std::optional<Database::UserId> userId {};
}; };
virtual CheckResult processEnv(Database::Session& session, const Wt::WEnvironment& env) = 0; virtual CheckResult processEnv(const Wt::WEnvironment& env) = 0;
virtual CheckResult processRequest(Database::Session& session, const Wt::Http::Request& request) = 0; virtual CheckResult processRequest(const Wt::Http::Request& request) = 0;
}; };
std::unique_ptr<IEnvService> createEnvService(std::string_view backendName); std::unique_ptr<IEnvService> createEnvService(std::string_view backendName, Database::Db& db);
} // namespace Auth } // namespace Auth
@@ -30,7 +30,7 @@
namespace Database namespace Database
{ {
class Session; class Db;
class User; class User;
} }
@@ -56,10 +56,9 @@ namespace Auth
std::optional<Database::UserId> userId {}; std::optional<Database::UserId> userId {};
std::optional<Wt::WDateTime> expiry {}; std::optional<Wt::WDateTime> expiry {};
}; };
virtual CheckResult checkUserPassword(Database::Session& session, virtual CheckResult checkUserPassword(const boost::asio::ip::address& clientAddress,
const boost::asio::ip::address& clientAddress, std::string_view loginName,
std::string_view loginName, std::string_view password) = 0;
std::string_view password) = 0;
virtual bool canSetPasswords() const = 0; virtual bool canSetPasswords() const = 0;
@@ -70,9 +69,9 @@ namespace Auth
MustMatchLoginName, MustMatchLoginName,
}; };
virtual PasswordAcceptabilityResult checkPasswordAcceptability(std::string_view password, const PasswordValidationContext& context) const = 0; virtual PasswordAcceptabilityResult checkPasswordAcceptability(std::string_view password, const PasswordValidationContext& context) const = 0;
virtual void setPassword(Database::Session& session, Database::UserId userId, std::string_view newPassword) = 0; virtual void setPassword(Database::UserId userId, std::string_view newPassword) = 0;
}; };
std::unique_ptr<IPasswordService> createPasswordService(std::string_view authPasswordBackend, std::size_t maxThrottlerEntryCount, IAuthTokenService& authTokenService); std::unique_ptr<IPasswordService> createPasswordService(std::string_view authPasswordBackend, Database::Db& db, std::size_t maxThrottlerEntryCount, IAuthTokenService& authTokenService);
} }
+9 -14
View File
@@ -506,7 +506,7 @@ handleChangePassword(RequestContext& context)
userId = user->getId(); userId = user->getId();
} }
Service<Auth::IPasswordService>::get()->setPassword(context.dbSession, userId, password); Service<Auth::IPasswordService>::get()->setPassword(userId, password);
} }
catch (const Auth::PasswordMustMatchLoginNameException&) catch (const Auth::PasswordMustMatchLoginNameException&)
{ {
@@ -604,7 +604,7 @@ handleCreateUserRequest(RequestContext& context)
try try
{ {
Service<Auth::IPasswordService>::get()->setPassword(context.dbSession, userId, password); Service<Auth::IPasswordService>::get()->setPassword(userId, password);
} }
catch (const Auth::PasswordMustMatchLoginNameException&) catch (const Auth::PasswordMustMatchLoginNameException&)
{ {
@@ -1620,7 +1620,7 @@ handleUpdateUserRequest(RequestContext& context)
try try
{ {
Service<::Auth::IPasswordService>()->setPassword(context.dbSession, userId, decodePasswordIfNeeded(*password)); Service<::Auth::IPasswordService>()->setPassword(userId, decodePasswordIfNeeded(*password));
} }
catch (const Auth::PasswordMustMatchLoginNameException&) catch (const Auth::PasswordMustMatchLoginNameException&)
{ {
@@ -2043,22 +2043,18 @@ RequestContext
SubsonicResource::buildRequestContext(const Wt::Http::Request& request) SubsonicResource::buildRequestContext(const Wt::Http::Request& request)
{ {
const Wt::Http::ParameterMap& parameters {request.getParameterMap()}; const Wt::Http::ParameterMap& parameters {request.getParameterMap()};
const ClientInfo clientInfo {getClientInfo(parameters)}; const ClientInfo clientInfo {getClientInfo(parameters)};
const Database::UserId userId {authenticateUser(request, clientInfo)};
Session& dbSession {_db.getTLSSession()}; return {parameters, _db.getTLSSession(), userId, clientInfo, getServerProtocolVersion(clientInfo.name)};
const Database::UserId userId {authenticateUser(request, clientInfo, dbSession)};
return {parameters, dbSession, userId, clientInfo, getServerProtocolVersion(clientInfo.name)};
} }
Database::UserId Database::UserId
SubsonicResource::authenticateUser(const Wt::Http::Request& request, const ClientInfo& clientInfo, Session& dbSession) SubsonicResource::authenticateUser(const Wt::Http::Request& request, const ClientInfo& clientInfo)
{ {
if (auto *authEnvService {Service<::Auth::IEnvService>::get()}) if (auto *authEnvService {Service<::Auth::IEnvService>::get()})
{ {
const auto checkResult {authEnvService->processRequest(dbSession, request)}; const auto checkResult {authEnvService->processRequest(request)};
if (checkResult.state != ::Auth::IEnvService::CheckResult::State::Granted) if (checkResult.state != ::Auth::IEnvService::CheckResult::State::Granted)
throw UserNotAuthorizedError {}; throw UserNotAuthorizedError {};
@@ -2066,9 +2062,8 @@ SubsonicResource::authenticateUser(const Wt::Http::Request& request, const Clien
} }
else if (auto *authPasswordService {Service<::Auth::IPasswordService>::get()}) else if (auto *authPasswordService {Service<::Auth::IPasswordService>::get()})
{ {
const auto checkResult {authPasswordService->checkUserPassword(dbSession, const auto checkResult {authPasswordService->checkUserPassword(boost::asio::ip::address::from_string(request.clientAddress()),
boost::asio::ip::address::from_string(request.clientAddress()), clientInfo.user, clientInfo.password)};
clientInfo.user, clientInfo.password)};
switch (checkResult.state) switch (checkResult.state)
{ {
+1 -1
View File
@@ -48,7 +48,7 @@ namespace API::Subsonic
static void checkProtocolVersion(ProtocolVersion client, ProtocolVersion server); static void checkProtocolVersion(ProtocolVersion client, ProtocolVersion server);
ClientInfo getClientInfo(const Wt::Http::ParameterMap& parameters); ClientInfo getClientInfo(const Wt::Http::ParameterMap& parameters);
RequestContext buildRequestContext(const Wt::Http::Request& request); RequestContext buildRequestContext(const Wt::Http::Request& request);
Database::UserId authenticateUser(const Wt::Http::Request& request, const ClientInfo& clientInfo, Database::Session& dbSession); Database::UserId authenticateUser(const Wt::Http::Request& request, const ClientInfo& clientInfo);
const std::unordered_map<std::string, ProtocolVersion> _serverProtocolVersionsByClient; const std::unordered_map<std::string, ProtocolVersion> _serverProtocolVersionsByClient;
Database::Db& _db; Database::Db& _db;
+3 -3
View File
@@ -245,12 +245,12 @@ int main(int argc, char* argv[])
const std::string authenticationBackend {StringUtils::stringToLower(config->getString("authentication-backend", "internal"))}; const std::string authenticationBackend {StringUtils::stringToLower(config->getString("authentication-backend", "internal"))};
if (authenticationBackend == "internal" || authenticationBackend == "pam") if (authenticationBackend == "internal" || authenticationBackend == "pam")
{ {
authTokenService.assign(Auth::createAuthTokenService(config->getULong("login-throttler-max-entriees", 10000))); authTokenService.assign(Auth::createAuthTokenService(database, config->getULong("login-throttler-max-entriees", 10000)));
authPasswordService.assign(Auth::createPasswordService(authenticationBackend, config->getULong("login-throttler-max-entriees", 10000), *authTokenService.get())); authPasswordService.assign(Auth::createPasswordService(authenticationBackend, database, config->getULong("login-throttler-max-entriees", 10000), *authTokenService.get()));
} }
else if (authenticationBackend == "http-headers") else if (authenticationBackend == "http-headers")
{ {
authEnvService.assign(Auth::createEnvService(authenticationBackend)); authEnvService.assign(Auth::createEnvService(authenticationBackend, database));
} }
else else
throw LmsException {"Bad value '" + authenticationBackend + "' for 'authentication-backend'"}; throw LmsException {"Bad value '" + authenticationBackend + "' for 'authentication-backend'"};
+2 -3
View File
@@ -49,7 +49,7 @@ static
void void
createAuthToken(Database::UserId userId, const Wt::WDateTime& expiry) createAuthToken(Database::UserId userId, const Wt::WDateTime& expiry)
{ {
const std::string secret {Service<::Auth::IAuthTokenService>::get()->createAuthToken(LmsApp->getDbSession(), userId, expiry)}; const std::string secret {Service<::Auth::IAuthTokenService>::get()->createAuthToken(userId, expiry)};
LmsApp->setCookie(authCookieName, LmsApp->setCookie(authCookieName,
secret, secret,
@@ -67,7 +67,7 @@ processAuthToken(const Wt::WEnvironment& env)
if (!authCookie) if (!authCookie)
return std::nullopt; return std::nullopt;
const auto res {Service<::Auth::IAuthTokenService>::get()->processAuthToken(LmsApp->getDbSession(), boost::asio::ip::address::from_string(env.clientAddress()), *authCookie)}; const auto res {Service<::Auth::IAuthTokenService>::get()->processAuthToken(boost::asio::ip::address::from_string(env.clientAddress()), *authCookie)};
switch (res.state) switch (res.state)
{ {
case ::Auth::IAuthTokenService::AuthTokenProcessResult::State::Denied: case ::Auth::IAuthTokenService::AuthTokenProcessResult::State::Denied:
@@ -131,7 +131,6 @@ class AuthModel : public Wt::WFormModel
if (field == PasswordField) if (field == PasswordField)
{ {
const auto checkResult {Service<::Auth::IPasswordService>::get()->checkUserPassword( const auto checkResult {Service<::Auth::IPasswordService>::get()->checkUserPassword(
LmsApp->getDbSession(),
boost::asio::ip::address::from_string(LmsApp->environment().clientAddress()), boost::asio::ip::address::from_string(LmsApp->environment().clientAddress()),
valueText(LoginNameField).toUTF8(), valueText(LoginNameField).toUTF8(),
valueText(PasswordField).toUTF8())}; valueText(PasswordField).toUTF8())};
+1 -1
View File
@@ -69,7 +69,7 @@ LmsApplication::create(const Wt::WEnvironment& env, Database::Db& db, LmsApplica
{ {
if (auto *authEnvService {Service<::Auth::IEnvService>::get()}) if (auto *authEnvService {Service<::Auth::IEnvService>::get()})
{ {
const auto checkResult {authEnvService->processEnv(db.getTLSSession(), env)}; const auto checkResult {authEnvService->processEnv(env)};
if (checkResult.state != ::Auth::IEnvService::CheckResult::State::Granted) if (checkResult.state != ::Auth::IEnvService::CheckResult::State::Granted)
{ {
LMS_LOG(UI, ERROR) << "Cannot authenticate user from environment!"; LMS_LOG(UI, ERROR) << "Cannot authenticate user from environment!";
+1 -1
View File
@@ -197,7 +197,7 @@ class SettingsModel : public Wt::WFormModel
if (_authPasswordService && !valueText(PasswordField).empty()) if (_authPasswordService && !valueText(PasswordField).empty())
{ {
_authPasswordService->setPassword(LmsApp->getDbSession(), user->getId(), valueText(PasswordField).toUTF8()); _authPasswordService->setPassword(user->getId(), valueText(PasswordField).toUTF8());
} }
} }
+1 -1
View File
@@ -70,7 +70,7 @@ class InitWizardModel : public Wt::WFormModel
Database::User::pointer user {Database::User::create(LmsApp->getDbSession(), valueText(AdminLoginField).toUTF8())}; Database::User::pointer user {Database::User::create(LmsApp->getDbSession(), valueText(AdminLoginField).toUTF8())};
user.modify()->setType(Database::UserType::ADMIN); user.modify()->setType(Database::UserType::ADMIN);
Service<::Auth::IPasswordService>::get()->setPassword(LmsApp->getDbSession(), user->getId(), valueText(PasswordField).toUTF8()); Service<::Auth::IPasswordService>::get()->setPassword(user->getId(), valueText(PasswordField).toUTF8());
} }
bool validateField(Field field) bool validateField(Field field)
+2 -2
View File
@@ -87,7 +87,7 @@ class UserModel : public Wt::WFormModel
throw UserNotFoundException {}; throw UserNotFoundException {};
if (_authPasswordService && !valueText(PasswordField).empty()) if (_authPasswordService && !valueText(PasswordField).empty())
_authPasswordService->setPassword(LmsApp->getDbSession(), user->getId(), valueText(PasswordField).toUTF8()); _authPasswordService->setPassword(user->getId(), valueText(PasswordField).toUTF8());
} }
else else
{ {
@@ -103,7 +103,7 @@ class UserModel : public Wt::WFormModel
user.modify()->setType(Database::UserType::DEMO); user.modify()->setType(Database::UserType::DEMO);
if (_authPasswordService) if (_authPasswordService)
_authPasswordService->setPassword(LmsApp->getDbSession(), user->getId(), valueText(PasswordField).toUTF8()); _authPasswordService->setPassword(user->getId(), valueText(PasswordField).toUTF8());
} }
} }
-1
View File
@@ -80,7 +80,6 @@ namespace UserInterface
return Wt::WValidator::validate(input); return Wt::WValidator::validate(input);
const auto checkResult {Service<::Auth::IPasswordService>::get()->checkUserPassword( const auto checkResult {Service<::Auth::IPasswordService>::get()->checkUserPassword(
LmsApp->getDbSession(),
boost::asio::ip::address::from_string(LmsApp->environment().clientAddress()), boost::asio::ip::address::from_string(LmsApp->environment().clientAddress()),
LmsApp->getUserLoginName(), LmsApp->getUserLoginName(),
input.toUTF8())}; input.toUTF8())};