Auto reformatted the base, ref #470
This commit is contained in:
@@ -19,11 +19,10 @@
|
||||
|
||||
#include "AuthServiceBase.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include "core/ILogger.hpp"
|
||||
#include "database/Db.hpp"
|
||||
#include "database/Session.hpp"
|
||||
#include "database/User.hpp"
|
||||
#include "core/ILogger.hpp"
|
||||
|
||||
namespace lms::auth
|
||||
{
|
||||
@@ -31,7 +30,8 @@ namespace lms::auth
|
||||
|
||||
AuthServiceBase::AuthServiceBase(Db& db)
|
||||
: _db{ db }
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
UserId AuthServiceBase::getOrCreateUser(std::string_view loginName)
|
||||
{
|
||||
@@ -91,4 +91,4 @@ namespace lms::auth
|
||||
{
|
||||
return _db.getTLSSession();
|
||||
}
|
||||
}
|
||||
} // namespace lms::auth
|
||||
|
||||
@@ -20,13 +20,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "database/UserId.hpp"
|
||||
|
||||
namespace lms::db
|
||||
{
|
||||
class Db;
|
||||
class Session;
|
||||
}
|
||||
} // namespace lms::db
|
||||
|
||||
namespace lms::auth
|
||||
{
|
||||
@@ -35,12 +36,12 @@ namespace lms::auth
|
||||
protected:
|
||||
AuthServiceBase(db::Db& db);
|
||||
|
||||
db::UserId getOrCreateUser(std::string_view loginName);
|
||||
void onUserAuthenticated(db::UserId userId);
|
||||
db::UserId getOrCreateUser(std::string_view loginName);
|
||||
void onUserAuthenticated(db::UserId userId);
|
||||
|
||||
db::Session& getDbSession();
|
||||
db::Session& getDbSession();
|
||||
|
||||
private:
|
||||
db::Db& _db;
|
||||
};
|
||||
}
|
||||
} // namespace lms::auth
|
||||
|
||||
@@ -23,121 +23,121 @@
|
||||
#include <Wt/Auth/PasswordStrengthValidator.h>
|
||||
#include <Wt/WRandom.h>
|
||||
|
||||
#include "services/auth/Types.hpp"
|
||||
#include "core/Exception.hpp"
|
||||
#include "core/ILogger.hpp"
|
||||
#include "database/AuthToken.hpp"
|
||||
#include "database/Session.hpp"
|
||||
#include "database/User.hpp"
|
||||
#include "core/Exception.hpp"
|
||||
#include "core/ILogger.hpp"
|
||||
#include "services/auth/Types.hpp"
|
||||
|
||||
namespace lms::auth
|
||||
{
|
||||
|
||||
std::unique_ptr<IAuthTokenService> createAuthTokenService(db::Db& db, std::size_t maxThrottlerEntries)
|
||||
{
|
||||
return std::make_unique<AuthTokenService>(db, maxThrottlerEntries);
|
||||
}
|
||||
std::unique_ptr<IAuthTokenService> createAuthTokenService(db::Db& db, std::size_t maxThrottlerEntries)
|
||||
{
|
||||
return std::make_unique<AuthTokenService>(db, maxThrottlerEntries);
|
||||
}
|
||||
|
||||
static const Wt::Auth::SHA1HashFunction sha1Function;
|
||||
static const Wt::Auth::SHA1HashFunction sha1Function;
|
||||
|
||||
AuthTokenService::AuthTokenService(db::Db& db, std::size_t maxThrottlerEntries)
|
||||
: AuthServiceBase {db}
|
||||
, _loginThrottler {maxThrottlerEntries}
|
||||
{
|
||||
}
|
||||
AuthTokenService::AuthTokenService(db::Db& db, std::size_t maxThrottlerEntries)
|
||||
: AuthServiceBase{ db }
|
||||
, _loginThrottler{ maxThrottlerEntries }
|
||||
{
|
||||
}
|
||||
|
||||
std::string
|
||||
AuthTokenService::createAuthToken(db::UserId userId, const Wt::WDateTime& expiry)
|
||||
{
|
||||
const std::string secret {Wt::WRandom::generateId(32)};
|
||||
const std::string secretHash {sha1Function.compute(secret, {})};
|
||||
std::string
|
||||
AuthTokenService::createAuthToken(db::UserId userId, const Wt::WDateTime& expiry)
|
||||
{
|
||||
const std::string secret{ Wt::WRandom::generateId(32) };
|
||||
const std::string secretHash{ sha1Function.compute(secret, {}) };
|
||||
|
||||
db::Session& session {getDbSession()};
|
||||
db::Session& session{ getDbSession() };
|
||||
|
||||
auto transaction {session.createWriteTransaction()};
|
||||
auto transaction{ session.createWriteTransaction() };
|
||||
|
||||
db::User::pointer user {db::User::find(session, userId)};
|
||||
if (!user)
|
||||
throw Exception {"User deleted"};
|
||||
db::User::pointer user{ db::User::find(session, userId) };
|
||||
if (!user)
|
||||
throw Exception{ "User deleted" };
|
||||
|
||||
db::AuthToken::pointer authToken {session.create<db::AuthToken>(secretHash, expiry, user)};
|
||||
db::AuthToken::pointer authToken{ session.create<db::AuthToken>(secretHash, expiry, user) };
|
||||
|
||||
LMS_LOG(UI, DEBUG, "Created auth token for user '" << user->getLoginName() << "', expiry = " << expiry.toString());
|
||||
LMS_LOG(UI, DEBUG, "Created auth token for user '" << user->getLoginName() << "', expiry = " << expiry.toString());
|
||||
|
||||
if (user->getAuthTokensCount() >= 50)
|
||||
db::AuthToken::removeExpiredTokens(session, Wt::WDateTime::currentDateTime());
|
||||
if (user->getAuthTokensCount() >= 50)
|
||||
db::AuthToken::removeExpiredTokens(session, Wt::WDateTime::currentDateTime());
|
||||
|
||||
return secret;
|
||||
}
|
||||
return secret;
|
||||
}
|
||||
|
||||
std::optional<AuthTokenService::AuthTokenProcessResult::AuthTokenInfo>
|
||||
AuthTokenService::processAuthToken(std::string_view secret)
|
||||
{
|
||||
const std::string secretHash {sha1Function.compute(std::string {secret}, {})};
|
||||
std::optional<AuthTokenService::AuthTokenProcessResult::AuthTokenInfo>
|
||||
AuthTokenService::processAuthToken(std::string_view secret)
|
||||
{
|
||||
const std::string secretHash{ sha1Function.compute(std::string{ secret }, {}) };
|
||||
|
||||
db::Session& session {getDbSession()};
|
||||
auto transaction {session.createWriteTransaction()};
|
||||
db::Session& session{ getDbSession() };
|
||||
auto transaction{ session.createWriteTransaction() };
|
||||
|
||||
db::AuthToken::pointer authToken {db::AuthToken::find(session, secretHash)};
|
||||
if (!authToken)
|
||||
return std::nullopt;
|
||||
db::AuthToken::pointer authToken{ db::AuthToken::find(session, secretHash) };
|
||||
if (!authToken)
|
||||
return std::nullopt;
|
||||
|
||||
if (authToken->getExpiry() < Wt::WDateTime::currentDateTime())
|
||||
{
|
||||
authToken.remove();
|
||||
return std::nullopt;
|
||||
}
|
||||
if (authToken->getExpiry() < Wt::WDateTime::currentDateTime())
|
||||
{
|
||||
authToken.remove();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
LMS_LOG(UI, DEBUG, "Found auth token for user '" << authToken->getUser()->getLoginName() << "'!");
|
||||
LMS_LOG(UI, DEBUG, "Found auth token for user '" << authToken->getUser()->getLoginName() << "'!");
|
||||
|
||||
AuthTokenService::AuthTokenProcessResult::AuthTokenInfo res {authToken->getUser()->getId(), authToken->getExpiry()};
|
||||
authToken.remove();
|
||||
AuthTokenService::AuthTokenProcessResult::AuthTokenInfo res{ authToken->getUser()->getId(), authToken->getExpiry() };
|
||||
authToken.remove();
|
||||
|
||||
return res;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
AuthTokenService::AuthTokenProcessResult
|
||||
AuthTokenService::processAuthToken(const boost::asio::ip::address& clientAddress, std::string_view tokenValue)
|
||||
{
|
||||
// Do not waste too much resource on brute force attacks (optim)
|
||||
{
|
||||
std::shared_lock lock {_mutex};
|
||||
AuthTokenService::AuthTokenProcessResult
|
||||
AuthTokenService::processAuthToken(const boost::asio::ip::address& clientAddress, std::string_view tokenValue)
|
||||
{
|
||||
// Do not waste too much resource on brute force attacks (optim)
|
||||
{
|
||||
std::shared_lock lock{ _mutex };
|
||||
|
||||
if (_loginThrottler.isClientThrottled(clientAddress))
|
||||
return AuthTokenProcessResult {AuthTokenProcessResult::State::Throttled};
|
||||
}
|
||||
if (_loginThrottler.isClientThrottled(clientAddress))
|
||||
return AuthTokenProcessResult{ AuthTokenProcessResult::State::Throttled };
|
||||
}
|
||||
|
||||
auto res {processAuthToken(tokenValue)};
|
||||
{
|
||||
std::unique_lock lock {_mutex};
|
||||
auto res{ processAuthToken(tokenValue) };
|
||||
{
|
||||
std::unique_lock lock{ _mutex };
|
||||
|
||||
if (_loginThrottler.isClientThrottled(clientAddress))
|
||||
return AuthTokenProcessResult {AuthTokenProcessResult::State::Throttled};
|
||||
if (_loginThrottler.isClientThrottled(clientAddress))
|
||||
return AuthTokenProcessResult{ AuthTokenProcessResult::State::Throttled };
|
||||
|
||||
if (!res)
|
||||
{
|
||||
_loginThrottler.onBadClientAttempt(clientAddress);
|
||||
return AuthTokenProcessResult {AuthTokenProcessResult::State::Denied};
|
||||
}
|
||||
if (!res)
|
||||
{
|
||||
_loginThrottler.onBadClientAttempt(clientAddress);
|
||||
return AuthTokenProcessResult{ AuthTokenProcessResult::State::Denied };
|
||||
}
|
||||
|
||||
_loginThrottler.onGoodClientAttempt(clientAddress);
|
||||
onUserAuthenticated(res->userId);
|
||||
return AuthTokenProcessResult {AuthTokenProcessResult::State::Granted, std::move(*res)};
|
||||
}
|
||||
}
|
||||
_loginThrottler.onGoodClientAttempt(clientAddress);
|
||||
onUserAuthenticated(res->userId);
|
||||
return AuthTokenProcessResult{ AuthTokenProcessResult::State::Granted, std::move(*res) };
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AuthTokenService::clearAuthTokens(db::UserId userId)
|
||||
{
|
||||
db::Session& session {getDbSession()};
|
||||
void
|
||||
AuthTokenService::clearAuthTokens(db::UserId userId)
|
||||
{
|
||||
db::Session& session{ getDbSession() };
|
||||
|
||||
auto transaction {session.createWriteTransaction()};
|
||||
auto transaction{ session.createWriteTransaction() };
|
||||
|
||||
db::User::pointer user {db::User::find(session, userId)};
|
||||
if (!user)
|
||||
throw Exception {"User deleted"};
|
||||
db::User::pointer user{ db::User::find(session, userId) };
|
||||
if (!user)
|
||||
throw Exception{ "User deleted" };
|
||||
|
||||
user.modify()->clearAuthTokens();
|
||||
}
|
||||
user.modify()->clearAuthTokens();
|
||||
}
|
||||
|
||||
} // namespace lms::auth
|
||||
|
||||
@@ -22,34 +22,35 @@
|
||||
#include <shared_mutex>
|
||||
|
||||
#include "services/auth/IAuthTokenService.hpp"
|
||||
|
||||
#include "AuthServiceBase.hpp"
|
||||
#include "LoginThrottler.hpp"
|
||||
|
||||
namespace lms::db
|
||||
{
|
||||
class Session;
|
||||
class Session;
|
||||
}
|
||||
|
||||
namespace lms::auth
|
||||
{
|
||||
class AuthTokenService : public IAuthTokenService, public AuthServiceBase
|
||||
{
|
||||
public:
|
||||
AuthTokenService(db::Db& db, std::size_t maxThrottlerEntries);
|
||||
class AuthTokenService : public IAuthTokenService, public AuthServiceBase
|
||||
{
|
||||
public:
|
||||
AuthTokenService(db::Db& db, std::size_t maxThrottlerEntries);
|
||||
|
||||
AuthTokenService(const AuthTokenService&) = delete;
|
||||
AuthTokenService& operator=(const AuthTokenService&) = delete;
|
||||
AuthTokenService(AuthTokenService&&) = delete;
|
||||
AuthTokenService& operator=(AuthTokenService&&) = delete;
|
||||
AuthTokenService(const AuthTokenService&) = delete;
|
||||
AuthTokenService& operator=(const AuthTokenService&) = delete;
|
||||
AuthTokenService(AuthTokenService&&) = delete;
|
||||
AuthTokenService& operator=(AuthTokenService&&) = delete;
|
||||
|
||||
private:
|
||||
AuthTokenProcessResult processAuthToken(const boost::asio::ip::address& clientAddress, std::string_view tokenValue) override;
|
||||
std::string createAuthToken(db::UserId userId, const Wt::WDateTime& expiry) override;
|
||||
void clearAuthTokens(db::UserId userId) override;
|
||||
private:
|
||||
AuthTokenProcessResult processAuthToken(const boost::asio::ip::address& clientAddress, std::string_view tokenValue) override;
|
||||
std::string createAuthToken(db::UserId userId, const Wt::WDateTime& expiry) override;
|
||||
void clearAuthTokens(db::UserId userId) override;
|
||||
|
||||
std::optional<AuthTokenService::AuthTokenProcessResult::AuthTokenInfo> processAuthToken(std::string_view secret);
|
||||
std::optional<AuthTokenService::AuthTokenProcessResult::AuthTokenInfo> processAuthToken(std::string_view secret);
|
||||
|
||||
std::shared_mutex _mutex;
|
||||
LoginThrottler _loginThrottler;
|
||||
};
|
||||
}
|
||||
std::shared_mutex _mutex;
|
||||
LoginThrottler _loginThrottler;
|
||||
};
|
||||
} // namespace lms::auth
|
||||
|
||||
@@ -20,16 +20,17 @@
|
||||
#include "services/auth/IEnvService.hpp"
|
||||
|
||||
#include "services/auth/Types.hpp"
|
||||
|
||||
#include "http-headers/HttpHeadersEnvService.hpp"
|
||||
|
||||
namespace lms::auth
|
||||
{
|
||||
std::unique_ptr<IEnvService>
|
||||
createEnvService(std::string_view backendName, db::Db& db)
|
||||
{
|
||||
if (backendName == "http-headers")
|
||||
return std::make_unique<HttpHeadersEnvService>(db);
|
||||
std::unique_ptr<IEnvService>
|
||||
createEnvService(std::string_view backendName, db::Db& db)
|
||||
{
|
||||
if (backendName == "http-headers")
|
||||
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!" };
|
||||
}
|
||||
} // namespace lms::auth
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* This file contains some classes in order to get info from file using the libavconv */
|
||||
|
||||
#include "LoginThrottler.hpp"
|
||||
|
||||
#include "core/ILogger.hpp"
|
||||
@@ -44,13 +42,13 @@ namespace lms::auth
|
||||
{
|
||||
return address.is_v6() ? getAddressWithMask(address.to_v6(), 64) : address;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void LoginThrottler::removeOutdatedEntries()
|
||||
{
|
||||
const Wt::WDateTime now{ Wt::WDateTime::currentDateTime() };
|
||||
|
||||
for (auto it{ std::begin(_attemptsInfo) }; it != std::end(_attemptsInfo); )
|
||||
for (auto it{ std::begin(_attemptsInfo) }; it != std::end(_attemptsInfo);)
|
||||
{
|
||||
if (it->second.nextAttempt <= now)
|
||||
it = _attemptsInfo.erase(it);
|
||||
@@ -110,4 +108,4 @@ namespace lms::auth
|
||||
|
||||
return it->second.nextAttempt > Wt::WDateTime::currentDateTime();
|
||||
}
|
||||
}
|
||||
} // namespace lms::auth
|
||||
@@ -24,34 +24,34 @@
|
||||
|
||||
#include <Wt/WDateTime.h>
|
||||
|
||||
#include "core/NetAddress.hpp"
|
||||
#include "core/Exception.hpp"
|
||||
#include "core/NetAddress.hpp"
|
||||
|
||||
namespace lms::auth
|
||||
{
|
||||
class LoginThrottler
|
||||
{
|
||||
public:
|
||||
LoginThrottler(std::size_t maxEntries) : _maxEntries {maxEntries} {}
|
||||
class LoginThrottler
|
||||
{
|
||||
public:
|
||||
LoginThrottler(std::size_t maxEntries)
|
||||
: _maxEntries{ maxEntries } {}
|
||||
|
||||
// user must lock these calls to avoid races
|
||||
bool isClientThrottled(const boost::asio::ip::address& address) const;
|
||||
void onBadClientAttempt(const boost::asio::ip::address& address);
|
||||
void onGoodClientAttempt(const boost::asio::ip::address& address);
|
||||
// user must lock these calls to avoid races
|
||||
bool isClientThrottled(const boost::asio::ip::address& address) const;
|
||||
void onBadClientAttempt(const boost::asio::ip::address& address);
|
||||
void onGoodClientAttempt(const boost::asio::ip::address& address);
|
||||
|
||||
private:
|
||||
void removeOutdatedEntries();
|
||||
private:
|
||||
void removeOutdatedEntries();
|
||||
|
||||
const std::size_t _maxEntries;
|
||||
static constexpr std::size_t _maxBadConsecutiveAttemptCount {5};
|
||||
static constexpr std::chrono::seconds _throttlingDuration {3};
|
||||
|
||||
struct AttemptInfo
|
||||
{
|
||||
Wt::WDateTime nextAttempt;
|
||||
std::size_t badConsecutiveAttemptCount{};
|
||||
};
|
||||
std::unordered_map<boost::asio::ip::address, AttemptInfo> _attemptsInfo;
|
||||
};
|
||||
} // Auth
|
||||
const std::size_t _maxEntries;
|
||||
static constexpr std::size_t _maxBadConsecutiveAttemptCount{ 5 };
|
||||
static constexpr std::chrono::seconds _throttlingDuration{ 3 };
|
||||
|
||||
struct AttemptInfo
|
||||
{
|
||||
Wt::WDateTime nextAttempt;
|
||||
std::size_t badConsecutiveAttemptCount{};
|
||||
};
|
||||
std::unordered_map<boost::asio::ip::address, AttemptInfo> _attemptsInfo;
|
||||
};
|
||||
} // namespace lms::auth
|
||||
|
||||
@@ -24,73 +24,72 @@
|
||||
|
||||
#include "internal/InternalPasswordService.hpp"
|
||||
#ifdef LMS_SUPPORT_PAM
|
||||
#include "pam/PAMPasswordService.hpp"
|
||||
#include "pam/PAMPasswordService.hpp"
|
||||
#endif // LMS_SUPPORT_PAM
|
||||
|
||||
#include "services/auth/Types.hpp"
|
||||
#include "database/Session.hpp"
|
||||
#include "database/User.hpp"
|
||||
#include "core/Exception.hpp"
|
||||
#include "core/ILogger.hpp"
|
||||
#include "database/Session.hpp"
|
||||
#include "database/User.hpp"
|
||||
#include "services/auth/Types.hpp"
|
||||
|
||||
namespace lms::auth
|
||||
{
|
||||
static const Wt::Auth::SHA1HashFunction sha1Function;
|
||||
static const Wt::Auth::SHA1HashFunction sha1Function;
|
||||
|
||||
std::unique_ptr<IPasswordService>
|
||||
createPasswordService(std::string_view passwordAuthenticationBackend, db::Db& db, std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService)
|
||||
{
|
||||
if (passwordAuthenticationBackend == "internal")
|
||||
return std::make_unique<InternalPasswordService>(db, maxThrottlerEntries, authTokenService);
|
||||
std::unique_ptr<IPasswordService>
|
||||
createPasswordService(std::string_view passwordAuthenticationBackend, db::Db& db, std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService)
|
||||
{
|
||||
if (passwordAuthenticationBackend == "internal")
|
||||
return std::make_unique<InternalPasswordService>(db, maxThrottlerEntries, authTokenService);
|
||||
#ifdef LMS_SUPPORT_PAM
|
||||
else if (passwordAuthenticationBackend == "pam")
|
||||
return std::make_unique<PAMPasswordService>(db, maxThrottlerEntries, authTokenService);
|
||||
else if (passwordAuthenticationBackend == "pam")
|
||||
return std::make_unique<PAMPasswordService>(db, maxThrottlerEntries, authTokenService);
|
||||
#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(db::Db& db, std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService)
|
||||
: AuthServiceBase {db}
|
||||
, _loginThrottler {maxThrottlerEntries}
|
||||
, _authTokenService {authTokenService}
|
||||
{
|
||||
}
|
||||
PasswordServiceBase::PasswordServiceBase(db::Db& db, std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService)
|
||||
: AuthServiceBase{ db }
|
||||
, _loginThrottler{ maxThrottlerEntries }
|
||||
, _authTokenService{ authTokenService }
|
||||
{
|
||||
}
|
||||
|
||||
PasswordServiceBase::CheckResult
|
||||
PasswordServiceBase::checkUserPassword(const boost::asio::ip::address& clientAddress, std::string_view loginName, std::string_view password)
|
||||
{
|
||||
LMS_LOG(AUTH, DEBUG, "Checking password for user '" << loginName << "'");
|
||||
PasswordServiceBase::CheckResult
|
||||
PasswordServiceBase::checkUserPassword(const boost::asio::ip::address& clientAddress, std::string_view loginName, std::string_view password)
|
||||
{
|
||||
LMS_LOG(AUTH, DEBUG, "Checking password for user '" << loginName << "'");
|
||||
|
||||
// Do not waste too much resource on brute force attacks (optim)
|
||||
{
|
||||
std::shared_lock lock {_mutex};
|
||||
// Do not waste too much resource on brute force attacks (optim)
|
||||
{
|
||||
std::shared_lock lock{ _mutex };
|
||||
|
||||
if (_loginThrottler.isClientThrottled(clientAddress))
|
||||
return {CheckResult::State::Throttled};
|
||||
}
|
||||
if (_loginThrottler.isClientThrottled(clientAddress))
|
||||
return { CheckResult::State::Throttled };
|
||||
}
|
||||
|
||||
const bool match {checkUserPassword(loginName, password)};
|
||||
{
|
||||
std::unique_lock lock {_mutex};
|
||||
const bool match{ checkUserPassword(loginName, password) };
|
||||
{
|
||||
std::unique_lock lock{ _mutex };
|
||||
|
||||
if (_loginThrottler.isClientThrottled(clientAddress))
|
||||
return {CheckResult::State::Throttled};
|
||||
if (_loginThrottler.isClientThrottled(clientAddress))
|
||||
return { CheckResult::State::Throttled };
|
||||
|
||||
if (match)
|
||||
{
|
||||
_loginThrottler.onGoodClientAttempt(clientAddress);
|
||||
if (match)
|
||||
{
|
||||
_loginThrottler.onGoodClientAttempt(clientAddress);
|
||||
|
||||
const db::UserId userId {getOrCreateUser(loginName)};
|
||||
onUserAuthenticated(userId);
|
||||
return {CheckResult::State::Granted, userId};
|
||||
}
|
||||
else
|
||||
{
|
||||
_loginThrottler.onBadClientAttempt(clientAddress);
|
||||
return {CheckResult::State::Denied};
|
||||
}
|
||||
}
|
||||
}
|
||||
const db::UserId userId{ getOrCreateUser(loginName) };
|
||||
onUserAuthenticated(userId);
|
||||
return { CheckResult::State::Granted, userId };
|
||||
}
|
||||
else
|
||||
{
|
||||
_loginThrottler.onBadClientAttempt(clientAddress);
|
||||
return { CheckResult::State::Denied };
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace lms::auth
|
||||
|
||||
|
||||
@@ -21,40 +21,40 @@
|
||||
|
||||
#include <shared_mutex>
|
||||
|
||||
#include "services/auth/IPasswordService.hpp"
|
||||
#include "AuthServiceBase.hpp"
|
||||
#include "LoginThrottler.hpp"
|
||||
#include "services/auth/IPasswordService.hpp"
|
||||
|
||||
namespace lms::db
|
||||
{
|
||||
class Db;
|
||||
class Session;
|
||||
}
|
||||
class Db;
|
||||
class Session;
|
||||
} // namespace lms::db
|
||||
|
||||
namespace lms::auth
|
||||
{
|
||||
class PasswordServiceBase : public IPasswordService, public AuthServiceBase
|
||||
{
|
||||
public:
|
||||
PasswordServiceBase(db::Db& db, std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService);
|
||||
class PasswordServiceBase : public IPasswordService, public AuthServiceBase
|
||||
{
|
||||
public:
|
||||
PasswordServiceBase(db::Db& db, std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService);
|
||||
|
||||
PasswordServiceBase(const PasswordServiceBase&) = delete;
|
||||
PasswordServiceBase& operator=(const PasswordServiceBase&) = delete;
|
||||
PasswordServiceBase(PasswordServiceBase&&) = delete;
|
||||
PasswordServiceBase& operator=(PasswordServiceBase&&) = delete;
|
||||
PasswordServiceBase(const PasswordServiceBase&) = delete;
|
||||
PasswordServiceBase& operator=(const PasswordServiceBase&) = delete;
|
||||
PasswordServiceBase(PasswordServiceBase&&) = delete;
|
||||
PasswordServiceBase& operator=(PasswordServiceBase&&) = delete;
|
||||
|
||||
protected:
|
||||
IAuthTokenService& getAuthTokenService() { return _authTokenService; }
|
||||
protected:
|
||||
IAuthTokenService& getAuthTokenService() { return _authTokenService; }
|
||||
|
||||
private:
|
||||
virtual bool checkUserPassword(std::string_view loginName, std::string_view password) = 0;
|
||||
private:
|
||||
virtual bool checkUserPassword(std::string_view loginName, std::string_view password) = 0;
|
||||
|
||||
CheckResult checkUserPassword(const boost::asio::ip::address& clientAddress,
|
||||
std::string_view loginName,
|
||||
std::string_view password) override;
|
||||
CheckResult checkUserPassword(const boost::asio::ip::address& clientAddress,
|
||||
std::string_view loginName,
|
||||
std::string_view password) override;
|
||||
|
||||
std::shared_mutex _mutex;
|
||||
LoginThrottler _loginThrottler;
|
||||
IAuthTokenService& _authTokenService;
|
||||
};
|
||||
}
|
||||
std::shared_mutex _mutex;
|
||||
LoginThrottler _loginThrottler;
|
||||
IAuthTokenService& _authTokenService;
|
||||
};
|
||||
} // namespace lms::auth
|
||||
|
||||
@@ -20,21 +20,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "services/auth/IEnvService.hpp"
|
||||
|
||||
#include "AuthServiceBase.hpp"
|
||||
|
||||
namespace lms::auth
|
||||
{
|
||||
class HttpHeadersEnvService : public IEnvService, public AuthServiceBase
|
||||
{
|
||||
public:
|
||||
HttpHeadersEnvService(db::Db& db);
|
||||
class HttpHeadersEnvService : public IEnvService, public AuthServiceBase
|
||||
{
|
||||
public:
|
||||
HttpHeadersEnvService(db::Db& db);
|
||||
|
||||
private:
|
||||
CheckResult processEnv(const Wt::WEnvironment& env) override;
|
||||
CheckResult processRequest(const Wt::Http::Request& request) override;
|
||||
private:
|
||||
CheckResult processEnv(const Wt::WEnvironment& env) override;
|
||||
CheckResult processRequest(const Wt::Http::Request& request) override;
|
||||
|
||||
std::string _fieldName;
|
||||
};
|
||||
std::string _fieldName;
|
||||
};
|
||||
|
||||
} // namespace lms::auth
|
||||
|
||||
|
||||
@@ -18,14 +18,15 @@
|
||||
*/
|
||||
|
||||
#include "InternalPasswordService.hpp"
|
||||
|
||||
#include <Wt/WRandom.h>
|
||||
|
||||
#include "services/auth/IAuthTokenService.hpp"
|
||||
#include "services/auth/Types.hpp"
|
||||
#include "database/Session.hpp"
|
||||
#include "database/User.hpp"
|
||||
#include "core/Exception.hpp"
|
||||
#include "core/ILogger.hpp"
|
||||
#include "database/Session.hpp"
|
||||
#include "database/User.hpp"
|
||||
#include "services/auth/IAuthTokenService.hpp"
|
||||
#include "services/auth/Types.hpp"
|
||||
|
||||
namespace lms::auth
|
||||
{
|
||||
@@ -120,14 +121,13 @@ namespace lms::auth
|
||||
{
|
||||
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
|
||||
InternalPasswordService::hashRandomPassword() const
|
||||
{
|
||||
hashPassword(Wt::WRandom::generateId(32));
|
||||
}
|
||||
|
||||
} // namespace lms::auth
|
||||
|
||||
|
||||
@@ -23,8 +23,9 @@
|
||||
#include <Wt/Auth/PasswordStrengthValidator.h>
|
||||
|
||||
#include "database/User.hpp"
|
||||
#include "PasswordServiceBase.hpp"
|
||||
|
||||
#include "LoginThrottler.hpp"
|
||||
#include "PasswordServiceBase.hpp"
|
||||
|
||||
namespace lms::auth
|
||||
{
|
||||
@@ -36,17 +37,17 @@ namespace lms::auth
|
||||
InternalPasswordService(db::Db& db, std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService);
|
||||
|
||||
private:
|
||||
bool checkUserPassword(std::string_view loginName, std::string_view password) override;
|
||||
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(db::UserId userId, std::string_view newPassword) override;
|
||||
bool canSetPasswords() const override;
|
||||
PasswordAcceptabilityResult checkPasswordAcceptability(std::string_view loginName, const PasswordValidationContext& context) const override;
|
||||
void setPassword(db::UserId userId, std::string_view newPassword) override;
|
||||
|
||||
db::User::PasswordHash hashPassword(std::string_view password) const;
|
||||
void hashRandomPassword() const;
|
||||
db::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;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace lms::auth
|
||||
|
||||
@@ -20,15 +20,15 @@
|
||||
#include "PAMPasswordService.hpp"
|
||||
|
||||
#ifndef LMS_SUPPORT_PAM
|
||||
#error "Should not compile this"
|
||||
#error "Should not compile this"
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
#include <security/pam_appl.h>
|
||||
|
||||
#include "services/auth/Types.hpp"
|
||||
#include "database/Session.hpp"
|
||||
#include "core/ILogger.hpp"
|
||||
#include "database/Session.hpp"
|
||||
#include "services/auth/Types.hpp"
|
||||
|
||||
namespace lms::auth
|
||||
{
|
||||
@@ -53,7 +53,7 @@ namespace lms::auth
|
||||
public:
|
||||
PAMContext(std::string_view loginName)
|
||||
{
|
||||
int err{ pam_start("lms", std::string {loginName}.c_str(), &_conv, &_pamh) };
|
||||
int err{ pam_start("lms", std::string{ loginName }.c_str(), &_conv, &_pamh) };
|
||||
if (err != PAM_SUCCESS)
|
||||
throw PAMError{ "start failed", _pamh, err };
|
||||
}
|
||||
@@ -92,7 +92,8 @@ namespace lms::auth
|
||||
class AuthenticateConvContext final : public ConvContext
|
||||
{
|
||||
public:
|
||||
AuthenticateConvContext(std::string_view password) : _password{ password } {}
|
||||
AuthenticateConvContext(std::string_view password)
|
||||
: _password{ password } {}
|
||||
|
||||
std::string_view getPassword() const { return _password; }
|
||||
|
||||
@@ -160,7 +161,7 @@ namespace lms::auth
|
||||
pam_conv _conv{ &PAMContext::conv, this };
|
||||
pam_handle_t* _pamh{};
|
||||
};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool PAMPasswordService::checkUserPassword(std::string_view loginName, std::string_view password)
|
||||
{
|
||||
@@ -197,4 +198,3 @@ namespace lms::auth
|
||||
}
|
||||
|
||||
} // namespace lms::auth
|
||||
|
||||
|
||||
@@ -25,15 +25,15 @@
|
||||
|
||||
namespace lms::auth
|
||||
{
|
||||
class PAMPasswordService: public PasswordServiceBase
|
||||
{
|
||||
public:
|
||||
using PasswordServiceBase::PasswordServiceBase;
|
||||
class PAMPasswordService : public PasswordServiceBase
|
||||
{
|
||||
public:
|
||||
using PasswordServiceBase::PasswordServiceBase;
|
||||
|
||||
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(db::UserId userId, std::string_view newPassword) 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(db::UserId userId, std::string_view newPassword) override;
|
||||
};
|
||||
} // namespace lms::auth
|
||||
|
||||
@@ -17,58 +17,56 @@
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* This file contains some classes in order to get info from file using the libavconv */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Wt/WDateTime.h>
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
#include <Wt/WDateTime.h>
|
||||
|
||||
#include "database/UserId.hpp"
|
||||
|
||||
namespace lms::db
|
||||
{
|
||||
class Db;
|
||||
class User;
|
||||
}
|
||||
class Db;
|
||||
class User;
|
||||
} // namespace lms::db
|
||||
|
||||
namespace lms::auth
|
||||
{
|
||||
class IAuthTokenService
|
||||
{
|
||||
public:
|
||||
virtual ~IAuthTokenService() = default;
|
||||
class IAuthTokenService
|
||||
{
|
||||
public:
|
||||
virtual ~IAuthTokenService() = default;
|
||||
|
||||
// Auth Token services
|
||||
struct AuthTokenProcessResult
|
||||
{
|
||||
enum class State
|
||||
{
|
||||
Granted,
|
||||
Throttled,
|
||||
Denied,
|
||||
};
|
||||
// Auth Token services
|
||||
struct AuthTokenProcessResult
|
||||
{
|
||||
enum class State
|
||||
{
|
||||
Granted,
|
||||
Throttled,
|
||||
Denied,
|
||||
};
|
||||
|
||||
struct AuthTokenInfo
|
||||
{
|
||||
db::UserId userId;
|
||||
Wt::WDateTime expiry;
|
||||
};
|
||||
struct AuthTokenInfo
|
||||
{
|
||||
db::UserId userId;
|
||||
Wt::WDateTime expiry;
|
||||
};
|
||||
|
||||
State state {State::Denied};
|
||||
std::optional<AuthTokenInfo> authTokenInfo {};
|
||||
};
|
||||
State state{ State::Denied };
|
||||
std::optional<AuthTokenInfo> authTokenInfo{};
|
||||
};
|
||||
|
||||
// Provided token is only accepted once
|
||||
virtual AuthTokenProcessResult processAuthToken(const boost::asio::ip::address& clientAddress, std::string_view tokenValue) = 0;
|
||||
// Provided token is only accepted once
|
||||
virtual AuthTokenProcessResult processAuthToken(const boost::asio::ip::address& clientAddress, std::string_view tokenValue) = 0;
|
||||
|
||||
// Returns a one time token
|
||||
virtual std::string createAuthToken(db::UserId userid, const Wt::WDateTime& expiry) = 0;
|
||||
virtual void clearAuthTokens(db::UserId userid) = 0;
|
||||
};
|
||||
// Returns a one time token
|
||||
virtual std::string createAuthToken(db::UserId userid, const Wt::WDateTime& expiry) = 0;
|
||||
virtual void clearAuthTokens(db::UserId userid) = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<IAuthTokenService> createAuthTokenService(db::Db& db, std::size_t maxThrottlerEntryCount);
|
||||
}
|
||||
std::unique_ptr<IAuthTokenService> createAuthTokenService(db::Db& db, std::size_t maxThrottlerEntryCount);
|
||||
} // namespace lms::auth
|
||||
|
||||
@@ -26,44 +26,44 @@
|
||||
|
||||
namespace lms::db
|
||||
{
|
||||
class Db;
|
||||
class Session;
|
||||
}
|
||||
class Db;
|
||||
class Session;
|
||||
} // namespace lms::db
|
||||
|
||||
namespace Wt
|
||||
{
|
||||
class WEnvironment;
|
||||
class WEnvironment;
|
||||
}
|
||||
|
||||
namespace Wt::Http
|
||||
{
|
||||
class Request;
|
||||
class Request;
|
||||
}
|
||||
|
||||
namespace lms::auth
|
||||
{
|
||||
class IEnvService
|
||||
{
|
||||
public:
|
||||
virtual ~IEnvService() = default;
|
||||
class IEnvService
|
||||
{
|
||||
public:
|
||||
virtual ~IEnvService() = default;
|
||||
|
||||
// Auth Token services
|
||||
struct CheckResult
|
||||
{
|
||||
enum class State
|
||||
{
|
||||
Granted,
|
||||
Denied,
|
||||
Throttled,
|
||||
};
|
||||
// Auth Token services
|
||||
struct CheckResult
|
||||
{
|
||||
enum class State
|
||||
{
|
||||
Granted,
|
||||
Denied,
|
||||
Throttled,
|
||||
};
|
||||
|
||||
State state {State::Denied};
|
||||
std::optional<db::UserId> userId {};
|
||||
};
|
||||
State state{ State::Denied };
|
||||
std::optional<db::UserId> userId{};
|
||||
};
|
||||
|
||||
virtual CheckResult processEnv(const Wt::WEnvironment& env) = 0;
|
||||
virtual CheckResult processRequest(const Wt::Http::Request& request) = 0;
|
||||
};
|
||||
virtual CheckResult processEnv(const Wt::WEnvironment& env) = 0;
|
||||
virtual CheckResult processRequest(const Wt::Http::Request& request) = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<IEnvService> createEnvService(std::string_view backendName, db::Db& db);
|
||||
std::unique_ptr<IEnvService> createEnvService(std::string_view backendName, db::Db& db);
|
||||
} // namespace lms::auth
|
||||
|
||||
@@ -19,59 +19,59 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
#include <Wt/WDateTime.h>
|
||||
#include <Wt/Dbo/ptr.h>
|
||||
#include <Wt/WDateTime.h>
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
|
||||
#include "services/auth/Types.hpp"
|
||||
#include "database/UserId.hpp"
|
||||
#include "services/auth/Types.hpp"
|
||||
|
||||
namespace lms::db
|
||||
{
|
||||
class Db;
|
||||
class User;
|
||||
}
|
||||
class Db;
|
||||
class User;
|
||||
} // namespace lms::db
|
||||
|
||||
namespace lms::auth
|
||||
{
|
||||
class IAuthTokenService;
|
||||
class IAuthTokenService;
|
||||
|
||||
class IPasswordService
|
||||
{
|
||||
public:
|
||||
virtual ~IPasswordService() = default;
|
||||
class IPasswordService
|
||||
{
|
||||
public:
|
||||
virtual ~IPasswordService() = default;
|
||||
|
||||
struct CheckResult
|
||||
{
|
||||
enum class State
|
||||
{
|
||||
Granted,
|
||||
Denied,
|
||||
Throttled,
|
||||
};
|
||||
State state {State::Denied};
|
||||
std::optional<db::UserId> userId {};
|
||||
std::optional<Wt::WDateTime> expiry {};
|
||||
};
|
||||
virtual CheckResult checkUserPassword(const boost::asio::ip::address& clientAddress,
|
||||
std::string_view loginName,
|
||||
std::string_view password) = 0;
|
||||
struct CheckResult
|
||||
{
|
||||
enum class State
|
||||
{
|
||||
Granted,
|
||||
Denied,
|
||||
Throttled,
|
||||
};
|
||||
State state{ State::Denied };
|
||||
std::optional<db::UserId> userId{};
|
||||
std::optional<Wt::WDateTime> expiry{};
|
||||
};
|
||||
virtual CheckResult checkUserPassword(const boost::asio::ip::address& clientAddress,
|
||||
std::string_view loginName,
|
||||
std::string_view password)
|
||||
= 0;
|
||||
|
||||
virtual bool canSetPasswords() const = 0;
|
||||
virtual bool canSetPasswords() const = 0;
|
||||
|
||||
enum class PasswordAcceptabilityResult
|
||||
{
|
||||
OK,
|
||||
TooWeak,
|
||||
MustMatchLoginName,
|
||||
};
|
||||
virtual PasswordAcceptabilityResult checkPasswordAcceptability(std::string_view password, const PasswordValidationContext& context) const = 0;
|
||||
virtual void setPassword(db::UserId userId, std::string_view newPassword) = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<IPasswordService> createPasswordService(std::string_view authPasswordBackend, db::Db& db, std::size_t maxThrottlerEntryCount, IAuthTokenService& authTokenService);
|
||||
}
|
||||
enum class PasswordAcceptabilityResult
|
||||
{
|
||||
OK,
|
||||
TooWeak,
|
||||
MustMatchLoginName,
|
||||
};
|
||||
virtual PasswordAcceptabilityResult checkPasswordAcceptability(std::string_view password, const PasswordValidationContext& context) const = 0;
|
||||
virtual void setPassword(db::UserId userId, std::string_view newPassword) = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<IPasswordService> createPasswordService(std::string_view authPasswordBackend, db::Db& db, std::size_t maxThrottlerEntryCount, IAuthTokenService& authTokenService);
|
||||
} // namespace lms::auth
|
||||
|
||||
@@ -20,50 +20,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "database/Types.hpp"
|
||||
|
||||
#include "core/Exception.hpp"
|
||||
#include "database/Types.hpp"
|
||||
|
||||
namespace lms::auth
|
||||
{
|
||||
class Exception : public core::LmsException
|
||||
{
|
||||
using core::LmsException::LmsException;
|
||||
};
|
||||
class Exception : public core::LmsException
|
||||
{
|
||||
using core::LmsException::LmsException;
|
||||
};
|
||||
|
||||
class NotImplementedException : public Exception
|
||||
{
|
||||
public:
|
||||
NotImplementedException() : Exception {"Not implemented"} {}
|
||||
};
|
||||
class NotImplementedException : public Exception
|
||||
{
|
||||
public:
|
||||
NotImplementedException()
|
||||
: Exception{ "Not implemented" } {}
|
||||
};
|
||||
|
||||
class UserNotFoundException : public Exception
|
||||
{
|
||||
public:
|
||||
UserNotFoundException() : Exception {"User not found"} {}
|
||||
};
|
||||
class UserNotFoundException : public Exception
|
||||
{
|
||||
public:
|
||||
UserNotFoundException()
|
||||
: Exception{ "User not found" } {}
|
||||
};
|
||||
|
||||
struct PasswordValidationContext
|
||||
{
|
||||
std::string loginName;
|
||||
db::UserType userType;
|
||||
};
|
||||
struct PasswordValidationContext
|
||||
{
|
||||
std::string loginName;
|
||||
db::UserType userType;
|
||||
};
|
||||
|
||||
class PasswordException : public Exception
|
||||
{
|
||||
public:
|
||||
using Exception::Exception;
|
||||
};
|
||||
class PasswordException : public Exception
|
||||
{
|
||||
public:
|
||||
using Exception::Exception;
|
||||
};
|
||||
|
||||
class PasswordTooWeakException : public PasswordException
|
||||
{
|
||||
public:
|
||||
PasswordTooWeakException() : PasswordException {"Password too weak"} {}
|
||||
};
|
||||
|
||||
class PasswordMustMatchLoginNameException : public PasswordException
|
||||
{
|
||||
public:
|
||||
PasswordMustMatchLoginNameException() : PasswordException {"Password must match login name"} {}
|
||||
};
|
||||
}
|
||||
class PasswordTooWeakException : public PasswordException
|
||||
{
|
||||
public:
|
||||
PasswordTooWeakException()
|
||||
: PasswordException{ "Password too weak" } {}
|
||||
};
|
||||
|
||||
class PasswordMustMatchLoginNameException : public PasswordException
|
||||
{
|
||||
public:
|
||||
PasswordMustMatchLoginNameException()
|
||||
: PasswordException{ "Password must match login name" } {}
|
||||
};
|
||||
} // namespace lms::auth
|
||||
|
||||
Reference in New Issue
Block a user