Moved away from Wt::Auth and use a simplified (?) login/password system with a login throttler based on IP address
This commit is contained in:
+27
-229
@@ -19,15 +19,6 @@
|
||||
|
||||
#include "Session.hpp"
|
||||
|
||||
#include <Wt/Auth/Dbo/AuthInfo.h>
|
||||
#include <Wt/Auth/Dbo/UserDatabase.h>
|
||||
#include <Wt/Auth/AuthService.h>
|
||||
#include <Wt/Auth/HashFunction.h>
|
||||
#include <Wt/Auth/Identity.h>
|
||||
#include <Wt/Auth/PasswordService.h>
|
||||
#include <Wt/Auth/PasswordStrengthValidator.h>
|
||||
#include <Wt/Auth/PasswordVerifier.h>
|
||||
|
||||
#include "utils/Exception.hpp"
|
||||
#include "utils/Logger.hpp"
|
||||
|
||||
@@ -44,12 +35,7 @@
|
||||
|
||||
namespace Database {
|
||||
|
||||
#define LMS_DATABASE_VERSION 4
|
||||
|
||||
namespace {
|
||||
Wt::Auth::AuthService authService;
|
||||
Wt::Auth::PasswordService passwordService {authService};
|
||||
}
|
||||
#define LMS_DATABASE_VERSION 5
|
||||
|
||||
using Version = std::size_t;
|
||||
|
||||
@@ -112,36 +98,6 @@ Session::doDatabaseMigrationIfNeeded()
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 3:
|
||||
|
||||
LMS_LOG(DB, INFO) << "Migrating database from version 3...";
|
||||
|
||||
_session.execute(R"(CREATE TABLE IF NOT EXISTS "user_artist_starred" (
|
||||
"user_id" bigint,
|
||||
"artist_id" bigint,
|
||||
primary key ("user_id", "artist_id"),
|
||||
constraint "fk_user_artist_starred_key1" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred,
|
||||
constraint "fk_user_artist_starred_key2" foreign key ("artist_id") references "artist" ("id") deferrable initially deferred);)");
|
||||
_session.execute(R"(CREATE INDEX "user_artist_starred_user" on "user_artist_starred" ("user_id");)");
|
||||
_session.execute(R"(CREATE INDEX "user_artist_starred_artist" on "user_artist_starred" ("artist_id");)");
|
||||
_session.execute(R"(CREATE TABLE IF NOT EXISTS "user_release_starred" (
|
||||
"user_id" bigint,
|
||||
"release_id" bigint,
|
||||
primary key ("user_id", "release_id"),
|
||||
constraint "fk_user_release_starred_key1" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred,
|
||||
constraint "fk_user_release_starred_key2" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred);)");
|
||||
_session.execute(R"(CREATE INDEX "user_release_starred_user" on "user_release_starred" ("user_id");)");
|
||||
_session.execute(R"(CREATE INDEX "user_release_starred_release" on "user_release_starred" ("release_id");)");
|
||||
_session.execute(R"(CREATE TABLE IF NOT EXISTS "user_track_starred" (
|
||||
"user_id" bigint,
|
||||
"track_id" bigint,
|
||||
primary key ("user_id", "track_id"),
|
||||
constraint "fk_user_track_starred_key1" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred,
|
||||
constraint "fk_user_track_starred_key2" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred);)");
|
||||
_session.execute(R"(CREATE INDEX "user_track_starred_user" on "user_track_starred" ("user_id");)");
|
||||
_session.execute(R"(CREATE INDEX "user_track_starred_track" on "user_track_starred" ("track_id");)");
|
||||
break;
|
||||
|
||||
default:
|
||||
LMS_LOG(DB, ERROR) << "Database version " << version << " cannot be handled using migration";
|
||||
throw LmsException {outdatedMsg};
|
||||
@@ -150,51 +106,6 @@ Session::doDatabaseMigrationIfNeeded()
|
||||
VersionInfo::get(*this).modify()->setVersion(LMS_DATABASE_VERSION);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Session::configureAuth(void)
|
||||
{
|
||||
authService.setEmailVerificationEnabled(false);
|
||||
authService.setAuthTokensEnabled(true, "lmsauth");
|
||||
authService.setAuthTokenValidity(24 * 60 * 365); // A year
|
||||
authService.setIdentityPolicy(Wt::Auth::IdentityPolicy::LoginName);
|
||||
authService.setRandomTokenLength(32);
|
||||
|
||||
#if WT_VERSION < 0X04000300
|
||||
authService.setTokenHashFunction(new Wt::Auth::BCryptHashFunction(8));
|
||||
#else
|
||||
authService.setTokenHashFunction(std::make_unique<Wt::Auth::BCryptHashFunction>(8));
|
||||
#endif
|
||||
|
||||
auto verifier = std::make_unique<Wt::Auth::PasswordVerifier>();
|
||||
verifier->addHashFunction(std::make_unique<Wt::Auth::BCryptHashFunction>(8));
|
||||
passwordService.setVerifier(std::move(verifier));
|
||||
passwordService.setAttemptThrottlingEnabled(true);
|
||||
|
||||
auto strengthValidator = std::make_unique<Wt::Auth::PasswordStrengthValidator>();
|
||||
// Reduce some constraints...
|
||||
strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthType::PassPhrase, 4);
|
||||
strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthType::OneCharClass, 4);
|
||||
strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthType::TwoCharClass, 4);
|
||||
strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthType::ThreeCharClass, 4 );
|
||||
strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthType::FourCharClass, 4 );
|
||||
|
||||
passwordService.setStrengthValidator(std::move(strengthValidator));
|
||||
}
|
||||
|
||||
const Wt::Auth::AuthService&
|
||||
Session::getAuthService()
|
||||
{
|
||||
return authService;
|
||||
}
|
||||
|
||||
const Wt::Auth::PasswordService&
|
||||
Session::getPasswordService()
|
||||
{
|
||||
return passwordService;
|
||||
}
|
||||
|
||||
|
||||
Session::Session(std::shared_timed_mutex& mutex, Wt::Dbo::SqlConnectionPool& connectionPool)
|
||||
: _mutex {mutex}
|
||||
{
|
||||
@@ -202,75 +113,69 @@ Session::Session(std::shared_timed_mutex& mutex, Wt::Dbo::SqlConnectionPool& con
|
||||
|
||||
_session.mapClass<VersionInfo>("version_info");
|
||||
_session.mapClass<Artist>("artist");
|
||||
_session.mapClass<AuthToken>("auth_token");
|
||||
_session.mapClass<Cluster>("cluster");
|
||||
_session.mapClass<ClusterType>("cluster_type");
|
||||
_session.mapClass<TrackList>("tracklist");
|
||||
_session.mapClass<TrackListEntry>("tracklist_entry");
|
||||
_session.mapClass<Release>("release");
|
||||
_session.mapClass<Track>("track");
|
||||
_session.mapClass<TrackArtistLink>("track_artist_link");
|
||||
_session.mapClass<TrackFeatures>("track_features");
|
||||
|
||||
_session.mapClass<ScanSettings>("scan_settings");
|
||||
_session.mapClass<SimilaritySettings>("similarity_settings");
|
||||
_session.mapClass<SimilaritySettingsFeature>("similarity_settings_feature");
|
||||
|
||||
_session.mapClass<AuthInfo>("auth_info");
|
||||
_session.mapClass<AuthInfo::AuthIdentityType>("auth_identity");
|
||||
_session.mapClass<AuthInfo::AuthTokenType>("auth_token");
|
||||
_session.mapClass<Track>("track");
|
||||
_session.mapClass<TrackArtistLink>("track_artist_link");
|
||||
_session.mapClass<TrackFeatures>("track_features");
|
||||
_session.mapClass<TrackList>("tracklist");
|
||||
_session.mapClass<TrackListEntry>("tracklist_entry");
|
||||
_session.mapClass<User>("user");
|
||||
|
||||
_users = std::make_unique<UserDatabase>(_session);
|
||||
}
|
||||
|
||||
// TODO make this per database
|
||||
static thread_local bool hasSharedLock {false};
|
||||
static thread_local bool hasUniqueLock {false};
|
||||
|
||||
enum class OwnedLock
|
||||
{
|
||||
None,
|
||||
Shared,
|
||||
Unique,
|
||||
};
|
||||
|
||||
static thread_local std::map<std::shared_timed_mutex*, OwnedLock> lockDebug;
|
||||
|
||||
UniqueTransaction::UniqueTransaction(std::shared_timed_mutex& mutex, Wt::Dbo::Session& session)
|
||||
: _lock {mutex},
|
||||
_transaction {session}
|
||||
{
|
||||
assert(!hasSharedLock);
|
||||
assert(!hasUniqueLock);
|
||||
hasUniqueLock = true;
|
||||
LMS_LOG(DB, DEBUG) << "UniqueTransaction ACQUIRED";
|
||||
assert(lockDebug[_lock.mutex()] == OwnedLock::None);
|
||||
lockDebug[_lock.mutex()] = OwnedLock::Unique;
|
||||
}
|
||||
|
||||
UniqueTransaction::~UniqueTransaction()
|
||||
{
|
||||
assert(hasUniqueLock);
|
||||
hasUniqueLock = false;
|
||||
LMS_LOG(DB, DEBUG) << "UniqueTransaction RELEASED";
|
||||
assert(lockDebug[_lock.mutex()] == OwnedLock::Unique);
|
||||
lockDebug[_lock.mutex()] = OwnedLock::None;
|
||||
}
|
||||
|
||||
SharedTransaction::SharedTransaction(std::shared_timed_mutex& mutex, Wt::Dbo::Session& session)
|
||||
: _lock {mutex},
|
||||
_transaction {session}
|
||||
{
|
||||
assert(!hasSharedLock);
|
||||
assert(!hasUniqueLock);
|
||||
hasSharedLock = true;
|
||||
LMS_LOG(DB, DEBUG) << "SharedTransaction ACQUIRED";
|
||||
assert(lockDebug[_lock.mutex()] == OwnedLock::None);
|
||||
lockDebug[_lock.mutex()] = OwnedLock::Shared;
|
||||
}
|
||||
|
||||
SharedTransaction::~SharedTransaction()
|
||||
{
|
||||
assert(hasSharedLock);
|
||||
hasSharedLock = false;
|
||||
LMS_LOG(DB, DEBUG) << "SharedTransaction RELEASED";
|
||||
assert(lockDebug[_lock.mutex()] == OwnedLock::Shared);
|
||||
lockDebug[_lock.mutex()] = OwnedLock::None;
|
||||
}
|
||||
|
||||
void
|
||||
Session::checkUniqueLocked()
|
||||
{
|
||||
assert(hasUniqueLock);
|
||||
assert(lockDebug[&_mutex] == OwnedLock::Unique);
|
||||
}
|
||||
|
||||
void
|
||||
Session::checkSharedLocked()
|
||||
{
|
||||
assert(hasUniqueLock || hasSharedLock);
|
||||
assert(lockDebug[&_mutex] != OwnedLock::None);
|
||||
}
|
||||
|
||||
std::unique_ptr<UniqueTransaction>
|
||||
@@ -307,6 +212,7 @@ Session::prepareTables()
|
||||
_session.execute("CREATE INDEX IF NOT EXISTS artist_name_idx ON artist(name)");
|
||||
_session.execute("CREATE INDEX IF NOT EXISTS artist_sort_name_nocase_idx ON artist(sort_name COLLATE NOCASE)");
|
||||
_session.execute("CREATE INDEX IF NOT EXISTS artist_mbid_idx ON artist(mbid)");
|
||||
_session.execute("CREATE INDEX IF NOT EXISTS auth_token_user_idx ON auth_token(user_id)");
|
||||
_session.execute("CREATE INDEX IF NOT EXISTS cluster_name_idx ON cluster(name)");
|
||||
_session.execute("CREATE INDEX IF NOT EXISTS cluster_cluster_type_idx ON cluster(cluster_type_id)");
|
||||
_session.execute("CREATE INDEX IF NOT EXISTS cluster_type_name_idx ON cluster_type(name)");
|
||||
@@ -346,112 +252,4 @@ Session::optimize()
|
||||
_session.execute("ANALYZE");
|
||||
}
|
||||
|
||||
std::string
|
||||
Session::getUserLoginName(Wt::Dbo::ptr<User> user)
|
||||
{
|
||||
const Wt::Auth::User authUser {_users->findWithId(std::to_string(user.id()))};
|
||||
if (!authUser.isValid())
|
||||
throw LmsException {"Invalid user state"};
|
||||
|
||||
return authUser.identity(Wt::Auth::Identity::LoginName).toUTF8();
|
||||
}
|
||||
|
||||
bool
|
||||
Session::checkUserPassword(const std::string& loginName, const std::string& password)
|
||||
{
|
||||
auto transaction {createUniqueTransaction()};
|
||||
|
||||
auto authUser {_users->findWithIdentity(Wt::Auth::Identity::LoginName, loginName)};
|
||||
if (!authUser.isValid())
|
||||
return false; // TODO const time?
|
||||
|
||||
return passwordService.verifyPassword(authUser, password) == Wt::Auth::PasswordResult::PasswordValid;
|
||||
}
|
||||
|
||||
void
|
||||
Session::updateUserPassword(Wt::Dbo::ptr<User> user, const std::string& password)
|
||||
{
|
||||
const Wt::Auth::User authUser {_users->findWithId(std::to_string(user.id()))};
|
||||
if (!authUser.isValid())
|
||||
throw LmsException {"Bad user state"};
|
||||
passwordService.updatePassword(authUser, password);
|
||||
}
|
||||
|
||||
Wt::WDateTime
|
||||
Session::getUserLastLoginAttempt(Wt::Dbo::ptr<User> user)
|
||||
{
|
||||
const Wt::Auth::User authUser {_users->findWithId(std::to_string(user.id()))};
|
||||
if (!authUser.isValid())
|
||||
throw LmsException {"Bad user state"};
|
||||
|
||||
return authUser.lastLoginAttempt();
|
||||
}
|
||||
|
||||
void
|
||||
Session::removeUser(Database::User::pointer user)
|
||||
{
|
||||
checkUniqueLocked();
|
||||
|
||||
auto authUser = _users->findWithId(std::to_string(user.id()));
|
||||
_users->deleteUser(authUser);
|
||||
user.remove();
|
||||
}
|
||||
|
||||
Wt::Auth::AbstractUserDatabase&
|
||||
Session::getUserDatabase()
|
||||
{
|
||||
return *_users;
|
||||
}
|
||||
|
||||
User::pointer
|
||||
Session::createUser(const std::string& loginName, const std::string& password)
|
||||
{
|
||||
Wt::Auth::User authUser {_users->registerNew()};
|
||||
if (!authUser.isValid())
|
||||
{
|
||||
LMS_LOG(DB, ERROR) << "Invalid authUser";
|
||||
return {};
|
||||
}
|
||||
User::pointer user {User::create(*this)};
|
||||
Wt::Dbo::ptr<AuthInfo> authInfo = _users->find(authUser);
|
||||
authInfo.modify()->setUser(user);
|
||||
|
||||
authUser.setIdentity(Wt::Auth::Identity::LoginName, loginName);
|
||||
passwordService.updatePassword(authUser, password);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
User::pointer
|
||||
Session::getLoggedUser()
|
||||
{
|
||||
if (_login.loggedIn())
|
||||
return getUser(_login.user());
|
||||
else
|
||||
return User::pointer();
|
||||
}
|
||||
|
||||
User::pointer
|
||||
Session::getUser(const Wt::Auth::User& authUser)
|
||||
{
|
||||
if (!authUser.isValid()) {
|
||||
LMS_LOG(DB, ERROR) << "Session::getUser: invalid authUser";
|
||||
return User::pointer();
|
||||
}
|
||||
|
||||
Wt::Dbo::ptr<AuthInfo> authInfo = _users->find(authUser);
|
||||
|
||||
return authInfo->user();
|
||||
}
|
||||
|
||||
User::pointer
|
||||
Session::getUser(const std::string& loginName)
|
||||
{
|
||||
auto authUser {getUserDatabase().findWithIdentity(Wt::Auth::Identity::LoginName, loginName)};
|
||||
if (!authUser.isValid())
|
||||
return User::pointer {};
|
||||
|
||||
return getUser(authUser);
|
||||
}
|
||||
|
||||
} // namespace Database
|
||||
|
||||
@@ -26,19 +26,9 @@
|
||||
|
||||
#include <Wt/Dbo/Dbo.h>
|
||||
#include <Wt/Dbo/SqlConnectionPool.h>
|
||||
#include <Wt/Auth/Dbo/AuthInfo.h>
|
||||
|
||||
#include <Wt/Auth/Dbo/UserDatabase.h>
|
||||
#include <Wt/Auth/Login.h>
|
||||
#include <Wt/Auth/PasswordService.h>
|
||||
|
||||
#include "User.hpp"
|
||||
|
||||
namespace Database {
|
||||
|
||||
using AuthInfo = Wt::Auth::Dbo::AuthInfo<User>;
|
||||
using UserDatabase = Wt::Auth::Dbo::UserDatabase<AuthInfo>;
|
||||
|
||||
class UniqueTransaction
|
||||
{
|
||||
public:
|
||||
@@ -81,24 +71,6 @@ class Session
|
||||
|
||||
void optimize();
|
||||
|
||||
// User management
|
||||
Wt::Dbo::ptr<User> getLoggedUser(); // get the current user, may return empty
|
||||
Wt::Dbo::ptr<User> getUser(const std::string& loginName);
|
||||
std::string getUserLoginName(Wt::Dbo::ptr<User> user);
|
||||
Wt::Dbo::ptr<User> createUser(const std::string& loginName, const std::string& password);
|
||||
void removeUser(Wt::Dbo::ptr<User> user);
|
||||
bool checkUserPassword(const std::string& loginName, const std::string& password);
|
||||
void updateUserPassword(Wt::Dbo::ptr<User> user, const std::string& password);
|
||||
Wt::WDateTime getUserLastLoginAttempt(Wt::Dbo::ptr<User> user);
|
||||
|
||||
Wt::Auth::AbstractUserDatabase& getUserDatabase();
|
||||
Wt::Auth::Login& getLogin() { return _login; } // TODO move
|
||||
|
||||
// Long living shared associated services
|
||||
static void configureAuth();
|
||||
static const Wt::Auth::AuthService& getAuthService();
|
||||
static const Wt::Auth::PasswordService& getPasswordService();
|
||||
|
||||
Wt::Dbo::Session& getDboSession() { return _session; }
|
||||
|
||||
private:
|
||||
@@ -109,13 +81,8 @@ class Session
|
||||
void doDatabaseMigrationIfNeeded();
|
||||
void prepareTables(); // need to run only once at startup
|
||||
|
||||
Wt::Dbo::ptr<User> getUser(const Wt::Auth::User& authUser);
|
||||
|
||||
std::shared_timed_mutex& _mutex;
|
||||
Wt::Dbo::Session _session;
|
||||
std::unique_ptr<UserDatabase> _users;
|
||||
Wt::Auth::Login _login;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Database
|
||||
|
||||
+54
-2
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "User.hpp"
|
||||
|
||||
#include "utils/Logger.hpp"
|
||||
#include "Artist.hpp"
|
||||
#include "Release.hpp"
|
||||
#include "Session.hpp"
|
||||
@@ -27,6 +28,36 @@
|
||||
|
||||
namespace Database {
|
||||
|
||||
|
||||
AuthToken::AuthToken(const std::string& value, const Wt::WDateTime& expiry, Wt::Dbo::ptr<User> user)
|
||||
: _value {value}
|
||||
, _expiry {expiry}
|
||||
, _user {user}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
AuthToken::pointer
|
||||
AuthToken::create(Session& session, const std::string& value, const Wt::WDateTime& expiry, Wt::Dbo::ptr<User> user)
|
||||
{
|
||||
session.checkUniqueLocked();
|
||||
|
||||
auto res {session.getDboSession().add(std::make_unique<AuthToken>(value, expiry, user))};
|
||||
|
||||
session.getDboSession().flush();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
AuthToken::pointer
|
||||
AuthToken::getByValue(Session& session, const std::string& value)
|
||||
{
|
||||
session.checkSharedLocked();
|
||||
|
||||
return session.getDboSession().find<AuthToken>()
|
||||
.where("value = ?").bind(value);
|
||||
}
|
||||
|
||||
static const std::string playedListName {"__played_tracks__"};
|
||||
static const std::string queuedListName {"__queued_tracks__"};
|
||||
|
||||
@@ -46,6 +77,14 @@ User::User()
|
||||
|
||||
}
|
||||
|
||||
User::User(const std::string& loginName, const PasswordHash& passwordHash)
|
||||
: User()
|
||||
{
|
||||
_loginName = loginName;
|
||||
_passwordHash = passwordHash.hash;
|
||||
_passwordSalt = passwordHash.salt;
|
||||
}
|
||||
|
||||
std::vector<User::pointer>
|
||||
User::getAll(Session& session)
|
||||
{
|
||||
@@ -65,11 +104,11 @@ User::getDemo(Session& session)
|
||||
}
|
||||
|
||||
User::pointer
|
||||
User::create(Session& session)
|
||||
User::create(Session& session, const std::string& loginName, const PasswordHash& passwordHash)
|
||||
{
|
||||
session.checkUniqueLocked();
|
||||
|
||||
User::pointer user {session.getDboSession().add(std::make_unique<User>())};
|
||||
User::pointer user {session.getDboSession().add(std::make_unique<User>(loginName, passwordHash))};
|
||||
|
||||
TrackList::create(session, playedListName, TrackList::Type::Internal, false, user);
|
||||
TrackList::create(session, queuedListName, TrackList::Type::Internal, false, user);
|
||||
@@ -85,6 +124,13 @@ User::getById(Session& session, IdType id)
|
||||
return session.getDboSession().find<User>().where("id = ?").bind( id );
|
||||
}
|
||||
|
||||
User::pointer
|
||||
User::getByLoginName(Session& session, const std::string& name)
|
||||
{
|
||||
return session.getDboSession().find<User>()
|
||||
.where("login_name = ?").bind(name);
|
||||
}
|
||||
|
||||
void
|
||||
User::setAudioTranscodeBitrate(Bitrate bitrate)
|
||||
{
|
||||
@@ -99,6 +145,12 @@ User::setMaxAudioTranscodeBitrate(Bitrate bitrate)
|
||||
_audioTranscodeBitrate = _maxAudioTranscodeBitrate;
|
||||
}
|
||||
|
||||
void
|
||||
User::clearAuthTokens()
|
||||
{
|
||||
_authTokens.clear();
|
||||
}
|
||||
|
||||
Bitrate
|
||||
User::getAudioTranscodeBitrate(void) const
|
||||
{
|
||||
|
||||
+84
-23
@@ -22,6 +22,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include <Wt/Dbo/Dbo.h>
|
||||
#include <Wt/WDateTime.h>
|
||||
|
||||
#include "Types.hpp"
|
||||
|
||||
@@ -46,6 +47,41 @@ enum class AudioFormat
|
||||
|
||||
using Bitrate = std::size_t;
|
||||
|
||||
class User;
|
||||
class AuthToken
|
||||
{
|
||||
public:
|
||||
|
||||
using pointer = Wt::Dbo::ptr<AuthToken>;
|
||||
|
||||
AuthToken() = default;
|
||||
AuthToken(const std::string& value, const Wt::WDateTime& expiry, Wt::Dbo::ptr<User> user);
|
||||
|
||||
// Utility
|
||||
static pointer create(Session& session, const std::string& value, const Wt::WDateTime&expiry, Wt::Dbo::ptr<User> user);
|
||||
static void removeExpiredTokens(Session& session, Wt::WDateTime now);
|
||||
static pointer getByValue(Session& session, const std::string& value);
|
||||
|
||||
// Accessors
|
||||
Wt::Dbo::ptr<User> getUser() const { return _user; }
|
||||
const Wt::WDateTime& getExpiry() const { return _expiry; }
|
||||
|
||||
template<class Action>
|
||||
void persist(Action& a)
|
||||
{
|
||||
Wt::Dbo::field(a, _value, "value");
|
||||
Wt::Dbo::field(a, _expiry, "expiry");
|
||||
Wt::Dbo::belongsTo(a, _user, "user", Wt::Dbo::OnDeleteCascade);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::string _value;
|
||||
Wt::WDateTime _expiry;
|
||||
|
||||
Wt::Dbo::ptr<User> _user;
|
||||
};
|
||||
|
||||
class User : public Wt::Dbo::Dbo<User>
|
||||
{
|
||||
public:
|
||||
@@ -61,21 +97,34 @@ class User : public Wt::Dbo::Dbo<User>
|
||||
DEMO
|
||||
};
|
||||
|
||||
struct PasswordHash
|
||||
{
|
||||
std::string salt;
|
||||
std::string hash;
|
||||
};
|
||||
|
||||
// list of audio parameters
|
||||
static const std::set<Bitrate> audioTranscodeAllowedBitrates;
|
||||
|
||||
User();
|
||||
User(const std::string& loginName, const PasswordHash& passwordHash);
|
||||
|
||||
// utility
|
||||
static pointer create(Session& session);
|
||||
static pointer create(Session& session, const std::string& loginName, const PasswordHash& passwordHash);
|
||||
|
||||
// accessors
|
||||
static pointer getById(Session& session, IdType id);
|
||||
static pointer getByLoginName(const std::string& loginName);
|
||||
static pointer getByLoginName(Session& session, const std::string& loginName);
|
||||
static std::vector<pointer> getAll(Session& session);
|
||||
static pointer getDemo(Session& session);
|
||||
|
||||
// accessors
|
||||
const std::string& getLoginName() const { return _loginName; }
|
||||
PasswordHash getPasswordHash() const { return PasswordHash {_passwordSalt, _passwordHash}; }
|
||||
Wt::WDateTime getLastLogin() const { return _lastLogin; }
|
||||
|
||||
// write
|
||||
void setLastLogin(const Wt::WDateTime& dateTime) { _lastLogin = dateTime; }
|
||||
void setPasswordHash(const PasswordHash& passwordHash) { _passwordSalt = passwordHash.salt; _passwordHash = passwordHash.hash; }
|
||||
void setType(Type type) { _type = type; }
|
||||
void setAudioTranscodeEnable(bool value) { _audioTranscodeEnable = value; }
|
||||
void setAudioTranscodeFormat(AudioFormat format) { _audioTranscodeFormat = format; }
|
||||
@@ -84,6 +133,7 @@ class User : public Wt::Dbo::Dbo<User>
|
||||
void setCurPlayingTrackPos(std::size_t pos) { _curPlayingTrackPos = pos; }
|
||||
void setRadio(bool val) { _radio = val; }
|
||||
void setRepeatAll(bool val) { _repeatAll = val; }
|
||||
void clearAuthTokens();
|
||||
|
||||
// read
|
||||
bool isAdmin() const { return _type == Type::ADMIN; }
|
||||
@@ -115,22 +165,27 @@ class User : public Wt::Dbo::Dbo<User>
|
||||
std::vector<Wt::Dbo::ptr<Track>> getStarredTracks() const;
|
||||
|
||||
template<class Action>
|
||||
void persist(Action& a)
|
||||
{
|
||||
Wt::Dbo::field(a, _type, "type");
|
||||
Wt::Dbo::field(a, _maxAudioTranscodeBitrate, "max_audio_bitrate");
|
||||
Wt::Dbo::field(a, _audioTranscodeEnable, "audio_transcode_enable");
|
||||
Wt::Dbo::field(a, _audioTranscodeBitrate, "audio_transcode_bitrate");
|
||||
Wt::Dbo::field(a, _audioTranscodeFormat, "audio_transcode_format");
|
||||
// User's dynamic data
|
||||
Wt::Dbo::field(a, _curPlayingTrackPos, "cur_playing_track_pos");
|
||||
Wt::Dbo::field(a, _repeatAll, "repeat_all");
|
||||
Wt::Dbo::field(a, _radio, "radio");
|
||||
Wt::Dbo::hasMany(a, _tracklists, Wt::Dbo::ManyToOne, "user");
|
||||
Wt::Dbo::hasMany(a, _starredArtists, Wt::Dbo::ManyToMany, "user_artist_starred", "", Wt::Dbo::OnDeleteCascade);
|
||||
Wt::Dbo::hasMany(a, _starredReleases, Wt::Dbo::ManyToMany, "user_release_starred", "", Wt::Dbo::OnDeleteCascade);
|
||||
Wt::Dbo::hasMany(a, _starredTracks, Wt::Dbo::ManyToMany, "user_track_starred", "", Wt::Dbo::OnDeleteCascade);
|
||||
}
|
||||
void persist(Action& a)
|
||||
{
|
||||
Wt::Dbo::field(a, _type, "type");
|
||||
Wt::Dbo::field(a, _loginName, "login_name");
|
||||
Wt::Dbo::field(a, _passwordSalt, "password_salt");
|
||||
Wt::Dbo::field(a, _passwordHash, "password_hash");
|
||||
Wt::Dbo::field(a, _lastLogin, "last_login");
|
||||
Wt::Dbo::field(a, _maxAudioTranscodeBitrate, "max_audio_bitrate");
|
||||
Wt::Dbo::field(a, _audioTranscodeEnable, "audio_transcode_enable");
|
||||
Wt::Dbo::field(a, _audioTranscodeBitrate, "audio_transcode_bitrate");
|
||||
Wt::Dbo::field(a, _audioTranscodeFormat, "audio_transcode_format");
|
||||
// User's dynamic data
|
||||
Wt::Dbo::field(a, _curPlayingTrackPos, "cur_playing_track_pos");
|
||||
Wt::Dbo::field(a, _repeatAll, "repeat_all");
|
||||
Wt::Dbo::field(a, _radio, "radio");
|
||||
Wt::Dbo::hasMany(a, _tracklists, Wt::Dbo::ManyToOne, "user");
|
||||
Wt::Dbo::hasMany(a, _starredArtists, Wt::Dbo::ManyToMany, "user_artist_starred", "", Wt::Dbo::OnDeleteCascade);
|
||||
Wt::Dbo::hasMany(a, _starredReleases, Wt::Dbo::ManyToMany, "user_release_starred", "", Wt::Dbo::OnDeleteCascade);
|
||||
Wt::Dbo::hasMany(a, _starredTracks, Wt::Dbo::ManyToMany, "user_track_starred", "", Wt::Dbo::OnDeleteCascade);
|
||||
Wt::Dbo::hasMany(a, _authTokens, Wt::Dbo::ManyToOne, "user");
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -138,16 +193,21 @@ class User : public Wt::Dbo::Dbo<User>
|
||||
static const AudioFormat defaultAudioTranscodeFormat {AudioFormat::OGG_OPUS};
|
||||
static const Bitrate defaultAudioTranscodeBitrate {128000};
|
||||
|
||||
std::string _loginName;
|
||||
std::string _passwordSalt;
|
||||
std::string _passwordHash;
|
||||
Wt::WDateTime _lastLogin;
|
||||
|
||||
// Admin defined settings
|
||||
int _maxAudioTranscodeBitrate;
|
||||
Type _type {Type::REGULAR};
|
||||
|
||||
// User defined settings
|
||||
bool _audioTranscodeEnable {defaultAudioTranscodeEnable};
|
||||
AudioFormat _audioTranscodeFormat {defaultAudioTranscodeFormat};
|
||||
int _audioTranscodeBitrate {defaultAudioTranscodeBitrate};
|
||||
bool _audioTranscodeEnable {defaultAudioTranscodeEnable};
|
||||
AudioFormat _audioTranscodeFormat {defaultAudioTranscodeFormat};
|
||||
int _audioTranscodeBitrate {defaultAudioTranscodeBitrate};
|
||||
|
||||
// User's dynamic data
|
||||
// User's dynamic data (UI)
|
||||
int _curPlayingTrackPos {}; // Current track position in queue
|
||||
bool _repeatAll {};
|
||||
bool _radio {};
|
||||
@@ -156,6 +216,7 @@ class User : public Wt::Dbo::Dbo<User>
|
||||
Wt::Dbo::collection<Wt::Dbo::ptr<Artist>> _starredArtists;
|
||||
Wt::Dbo::collection<Wt::Dbo::ptr<Release>> _starredReleases;
|
||||
Wt::Dbo::collection<Wt::Dbo::ptr<Track>> _starredTracks;
|
||||
Wt::Dbo::collection<Wt::Dbo::ptr<AuthToken>> _authTokens;
|
||||
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user