From 3e96534e089a1045f74f997014a160d2caa5c4e9 Mon Sep 17 00:00:00 2001 From: emeric Date: Tue, 8 Jul 2014 19:39:49 +0200 Subject: [PATCH] WIP. User authentication on user interface --- Makefile.am | 1 + database/DatabaseHandler.cpp | 63 +++++++++++++++++++++++- database/DatabaseHandler.hpp | 24 +++++++-- database/User.hpp | 30 ++++++++++++ main/DatabaseUpdateService.cpp | 2 +- main/main.cpp | 4 +- remote/server/Connection.cpp | 4 +- remote/server/Connection.hpp | 5 +- remote/server/Server.cpp | 4 +- remote/server/Server.hpp | 4 +- test/CheckDatabaseUser.cpp | 44 +++++++++++++++++ test/Makefile.am | 18 ++++++- ui/LmsApplication.cpp | 89 ++++++++++++++-------------------- ui/LmsApplication.hpp | 12 ++--- ui/LmsAuth.hpp | 29 +++++++++++ ui/LmsHome.cpp | 78 +++++++++++++++++++++++++++++ ui/LmsHome.hpp | 35 +++++++++++++ 17 files changed, 370 insertions(+), 76 deletions(-) create mode 100644 database/User.hpp create mode 100644 test/CheckDatabaseUser.cpp create mode 100644 ui/LmsAuth.hpp create mode 100644 ui/LmsHome.cpp create mode 100644 ui/LmsHome.hpp diff --git a/Makefile.am b/Makefile.am index 5b5c8c6f..a14eb6e9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -19,6 +19,7 @@ lms_SOURCES = \ $(top_srcdir)/cover/CoverArt.cpp \ $(top_srcdir)/cover/CoverArtGrabber.cpp \ $(top_srcdir)/ui/LmsApplication.cpp \ + $(top_srcdir)/ui/LmsHome.cpp \ $(top_srcdir)/ui/audio/AudioWidget.cpp \ $(top_srcdir)/ui/audio/AudioDatabaseWidget.cpp \ $(top_srcdir)/ui/audio/AudioMediaPlayerWidget.cpp \ diff --git a/database/DatabaseHandler.cpp b/database/DatabaseHandler.cpp index 8572786f..8452a078 100644 --- a/database/DatabaseHandler.cpp +++ b/database/DatabaseHandler.cpp @@ -1,14 +1,55 @@ -#include "DatabaseHandler.hpp" +#include +#include +#include +#include +#include +#include +#include +// Db types #include "AudioTypes.hpp" #include "FileTypes.hpp" #include "MediaDirectory.hpp" +#include "User.hpp" + +#include "DatabaseHandler.hpp" namespace Database { + +namespace { + Wt::Auth::AuthService authService; + Wt::Auth::PasswordService passwordService(authService); +} + + +void +Handler::configureAuth(void) +{ + authService.setEmailVerificationEnabled(true); + + Wt::Auth::PasswordVerifier *verifier = new Wt::Auth::PasswordVerifier(); + verifier->addHashFunction(new Wt::Auth::BCryptHashFunction(8)); + passwordService.setVerifier(verifier); + passwordService.setAttemptThrottlingEnabled(true); + passwordService.setStrengthValidator(new Wt::Auth::PasswordStrengthValidator()); +} + +const Wt::Auth::AuthService& +Handler::getAuthService() +{ + return authService; +} + +const Wt::Auth::PasswordService& +Handler::getPasswordService() +{ + return passwordService; +} + + Handler::Handler(boost::filesystem::path db) : -_path(db), _dbBackend( db.string() ) { _session.setConnection(_dbBackend); @@ -22,6 +63,11 @@ _dbBackend( db.string() ) _session.mapClass("media_directory"); _session.mapClass("media_directory_settings"); + _session.mapClass("user"); + _session.mapClass("auth_info"); + _session.mapClass("auth_identity"); + _session.mapClass("auth_token"); + try { _session.createTables(); } @@ -30,6 +76,19 @@ _dbBackend( db.string() ) } _dbBackend.executeSql("pragma journal_mode=WAL"); + + _users = new UserDatabase(_session); +} + +Handler::~Handler() +{ + delete _users; +} + +Wt::Auth::AbstractUserDatabase& +Handler::getUserDatabase() +{ + return *_users; } } // namespace Database diff --git a/database/DatabaseHandler.hpp b/database/DatabaseHandler.hpp index 083a70fc..f71b075c 100644 --- a/database/DatabaseHandler.hpp +++ b/database/DatabaseHandler.hpp @@ -5,26 +5,44 @@ #include #include +#include +#include +#include + +#include "User.hpp" namespace Database { -// Long living class handling the database + typedef Wt::Auth::Dbo::UserDatabase UserDatabase; + +// Session living class handling the database class Handler { public: + Handler(boost::filesystem::path db); + ~Handler(); Wt::Dbo::Session& getSession() { return _session; } - boost::filesystem::path getPath() const { return _path; } + Wt::Auth::AbstractUserDatabase& getUserDatabase(); + Wt::Auth::Login& getLogin() { return _login; } + + // Long living shared associated services + static void configureAuth(); + + static const Wt::Auth::AuthService& getAuthService(); + static const Wt::Auth::PasswordService& getPasswordService(); private: - boost::filesystem::path _path; Wt::Dbo::backend::Sqlite3 _dbBackend; Wt::Dbo::Session _session; + UserDatabase* _users; + Wt::Auth::Login _login; + }; } // namespace Database diff --git a/database/User.hpp b/database/User.hpp new file mode 100644 index 00000000..cc59a95b --- /dev/null +++ b/database/User.hpp @@ -0,0 +1,30 @@ +#ifndef DATABASE_USER_HPP +#define DATABASE_USER_HPP + + +#include + +namespace Database { + +class User; +typedef Wt::Auth::Dbo::AuthInfo AuthInfo; + +class User { + + public: + + template + void persist(Action& a) + { + Wt::Dbo::field(a, _isAdmin, "admin"); + } + + private: + + bool _isAdmin; + +}; + +} // namespace Databas' + +#endif diff --git a/main/DatabaseUpdateService.cpp b/main/DatabaseUpdateService.cpp index 52740a23..c5fe0faf 100644 --- a/main/DatabaseUpdateService.cpp +++ b/main/DatabaseUpdateService.cpp @@ -11,7 +11,7 @@ DatabaseUpdateService::DatabaseUpdateService(boost::asio::io_service& ioService, void DatabaseUpdateService::start(void) { -// _thread = boost::thread(boost::bind(&DatabaseUpdater::Updater::process, &_databaseUpdater)); + _thread = boost::thread(boost::bind(&DatabaseUpdater::Updater::process, &_databaseUpdater)); } void diff --git a/main/main.cpp b/main/main.cpp index eba76da9..7c85b0a9 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -5,6 +5,7 @@ #include "transcode/AvConvTranscoder.hpp" #include "av/Common.hpp" +#include "database/DatabaseHandler.hpp" #include "ServiceManager.hpp" #include "DatabaseUpdateService.hpp" @@ -29,10 +30,11 @@ int main(int argc, char* argv[]) // lib init Av::AvInit(); Transcode::AvConvTranscoder::init(); + Database::Handler::configureAuth(); std::cout << "Starting services..." << std::endl; - serviceManager.startService( std::make_shared( serviceManager.getIoService(), dbPath) ); +// serviceManager.startService( std::make_shared( serviceManager.getIoService(), dbPath) ); serviceManager.startService( std::make_shared( serviceManager.getIoService(), remoteListenEndpoint, dbPath) ); serviceManager.startService( std::make_shared(argc, argv, dbPath) ); diff --git a/remote/server/Connection.cpp b/remote/server/Connection.cpp index e28e1e07..afff9d54 100644 --- a/remote/server/Connection.cpp +++ b/remote/server/Connection.cpp @@ -18,11 +18,11 @@ namespace Server { Connection::Connection(boost::asio::io_service& ioService, boost::asio::ssl::context& context, ConnectionManager& manager, - RequestHandler& handler) + const boost::filesystem::path& dbPath) : _closing(false), _socket(ioService, context), _connectionManager(manager), -_requestHandler(handler) +_requestHandler(dbPath) { std::cout << "Server::Connection::Connection, Creating connection" << std::endl; } diff --git a/remote/server/Connection.hpp b/remote/server/Connection.hpp index 605c02b8..6a0fcbbe 100644 --- a/remote/server/Connection.hpp +++ b/remote/server/Connection.hpp @@ -30,7 +30,8 @@ class Connection : public std::enable_shared_from_this /// Construct a connection with the given io_service. explicit Connection(boost::asio::io_service& ioService, boost::asio::ssl::context& context, - ConnectionManager& manager, RequestHandler& handler); + ConnectionManager& manager, + const boost::filesystem::path& dbPath); ssl_socket::lowest_layer_type& getSocket() {return _socket.lowest_layer();} @@ -63,7 +64,7 @@ class Connection : public std::enable_shared_from_this ConnectionManager& _connectionManager; /// The handler used to process the incoming requests. - RequestHandler& _requestHandler; + RequestHandler _requestHandler; boost::asio::streambuf _inputStreamBuf; boost::asio::streambuf _outputStreamBuf; diff --git a/remote/server/Server.cpp b/remote/server/Server.cpp index 51e80752..88d21459 100644 --- a/remote/server/Server.cpp +++ b/remote/server/Server.cpp @@ -15,7 +15,7 @@ _ioService(ioService), _acceptor(_ioService, bindEndpoint, true /*SO_REUSEADDR*/), _connectionManager(), _context(boost::asio::ssl::context::tlsv1_server), -_requestHandler(dbPath) +_dbPath(dbPath) { _context.set_options( boost::asio::ssl::context::default_workarounds // TODO check this thing | boost::asio::ssl::context::single_dh_use @@ -40,7 +40,7 @@ Server::run() void Server::asyncAccept() { - std::shared_ptr newConnection = std::make_shared(_ioService, _context, _connectionManager, _requestHandler); + std::shared_ptr newConnection = std::make_shared(_ioService, _context, _connectionManager, _dbPath); _acceptor.async_accept(newConnection->getSocket(), boost::bind(&Server::handleAccept, this, newConnection, boost::asio::placeholders::error)); diff --git a/remote/server/Server.hpp b/remote/server/Server.hpp index e0ead17b..2a70fb8f 100644 --- a/remote/server/Server.hpp +++ b/remote/server/Server.hpp @@ -46,8 +46,8 @@ class Server boost::asio::ssl::context _context; - /// The handler for all incoming requests. - RequestHandler _requestHandler; + /// The database to be used for requests + boost::filesystem::path _dbPath; }; } // namespace Server diff --git a/test/CheckDatabaseUser.cpp b/test/CheckDatabaseUser.cpp new file mode 100644 index 00000000..617c55d6 --- /dev/null +++ b/test/CheckDatabaseUser.cpp @@ -0,0 +1,44 @@ +#include "database/DatabaseHandler.hpp" +#include +#include + +int main(void) +{ + try + { + boost::filesystem::remove("test_user.db"); + + // Set up the database session + Database::Handler::configureAuth(); + + Database::Handler db("test_user.db"); + + Wt::Dbo::Transaction transaction(db.getSession()); + + Wt::Auth::Identity identity; + Wt::Auth::User user = db.getUserDatabase().registerNew(); + std::cout << "User is valid = " << std::boolalpha << user.isValid() << std::endl; + + user.setIdentity(identity.provider(), "toto"); + + std::cout << "User is valid = " << std::boolalpha << user.isValid() << std::endl; + + std::cout << "Updating password" << std::endl; + db.getPasswordService().updatePassword(user, "This is my password"); + +// db.getSession().add( user ); + + std::cout << "Committing" << std::endl; + + transaction.commit(); + + } + catch(std::exception& e) + { + std::cerr << "Caught exception " << e.what() << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + diff --git a/test/Makefile.am b/test/Makefile.am index ef142f6d..90eb562b 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,7 +1,7 @@ -TESTS = database-integrity sql-query database-basics remote +TESTS = database-integrity sql-query database-basics database-user remote -check_PROGRAMS = database-integrity sql-query database-basics remote +check_PROGRAMS = database-integrity sql-query database-basics database-user remote remote_SOURCES = \ $(srcdir)/RemoteClientServer.cpp \ @@ -22,6 +22,20 @@ remote_SOURCES = \ remote_CXXFLAGS=-std=c++11 -Wall -Wextra -I$(top_srcdir) -I$(top_srcdir)/remote -I$(top_srcdir)/boost +database_user_SOURCES = \ + $(srcdir)/CheckDatabaseUser.cpp \ + $(top_srcdir)/database/Artist.cpp \ + $(top_srcdir)/database/Genre.cpp \ + $(top_srcdir)/database/Release.cpp \ + $(top_srcdir)/database/Track.cpp \ + $(top_srcdir)/database/DatabaseHandler.cpp \ + $(top_srcdir)/database/SqlQuery.cpp \ + $(top_srcdir)/database/Path.cpp \ + $(top_srcdir)/database/Video.cpp + +database_user_CXXFLAGS=-std=c++11 -Wall -Wextra -I$(top_srcdir) + + database_integrity_SOURCES = \ $(srcdir)/DatabaseIntegrity.cpp \ $(top_srcdir)/database/Artist.cpp \ diff --git a/ui/LmsApplication.cpp b/ui/LmsApplication.cpp index 1312e9de..9e8e364c 100644 --- a/ui/LmsApplication.cpp +++ b/ui/LmsApplication.cpp @@ -1,13 +1,6 @@ - -#include -#include -#include #include -#include -#include -#include -#include -#include +#include "LmsAuth.hpp" +#include "LmsHome.hpp" #include "LmsApplication.hpp" @@ -32,65 +25,57 @@ LmsApplication::create(const Wt::WEnvironment& env, boost::filesystem::path dbPa */ LmsApplication::LmsApplication(const Wt::WEnvironment& env, boost::filesystem::path dbPath) : Wt::WApplication(env), - _sessionData(dbPath) + _sessionData(dbPath), + _home(nullptr) { setTheme(new Wt::WBootstrapTheme()); setTitle("LMS"); // application title - Wt::WContainerWidget* container (new Wt::WContainerWidget(root())); + _sessionData.getDatabaseHandler().getLogin().changed().connect(this, &LmsApplication::handleAuthEvent); - // Create a navigation bar with a link to a web page. - Wt::WNavigationBar *navigation = new Wt::WNavigationBar(container); - navigation->setTitle("LMS"); - navigation->setResponsive(true); + LmsAuth *authWidget = new LmsAuth(Database::Handler::getAuthService(), + _sessionData.getDatabaseHandler().getUserDatabase(), + _sessionData.getDatabaseHandler().getLogin()); - Wt::WStackedWidget *contentsStack = new Wt::WStackedWidget(container); - contentsStack->addStyleClass("contents"); + authWidget->model()->addPasswordAuth(&Database::Handler::getPasswordService()); + authWidget->setRegistrationEnabled(true); - // Setup a Left-aligned menu. - Wt::WMenu *leftMenu = new Wt::WMenu(contentsStack, container); - navigation->addMenu(leftMenu); + authWidget->processEnvironment(); - _audioWidget = new AudioWidget(_sessionData); - _videoWidget = new VideoWidget(_sessionData); - - leftMenu->addItem("Audio", _audioWidget); - leftMenu->addItem("Video", _videoWidget); - - // Setup a Right-aligned menu. - Wt::WMenu *rightMenu = new Wt::WMenu(); - navigation->addMenu(rightMenu, Wt::AlignRight); - - Wt::WPopupMenu *popup = new Wt::WPopupMenu(); - popup->addItem("Parameters"); - popup->addSeparator(); - popup->addItem("Logout"); - - Wt::WMenuItem *item = new Wt::WMenuItem("User"); - item->setMenu(popup); - rightMenu->addItem(item); - - // Add a Search control. - _searchEdit = new Wt::WLineEdit(); - _searchEdit->setEmptyText("Search..."); - - _searchEdit->keyWentUp().connect(this, &LmsApplication::handleSearch); - - navigation->addSearch(_searchEdit, Wt::AlignLeft); - - container->addWidget(contentsStack); + root()->addWidget(authWidget); } + void -LmsApplication::handleSearch(void) +LmsApplication::handleAuthEvent(void) { - // Check currently selected menu item and search it - _audioWidget->search( _searchEdit->text().toUTF8() ); + _sessionData.getDatabaseHandler().getLogin().changed().connect(this, &LmsApplication::handleAuthEvent); + if (_sessionData.getDatabaseHandler().getLogin().loggedIn()) + { + if (_home == nullptr) { + _home = new LmsHome(_sessionData ); + root()->addWidget( _home ); + } + else + std::cerr << "Already logged in??" << std::endl; + } + else + { + std::cerr << "user log out" << std::endl; + if (_home != nullptr) { + std::cerr << "Deleting home pointer..." << std::endl; + delete _home; + _home = nullptr; + } + else + std::cerr << "Already logged out??" << std::endl; + } } -} // namespace DatabaseHandler +} // namespace UserInterface + diff --git a/ui/LmsApplication.hpp b/ui/LmsApplication.hpp index 33f0ca0e..93ec332f 100644 --- a/ui/LmsApplication.hpp +++ b/ui/LmsApplication.hpp @@ -1,9 +1,9 @@ #include -#include -#include "audio/AudioWidget.hpp" -#include "video/VideoWidget.hpp" +#include "common/SessionData.hpp" + +#include "LmsHome.hpp" namespace UserInterface { @@ -18,13 +18,11 @@ class LmsApplication : public Wt::WApplication private: - void handleSearch(); + void handleAuthEvent(void); SessionData _sessionData; - Wt::WLineEdit* _searchEdit; - AudioWidget* _audioWidget; - VideoWidget* _videoWidget; + LmsHome* _home; }; diff --git a/ui/LmsAuth.hpp b/ui/LmsAuth.hpp new file mode 100644 index 00000000..0981c697 --- /dev/null +++ b/ui/LmsAuth.hpp @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include + +namespace UserInterface { + +class LmsAuth : public Wt::Auth::AuthWidget +{ + public: + + LmsAuth(const Wt::Auth::AuthService &baseAuth, + Wt::Auth::AbstractUserDatabase &users, + Wt::Auth::Login &login, + Wt::WContainerWidget *parent=0) + : Wt::Auth::AuthWidget(baseAuth, users, login, parent) {} + + // LoggedInView is delegated to LmsHome + void createLoggedInView () { hide();} + + void createLoginView () { show(); Wt::Auth::AuthWidget::createLoginView();} + + private: + + +}; + +} // namespace UserInterface diff --git a/ui/LmsHome.cpp b/ui/LmsHome.cpp new file mode 100644 index 00000000..56a167dd --- /dev/null +++ b/ui/LmsHome.cpp @@ -0,0 +1,78 @@ +#include +#include +#include +#include +#include +#include + +#include "LmsHome.hpp" + +namespace UserInterface { + +LmsHome::LmsHome(SessionData& sessionData) +: _sessionData(sessionData) +{ + const Wt::Auth::User& user = sessionData.getDatabaseHandler().getLogin().user(); + + // Create a navigation bar with a link to a web page. + Wt::WNavigationBar *navigation = new Wt::WNavigationBar(this); + navigation->setTitle("LMS"); + navigation->setResponsive(true); + + Wt::WStackedWidget *contentsStack = new Wt::WStackedWidget(this); + contentsStack->addStyleClass("contents"); + + // Setup a Left-aligned menu. + Wt::WMenu *leftMenu = new Wt::WMenu(contentsStack, this); + navigation->addMenu(leftMenu); + + _audioWidget = new AudioWidget(_sessionData); + _videoWidget = new VideoWidget(_sessionData); + + leftMenu->addItem("Audio", _audioWidget); + leftMenu->addItem("Video", _videoWidget); + + // Setup a Right-aligned menu. + Wt::WMenu *rightMenu = new Wt::WMenu(); + + navigation->addMenu(rightMenu, Wt::AlignRight); + + Wt::WPopupMenu *popup = new Wt::WPopupMenu(); + popup->addItem("Parameters"); + popup->addSeparator(); + popup->addItem("Logout"); + + popup->itemSelected().connect(this, &LmsHome::handleUserMenuSelected); + + Wt::WMenuItem *item = new Wt::WMenuItem( user.identity(Wt::Auth::Identity::LoginName) ); + item->setMenu(popup); + rightMenu->addItem(item); + + // Add a Search control. + _searchEdit = new Wt::WLineEdit(); + _searchEdit->setEmptyText("Search..."); + + _searchEdit->keyWentUp().connect(this, &LmsHome::handleSearch); + + navigation->addSearch(_searchEdit, Wt::AlignLeft); + + addWidget(contentsStack); + +} + +void +LmsHome::handleUserMenuSelected( Wt::WMenuItem* item) +{ + if (item && item->text() == "Logout") { + _sessionData.getDatabaseHandler().getLogin().logout(); + } +} + +void +LmsHome::handleSearch(void) +{ + // TODO Check currently selected menu item and search it + _audioWidget->search( _searchEdit->text().toUTF8() ); +} + +} // namespace UserInterface diff --git a/ui/LmsHome.hpp b/ui/LmsHome.hpp new file mode 100644 index 00000000..310668f3 --- /dev/null +++ b/ui/LmsHome.hpp @@ -0,0 +1,35 @@ +#ifndef LMS_HOME_HPP +#define LMS_HOME_HPP + +#include +#include + +#include "common/SessionData.hpp" + +#include "audio/AudioWidget.hpp" +#include "video/VideoWidget.hpp" + +namespace UserInterface { + +class LmsHome : public Wt::WContainerWidget +{ + public: + + LmsHome(SessionData& sessionData); + + private: + + void handleSearch(void); + void handleUserMenuSelected( Wt::WMenuItem* item ); + + SessionData& _sessionData; + + Wt::WLineEdit* _searchEdit; + AudioWidget* _audioWidget; + VideoWidget* _videoWidget; +}; + +} // namespace UserInterface + +#endif +