diff --git a/approot/admin-user.xml b/approot/admin-user.xml index f3a9ebc9..b74f00b4 100644 --- a/approot/admin-user.xml +++ b/approot/admin-user.xml @@ -33,19 +33,31 @@
- ${audio-bitrate-limit} - kbps + ${audio-bitrate-limit} + kbps
${audio-bitrate-limit-info}
- + ${} +
+ +
+ ${demo} +
+
+ ${demo-info} +
+
+ ${
}
${save-btn class="btn-primary"} diff --git a/approot/admin-users.xml b/approot/admin-users.xml index be2e36d9..481a79bd 100644 --- a/approot/admin-users.xml +++ b/approot/admin-users.xml @@ -11,7 +11,7 @@
- ${name} ${}${edit-btn} ${del-btn class="btn-danger"}${} + ${name} ${}${tag}${} ${}${edit-btn} ${del-btn class="btn-danger"}${}
diff --git a/approot/messages.xml b/approot/messages.xml index 1912f986..561a2f49 100644 --- a/approot/messages.xml +++ b/approot/messages.xml @@ -36,10 +36,17 @@ New user +Admin Delete +Demo Edit Users + +Audio bitrate limit +Demo account +Demo account already exists! +Demo password must be the login name! User already exists! New user New user created! @@ -83,6 +90,7 @@ Audio Auto Bitrate +Cannot save using a demo account! Encoding MP3 OGA diff --git a/conf/lms.conf b/conf/lms.conf index 3d9b06f5..4c8acc54 100644 --- a/conf/lms.conf +++ b/conf/lms.conf @@ -20,4 +20,5 @@ wt-resources="/usr/share/Wt/resources"; docroot = "/usr/share/lms/docroot/;/resources,/css,/images,/js,/favicon.ico"; approot = "/usr/share/lms/approot"; - +# Turn on this option to allow the demo account creation/use +#demo = false; diff --git a/src/database/User.cpp b/src/database/User.cpp index dd8ed1dd..b687572f 100644 --- a/src/database/User.cpp +++ b/src/database/User.cpp @@ -33,7 +33,7 @@ User::audioBitrates = User::User() : _maxAudioBitrate(audioBitrates.back()), -_isAdmin(false), +_type(Type::REGULAR), _audioBitrate(defaultAudioBitrate), _audioEncoding(AudioEncoding::AUTO), _curPlayingTrackPos(0) @@ -48,6 +48,13 @@ User::getAll(Wt::Dbo::Session& session) return std::vector(res.begin(), res.end()); } +User::pointer +User::getDemo(Wt::Dbo::Session& session) +{ + pointer res = session.find().where("type = ?").bind(Type::DEMO); + return res; +} + User::pointer User::create(Wt::Dbo::Session& session) { @@ -55,7 +62,7 @@ User::create(Wt::Dbo::Session& session) } User::pointer -User::getById(Wt::Dbo::Session& session, id_type id) +User::getById(Wt::Dbo::Session& session, IdType id) { return session.find().where("id = ?").bind( id ); } diff --git a/src/database/User.hpp b/src/database/User.hpp index 325b11a9..fd536172 100644 --- a/src/database/User.hpp +++ b/src/database/User.hpp @@ -24,6 +24,8 @@ #include #include +#include "Types.hpp" + namespace Database { class User; @@ -43,12 +45,18 @@ enum class AudioEncoding class User : public Wt::Dbo::Dbo { public: - typedef Wt::Dbo::dbo_traits::IdType id_type; - typedef Wt::Dbo::ptr pointer; + using pointer = Wt::Dbo::ptr; static const std::size_t MinNameLength = 3; static const std::size_t MaxNameLength = 15; + enum class Type + { + REGULAR, + ADMIN, + DEMO + }; + // list of audio parameters static const std::vector audioBitrates; @@ -58,18 +66,20 @@ class User : public Wt::Dbo::Dbo static pointer create(Wt::Dbo::Session& session); // accessors - static pointer getById(Wt::Dbo::Session& session, id_type id); + static pointer getById(Wt::Dbo::Session& session, IdType id); static std::vector getAll(Wt::Dbo::Session& session); + static pointer getDemo(Wt::Dbo::Session& session); // write - void setAdmin(bool admin) { _isAdmin = admin; } + void setType(Type type) { _type = type; } void setAudioBitrate(std::size_t bitrate); void setAudioEncoding(AudioEncoding encoding) { _audioEncoding = encoding; } void setMaxAudioBitrate(std::size_t bitrate); void setCurPlayingTrackPos(std::size_t pos) { _curPlayingTrackPos = pos; } // read - bool isAdmin() const { return _isAdmin; } + bool isAdmin() const { return _type == Type::ADMIN; } + bool isDemo() const { return _type == Type::DEMO; } std::size_t getAudioBitrate() const; AudioEncoding getAudioEncoding() const { return _audioEncoding; } std::size_t getMaxAudioBitrate() const; @@ -79,7 +89,7 @@ class User : public Wt::Dbo::Dbo void persist(Action& a) { Wt::Dbo::field(a, _maxAudioBitrate, "max_audio_bitrate"); - Wt::Dbo::field(a, _isAdmin, "admin"); + Wt::Dbo::field(a, _type, "type"); Wt::Dbo::field(a, _audioBitrate, "audio_bitrate"); Wt::Dbo::field(a, _audioEncoding, "audio_encoding"); // User's dynamic data @@ -93,7 +103,7 @@ class User : public Wt::Dbo::Dbo // Admin defined settings int _maxAudioBitrate; - bool _isAdmin; + Type _type; // User defined settings int _audioBitrate; diff --git a/src/ui/Auth.cpp b/src/ui/Auth.cpp index c2837d2c..c3f4d4b5 100644 --- a/src/ui/Auth.cpp +++ b/src/ui/Auth.cpp @@ -40,6 +40,18 @@ Auth::Auth() // LoginName setFormWidget(Wt::Auth::AuthModel::LoginNameField, std::make_unique()); + { + Wt::Dbo::Transaction transaction(LmsApp->getDboSession()); + + auto demoUser = Database::User::getDemo(LmsApp->getDboSession()); + if (demoUser) + { + Wt::Auth::User authUser = LmsApp->getDb().getUserDatabase().findWithId(std::to_string(demoUser.id())); + _model->setValue(Wt::Auth::AuthModel::LoginNameField, authUser.identity(Wt::Auth::Identity::LoginName)); + _model->setValue(Wt::Auth::AuthModel::PasswordField, authUser.identity(Wt::Auth::Identity::LoginName)); + } + } + // Password auto password = std::make_unique(); password->setEchoMode(Wt::EchoMode::Password); diff --git a/src/ui/SettingsView.cpp b/src/ui/SettingsView.cpp index 5fbcb668..f6c7a8eb 100644 --- a/src/ui/SettingsView.cpp +++ b/src/ui/SettingsView.cpp @@ -295,6 +295,17 @@ SettingsView::refreshView() saveBtn->clicked().connect(std::bind([=] () { + + { + Wt::Dbo::Transaction transaction(LmsApp->getDboSession()); + + if (LmsApp->getUser()->isDemo()) + { + LmsApp->notifyMsg(Wt::WString::tr("Lms.Settings.demo-cannot-save")); + return; + } + } + t->updateModel(model.get()); if (model->validate()) diff --git a/src/ui/admin/InitWizardView.cpp b/src/ui/admin/InitWizardView.cpp index bbc97b0f..4bf99a02 100644 --- a/src/ui/admin/InitWizardView.cpp +++ b/src/ui/admin/InitWizardView.cpp @@ -69,7 +69,7 @@ class InitWizardModel : public Wt::WFormModel authUser.setIdentity(Wt::Auth::Identity::LoginName, valueText(AdminLoginField)); Database::Handler::getPasswordService().updatePassword(authUser, valueText(PasswordField)); - user.modify()->setAdmin( true ); + user.modify()->setType(Database::User::Type::ADMIN); } bool validateField(Field field) diff --git a/src/ui/admin/UserView.cpp b/src/ui/admin/UserView.cpp index da5ba9bd..d28de35d 100644 --- a/src/ui/admin/UserView.cpp +++ b/src/ui/admin/UserView.cpp @@ -30,6 +30,7 @@ #include #include "common/Validators.hpp" +#include "utils/Config.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" #include "utils/Utils.hpp" @@ -45,8 +46,9 @@ class UserModel : public Wt::WFormModel static const Field LoginField; static const Field PasswordField; static const Field BitrateLimitField; + static const Field DemoField; - UserModel(boost::optional userId) + UserModel(boost::optional userId) : Wt::WFormModel(), _userId(userId) { @@ -58,7 +60,7 @@ class UserModel : public Wt::WFormModel addField(PasswordField); addField(BitrateLimitField); - + addField(DemoField); if (!_userId) setValidator(PasswordField, createMandatoryValidator()); @@ -76,8 +78,8 @@ class UserModel : public Wt::WFormModel Wt::Dbo::Transaction transaction(LmsApp->getDboSession()); - Wt::Auth::User authUser = LmsApp->getDb().getUserDatabase().findWithId( std::to_string(*_userId) ); - Database::User::pointer user = LmsApp->getDb().getUser(authUser); + auto authUser = LmsApp->getDb().getUserDatabase().findWithId( std::to_string(*_userId) ); + auto user = LmsApp->getDb().getUser(authUser); if (user == LmsApp->getUser()) throw LmsException("Cannot edit ourselves"); @@ -117,6 +119,9 @@ class UserModel : public Wt::WFormModel auto bitrateLimitRow = getBitrateLimitRow(Wt::asString(value(BitrateLimitField))); user.modify()->setMaxAudioBitrate(bitrateLimit(*bitrateLimitRow)); + + if (Wt::asNumber(value(DemoField))) + user.modify()->setType(Database::User::Type::DEMO); } } @@ -140,30 +145,41 @@ class UserModel : public Wt::WFormModel auto user = LmsApp->getDb().getUserDatabase().findWithIdentity(Wt::Auth::Identity::LoginName, valueText(LoginField)); if (user.isValid()) error = Wt::WString::tr("Lms.Admin.User.user-already-exists"); - else - return Wt::WFormModel::validateField(field); } else if (field == PasswordField) { if (!valueText(PasswordField).empty()) { - // Evaluate the strength of the password - auto res = Database::Handler::getPasswordService().strengthValidator()->evaluateStrength(valueText(PasswordField), getLogin(), ""); + if (Wt::asNumber(value(DemoField))) + { + //Demo account: password must be the same as the login name + if (valueText(PasswordField) != getLogin()) + error = Wt::WString::tr("Lms.Admin.User.demo-password-invalid"); + } + else + { + // Evaluate the strength of the password for non demo accounts + auto res = Database::Handler::getPasswordService().strengthValidator()->evaluateStrength(valueText(PasswordField), getLogin(), ""); - if (!res.isValid()) - error = res.message(); + if (!res.isValid()) + error = res.message(); + } } - else - return Wt::WFormModel::validateField(field); } - else + else if (field == DemoField) { - return Wt::WFormModel::validateField(field); + Wt::Dbo::Transaction transaction(LmsApp->getDboSession()); + + if (Wt::asNumber(value(DemoField)) && Database::User::getDemo(LmsApp->getDboSession())) + error = Wt::WString::tr("Lms.Admin.User.demo-account-already-exists"); } - setValidation(field, Wt::WValidator::Result( error.empty() ? Wt::ValidationState::Valid : Wt::ValidationState::Invalid, error)); + if (error.empty()) + return Wt::WFormModel::validateField(field); - return (validation(field).state() == Wt::ValidationState::Valid); + setValidation(field, Wt::WValidator::Result( Wt::ValidationState::Invalid, error)); + + return false; } boost::optional getBitrateLimitRow(Wt::WString value) @@ -219,12 +235,13 @@ class UserModel : public Wt::WFormModel } std::shared_ptr _bitrateModel; - boost::optional _userId; + boost::optional _userId; }; const Wt::WFormModel::Field UserModel::LoginField = "login"; const Wt::WFormModel::Field UserModel::PasswordField = "password"; const Wt::WFormModel::Field UserModel::BitrateLimitField = "audio-bitrate-limit"; +const Wt::WFormModel::Field UserModel::DemoField = "demo"; UserView::UserView() { @@ -242,7 +259,7 @@ UserView::refreshView() if (!wApp->internalPathMatches("/admin/user")) return; - auto userId = readAs(wApp->internalPathNextPart("/admin/user/")); + auto userId = readAs(wApp->internalPathNextPart("/admin/user/")); LMS_LOG(UI, DEBUG) << "userId = " << (userId ? std::to_string(*userId) : "none"); @@ -276,6 +293,11 @@ UserView::refreshView() bitrate->setModel(model->bitrateModel()); t->setFormWidget(UserModel::BitrateLimitField, std::move(bitrate)); + // Demo account + t->setFormWidget(UserModel::DemoField, std::make_unique()); + if (!userId && Config::instance().getBool("demo", false)) + t->setCondition("if-demo", true); + Wt::WPushButton* saveBtn = t->bindNew("save-btn", Wt::WString::tr(userId ? "Lms.save" : "Lms.create")); saveBtn->clicked().connect(std::bind([=] { diff --git a/src/ui/admin/UsersView.cpp b/src/ui/admin/UsersView.cpp index e38140b3..226a3acc 100644 --- a/src/ui/admin/UsersView.cpp +++ b/src/ui/admin/UsersView.cpp @@ -74,6 +74,13 @@ UsersView::refreshView() entry->bindString("name", authUser.identity(Wt::Auth::Identity::LoginName), Wt::TextFormat::Plain); + // Create tag + if (user->isAdmin() || user->isDemo()) + { + entry->setCondition("if-tag", true); + entry->bindString("tag", Wt::WString::tr(user->isAdmin() ? "Lms.Admin.Users.admin" : "Lms.Admin.Users.demo")); + } + // Don't edit ourself this way if (LmsApp->getUser() == user) continue;