WIP. User authentication on user interface

This commit is contained in:
emeric
2014-07-08 19:39:49 +02:00
parent 4744111959
commit 3e96534e08
17 changed files with 370 additions and 76 deletions
+1
View File
@@ -19,6 +19,7 @@ lms_SOURCES = \
$(top_srcdir)/cover/CoverArt.cpp \ $(top_srcdir)/cover/CoverArt.cpp \
$(top_srcdir)/cover/CoverArtGrabber.cpp \ $(top_srcdir)/cover/CoverArtGrabber.cpp \
$(top_srcdir)/ui/LmsApplication.cpp \ $(top_srcdir)/ui/LmsApplication.cpp \
$(top_srcdir)/ui/LmsHome.cpp \
$(top_srcdir)/ui/audio/AudioWidget.cpp \ $(top_srcdir)/ui/audio/AudioWidget.cpp \
$(top_srcdir)/ui/audio/AudioDatabaseWidget.cpp \ $(top_srcdir)/ui/audio/AudioDatabaseWidget.cpp \
$(top_srcdir)/ui/audio/AudioMediaPlayerWidget.cpp \ $(top_srcdir)/ui/audio/AudioMediaPlayerWidget.cpp \
+61 -2
View File
@@ -1,14 +1,55 @@
#include "DatabaseHandler.hpp" #include <Wt/Auth/Dbo/AuthInfo>
#include <Wt/Auth/Dbo/UserDatabase>
#include <Wt/Auth/AuthService>
#include <Wt/Auth/HashFunction>
#include <Wt/Auth/PasswordService>
#include <Wt/Auth/PasswordStrengthValidator>
#include <Wt/Auth/PasswordVerifier>
// Db types
#include "AudioTypes.hpp" #include "AudioTypes.hpp"
#include "FileTypes.hpp" #include "FileTypes.hpp"
#include "MediaDirectory.hpp" #include "MediaDirectory.hpp"
#include "User.hpp"
#include "DatabaseHandler.hpp"
namespace Database { 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) Handler::Handler(boost::filesystem::path db)
: :
_path(db),
_dbBackend( db.string() ) _dbBackend( db.string() )
{ {
_session.setConnection(_dbBackend); _session.setConnection(_dbBackend);
@@ -22,6 +63,11 @@ _dbBackend( db.string() )
_session.mapClass<Database::MediaDirectory>("media_directory"); _session.mapClass<Database::MediaDirectory>("media_directory");
_session.mapClass<Database::MediaDirectorySettings>("media_directory_settings"); _session.mapClass<Database::MediaDirectorySettings>("media_directory_settings");
_session.mapClass<Database::User>("user");
_session.mapClass<Database::AuthInfo>("auth_info");
_session.mapClass<Database::AuthInfo::AuthIdentityType>("auth_identity");
_session.mapClass<Database::AuthInfo::AuthTokenType>("auth_token");
try { try {
_session.createTables(); _session.createTables();
} }
@@ -30,6 +76,19 @@ _dbBackend( db.string() )
} }
_dbBackend.executeSql("pragma journal_mode=WAL"); _dbBackend.executeSql("pragma journal_mode=WAL");
_users = new UserDatabase(_session);
}
Handler::~Handler()
{
delete _users;
}
Wt::Auth::AbstractUserDatabase&
Handler::getUserDatabase()
{
return *_users;
} }
} // namespace Database } // namespace Database
+21 -3
View File
@@ -5,26 +5,44 @@
#include <Wt/Dbo/Dbo> #include <Wt/Dbo/Dbo>
#include <Wt/Dbo/backend/Sqlite3> #include <Wt/Dbo/backend/Sqlite3>
#include <Wt/Auth/Dbo/UserDatabase>
#include <Wt/Auth/Login>
#include <Wt/Auth/PasswordService>
#include "User.hpp"
namespace Database { namespace Database {
// Long living class handling the database typedef Wt::Auth::Dbo::UserDatabase<AuthInfo> UserDatabase;
// Session living class handling the database
class Handler class Handler
{ {
public: public:
Handler(boost::filesystem::path db); Handler(boost::filesystem::path db);
~Handler();
Wt::Dbo::Session& getSession() { return _session; } 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: private:
boost::filesystem::path _path;
Wt::Dbo::backend::Sqlite3 _dbBackend; Wt::Dbo::backend::Sqlite3 _dbBackend;
Wt::Dbo::Session _session; Wt::Dbo::Session _session;
UserDatabase* _users;
Wt::Auth::Login _login;
}; };
} // namespace Database } // namespace Database
+30
View File
@@ -0,0 +1,30 @@
#ifndef DATABASE_USER_HPP
#define DATABASE_USER_HPP
#include <Wt/Auth/Dbo/AuthInfo>
namespace Database {
class User;
typedef Wt::Auth::Dbo::AuthInfo<User> AuthInfo;
class User {
public:
template<class Action>
void persist(Action& a)
{
Wt::Dbo::field(a, _isAdmin, "admin");
}
private:
bool _isAdmin;
};
} // namespace Databas'
#endif
+1 -1
View File
@@ -11,7 +11,7 @@ DatabaseUpdateService::DatabaseUpdateService(boost::asio::io_service& ioService,
void void
DatabaseUpdateService::start(void) DatabaseUpdateService::start(void)
{ {
// _thread = boost::thread(boost::bind(&DatabaseUpdater::Updater::process, &_databaseUpdater)); _thread = boost::thread(boost::bind(&DatabaseUpdater::Updater::process, &_databaseUpdater));
} }
void void
+3 -1
View File
@@ -5,6 +5,7 @@
#include "transcode/AvConvTranscoder.hpp" #include "transcode/AvConvTranscoder.hpp"
#include "av/Common.hpp" #include "av/Common.hpp"
#include "database/DatabaseHandler.hpp"
#include "ServiceManager.hpp" #include "ServiceManager.hpp"
#include "DatabaseUpdateService.hpp" #include "DatabaseUpdateService.hpp"
@@ -29,10 +30,11 @@ int main(int argc, char* argv[])
// lib init // lib init
Av::AvInit(); Av::AvInit();
Transcode::AvConvTranscoder::init(); Transcode::AvConvTranscoder::init();
Database::Handler::configureAuth();
std::cout << "Starting services..." << std::endl; std::cout << "Starting services..." << std::endl;
serviceManager.startService( std::make_shared<DatabaseUpdateService>( serviceManager.getIoService(), dbPath) ); // serviceManager.startService( std::make_shared<DatabaseUpdateService>( serviceManager.getIoService(), dbPath) );
serviceManager.startService( std::make_shared<RemoteServerService>( serviceManager.getIoService(), remoteListenEndpoint, dbPath) ); serviceManager.startService( std::make_shared<RemoteServerService>( serviceManager.getIoService(), remoteListenEndpoint, dbPath) );
serviceManager.startService( std::make_shared<UserInterfaceService>(argc, argv, dbPath) ); serviceManager.startService( std::make_shared<UserInterfaceService>(argc, argv, dbPath) );
+2 -2
View File
@@ -18,11 +18,11 @@ namespace Server {
Connection::Connection(boost::asio::io_service& ioService, Connection::Connection(boost::asio::io_service& ioService,
boost::asio::ssl::context& context, boost::asio::ssl::context& context,
ConnectionManager& manager, ConnectionManager& manager,
RequestHandler& handler) const boost::filesystem::path& dbPath)
: _closing(false), : _closing(false),
_socket(ioService, context), _socket(ioService, context),
_connectionManager(manager), _connectionManager(manager),
_requestHandler(handler) _requestHandler(dbPath)
{ {
std::cout << "Server::Connection::Connection, Creating connection" << std::endl; std::cout << "Server::Connection::Connection, Creating connection" << std::endl;
} }
+3 -2
View File
@@ -30,7 +30,8 @@ class Connection : public std::enable_shared_from_this<Connection>
/// Construct a connection with the given io_service. /// Construct a connection with the given io_service.
explicit Connection(boost::asio::io_service& ioService, boost::asio::ssl::context& context, 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();} ssl_socket::lowest_layer_type& getSocket() {return _socket.lowest_layer();}
@@ -63,7 +64,7 @@ class Connection : public std::enable_shared_from_this<Connection>
ConnectionManager& _connectionManager; ConnectionManager& _connectionManager;
/// The handler used to process the incoming requests. /// The handler used to process the incoming requests.
RequestHandler& _requestHandler; RequestHandler _requestHandler;
boost::asio::streambuf _inputStreamBuf; boost::asio::streambuf _inputStreamBuf;
boost::asio::streambuf _outputStreamBuf; boost::asio::streambuf _outputStreamBuf;
+2 -2
View File
@@ -15,7 +15,7 @@ _ioService(ioService),
_acceptor(_ioService, bindEndpoint, true /*SO_REUSEADDR*/), _acceptor(_ioService, bindEndpoint, true /*SO_REUSEADDR*/),
_connectionManager(), _connectionManager(),
_context(boost::asio::ssl::context::tlsv1_server), _context(boost::asio::ssl::context::tlsv1_server),
_requestHandler(dbPath) _dbPath(dbPath)
{ {
_context.set_options( boost::asio::ssl::context::default_workarounds // TODO check this thing _context.set_options( boost::asio::ssl::context::default_workarounds // TODO check this thing
| boost::asio::ssl::context::single_dh_use | boost::asio::ssl::context::single_dh_use
@@ -40,7 +40,7 @@ Server::run()
void void
Server::asyncAccept() Server::asyncAccept()
{ {
std::shared_ptr<Connection> newConnection = std::make_shared<Connection>(_ioService, _context, _connectionManager, _requestHandler); std::shared_ptr<Connection> newConnection = std::make_shared<Connection>(_ioService, _context, _connectionManager, _dbPath);
_acceptor.async_accept(newConnection->getSocket(), _acceptor.async_accept(newConnection->getSocket(),
boost::bind(&Server::handleAccept, this, newConnection, boost::asio::placeholders::error)); boost::bind(&Server::handleAccept, this, newConnection, boost::asio::placeholders::error));
+2 -2
View File
@@ -46,8 +46,8 @@ class Server
boost::asio::ssl::context _context; boost::asio::ssl::context _context;
/// The handler for all incoming requests. /// The database to be used for requests
RequestHandler _requestHandler; boost::filesystem::path _dbPath;
}; };
} // namespace Server } // namespace Server
+44
View File
@@ -0,0 +1,44 @@
#include "database/DatabaseHandler.hpp"
#include <Wt/Auth/PasswordService>
#include <Wt/Auth/Identity>
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;
}
+16 -2
View File
@@ -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 = \ remote_SOURCES = \
$(srcdir)/RemoteClientServer.cpp \ $(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 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 = \ database_integrity_SOURCES = \
$(srcdir)/DatabaseIntegrity.cpp \ $(srcdir)/DatabaseIntegrity.cpp \
$(top_srcdir)/database/Artist.cpp \ $(top_srcdir)/database/Artist.cpp \
+37 -52
View File
@@ -1,13 +1,6 @@
#include <Wt/WContainerWidget>
#include <Wt/WBreak>
#include <Wt/WLabel>
#include <Wt/WBootstrapTheme> #include <Wt/WBootstrapTheme>
#include <Wt/WStackedWidget> #include "LmsAuth.hpp"
#include <Wt/WMenu> #include "LmsHome.hpp"
#include <Wt/WNavigationBar>
#include <Wt/WPopupMenu>
#include <Wt/WPopupMenuItem>
#include "LmsApplication.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) LmsApplication::LmsApplication(const Wt::WEnvironment& env, boost::filesystem::path dbPath)
: Wt::WApplication(env), : Wt::WApplication(env),
_sessionData(dbPath) _sessionData(dbPath),
_home(nullptr)
{ {
setTheme(new Wt::WBootstrapTheme()); setTheme(new Wt::WBootstrapTheme());
setTitle("LMS"); // application title 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. LmsAuth *authWidget = new LmsAuth(Database::Handler::getAuthService(),
Wt::WNavigationBar *navigation = new Wt::WNavigationBar(container); _sessionData.getDatabaseHandler().getUserDatabase(),
navigation->setTitle("LMS"); _sessionData.getDatabaseHandler().getLogin());
navigation->setResponsive(true);
Wt::WStackedWidget *contentsStack = new Wt::WStackedWidget(container); authWidget->model()->addPasswordAuth(&Database::Handler::getPasswordService());
contentsStack->addStyleClass("contents"); authWidget->setRegistrationEnabled(true);
// Setup a Left-aligned menu. authWidget->processEnvironment();
Wt::WMenu *leftMenu = new Wt::WMenu(contentsStack, container);
navigation->addMenu(leftMenu);
_audioWidget = new AudioWidget(_sessionData); root()->addWidget(authWidget);
_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);
} }
void void
LmsApplication::handleSearch(void) LmsApplication::handleAuthEvent(void)
{ {
// Check currently selected menu item and search it _sessionData.getDatabaseHandler().getLogin().changed().connect(this, &LmsApplication::handleAuthEvent);
_audioWidget->search( _searchEdit->text().toUTF8() ); 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
+5 -7
View File
@@ -1,9 +1,9 @@
#include <Wt/WApplication> #include <Wt/WApplication>
#include <Wt/WLineEdit>
#include "audio/AudioWidget.hpp" #include "common/SessionData.hpp"
#include "video/VideoWidget.hpp"
#include "LmsHome.hpp"
namespace UserInterface { namespace UserInterface {
@@ -18,13 +18,11 @@ class LmsApplication : public Wt::WApplication
private: private:
void handleSearch(); void handleAuthEvent(void);
SessionData _sessionData; SessionData _sessionData;
Wt::WLineEdit* _searchEdit; LmsHome* _home;
AudioWidget* _audioWidget;
VideoWidget* _videoWidget;
}; };
+29
View File
@@ -0,0 +1,29 @@
#include <Wt/Auth/AuthService>
#include <Wt/Auth/AbstractUserDatabase>
#include <Wt/Auth/Login>
#include <Wt/Auth/AuthWidget>
#include <Wt/WContainerWidget>
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
+78
View File
@@ -0,0 +1,78 @@
#include <Wt/WStackedWidget>
#include <Wt/WMenu>
#include <Wt/WNavigationBar>
#include <Wt/WPopupMenu>
#include <Wt/WPopupMenuItem>
#include <Wt/Auth/Identity>
#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
+35
View File
@@ -0,0 +1,35 @@
#ifndef LMS_HOME_HPP
#define LMS_HOME_HPP
#include <Wt/WContainerWidget>
#include <Wt/WLineEdit>
#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