Fixed demo account creation. fixes #167

This commit is contained in:
emeric
2021-09-02 21:05:13 +02:00
parent 9e37012090
commit 56a588a06b
19 changed files with 172 additions and 162 deletions
+2 -2
View File
@@ -33,9 +33,9 @@ namespace Auth
Database::User::pointer user {Database::User::getByLoginName(session, loginName)}; Database::User::pointer user {Database::User::getByLoginName(session, loginName)};
if (!user) if (!user)
{ {
const Database::User::Type type {Database::User::getCount(session) == 0 ? Database::User::Type::ADMIN : Database::User::Type::REGULAR}; const Database::UserType type {Database::User::getCount(session) == 0 ? Database::UserType::ADMIN : Database::UserType::REGULAR};
LMS_LOG(AUTH, DEBUG) << "Creating user '" << loginName << "', admin = " << (type == Database::User::Type::ADMIN); LMS_LOG(AUTH, DEBUG) << "Creating user '" << loginName << "', admin = " << (type == Database::UserType::ADMIN);
user = Database::User::create(session, loginName); user = Database::User::create(session, loginName);
user.modify()->setType(type); user.modify()->setType(type);
@@ -81,9 +81,18 @@ namespace Auth
} }
bool bool
InternalPasswordService::isPasswordSecureEnough(std::string_view loginName, std::string_view password) const InternalPasswordService::isPasswordSecureEnough(std::string_view password, const PasswordValidationContext& context) const
{ {
return _validator.evaluateStrength(std::string {password}, std::string {loginName}, "").isValid(); switch (context.userType)
{
case Database::UserType::ADMIN:
case Database::UserType::REGULAR:
return _validator.evaluateStrength(std::string {password}, context.loginName, "").isValid();
case Database::UserType::DEMO:
return true; // no constraint
}
throw NotImplementedException {};
} }
void void
@@ -97,7 +106,7 @@ namespace Auth
if (!user) if (!user)
throw Exception {"User not found!"}; throw Exception {"User not found!"};
if (!isPasswordSecureEnough(user->getLoginName(), newPassword)) if (!isPasswordSecureEnough(newPassword, PasswordValidationContext {user->getLoginName(), user->getType()} ))
throw PasswordTooWeakException {}; throw PasswordTooWeakException {};
user.modify()->setPasswordHash(passwordHash); user.modify()->setPasswordHash(passwordHash);
@@ -41,7 +41,7 @@ namespace Auth
std::string_view password) override; std::string_view password) override;
bool canSetPasswords() const override; bool canSetPasswords() const override;
bool isPasswordSecureEnough(std::string_view loginName, std::string_view password) const override; bool isPasswordSecureEnough(std::string_view loginName, const PasswordValidationContext& context) const override;
void setPassword(Database::Session& session, Database::IdType userId, std::string_view newPassword) override; void setPassword(Database::Session& session, Database::IdType userId, std::string_view newPassword) override;
Database::User::PasswordHash hashPassword(std::string_view password) const; Database::User::PasswordHash hashPassword(std::string_view password) const;
@@ -187,7 +187,7 @@ namespace Auth
} }
bool bool
PAMPasswordService::isPasswordSecureEnough(std::string_view, std::string_view) const PAMPasswordService::isPasswordSecureEnough(std::string_view, const PasswordValidationContext&) const
{ {
throw NotImplementedException {}; throw NotImplementedException {};
} }
@@ -36,8 +36,7 @@ namespace Auth
std::string_view password) override; std::string_view password) override;
bool canSetPasswords() const override; bool canSetPasswords() const override;
bool isPasswordSecureEnough(std::string_view loginName, bool isPasswordSecureEnough(std::string_view loginName, const PasswordValidationContext& context) const override;
std::string_view password) const override;
void setPassword(Database::Session& session, void setPassword(Database::Session& session,
Database::IdType userId, Database::IdType userId,
std::string_view newPassword) override; std::string_view newPassword) override;
@@ -61,14 +61,9 @@ namespace Auth
std::string_view loginName, std::string_view loginName,
std::string_view password) = 0; std::string_view password) = 0;
class PasswordTooWeakException : public Auth::Exception
{
public:
PasswordTooWeakException() : Auth::Exception {"Password too weak"} {}
};
virtual bool canSetPasswords() const = 0; virtual bool canSetPasswords() const = 0;
virtual bool isPasswordSecureEnough(std::string_view username, std::string_view password) const = 0;
virtual bool isPasswordSecureEnough(std::string_view password, const PasswordValidationContext& context) const = 0;
virtual void setPassword(Database::Session& session, Database::IdType userId, std::string_view newPassword) = 0; virtual void setPassword(Database::Session& session, Database::IdType userId, std::string_view newPassword) = 0;
}; };
+14
View File
@@ -19,6 +19,8 @@
#pragma once #pragma once
#include <string>
#include "database/Types.hpp"
#include "utils/Exception.hpp" #include "utils/Exception.hpp"
namespace Auth namespace Auth
@@ -33,5 +35,17 @@ namespace Auth
public: public:
NotImplementedException() : Auth::Exception {"Not implemented"} {} NotImplementedException() : Auth::Exception {"Not implemented"} {}
}; };
struct PasswordValidationContext
{
std::string loginName;
Database::UserType userType;
};
class PasswordTooWeakException : public Exception
{
public:
PasswordTooWeakException() : Auth::Exception {"Password too weak"} {}
};
} }
+1 -1
View File
@@ -98,7 +98,7 @@ User::getDemo(Session& session)
{ {
session.checkSharedLocked(); session.checkSharedLocked();
pointer res = session.getDboSession().find<User>().where("type = ?").bind(Type::DEMO); pointer res = session.getDboSession().find<User>().where("type = ?").bind(UserType::DEMO);
return res; return res;
} }
@@ -70,5 +70,13 @@ namespace Database
Internal = 0, Internal = 0,
ListenBrainz = 1, ListenBrainz = 1,
}; };
// Do not change enum values!
enum class UserType
{
REGULAR = 0,
ADMIN = 1,
DEMO = 2,
};
} }
+5 -13
View File
@@ -80,15 +80,6 @@ class User : public Wt::Dbo::Dbo<User>
public: public:
using pointer = Wt::Dbo::ptr<User>; using pointer = Wt::Dbo::ptr<User>;
// Do not change enum values!
enum class Type
{
REGULAR = 0,
ADMIN = 1,
DEMO = 2,
};
struct PasswordHash struct PasswordHash
{ {
std::string salt; std::string salt;
@@ -152,7 +143,7 @@ class User : public Wt::Dbo::Dbo<User>
// write // write
void setLastLogin(const Wt::WDateTime& dateTime) { _lastLogin = dateTime; } void setLastLogin(const Wt::WDateTime& dateTime) { _lastLogin = dateTime; }
void setPasswordHash(const PasswordHash& passwordHash) { _passwordSalt = passwordHash.salt; _passwordHash = passwordHash.hash; } void setPasswordHash(const PasswordHash& passwordHash) { _passwordSalt = passwordHash.salt; _passwordHash = passwordHash.hash; }
void setType(Type type) { _type = type; } void setType(UserType type) { _type = type; }
void setSubsonicTranscodeEnable(bool value) { _subsonicTranscodeEnable = value; } void setSubsonicTranscodeEnable(bool value) { _subsonicTranscodeEnable = value; }
void setSubsonicTranscodeFormat(AudioFormat encoding) { _subsonicTranscodeFormat = encoding; } void setSubsonicTranscodeFormat(AudioFormat encoding) { _subsonicTranscodeFormat = encoding; }
void setSubsonicTranscodeBitrate(Bitrate bitrate); void setSubsonicTranscodeBitrate(Bitrate bitrate);
@@ -166,8 +157,9 @@ class User : public Wt::Dbo::Dbo<User>
void setListenBrainzToken(const std::optional<UUID>& MBID) { _listenbrainzToken = MBID ? MBID->getAsString() : ""; } void setListenBrainzToken(const std::optional<UUID>& MBID) { _listenbrainzToken = MBID ? MBID->getAsString() : ""; }
// read // read
bool isAdmin() const { return _type == Type::ADMIN; } bool isAdmin() const { return _type == UserType::ADMIN; }
bool isDemo() const { return _type == Type::DEMO; } bool isDemo() const { return _type == UserType::DEMO; }
UserType getType() const { return _type; }
bool getSubsonicTranscodeEnable() const { return _subsonicTranscodeEnable; } bool getSubsonicTranscodeEnable() const { return _subsonicTranscodeEnable; }
AudioFormat getSubsonicTranscodeFormat() const { return _subsonicTranscodeFormat; } AudioFormat getSubsonicTranscodeFormat() const { return _subsonicTranscodeFormat; }
Bitrate getSubsonicTranscodeBitrate() const { return _subsonicTranscodeBitrate; } Bitrate getSubsonicTranscodeBitrate() const { return _subsonicTranscodeBitrate; }
@@ -233,7 +225,7 @@ class User : public Wt::Dbo::Dbo<User>
std::string _listenbrainzToken; // Musicbrainz Identifier std::string _listenbrainzToken; // Musicbrainz Identifier
// Admin defined settings // Admin defined settings
Type _type {Type::REGULAR}; UserType _type {UserType::REGULAR};
// User defined settings // User defined settings
SubsonicArtistListMode _subsonicArtistListMode {defaultSubsonicArtistListMode}; SubsonicArtistListMode _subsonicArtistListMode {defaultSubsonicArtistListMode};
+82 -91
View File
@@ -180,7 +180,7 @@ std::string parameterMapToDebugString(const Wt::Http::ParameterMap& parameterMap
auto censorValue = [](const std::string& type, const std::string& value) -> std::string auto censorValue = [](const std::string& type, const std::string& value) -> std::string
{ {
if (type == "p" || type == "password") if (type == "p" || type == "password")
return "*SENSIBLE DATA*"; return "*REDACTED*";
else else
return value; return value;
}; };
@@ -221,24 +221,16 @@ checkUserIsMySelfOrAdmin(RequestContext& context, const std::string& username)
static static
void void
checkUserIsAdmin(RequestContext& context) checkUserTypeIsAllowed(RequestContext& context, EnumSet<Database::UserType> allowedUserTypes)
{ {
LMS_LOG(API_SUBSONIC, DEBUG) << "Check user is admin";
auto transaction {context.dbSession.createSharedTransaction()}; auto transaction {context.dbSession.createSharedTransaction()};
User::pointer currentUser {User::getById(context.dbSession, context.userId)}; User::pointer currentUser {User::getById(context.dbSession, context.userId)};
if (!currentUser) if (!currentUser)
{
LMS_LOG(API_SUBSONIC, DEBUG) << "NOT FOUND";
throw RequestedDataNotFoundError {}; throw RequestedDataNotFoundError {};
}
if (!currentUser->isAdmin()) if (!allowedUserTypes.contains(currentUser->getType()))
{
LMS_LOG(API_SUBSONIC, DEBUG) << "NOT ADMIN";
throw UserNotAuthorizedError {}; throw UserNotAuthorizedError {};
}
} }
static static
@@ -567,7 +559,7 @@ handleChangePassword(RequestContext& context)
Service<Auth::IPasswordService>::get()->setPassword(context.dbSession, userId, password); Service<Auth::IPasswordService>::get()->setPassword(context.dbSession, userId, password);
} }
catch (Auth::IPasswordService::PasswordTooWeakException&) catch (Auth::PasswordTooWeakException&)
{ {
throw PasswordTooWeakGenericError {}; throw PasswordTooWeakGenericError {};
} }
@@ -666,7 +658,7 @@ handleCreateUserRequest(RequestContext& context)
{ {
Service<Auth::IPasswordService>::get()->setPassword(context.dbSession, userId, password); Service<Auth::IPasswordService>::get()->setPassword(context.dbSession, userId, password);
} }
catch (const Auth::IPasswordService::PasswordTooWeakException&) catch (const Auth::PasswordTooWeakException&)
{ {
removeCreatedUser(); removeCreatedUser();
throw PasswordTooWeakGenericError {}; throw PasswordTooWeakGenericError {};
@@ -1719,7 +1711,7 @@ handleUpdateUserRequest(RequestContext& context)
{ {
Service<::Auth::IPasswordService>()->setPassword(context.dbSession, userId, decodePasswordIfNeeded(*password)); Service<::Auth::IPasswordService>()->setPassword(context.dbSession, userId, decodePasswordIfNeeded(*password));
} }
catch (const Auth::IPasswordService::PasswordTooWeakException&) catch (const Auth::PasswordTooWeakException&)
{ {
throw PasswordTooWeakGenericError {}; throw PasswordTooWeakGenericError {};
} }
@@ -1922,114 +1914,114 @@ using RequestHandlerFunc = std::function<Response(RequestContext& context)>;
using CheckImplementedFunc = std::function<void()>; using CheckImplementedFunc = std::function<void()>;
struct RequestEntryPointInfo struct RequestEntryPointInfo
{ {
RequestHandlerFunc func; RequestHandlerFunc func;
bool mustBeAdmin; EnumSet<Database::UserType> allowedUserTypes {Database::UserType::DEMO, Database::UserType::REGULAR, Database::UserType::ADMIN};
CheckImplementedFunc checkFunc {}; CheckImplementedFunc checkFunc {};
}; };
static std::unordered_map<std::string, RequestEntryPointInfo> requestEntryPoints static std::unordered_map<std::string, RequestEntryPointInfo> requestEntryPoints
{ {
// System // System
{"ping", {handlePingRequest, false}}, {"ping", {handlePingRequest}},
{"getLicense", {handleGetLicenseRequest, false}}, {"getLicense", {handleGetLicenseRequest}},
// Browsing // Browsing
{"getMusicFolders", {handleGetMusicFoldersRequest, false}}, {"getMusicFolders", {handleGetMusicFoldersRequest}},
{"getIndexes", {handleGetIndexesRequest, false}}, {"getIndexes", {handleGetIndexesRequest}},
{"getMusicDirectory", {handleGetMusicDirectoryRequest, false}}, {"getMusicDirectory", {handleGetMusicDirectoryRequest}},
{"getGenres", {handleGetGenresRequest, false}}, {"getGenres", {handleGetGenresRequest}},
{"getArtists", {handleGetArtistsRequest, false}}, {"getArtists", {handleGetArtistsRequest}},
{"getArtist", {handleGetArtistRequest, false}}, {"getArtist", {handleGetArtistRequest}},
{"getAlbum", {handleGetAlbumRequest, false}}, {"getAlbum", {handleGetAlbumRequest}},
{"getSong", {handleNotImplemented, false}}, {"getSong", {handleNotImplemented}},
{"getVideos", {handleNotImplemented, false}}, {"getVideos", {handleNotImplemented}},
{"getArtistInfo", {handleGetArtistInfoRequest, false}}, {"getArtistInfo", {handleGetArtistInfoRequest}},
{"getArtistInfo2", {handleGetArtistInfo2Request, false}}, {"getArtistInfo2", {handleGetArtistInfo2Request}},
{"getAlbumInfo", {handleNotImplemented, false}}, {"getAlbumInfo", {handleNotImplemented}},
{"getAlbumInfo2", {handleNotImplemented, false}}, {"getAlbumInfo2", {handleNotImplemented}},
{"getSimilarSongs", {handleGetSimilarSongsRequest, false}}, {"getSimilarSongs", {handleGetSimilarSongsRequest}},
{"getSimilarSongs2", {handleGetSimilarSongs2Request, false}}, {"getSimilarSongs2", {handleGetSimilarSongs2Request}},
{"getTopSongs", {handleNotImplemented, false}}, {"getTopSongs", {handleNotImplemented}},
// Album/song lists // Album/song lists
{"getAlbumList", {handleGetAlbumListRequest, false}}, {"getAlbumList", {handleGetAlbumListRequest}},
{"getAlbumList2", {handleGetAlbumList2Request, false}}, {"getAlbumList2", {handleGetAlbumList2Request}},
{"getRandomSongs", {handleGetRandomSongsRequest, false}}, {"getRandomSongs", {handleGetRandomSongsRequest}},
{"getSongsByGenre", {handleGetSongsByGenreRequest, false}}, {"getSongsByGenre", {handleGetSongsByGenreRequest}},
{"getNowPlaying", {handleNotImplemented, false}}, {"getNowPlaying", {handleNotImplemented}},
{"getStarred", {handleGetStarredRequest, false}}, {"getStarred", {handleGetStarredRequest}},
{"getStarred2", {handleGetStarred2Request, false}}, {"getStarred2", {handleGetStarred2Request}},
// Searching // Searching
{"search", {handleNotImplemented, false}}, {"search", {handleNotImplemented}},
{"search2", {handleSearch2Request, false}}, {"search2", {handleSearch2Request}},
{"search3", {handleSearch3Request, false}}, {"search3", {handleSearch3Request}},
// Playlists // Playlists
{"getPlaylists", {handleGetPlaylistsRequest, false}}, {"getPlaylists", {handleGetPlaylistsRequest}},
{"getPlaylist", {handleGetPlaylistRequest, false}}, {"getPlaylist", {handleGetPlaylistRequest}},
{"createPlaylist", {handleCreatePlaylistRequest, false}}, {"createPlaylist", {handleCreatePlaylistRequest}},
{"updatePlaylist", {handleUpdatePlaylistRequest, false}}, {"updatePlaylist", {handleUpdatePlaylistRequest}},
{"deletePlaylist", {handleDeletePlaylistRequest, false}}, {"deletePlaylist", {handleDeletePlaylistRequest}},
// Media retrieval // Media retrieval
{"hls", {handleNotImplemented, false}}, {"hls", {handleNotImplemented}},
{"getCaptions", {handleNotImplemented, false}}, {"getCaptions", {handleNotImplemented}},
{"getLyrics", {handleNotImplemented, false}}, {"getLyrics", {handleNotImplemented}},
{"getAvatar", {handleNotImplemented, false}}, {"getAvatar", {handleNotImplemented}},
// Media annotation // Media annotation
{"star", {handleStarRequest, false}}, {"star", {handleStarRequest}},
{"unstar", {handleUnstarRequest, false}}, {"unstar", {handleUnstarRequest}},
{"setRating", {handleNotImplemented, false}}, {"setRating", {handleNotImplemented}},
{"scrobble", {handleScrobble, false}}, {"scrobble", {handleScrobble}},
// Sharing // Sharing
{"getShares", {handleNotImplemented, false}}, {"getShares", {handleNotImplemented}},
{"createShares", {handleNotImplemented, false}}, {"createShares", {handleNotImplemented}},
{"updateShare", {handleNotImplemented, false}}, {"updateShare", {handleNotImplemented}},
{"deleteShare", {handleNotImplemented, false}}, {"deleteShare", {handleNotImplemented}},
// Podcast // Podcast
{"getPodcasts", {handleNotImplemented, false}}, {"getPodcasts", {handleNotImplemented}},
{"getNewestPodcasts", {handleNotImplemented, false}}, {"getNewestPodcasts", {handleNotImplemented}},
{"refreshPodcasts", {handleNotImplemented, false}}, {"refreshPodcasts", {handleNotImplemented}},
{"createPodcastChannel", {handleNotImplemented, false}}, {"createPodcastChannel", {handleNotImplemented}},
{"deletePodcastChannel", {handleNotImplemented, false}}, {"deletePodcastChannel", {handleNotImplemented}},
{"deletePodcastEpisode", {handleNotImplemented, false}}, {"deletePodcastEpisode", {handleNotImplemented}},
{"downloadPodcastEpisode", {handleNotImplemented, false}}, {"downloadPodcastEpisode", {handleNotImplemented}},
// Jukebox // Jukebox
{"jukeboxControl", {handleNotImplemented, false}}, {"jukeboxControl", {handleNotImplemented}},
// Internet radio // Internet radio
{"getInternetRadioStations", {handleNotImplemented, false}}, {"getInternetRadioStations", {handleNotImplemented}},
{"createInternetRadioStation", {handleNotImplemented, false}}, {"createInternetRadioStation", {handleNotImplemented}},
{"updateInternetRadioStation", {handleNotImplemented, false}}, {"updateInternetRadioStation", {handleNotImplemented}},
{"deleteInternetRadioStation", {handleNotImplemented, false}}, {"deleteInternetRadioStation", {handleNotImplemented}},
// Chat // Chat
{"getChatMessages", {handleNotImplemented, false}}, {"getChatMessages", {handleNotImplemented}},
{"addChatMessages", {handleNotImplemented, false}}, {"addChatMessages", {handleNotImplemented}},
// User management // User management
{"getUser", {handleGetUserRequest, false}}, {"getUser", {handleGetUserRequest}},
{"getUsers", {handleGetUsersRequest, true}}, {"getUsers", {handleGetUsersRequest, {Database::UserType::ADMIN}}},
{"createUser", {handleCreateUserRequest, true, &checkSetPasswordImplemented}}, {"createUser", {handleCreateUserRequest, {Database::UserType::ADMIN}, &checkSetPasswordImplemented}},
{"updateUser", {handleUpdateUserRequest, true}}, {"updateUser", {handleUpdateUserRequest, {Database::UserType::ADMIN}}},
{"deleteUser", {handleDeleteUserRequest, true}}, {"deleteUser", {handleDeleteUserRequest, {Database::UserType::ADMIN}}},
{"changePassword", {handleChangePassword, false, &checkSetPasswordImplemented}}, {"changePassword", {handleChangePassword, {Database::UserType::REGULAR, Database::UserType::ADMIN}, &checkSetPasswordImplemented}},
// Bookmarks // Bookmarks
{"getBookmarks", {handleGetBookmarks, false}}, {"getBookmarks", {handleGetBookmarks}},
{"createBookmark", {handleCreateBookmark, false}}, {"createBookmark", {handleCreateBookmark}},
{"deleteBookmark", {handleDeleteBookmark, false}}, {"deleteBookmark", {handleDeleteBookmark}},
{"getPlayQueue", {handleNotImplemented, false}}, {"getPlayQueue", {handleNotImplemented}},
{"savePlayQueue", {handleNotImplemented, false}}, {"savePlayQueue", {handleNotImplemented}},
// Media library scanning // Media library scanning
{"getScanStatus", {Scan::handleGetScanStatus, true}}, {"getScanStatus", {Scan::handleGetScanStatus, {Database::UserType::ADMIN}}},
{"startScan", {Scan::handleStartScan, true}}, {"startScan", {Scan::handleStartScan, {Database::UserType::ADMIN}}},
}; };
using MediaRetrievalHandlerFunc = std::function<void(RequestContext&, const Wt::Http::Request&, Wt::Http::Response&)>; using MediaRetrievalHandlerFunc = std::function<void(RequestContext&, const Wt::Http::Request&, Wt::Http::Response&)>;
@@ -2112,8 +2104,7 @@ SubsonicResource::handleRequest(const Wt::Http::Request &request, Wt::Http::Resp
if (itEntryPoint->second.checkFunc) if (itEntryPoint->second.checkFunc)
itEntryPoint->second.checkFunc(); itEntryPoint->second.checkFunc();
if (itEntryPoint->second.mustBeAdmin) checkUserTypeIsAllowed(requestContext, itEntryPoint->second.allowedUserTypes);
checkUserIsAdmin(requestContext);
Response resp {(itEntryPoint->second.func)(requestContext)}; Response resp {(itEntryPoint->second.func)(requestContext)};
@@ -152,6 +152,11 @@ class PasswordTooWeakGenericError : public GenericError
std::string getMessage() const override { return "Password too weak"; } std::string getMessage() const override { return "Password too weak"; }
}; };
class DemoUserCannotChangePasswordGenericError : public GenericError
{
std::string getMessage() const override { return "Demo user cannot change its password"; }
};
class UserAlreadyExistsGenericError : public GenericError class UserAlreadyExistsGenericError : public GenericError
{ {
std::string getMessage() const override { return "User already exists"; } std::string getMessage() const override { return "User already exists"; }
+10 -17
View File
@@ -116,20 +116,12 @@ LmsApplication::isUserAuthStrong() const
return _authenticatedUser->strongAuth; return _authenticatedUser->strongAuth;
} }
bool Database::UserType
LmsApplication::isUserAdmin() LmsApplication::getUserType()
{ {
auto transaction {getDbSession().createSharedTransaction()}; auto transaction {getDbSession().createSharedTransaction()};
return getUser()->isAdmin(); return getUser()->getType();
}
bool
LmsApplication::isUserDemo()
{
auto transaction {getDbSession().createSharedTransaction()};
return getUser()->isDemo();
} }
std::string std::string
@@ -458,7 +450,7 @@ LmsApplication::onUserLoggedIn()
// Only one active session by user // Only one active session by user
if (otherApplication.getUserId() == getUserId()) if (otherApplication.getUserId() == getUserId())
{ {
if (!LmsApp->isUserDemo()) if (LmsApp->getUserType() != Database::UserType::DEMO)
{ {
quit(Wt::WString::tr("Lms.quit-other-session")); quit(Wt::WString::tr("Lms.quit-other-session"));
} }
@@ -502,7 +494,7 @@ LmsApplication::createHome()
Wt::WLineEdit* searchEdit {main->bindNew<Wt::WLineEdit>("search")}; Wt::WLineEdit* searchEdit {main->bindNew<Wt::WLineEdit>("search")};
searchEdit->setPlaceholderText(Wt::WString::tr("Lms.Explore.Search.search-placeholder")); searchEdit->setPlaceholderText(Wt::WString::tr("Lms.Explore.Search.search-placeholder"));
if (isUserAdmin()) if (LmsApp->getUserType() == Database::UserType::ADMIN)
{ {
main->setCondition("if-is-admin", true); main->setCondition("if-is-admin", true);
main->bindNew<Wt::WAnchor>("database", Wt::WLink {Wt::LinkType::InternalPath, "/admin/database"}, Wt::WString::tr("Lms.Admin.Database.menu-database")); main->bindNew<Wt::WAnchor>("database", Wt::WLink {Wt::LinkType::InternalPath, "/admin/database"}, Wt::WString::tr("Lms.Admin.Database.menu-database"));
@@ -530,7 +522,7 @@ LmsApplication::createHome()
}); });
// Admin stuff // Admin stuff
if (isUserAdmin()) if (getUserType() == Database::UserType::ADMIN)
{ {
mainStack->addNew<DatabaseSettingsView>(); mainStack->addNew<DatabaseSettingsView>();
mainStack->addNew<UsersView>(); mainStack->addNew<UsersView>();
@@ -581,7 +573,8 @@ LmsApplication::createHome()
_mediaPlayer->stop(); _mediaPlayer->stop();
}); });
if (isUserAdmin()) const bool isAdmin {getUserType() == Database::UserType::ADMIN};
if (isAdmin)
{ {
_scannerEvents.scanComplete.connect([=] (const Scanner::ScanStats& stats) _scannerEvents.scanComplete.connect([=] (const Scanner::ScanStats& stats)
{ {
@@ -597,10 +590,10 @@ LmsApplication::createHome()
internalPathChanged().connect([=] internalPathChanged().connect([=]
{ {
handlePathChange(*mainStack, isUserAdmin()); handlePathChange(*mainStack, isAdmin);
}); });
handlePathChange(*mainStack, isUserAdmin()); handlePathChange(*mainStack, isAdmin);
} }
void void
+2 -3
View File
@@ -65,9 +65,8 @@ class LmsApplication : public Wt::WApplication
Wt::Dbo::ptr<Database::User> getUser(); Wt::Dbo::ptr<Database::User> getUser();
Database::IdType getUserId(); Database::IdType getUserId();
bool isUserAuthStrong() const; // user must be logged in prior this call bool isUserAuthStrong() const; // user must be logged in prior this call
bool isUserAdmin(); // user must be logged in prior this call Database::UserType getUserType(); // user must be logged in prior this call
bool isUserDemo(); // user must be logged in prior this call std::string getUserLoginName(); // user must be logged in prior this call
std::string getUserLoginName(); // user must be logged in prior this call
// Proxified scanner events // Proxified scanner events
Scanner::Events& getScannerEvents() { return _scannerEvents; } Scanner::Events& getScannerEvents() { return _scannerEvents; }
+1 -1
View File
@@ -102,7 +102,7 @@ class SettingsModel : public Wt::WFormModel
} }
addField(PasswordField); addField(PasswordField);
setValidator(PasswordField, createPasswordStrengthValidator(LmsApp->getUserLoginName())); setValidator(PasswordField, createPasswordStrengthValidator([] { return ::Auth::PasswordValidationContext {LmsApp->getUserLoginName(), LmsApp->getUserType()}; }));
addField(PasswordConfirmField); addField(PasswordConfirmField);
} }
+2 -2
View File
@@ -54,7 +54,7 @@ class InitWizardModel : public Wt::WFormModel
addField(PasswordConfirmField); addField(PasswordConfirmField);
setValidator(AdminLoginField, createLoginNameValidator()); setValidator(AdminLoginField, createLoginNameValidator());
setValidator(PasswordField, createPasswordStrengthValidator([this] { return valueText(AdminLoginField).toUTF8(); })); setValidator(PasswordField, createPasswordStrengthValidator([this] { return ::Auth::PasswordValidationContext {valueText(AdminLoginField).toUTF8(), Database::UserType::ADMIN}; }));
validator(PasswordField)->setMandatory(true); validator(PasswordField)->setMandatory(true);
setValidator(PasswordConfirmField, createMandatoryValidator()); setValidator(PasswordConfirmField, createMandatoryValidator());
} }
@@ -69,7 +69,7 @@ class InitWizardModel : public Wt::WFormModel
throw LmsException {"Admin user already created"}; throw LmsException {"Admin user already created"};
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::User::Type::ADMIN); user.modify()->setType(Database::UserType::ADMIN);
Service<::Auth::IPasswordService>::get()->setPassword(LmsApp->getDbSession(), user.id(), valueText(PasswordField).toUTF8()); Service<::Auth::IPasswordService>::get()->setPassword(LmsApp->getDbSession(), user.id(), valueText(PasswordField).toUTF8());
} }
+6 -3
View File
@@ -66,7 +66,7 @@ class UserModel : public Wt::WFormModel
if (authPasswordService) if (authPasswordService)
{ {
addField(PasswordField); addField(PasswordField);
setValidator(PasswordField, createPasswordStrengthValidator([this] { return getLoginName(); })); setValidator(PasswordField, createPasswordStrengthValidator([this] { return ::Auth::PasswordValidationContext {getLoginName(), Wt::asNumber(value(DemoField)) ? UserType::DEMO : UserType::REGULAR}; }));
if (!userId) if (!userId)
validator(PasswordField)->setMandatory(true); validator(PasswordField)->setMandatory(true);
} }
@@ -100,7 +100,7 @@ class UserModel : public Wt::WFormModel
user = Database::User::create(LmsApp->getDbSession(), valueText(LoginField).toUTF8()); user = Database::User::create(LmsApp->getDbSession(), valueText(LoginField).toUTF8());
if (Wt::asNumber(value(DemoField))) if (Wt::asNumber(value(DemoField)))
user.modify()->setType(Database::User::Type::DEMO); user.modify()->setType(Database::UserType::DEMO);
if (_authPasswordService) if (_authPasswordService)
_authPasswordService->setPassword(LmsApp->getDbSession(), user.id(), valueText(PasswordField).toUTF8()); _authPasswordService->setPassword(LmsApp->getDbSession(), user.id(), valueText(PasswordField).toUTF8());
@@ -159,6 +159,9 @@ class UserModel : public Wt::WFormModel
} }
else if (field == PasswordField) else if (field == PasswordField)
{ {
if (Wt::asNumber(value(DemoField)))
setValidator(PasswordField, {});
validatePassword(error); validatePassword(error);
} }
else if (field == DemoField) else if (field == DemoField)
@@ -172,7 +175,7 @@ class UserModel : public Wt::WFormModel
if (error.empty()) if (error.empty())
return Wt::WFormModel::validateField(field); return Wt::WFormModel::validateField(field);
setValidation(field, Wt::WValidator::Result( Wt::ValidationState::Invalid, error)); setValidation(field, Wt::WValidator::Result {Wt::ValidationState::Invalid, error});
return false; return false;
} }
+11 -12
View File
@@ -30,12 +30,14 @@ namespace UserInterface
class PasswordStrengthValidator : public Wt::WValidator class PasswordStrengthValidator : public Wt::WValidator
{ {
public: public:
PasswordStrengthValidator(LoginNameGetFunc loginNameGetFunc) : _loginNameGetFunc {std::move(loginNameGetFunc)} {} PasswordStrengthValidator(PasswordValidationContextGetFunc passwordValidationContextGetFunc)
: _passwordValidationContextGetFunc {std::move(passwordValidationContextGetFunc)}
Wt::WValidator::Result validate(const Wt::WString& input) const override; {}
private: private:
LoginNameGetFunc _loginNameGetFunc; Wt::WValidator::Result validate(const Wt::WString& input) const override;
PasswordValidationContextGetFunc _passwordValidationContextGetFunc;
}; };
Wt::WValidator::Result Wt::WValidator::Result
@@ -44,21 +46,18 @@ namespace UserInterface
if (input.empty()) if (input.empty())
return Wt::WValidator::validate(input); return Wt::WValidator::validate(input);
if (Service<::Auth::IPasswordService>::get()->isPasswordSecureEnough(_loginNameGetFunc(), input.toUTF8())) const ::Auth::PasswordValidationContext context {_passwordValidationContextGetFunc()};
if (Service<::Auth::IPasswordService>::get()->isPasswordSecureEnough(input.toUTF8(), context))
return Wt::WValidator::Result {Wt::ValidationState::Valid}; return Wt::WValidator::Result {Wt::ValidationState::Valid};
return Wt::WValidator::Result {Wt::ValidationState::Invalid, Wt::WString::tr("Lms.password-too-weak")}; return Wt::WValidator::Result {Wt::ValidationState::Invalid, Wt::WString::tr("Lms.password-too-weak")};
} }
std::shared_ptr<Wt::WValidator> std::shared_ptr<Wt::WValidator>
createPasswordStrengthValidator(std::string_view loginName) createPasswordStrengthValidator(PasswordValidationContextGetFunc passwordValidationContextGetFunc)
{ {
return std::make_shared<PasswordStrengthValidator>([loginName = std::string {loginName}] { return loginName; }); return std::make_shared<PasswordStrengthValidator>(std::move(passwordValidationContextGetFunc));
}
std::shared_ptr<Wt::WValidator> createPasswordStrengthValidator(LoginNameGetFunc loginNameGetFunc)
{
return std::make_shared<PasswordStrengthValidator>(std::move(loginNameGetFunc));
} }
class PasswordCheckValidator : public Wt::WValidator class PasswordCheckValidator : public Wt::WValidator
+6 -3
View File
@@ -19,13 +19,16 @@
#pragma once #pragma once
#include <functional>
#include <Wt/WValidator.h> #include <Wt/WValidator.h>
#include "database/Types.hpp"
#include "auth/Types.hpp"
namespace UserInterface namespace UserInterface
{ {
std::shared_ptr<Wt::WValidator> createPasswordStrengthValidator(std::string_view loginName); using PasswordValidationContextGetFunc = std::function<::Auth::PasswordValidationContext()>;
using LoginNameGetFunc = std::function<std::string()>; std::shared_ptr<Wt::WValidator> createPasswordStrengthValidator(PasswordValidationContextGetFunc passwordValidationContextGetFunc);
std::shared_ptr<Wt::WValidator> createPasswordStrengthValidator(LoginNameGetFunc loginNameGetFunc);
// Check current user password // Check current user password
std::shared_ptr<Wt::WValidator> createPasswordCheckValidator(); std::shared_ptr<Wt::WValidator> createPasswordCheckValidator();