diff --git a/src/blackcore/databaseauthentication.cpp b/src/blackcore/databaseauthentication.cpp new file mode 100644 index 000000000..e029ef33d --- /dev/null +++ b/src/blackcore/databaseauthentication.cpp @@ -0,0 +1,146 @@ +/* Copyright (C) 2015 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "databaseauthentication.h" +#include "blackmisc/network/networkutils.h" +#include "blackmisc/network/url.h" +#include "blackmisc/logmessage.h" + +#include +#include +#include +#include +#include + +using namespace BlackMisc; +using namespace BlackMisc::Network; + +namespace BlackCore +{ + CDatabaseAuthenticationService::CDatabaseAuthenticationService(QObject *parent) : + QObject(parent), + m_networkManager(new QNetworkAccessManager(this)) + { + this->connect(this->m_networkManager, &QNetworkAccessManager::finished, this, &CDatabaseAuthenticationService::ps_parseServerResponse); + } + + const Network::CAuthenticatedUser &CDatabaseAuthenticationService::getUser() const + { + return m_user; + } + + void CDatabaseAuthenticationService::gracefulShutdown() + { + if (this->m_shutdown) { return; } + this->m_shutdown = true; + this->logoff(); + } + + BlackMisc::CStatusMessageList CDatabaseAuthenticationService::login(const QString &username, const QString &password) + { + CStatusMessageList msgs; + static const CLogCategoryList cats(CLogCategoryList(this).join({ CLogCategory::validation()})); + + if (this->m_shutdown) { msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "Shutdown in progress")); return msgs; } + + QString un(username.trimmed()); + QString pw(password.trimmed()); + if (un.isEmpty()) { msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "No user name/id")); } + if (pw.isEmpty()) { msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "No password")); } + if (!msgs.isEmpty()) { return msgs; } + + CUrl url(this->m_setup.get().dbLoginService()); + QString msg; + if (!CNetworkUtils::canConnect(url, msg)) + { + msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, msg)); + return msgs; + } + + QUrlQuery params; + params.addQueryItem("username", un); + params.addQueryItem("password", pw); + if (m_setup.get().dbDebugFlag()) { CNetworkUtils::addDebugFlag(params); } + + QString query = params.toString(); + QNetworkRequest request(CNetworkUtils::getNetworkRequest(url, CNetworkUtils::PostUrlEncoded)); + QNetworkReply *r = this->m_networkManager->post(request, query.toUtf8()); + if (!r) + { + QString rm("Cannot send request to authentication server %1"); + msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, rm.arg(url.toQString()))); + } + else + { + QString rm("Sent request to authentication server %1"); + msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityInfo, rm.arg(url.toQString()))); + } + return msgs; + } + + void CDatabaseAuthenticationService::logoff() + { + CUrl url(this->m_setup.get().dbLoginService()); + url.setQuery("logoff=true"); + QNetworkRequest request(CNetworkUtils::getNetworkRequest(url)); + this->m_networkManager->get(request); + } + + void CDatabaseAuthenticationService::ps_parseServerResponse(QNetworkReply *nwReplyPtr) + { + // always cleanup reply + QScopedPointer nwReply(nwReplyPtr); + + if (this->m_shutdown) { return; } + QString urlString(nwReply->url().toString()); + if (urlString.toLower().contains("logoff")) + { + emit logoffFinished(); + return; + } + + if (nwReply->error() == QNetworkReply::NoError) + { + QString json(nwReply->readAll()); + if (json.isEmpty()) + { + CLogMessage(this).error("Authentication failed, no response from %1") << urlString; + return; + } + QJsonObject jsonObj(Json::jsonObjectFromString(json)); + CAuthenticatedUser user(CAuthenticatedUser::fromDatabaseJson(jsonObj)); + + CStatusMessageList msgs; + static const CLogCategoryList cats(CLogCategoryList(this).join({ CLogCategory::validation()})); + + if (!user.isAuthenticated() || !user.isValid()) + { + msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "Cannot login, user or password wrong")); + } + else + { + if (!user.isEnabled()) + { + msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "User is disabled")); + } + if (user.getRoles().isEmpty()) + { + msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "User has no roles")); + } + } + emit userAuthenticationFinished(user, msgs); + } + else + { + CLogMessage(this).error("Authentication failed, %1") << nwReply->errorString(); + return; + } + } + +} // namespace diff --git a/src/blackcore/databaseauthentication.h b/src/blackcore/databaseauthentication.h new file mode 100644 index 000000000..36e37ab60 --- /dev/null +++ b/src/blackcore/databaseauthentication.h @@ -0,0 +1,66 @@ +/* Copyright (C) 2015 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#ifndef BLACKCORE_DATABASE_USER_H +#define BLACKCORE_DATABASE_USER_H + +//! \file + +#include "blackcore/blackcoreexport.h" +#include "blackmisc/network/authenticateduser.h" +#include "blackcore/data/globalsetup.h" +#include "blackmisc/statusmessagelist.h" + +#include +#include + +namespace BlackCore +{ + //! Databse user used with swift DB. Features role and cookie handling. + class BLACKCORE_EXPORT CDatabaseAuthenticationService: public QObject + { + Q_OBJECT + + public: + //! Constructor + CDatabaseAuthenticationService(QObject *parent = nullptr); + + //! Get the user + const BlackMisc::Network::CAuthenticatedUser &getUser() const; + + //! Shutdown + void gracefulShutdown(); + + public slots: + //! Try to login to authentication web service + BlackMisc::CStatusMessageList login(const QString &id, const QString &password); + + //! Logoff + void logoff(); + + signals: + //! User authenticated + void userAuthenticationFinished(const BlackMisc::Network::CAuthenticatedUser &user, const BlackMisc::CStatusMessageList &loginStatus); + + //! Logoff completed + void logoffFinished(); + + private slots: + //! Parse login answer + void ps_parseServerResponse(QNetworkReply *nwReplyPtr); + + private: + BlackMisc::Network::CAuthenticatedUser m_user; + CData m_setup {this}; //!< data cache + QNetworkAccessManager *m_networkManager = nullptr; + bool m_shutdown = false; + }; +} // namespace + +#endif // guard diff --git a/src/blackgui/components/dblogincomponent.cpp b/src/blackgui/components/dblogincomponent.cpp index 81cdc6a91..e526f3ace 100644 --- a/src/blackgui/components/dblogincomponent.cpp +++ b/src/blackgui/components/dblogincomponent.cpp @@ -13,6 +13,7 @@ #include "blackmisc/network/url.h" #include "blackmisc/logmessage.h" +using namespace BlackCore; using namespace BlackMisc; using namespace BlackMisc::Network; @@ -25,13 +26,12 @@ namespace BlackGui ui(new Ui::CDbLoginComponent) { ui->setupUi(this); - CUrl url(m_setup.get().dbHomePage()); - ui->lbl_SwiftDB->setText("swift DB@" + url.getHost() + ""); - ui->lbl_SwiftDB->setTextFormat(Qt::RichText); - ui->lbl_SwiftDB->setTextInteractionFlags(Qt::TextBrowserInteraction); - ui->lbl_SwiftDB->setOpenExternalLinks(true); + this->setModeLogin(true); + this->ps_setupChanged(); connect(ui->pb_Login, &QPushButton::clicked, this, &CDbLoginComponent::ps_onLoginClicked); + connect(ui->pb_Logoff, &QPushButton::clicked, this, &CDbLoginComponent::ps_onLogoffClicked); + connect(&m_loginService, &CDatabaseAuthenticationService::userAuthenticationFinished, this, &CDbLoginComponent::ps_AuthenticationFinished); } CDbLoginComponent::~CDbLoginComponent() @@ -48,19 +48,59 @@ namespace BlackGui void CDbLoginComponent::ps_onLoginClicked() { - CStatusMessageList msgs; - static const CLogCategoryList cats(CLogCategoryList(this).join({ CLogCategory::validation()})); - QString un(ui->le_Username->text().trimmed()); QString pw(ui->le_Password->text().trimmed()); - if (un.isEmpty()) { msgs.push_back(CStatusMessage::CStatusMessage(cats, CStatusMessage::SeverityError, "No user name")); } - if (pw.isEmpty()) { msgs.push_back(CStatusMessage::CStatusMessage(cats, CStatusMessage::SeverityError, "No password")); } + CStatusMessageList msgs = m_loginService.login(un, pw); + if (msgs.hasWarningOrErrorMessages()) { CLogMessage::preformatted(msgs); displayOverlayMessages(msgs); return; } + else if (!msgs.empty()) + { + CLogMessage::preformatted(msgs); + } + } + + void CDbLoginComponent::ps_onLogoffClicked() + { + this->m_loginService.logoff(); + this->setModeLogin(true); + } + + void CDbLoginComponent::ps_AuthenticationFinished(const CAuthenticatedUser &user, const CStatusMessageList &status) + { + bool ok = !status.hasErrorMessages(); + if (ok) + { + CLogMessage(this).info("User authenticated: %1") << user.toQString(); + this->setModeLogin(false); + this->ui->le_Name->setText(user.getRealNameAndId()); + this->ui->te_Roles->setPlainText(user.getRolesAsString()); + } + else + { + this->setModeLogin(true); + this->displayOverlayMessages(status); + CLogMessage(this).preformatted(status); + } + } + + void CDbLoginComponent::ps_setupChanged() + { + CUrl url(m_setup.get().dbHomePage()); + ui->lbl_SwiftDB->setText("swift DB@" + url.getHost() + ""); + ui->lbl_SwiftDB->setTextFormat(Qt::RichText); + ui->lbl_SwiftDB->setTextInteractionFlags(Qt::TextBrowserInteraction); + ui->lbl_SwiftDB->setOpenExternalLinks(true); + } + + void CDbLoginComponent::setModeLogin(bool modeLogin) + { + this->ui->fr_Login->setVisible(modeLogin); + this->ui->fr_Logoff->setVisible(!modeLogin); } } // ns diff --git a/src/blackgui/components/dblogincomponent.h b/src/blackgui/components/dblogincomponent.h index ba6e71a42..9d542a140 100644 --- a/src/blackgui/components/dblogincomponent.h +++ b/src/blackgui/components/dblogincomponent.h @@ -13,6 +13,7 @@ #define BLACKGUI_COMPONENTS_DBLOGINCOMPONENT_H #include "blackgui/blackguiexport.h" +#include "blackcore/databaseauthentication.h" #include "blackcore/data/globalsetup.h" #include #include @@ -39,17 +40,28 @@ namespace BlackGui private: QScopedPointer ui; - BlackCore::CData m_setup {this}; //!< data cache + BlackCore::CData m_setup {this, &CDbLoginComponent::ps_setupChanged}; //!< data cache + BlackCore::CDatabaseAuthenticationService m_loginService {this}; //!< login service //! Overlay messages void displayOverlayMessages(const BlackMisc::CStatusMessageList &msgs); + //! Mode login + void setModeLogin(bool modeLogin); + private slots: //! Login void ps_onLoginClicked(); - }; + //! Logoff + void ps_onLogoffClicked(); + //! User authentication completed + void ps_AuthenticationFinished(const BlackMisc::Network::CAuthenticatedUser &user, const BlackMisc::CStatusMessageList &status); + + //! Setup changed + void ps_setupChanged(); + }; } // ns } // ns diff --git a/src/blackgui/components/dblogincomponent.ui b/src/blackgui/components/dblogincomponent.ui index 70583dc99..d67b14d1a 100644 --- a/src/blackgui/components/dblogincomponent.ui +++ b/src/blackgui/components/dblogincomponent.ui @@ -2,6 +2,14 @@ CDbLoginComponent + + + 0 + 0 + 333 + 299 + + 300 @@ -17,66 +25,170 @@ QFrame::Raised - - - - - 40 + + + + + QFrame::StyledPanel - - user name or id - - - true + + QFrame::Raised + + + + + + 40 + 16777215 + + + + + + + :/own/icons/own/swift/swift32Database.png + + + + + + + DB + + + + - - - - + + + + + 0 + 100 + - - :/own/icons/own/swift/swift32Database.png + + QFrame::StyledPanel + + QFrame::Raised + + + + + + 40 + + + user name or id + + + true + + + + + + + User: + + + + + + + Password: + + + + + + + QLineEdit::PasswordEchoOnEdit + + + password + + + true + + + + + + + login + + + + - - - - login + + + + QFrame::StyledPanel - - - - - - Password: - - - - - - - password - - - true - - - - - - - User: - - - - - - - DB + + QFrame::Raised + + + + + Name: + + + + + + + true + + + false + + + + + + + Roles: + + + + + + + logoff + + + + + + + + 0 + 40 + + + + + 16777215 + 40 + + + + User roles + + + true + + + + diff --git a/src/blackgui/components/dbmappingcomponent.cpp b/src/blackgui/components/dbmappingcomponent.cpp index 1b2771e2a..a6eac0a7d 100644 --- a/src/blackgui/components/dbmappingcomponent.cpp +++ b/src/blackgui/components/dbmappingcomponent.cpp @@ -13,8 +13,8 @@ #include "blackmisc/logmessage.h" #include "blackmisc/project.h" #include "blackgui/guiutility.h" -#include "blackgui/roles.h" +using namespace BlackCore; using namespace BlackMisc; using namespace BlackMisc::Aviation; using namespace BlackMisc::Simulation; @@ -32,6 +32,8 @@ namespace BlackGui COverlayMessagesFrame(parent), ui(new Ui::CDbMappingComponent) { + m_authenticationService = new CDatabaseAuthenticationService(this); + ui->setupUi(this); this->ui->tvp_AircraftModelsForVPilot->setAircraftModelMode(CAircraftModelListModel::VPilotRuleModel); connect(ui->editor_Model, &CModelMappingForm::requestSave, this, &CDbMappingComponent::save); @@ -55,7 +57,7 @@ namespace BlackGui void CDbMappingComponent::initVPilotLoading() { - if (CRoles::roles().isAdmin() && + if (m_authenticationService->getUser().isAdmin() && CProject::isRunningOnWindowsNtPlatform() && CProject::isCompiledWithMsFlightSimulatorSupport()) { @@ -112,6 +114,7 @@ namespace BlackGui this->disconnect(); CWebDataServicesAware::gracefulShutdown(); this->m_vPilotReader.gracefulShutdown(); + this->m_authenticationService->gracefulShutdown(); if (this->m_modelLoader) { this->m_modelLoader->gracefulShutdown(); } } @@ -368,12 +371,17 @@ namespace BlackGui CDbMappingComponent *mapComp = qobject_cast(this->parent()); Q_ASSERT_X(mapComp, Q_FUNC_INFO, "Cannot access parent"); - if (CRoles::roles().isAdmin()) + if (this->mappingComponent()->m_authenticationService->getUser().isAdmin()) { menu.addAction(CIcons::appMappings16(), "Load vPilot Rules", mapComp, SLOT(ps_loadVPilotData())); menu.addSeparator(); } } + CDbMappingComponent *CDbMappingComponent::CMappingVPilotMenu::mappingComponent() const + { + return qobject_cast(this->parent()); + } + } // ns } // ns diff --git a/src/blackgui/components/dbmappingcomponent.h b/src/blackgui/components/dbmappingcomponent.h index aed061345..38e7a2c03 100644 --- a/src/blackgui/components/dbmappingcomponent.h +++ b/src/blackgui/components/dbmappingcomponent.h @@ -12,6 +12,7 @@ #ifndef BLACKGUI_COMPONENTS_DBMAPPINGCOMPONENT_H #define BLACKGUI_COMPONENTS_DBMAPPINGCOMPONENT_H +#include "blackcore/databaseauthentication.h" #include "blackgui/blackguiexport.h" #include "blackgui/overlaymessagesframe.h" #include "blackgui/menudelegate.h" @@ -106,6 +107,7 @@ namespace BlackGui QScopedPointer ui; BlackMisc::Simulation::FsCommon::CVPilotRulesReader m_vPilotReader; std::unique_ptr m_modelLoader; + BlackCore::CDatabaseAuthenticationService *m_authenticationService = nullptr; bool m_withVPilot = false; //! Consolidated aircraft model @@ -117,7 +119,7 @@ namespace BlackGui //! Init model loader bool initModelLoader(const BlackMisc::Simulation::CSimulatorInfo &simInfo); - // -------------------- component specifi menus -------------------------- + // -------------------- component specific menus -------------------------- //! The menu for loading and handling own models for mapping //! \note This is specific for that very component @@ -145,6 +147,9 @@ namespace BlackGui //! \copydoc IMenuDelegate::customMenu virtual void customMenu(QMenu &menu) const override; + + private: + CDbMappingComponent *mappingComponent() const; }; };