From 56a588a06b5ba85c4ec9dd47fe7eec095009c7d7 Mon Sep 17 00:00:00 2001 From: emeric Date: Thu, 2 Sep 2021 21:05:13 +0200 Subject: [PATCH] Fixed demo account creation. fixes #167 --- src/libs/auth/impl/AuthServiceBase.cpp | 4 +- .../impl/internal/InternalPasswordService.cpp | 15 +- .../impl/internal/InternalPasswordService.hpp | 2 +- src/libs/auth/impl/pam/PAMPasswordService.cpp | 2 +- src/libs/auth/impl/pam/PAMPasswordService.hpp | 3 +- .../auth/include/auth/IPasswordService.hpp | 9 +- src/libs/auth/include/auth/Types.hpp | 14 ++ src/libs/database/impl/User.cpp | 2 +- src/libs/database/include/database/Types.hpp | 8 + src/libs/database/include/database/User.hpp | 18 +- src/libs/subsonic/impl/SubsonicResource.cpp | 173 +++++++++--------- src/libs/subsonic/impl/SubsonicResponse.hpp | 5 + src/lms/ui/LmsApplication.cpp | 27 +-- src/lms/ui/LmsApplication.hpp | 5 +- src/lms/ui/SettingsView.cpp | 2 +- src/lms/ui/admin/InitWizardView.cpp | 4 +- src/lms/ui/admin/UserView.cpp | 9 +- src/lms/ui/common/PasswordValidator.cpp | 23 ++- src/lms/ui/common/PasswordValidator.hpp | 9 +- 19 files changed, 172 insertions(+), 162 deletions(-) diff --git a/src/libs/auth/impl/AuthServiceBase.cpp b/src/libs/auth/impl/AuthServiceBase.cpp index a46280f2..f31f737e 100644 --- a/src/libs/auth/impl/AuthServiceBase.cpp +++ b/src/libs/auth/impl/AuthServiceBase.cpp @@ -33,9 +33,9 @@ namespace Auth Database::User::pointer user {Database::User::getByLoginName(session, loginName)}; 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.modify()->setType(type); diff --git a/src/libs/auth/impl/internal/InternalPasswordService.cpp b/src/libs/auth/impl/internal/InternalPasswordService.cpp index a662a76f..2d17c044 100644 --- a/src/libs/auth/impl/internal/InternalPasswordService.cpp +++ b/src/libs/auth/impl/internal/InternalPasswordService.cpp @@ -81,9 +81,18 @@ namespace Auth } 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 @@ -97,7 +106,7 @@ namespace Auth if (!user) throw Exception {"User not found!"}; - if (!isPasswordSecureEnough(user->getLoginName(), newPassword)) + if (!isPasswordSecureEnough(newPassword, PasswordValidationContext {user->getLoginName(), user->getType()} )) throw PasswordTooWeakException {}; user.modify()->setPasswordHash(passwordHash); diff --git a/src/libs/auth/impl/internal/InternalPasswordService.hpp b/src/libs/auth/impl/internal/InternalPasswordService.hpp index 6cef56fb..67a5da08 100644 --- a/src/libs/auth/impl/internal/InternalPasswordService.hpp +++ b/src/libs/auth/impl/internal/InternalPasswordService.hpp @@ -41,7 +41,7 @@ namespace Auth std::string_view password) 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; Database::User::PasswordHash hashPassword(std::string_view password) const; diff --git a/src/libs/auth/impl/pam/PAMPasswordService.cpp b/src/libs/auth/impl/pam/PAMPasswordService.cpp index 60623c28..7d8e1af2 100644 --- a/src/libs/auth/impl/pam/PAMPasswordService.cpp +++ b/src/libs/auth/impl/pam/PAMPasswordService.cpp @@ -187,7 +187,7 @@ namespace Auth } bool - PAMPasswordService::isPasswordSecureEnough(std::string_view, std::string_view) const + PAMPasswordService::isPasswordSecureEnough(std::string_view, const PasswordValidationContext&) const { throw NotImplementedException {}; } diff --git a/src/libs/auth/impl/pam/PAMPasswordService.hpp b/src/libs/auth/impl/pam/PAMPasswordService.hpp index 7b418f47..1b1bcc1c 100644 --- a/src/libs/auth/impl/pam/PAMPasswordService.hpp +++ b/src/libs/auth/impl/pam/PAMPasswordService.hpp @@ -36,8 +36,7 @@ namespace Auth std::string_view password) 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; diff --git a/src/libs/auth/include/auth/IPasswordService.hpp b/src/libs/auth/include/auth/IPasswordService.hpp index 896c4156..0c4b1cc5 100644 --- a/src/libs/auth/include/auth/IPasswordService.hpp +++ b/src/libs/auth/include/auth/IPasswordService.hpp @@ -61,14 +61,9 @@ namespace Auth std::string_view loginName, std::string_view password) = 0; - class PasswordTooWeakException : public Auth::Exception - { - public: - PasswordTooWeakException() : Auth::Exception {"Password too weak"} {} - }; - 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; }; diff --git a/src/libs/auth/include/auth/Types.hpp b/src/libs/auth/include/auth/Types.hpp index 9b79bab7..dec88e0a 100644 --- a/src/libs/auth/include/auth/Types.hpp +++ b/src/libs/auth/include/auth/Types.hpp @@ -19,6 +19,8 @@ #pragma once +#include +#include "database/Types.hpp" #include "utils/Exception.hpp" namespace Auth @@ -33,5 +35,17 @@ namespace Auth public: NotImplementedException() : Auth::Exception {"Not implemented"} {} }; + + struct PasswordValidationContext + { + std::string loginName; + Database::UserType userType; + }; + + class PasswordTooWeakException : public Exception + { + public: + PasswordTooWeakException() : Auth::Exception {"Password too weak"} {} + }; } diff --git a/src/libs/database/impl/User.cpp b/src/libs/database/impl/User.cpp index b0dc3f01..9a6deb73 100644 --- a/src/libs/database/impl/User.cpp +++ b/src/libs/database/impl/User.cpp @@ -98,7 +98,7 @@ User::getDemo(Session& session) { session.checkSharedLocked(); - pointer res = session.getDboSession().find().where("type = ?").bind(Type::DEMO); + pointer res = session.getDboSession().find().where("type = ?").bind(UserType::DEMO); return res; } diff --git a/src/libs/database/include/database/Types.hpp b/src/libs/database/include/database/Types.hpp index 080075f1..174bb65b 100644 --- a/src/libs/database/include/database/Types.hpp +++ b/src/libs/database/include/database/Types.hpp @@ -70,5 +70,13 @@ namespace Database Internal = 0, ListenBrainz = 1, }; + + // Do not change enum values! + enum class UserType + { + REGULAR = 0, + ADMIN = 1, + DEMO = 2, + }; } diff --git a/src/libs/database/include/database/User.hpp b/src/libs/database/include/database/User.hpp index 82980188..2bd593fb 100644 --- a/src/libs/database/include/database/User.hpp +++ b/src/libs/database/include/database/User.hpp @@ -80,15 +80,6 @@ class User : public Wt::Dbo::Dbo public: using pointer = Wt::Dbo::ptr; - - // Do not change enum values! - enum class Type - { - REGULAR = 0, - ADMIN = 1, - DEMO = 2, - }; - struct PasswordHash { std::string salt; @@ -152,7 +143,7 @@ class User : public Wt::Dbo::Dbo // 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 setType(UserType type) { _type = type; } void setSubsonicTranscodeEnable(bool value) { _subsonicTranscodeEnable = value; } void setSubsonicTranscodeFormat(AudioFormat encoding) { _subsonicTranscodeFormat = encoding; } void setSubsonicTranscodeBitrate(Bitrate bitrate); @@ -166,8 +157,9 @@ class User : public Wt::Dbo::Dbo void setListenBrainzToken(const std::optional& MBID) { _listenbrainzToken = MBID ? MBID->getAsString() : ""; } // read - bool isAdmin() const { return _type == Type::ADMIN; } - bool isDemo() const { return _type == Type::DEMO; } + bool isAdmin() const { return _type == UserType::ADMIN; } + bool isDemo() const { return _type == UserType::DEMO; } + UserType getType() const { return _type; } bool getSubsonicTranscodeEnable() const { return _subsonicTranscodeEnable; } AudioFormat getSubsonicTranscodeFormat() const { return _subsonicTranscodeFormat; } Bitrate getSubsonicTranscodeBitrate() const { return _subsonicTranscodeBitrate; } @@ -233,7 +225,7 @@ class User : public Wt::Dbo::Dbo std::string _listenbrainzToken; // Musicbrainz Identifier // Admin defined settings - Type _type {Type::REGULAR}; + UserType _type {UserType::REGULAR}; // User defined settings SubsonicArtistListMode _subsonicArtistListMode {defaultSubsonicArtistListMode}; diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index 19727338..fa22a14b 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -180,7 +180,7 @@ std::string parameterMapToDebugString(const Wt::Http::ParameterMap& parameterMap auto censorValue = [](const std::string& type, const std::string& value) -> std::string { if (type == "p" || type == "password") - return "*SENSIBLE DATA*"; + return "*REDACTED*"; else return value; }; @@ -221,24 +221,16 @@ checkUserIsMySelfOrAdmin(RequestContext& context, const std::string& username) static void -checkUserIsAdmin(RequestContext& context) +checkUserTypeIsAllowed(RequestContext& context, EnumSet allowedUserTypes) { - LMS_LOG(API_SUBSONIC, DEBUG) << "Check user is admin"; - auto transaction {context.dbSession.createSharedTransaction()}; User::pointer currentUser {User::getById(context.dbSession, context.userId)}; if (!currentUser) - { - LMS_LOG(API_SUBSONIC, DEBUG) << "NOT FOUND"; throw RequestedDataNotFoundError {}; - } - if (!currentUser->isAdmin()) - { - LMS_LOG(API_SUBSONIC, DEBUG) << "NOT ADMIN"; + if (!allowedUserTypes.contains(currentUser->getType())) throw UserNotAuthorizedError {}; - } } static @@ -567,7 +559,7 @@ handleChangePassword(RequestContext& context) Service::get()->setPassword(context.dbSession, userId, password); } - catch (Auth::IPasswordService::PasswordTooWeakException&) + catch (Auth::PasswordTooWeakException&) { throw PasswordTooWeakGenericError {}; } @@ -666,7 +658,7 @@ handleCreateUserRequest(RequestContext& context) { Service::get()->setPassword(context.dbSession, userId, password); } - catch (const Auth::IPasswordService::PasswordTooWeakException&) + catch (const Auth::PasswordTooWeakException&) { removeCreatedUser(); throw PasswordTooWeakGenericError {}; @@ -1719,7 +1711,7 @@ handleUpdateUserRequest(RequestContext& context) { Service<::Auth::IPasswordService>()->setPassword(context.dbSession, userId, decodePasswordIfNeeded(*password)); } - catch (const Auth::IPasswordService::PasswordTooWeakException&) + catch (const Auth::PasswordTooWeakException&) { throw PasswordTooWeakGenericError {}; } @@ -1922,114 +1914,114 @@ using RequestHandlerFunc = std::function; using CheckImplementedFunc = std::function; struct RequestEntryPointInfo { - RequestHandlerFunc func; - bool mustBeAdmin; - CheckImplementedFunc checkFunc {}; + RequestHandlerFunc func; + EnumSet allowedUserTypes {Database::UserType::DEMO, Database::UserType::REGULAR, Database::UserType::ADMIN}; + CheckImplementedFunc checkFunc {}; }; static std::unordered_map requestEntryPoints { // System - {"ping", {handlePingRequest, false}}, - {"getLicense", {handleGetLicenseRequest, false}}, + {"ping", {handlePingRequest}}, + {"getLicense", {handleGetLicenseRequest}}, // Browsing - {"getMusicFolders", {handleGetMusicFoldersRequest, false}}, - {"getIndexes", {handleGetIndexesRequest, false}}, - {"getMusicDirectory", {handleGetMusicDirectoryRequest, false}}, - {"getGenres", {handleGetGenresRequest, false}}, - {"getArtists", {handleGetArtistsRequest, false}}, - {"getArtist", {handleGetArtistRequest, false}}, - {"getAlbum", {handleGetAlbumRequest, false}}, - {"getSong", {handleNotImplemented, false}}, - {"getVideos", {handleNotImplemented, false}}, - {"getArtistInfo", {handleGetArtistInfoRequest, false}}, - {"getArtistInfo2", {handleGetArtistInfo2Request, false}}, - {"getAlbumInfo", {handleNotImplemented, false}}, - {"getAlbumInfo2", {handleNotImplemented, false}}, - {"getSimilarSongs", {handleGetSimilarSongsRequest, false}}, - {"getSimilarSongs2", {handleGetSimilarSongs2Request, false}}, - {"getTopSongs", {handleNotImplemented, false}}, + {"getMusicFolders", {handleGetMusicFoldersRequest}}, + {"getIndexes", {handleGetIndexesRequest}}, + {"getMusicDirectory", {handleGetMusicDirectoryRequest}}, + {"getGenres", {handleGetGenresRequest}}, + {"getArtists", {handleGetArtistsRequest}}, + {"getArtist", {handleGetArtistRequest}}, + {"getAlbum", {handleGetAlbumRequest}}, + {"getSong", {handleNotImplemented}}, + {"getVideos", {handleNotImplemented}}, + {"getArtistInfo", {handleGetArtistInfoRequest}}, + {"getArtistInfo2", {handleGetArtistInfo2Request}}, + {"getAlbumInfo", {handleNotImplemented}}, + {"getAlbumInfo2", {handleNotImplemented}}, + {"getSimilarSongs", {handleGetSimilarSongsRequest}}, + {"getSimilarSongs2", {handleGetSimilarSongs2Request}}, + {"getTopSongs", {handleNotImplemented}}, // Album/song lists - {"getAlbumList", {handleGetAlbumListRequest, false}}, - {"getAlbumList2", {handleGetAlbumList2Request, false}}, - {"getRandomSongs", {handleGetRandomSongsRequest, false}}, - {"getSongsByGenre", {handleGetSongsByGenreRequest, false}}, - {"getNowPlaying", {handleNotImplemented, false}}, - {"getStarred", {handleGetStarredRequest, false}}, - {"getStarred2", {handleGetStarred2Request, false}}, + {"getAlbumList", {handleGetAlbumListRequest}}, + {"getAlbumList2", {handleGetAlbumList2Request}}, + {"getRandomSongs", {handleGetRandomSongsRequest}}, + {"getSongsByGenre", {handleGetSongsByGenreRequest}}, + {"getNowPlaying", {handleNotImplemented}}, + {"getStarred", {handleGetStarredRequest}}, + {"getStarred2", {handleGetStarred2Request}}, // Searching - {"search", {handleNotImplemented, false}}, - {"search2", {handleSearch2Request, false}}, - {"search3", {handleSearch3Request, false}}, + {"search", {handleNotImplemented}}, + {"search2", {handleSearch2Request}}, + {"search3", {handleSearch3Request}}, // Playlists - {"getPlaylists", {handleGetPlaylistsRequest, false}}, - {"getPlaylist", {handleGetPlaylistRequest, false}}, - {"createPlaylist", {handleCreatePlaylistRequest, false}}, - {"updatePlaylist", {handleUpdatePlaylistRequest, false}}, - {"deletePlaylist", {handleDeletePlaylistRequest, false}}, + {"getPlaylists", {handleGetPlaylistsRequest}}, + {"getPlaylist", {handleGetPlaylistRequest}}, + {"createPlaylist", {handleCreatePlaylistRequest}}, + {"updatePlaylist", {handleUpdatePlaylistRequest}}, + {"deletePlaylist", {handleDeletePlaylistRequest}}, // Media retrieval - {"hls", {handleNotImplemented, false}}, - {"getCaptions", {handleNotImplemented, false}}, - {"getLyrics", {handleNotImplemented, false}}, - {"getAvatar", {handleNotImplemented, false}}, + {"hls", {handleNotImplemented}}, + {"getCaptions", {handleNotImplemented}}, + {"getLyrics", {handleNotImplemented}}, + {"getAvatar", {handleNotImplemented}}, // Media annotation - {"star", {handleStarRequest, false}}, - {"unstar", {handleUnstarRequest, false}}, - {"setRating", {handleNotImplemented, false}}, - {"scrobble", {handleScrobble, false}}, + {"star", {handleStarRequest}}, + {"unstar", {handleUnstarRequest}}, + {"setRating", {handleNotImplemented}}, + {"scrobble", {handleScrobble}}, // Sharing - {"getShares", {handleNotImplemented, false}}, - {"createShares", {handleNotImplemented, false}}, - {"updateShare", {handleNotImplemented, false}}, - {"deleteShare", {handleNotImplemented, false}}, + {"getShares", {handleNotImplemented}}, + {"createShares", {handleNotImplemented}}, + {"updateShare", {handleNotImplemented}}, + {"deleteShare", {handleNotImplemented}}, // Podcast - {"getPodcasts", {handleNotImplemented, false}}, - {"getNewestPodcasts", {handleNotImplemented, false}}, - {"refreshPodcasts", {handleNotImplemented, false}}, - {"createPodcastChannel", {handleNotImplemented, false}}, - {"deletePodcastChannel", {handleNotImplemented, false}}, - {"deletePodcastEpisode", {handleNotImplemented, false}}, - {"downloadPodcastEpisode", {handleNotImplemented, false}}, + {"getPodcasts", {handleNotImplemented}}, + {"getNewestPodcasts", {handleNotImplemented}}, + {"refreshPodcasts", {handleNotImplemented}}, + {"createPodcastChannel", {handleNotImplemented}}, + {"deletePodcastChannel", {handleNotImplemented}}, + {"deletePodcastEpisode", {handleNotImplemented}}, + {"downloadPodcastEpisode", {handleNotImplemented}}, // Jukebox - {"jukeboxControl", {handleNotImplemented, false}}, + {"jukeboxControl", {handleNotImplemented}}, // Internet radio - {"getInternetRadioStations", {handleNotImplemented, false}}, - {"createInternetRadioStation", {handleNotImplemented, false}}, - {"updateInternetRadioStation", {handleNotImplemented, false}}, - {"deleteInternetRadioStation", {handleNotImplemented, false}}, + {"getInternetRadioStations", {handleNotImplemented}}, + {"createInternetRadioStation", {handleNotImplemented}}, + {"updateInternetRadioStation", {handleNotImplemented}}, + {"deleteInternetRadioStation", {handleNotImplemented}}, // Chat - {"getChatMessages", {handleNotImplemented, false}}, - {"addChatMessages", {handleNotImplemented, false}}, + {"getChatMessages", {handleNotImplemented}}, + {"addChatMessages", {handleNotImplemented}}, // User management - {"getUser", {handleGetUserRequest, false}}, - {"getUsers", {handleGetUsersRequest, true}}, - {"createUser", {handleCreateUserRequest, true, &checkSetPasswordImplemented}}, - {"updateUser", {handleUpdateUserRequest, true}}, - {"deleteUser", {handleDeleteUserRequest, true}}, - {"changePassword", {handleChangePassword, false, &checkSetPasswordImplemented}}, + {"getUser", {handleGetUserRequest}}, + {"getUsers", {handleGetUsersRequest, {Database::UserType::ADMIN}}}, + {"createUser", {handleCreateUserRequest, {Database::UserType::ADMIN}, &checkSetPasswordImplemented}}, + {"updateUser", {handleUpdateUserRequest, {Database::UserType::ADMIN}}}, + {"deleteUser", {handleDeleteUserRequest, {Database::UserType::ADMIN}}}, + {"changePassword", {handleChangePassword, {Database::UserType::REGULAR, Database::UserType::ADMIN}, &checkSetPasswordImplemented}}, // Bookmarks - {"getBookmarks", {handleGetBookmarks, false}}, - {"createBookmark", {handleCreateBookmark, false}}, - {"deleteBookmark", {handleDeleteBookmark, false}}, - {"getPlayQueue", {handleNotImplemented, false}}, - {"savePlayQueue", {handleNotImplemented, false}}, + {"getBookmarks", {handleGetBookmarks}}, + {"createBookmark", {handleCreateBookmark}}, + {"deleteBookmark", {handleDeleteBookmark}}, + {"getPlayQueue", {handleNotImplemented}}, + {"savePlayQueue", {handleNotImplemented}}, // Media library scanning - {"getScanStatus", {Scan::handleGetScanStatus, true}}, - {"startScan", {Scan::handleStartScan, true}}, + {"getScanStatus", {Scan::handleGetScanStatus, {Database::UserType::ADMIN}}}, + {"startScan", {Scan::handleStartScan, {Database::UserType::ADMIN}}}, }; using MediaRetrievalHandlerFunc = std::function; @@ -2112,8 +2104,7 @@ SubsonicResource::handleRequest(const Wt::Http::Request &request, Wt::Http::Resp if (itEntryPoint->second.checkFunc) itEntryPoint->second.checkFunc(); - if (itEntryPoint->second.mustBeAdmin) - checkUserIsAdmin(requestContext); + checkUserTypeIsAllowed(requestContext, itEntryPoint->second.allowedUserTypes); Response resp {(itEntryPoint->second.func)(requestContext)}; diff --git a/src/libs/subsonic/impl/SubsonicResponse.hpp b/src/libs/subsonic/impl/SubsonicResponse.hpp index 76f05a2d..bd7339c4 100644 --- a/src/libs/subsonic/impl/SubsonicResponse.hpp +++ b/src/libs/subsonic/impl/SubsonicResponse.hpp @@ -152,6 +152,11 @@ class PasswordTooWeakGenericError : public GenericError 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 { std::string getMessage() const override { return "User already exists"; } diff --git a/src/lms/ui/LmsApplication.cpp b/src/lms/ui/LmsApplication.cpp index aba96fab..5a766550 100644 --- a/src/lms/ui/LmsApplication.cpp +++ b/src/lms/ui/LmsApplication.cpp @@ -116,20 +116,12 @@ LmsApplication::isUserAuthStrong() const return _authenticatedUser->strongAuth; } -bool -LmsApplication::isUserAdmin() +Database::UserType +LmsApplication::getUserType() { auto transaction {getDbSession().createSharedTransaction()}; - return getUser()->isAdmin(); -} - -bool -LmsApplication::isUserDemo() -{ - auto transaction {getDbSession().createSharedTransaction()}; - - return getUser()->isDemo(); + return getUser()->getType(); } std::string @@ -458,7 +450,7 @@ LmsApplication::onUserLoggedIn() // Only one active session by user if (otherApplication.getUserId() == getUserId()) { - if (!LmsApp->isUserDemo()) + if (LmsApp->getUserType() != Database::UserType::DEMO) { quit(Wt::WString::tr("Lms.quit-other-session")); } @@ -502,7 +494,7 @@ LmsApplication::createHome() Wt::WLineEdit* searchEdit {main->bindNew("search")}; 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->bindNew("database", Wt::WLink {Wt::LinkType::InternalPath, "/admin/database"}, Wt::WString::tr("Lms.Admin.Database.menu-database")); @@ -530,7 +522,7 @@ LmsApplication::createHome() }); // Admin stuff - if (isUserAdmin()) + if (getUserType() == Database::UserType::ADMIN) { mainStack->addNew(); mainStack->addNew(); @@ -581,7 +573,8 @@ LmsApplication::createHome() _mediaPlayer->stop(); }); - if (isUserAdmin()) + const bool isAdmin {getUserType() == Database::UserType::ADMIN}; + if (isAdmin) { _scannerEvents.scanComplete.connect([=] (const Scanner::ScanStats& stats) { @@ -597,10 +590,10 @@ LmsApplication::createHome() internalPathChanged().connect([=] { - handlePathChange(*mainStack, isUserAdmin()); + handlePathChange(*mainStack, isAdmin); }); - handlePathChange(*mainStack, isUserAdmin()); + handlePathChange(*mainStack, isAdmin); } void diff --git a/src/lms/ui/LmsApplication.hpp b/src/lms/ui/LmsApplication.hpp index 37d7cbf7..5a00c168 100644 --- a/src/lms/ui/LmsApplication.hpp +++ b/src/lms/ui/LmsApplication.hpp @@ -65,9 +65,8 @@ class LmsApplication : public Wt::WApplication Wt::Dbo::ptr getUser(); Database::IdType getUserId(); bool isUserAuthStrong() const; // user must be logged in prior this call - bool isUserAdmin(); // 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 + Database::UserType getUserType(); // user must be logged in prior this call + std::string getUserLoginName(); // user must be logged in prior this call // Proxified scanner events Scanner::Events& getScannerEvents() { return _scannerEvents; } diff --git a/src/lms/ui/SettingsView.cpp b/src/lms/ui/SettingsView.cpp index f0a84ac7..59e0cbff 100644 --- a/src/lms/ui/SettingsView.cpp +++ b/src/lms/ui/SettingsView.cpp @@ -102,7 +102,7 @@ class SettingsModel : public Wt::WFormModel } addField(PasswordField); - setValidator(PasswordField, createPasswordStrengthValidator(LmsApp->getUserLoginName())); + setValidator(PasswordField, createPasswordStrengthValidator([] { return ::Auth::PasswordValidationContext {LmsApp->getUserLoginName(), LmsApp->getUserType()}; })); addField(PasswordConfirmField); } diff --git a/src/lms/ui/admin/InitWizardView.cpp b/src/lms/ui/admin/InitWizardView.cpp index be9561b3..c353dbf0 100644 --- a/src/lms/ui/admin/InitWizardView.cpp +++ b/src/lms/ui/admin/InitWizardView.cpp @@ -54,7 +54,7 @@ class InitWizardModel : public Wt::WFormModel addField(PasswordConfirmField); 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); setValidator(PasswordConfirmField, createMandatoryValidator()); } @@ -69,7 +69,7 @@ class InitWizardModel : public Wt::WFormModel throw LmsException {"Admin user already created"}; 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()); } diff --git a/src/lms/ui/admin/UserView.cpp b/src/lms/ui/admin/UserView.cpp index 2d4c7ab6..20ac8b43 100644 --- a/src/lms/ui/admin/UserView.cpp +++ b/src/lms/ui/admin/UserView.cpp @@ -66,7 +66,7 @@ class UserModel : public Wt::WFormModel if (authPasswordService) { 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) validator(PasswordField)->setMandatory(true); } @@ -100,7 +100,7 @@ class UserModel : public Wt::WFormModel user = Database::User::create(LmsApp->getDbSession(), valueText(LoginField).toUTF8()); if (Wt::asNumber(value(DemoField))) - user.modify()->setType(Database::User::Type::DEMO); + user.modify()->setType(Database::UserType::DEMO); if (_authPasswordService) _authPasswordService->setPassword(LmsApp->getDbSession(), user.id(), valueText(PasswordField).toUTF8()); @@ -159,6 +159,9 @@ class UserModel : public Wt::WFormModel } else if (field == PasswordField) { + if (Wt::asNumber(value(DemoField))) + setValidator(PasswordField, {}); + validatePassword(error); } else if (field == DemoField) @@ -172,7 +175,7 @@ class UserModel : public Wt::WFormModel if (error.empty()) 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; } diff --git a/src/lms/ui/common/PasswordValidator.cpp b/src/lms/ui/common/PasswordValidator.cpp index 20559f5d..b923fe80 100644 --- a/src/lms/ui/common/PasswordValidator.cpp +++ b/src/lms/ui/common/PasswordValidator.cpp @@ -30,12 +30,14 @@ namespace UserInterface class PasswordStrengthValidator : public Wt::WValidator { public: - PasswordStrengthValidator(LoginNameGetFunc loginNameGetFunc) : _loginNameGetFunc {std::move(loginNameGetFunc)} {} - - Wt::WValidator::Result validate(const Wt::WString& input) const override; + PasswordStrengthValidator(PasswordValidationContextGetFunc passwordValidationContextGetFunc) + : _passwordValidationContextGetFunc {std::move(passwordValidationContextGetFunc)} + {} private: - LoginNameGetFunc _loginNameGetFunc; + Wt::WValidator::Result validate(const Wt::WString& input) const override; + + PasswordValidationContextGetFunc _passwordValidationContextGetFunc; }; Wt::WValidator::Result @@ -44,21 +46,18 @@ namespace UserInterface if (input.empty()) 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::Invalid, Wt::WString::tr("Lms.password-too-weak")}; } std::shared_ptr - createPasswordStrengthValidator(std::string_view loginName) + createPasswordStrengthValidator(PasswordValidationContextGetFunc passwordValidationContextGetFunc) { - return std::make_shared([loginName = std::string {loginName}] { return loginName; }); - } - - std::shared_ptr createPasswordStrengthValidator(LoginNameGetFunc loginNameGetFunc) - { - return std::make_shared(std::move(loginNameGetFunc)); + return std::make_shared(std::move(passwordValidationContextGetFunc)); } class PasswordCheckValidator : public Wt::WValidator diff --git a/src/lms/ui/common/PasswordValidator.hpp b/src/lms/ui/common/PasswordValidator.hpp index 481d0270..d504dbc1 100644 --- a/src/lms/ui/common/PasswordValidator.hpp +++ b/src/lms/ui/common/PasswordValidator.hpp @@ -19,13 +19,16 @@ #pragma once +#include #include +#include "database/Types.hpp" +#include "auth/Types.hpp" + namespace UserInterface { - std::shared_ptr createPasswordStrengthValidator(std::string_view loginName); - using LoginNameGetFunc = std::function; - std::shared_ptr createPasswordStrengthValidator(LoginNameGetFunc loginNameGetFunc); + using PasswordValidationContextGetFunc = std::function<::Auth::PasswordValidationContext()>; + std::shared_ptr createPasswordStrengthValidator(PasswordValidationContextGetFunc passwordValidationContextGetFunc); // Check current user password std::shared_ptr createPasswordCheckValidator();