diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,7 @@ find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/") +find_package(PolkitQt5-1 REQUIRED) include(KDEInstallDirs) include(KDECMakeSettings) @@ -60,6 +61,7 @@ Core DBus Gui + Test Widgets ) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -43,8 +43,10 @@ Qt5::Core PRIVATE ${BLKID_LIBRARIES} + ${POLKITQT-1_LIBRARIES} Qt5::DBus Qt5::Gui + Qt5::Test qca-qt5 KF5::I18n KF5::CoreAddons diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -19,6 +19,7 @@ set(UTIL_SRC ${HelperInterface_SRCS} util/capacity.cpp + util/externalcommand_authagent.cpp util/externalcommand.cpp util/globallog.cpp util/helpers.cpp @@ -29,6 +30,7 @@ set(UTIL_LIB_HDRS util/libpartitionmanagerexport.h util/capacity.h + util/externalcommand_authagent.h util/externalcommand.h util/globallog.h util/helpers.h @@ -43,8 +45,10 @@ target_link_libraries(kpmcore_externalcommand qca-qt5 + ${POLKITQT-1_LIBRARIES} Qt5::Core Qt5::DBus + Qt5::Test KF5::AuthCore KF5::I18n ) diff --git a/src/util/externalcommand_authagent.h b/src/util/externalcommand_authagent.h new file mode 100644 --- /dev/null +++ b/src/util/externalcommand_authagent.h @@ -0,0 +1,175 @@ +/************************************************************************* + * Copyright (C) 2019 by Shubham * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 3 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see .* + *************************************************************************/ + +#ifndef EXTERNALCOMMAND_AUTHAGENT_H +#define EXTERNALCOMMAND_AUTHAGENT_H + +#include +#include +#include +#include +#include + +using namespace PolkitQt1; + +/** An Authentication agent/listener class. + + This class implements the Listener class + of PolkitQt1. It is used to spawn a fresh + authentication agent which can be used to + authenticate privileged actions. + + @author Shubham +*/ +class Listener : public Agent::Listener +{ + Q_OBJECT + Q_DISABLE_COPY(Listener) +public: + /** + * \brief Constructor of Listener class + */ + Listener(QObject *parent = nullptr); + + /** + * \brief Destructor of Listener class + */ + ~Listener() override; + +public Q_SLOTS: + /** + * \brief Registers an Authentication agent. + * + * \param subject caller subject + * \param locale the locale of the authentication agent + * \param objectPath the object path for the authentication agent + * + * \return \c true if the Authentication agent has been successfully registered + * \c false if the Authentication agent registration failed + */ + bool registerListener(const Subject &subject, const QString &locale, const QString &objectPath); + + /** + * Unregisters an Authentication agent. + * + * \param subject caller subject + * \param objectPath the object path for the Authentication agent + * + * \return \c true if the Authentication agent has been successfully unregistered + * \c false if the Authentication agent unregistration failed + */ + bool unregisterListener(const Subject &subject, const QString &objectPath); + + /** + * \brief Initiate authentication for the action + * + * This method will be called on a registered authentication agent when the user owning + * the session needs to prove they are one of the identities listed in \p identities. + * + * \param actionId The action to authenticate for + * \param message The message to present to the user + * \param iconName The name of the icon which is representing the action + * \param details Details describing the action + * \param cookie The cookie for the authentication request + * \param identities A list of Identity object that the user can choose to authenticate as + * \param result This AsyncResult MUST be completed by using complete() method when the + * authentication is done. + */ + void initiateAuthentication(const QString &actionId, + const QString &message, + const QString &iconName, + const PolkitQt1::Details &details, + const QString &cookie, + const Identity::List &identities, + Agent::AsyncResult *result) override; + + /** + * \brief Cancels an authentication request from the polkit daemon. + */ + void cancelAuthentication() override; + + /** + * \brief Checks for authorization of the subject. + * + * \param actionId the Id of the action in question + * \param subject subject that the action is authorized for (e.g. unix process) + * \param flag flags that influences the authorization checking + * + */ + Authority::Result checkAuthorization(const QString &actionId, + const Subject &subject, + Authority::AuthorizationFlags flag = Authority::AllowUserInteraction) const; + + /** + * \return the code of last error + */ + Authority::ErrorCode pollError() const; + + /** + * \brief Provide response that \p identity successfully authenticated for the authentication request identified by \p cookie. + * + * \param cookie The cookie passed to the authentication agent from the authority. + * \param identity The identity that was authenticated. + * + * \return \c true if authority acknowledged the call, \c false if error is set. + * + */ + bool userSuccessfullyAuthenticated(const QString &cookie, const PolkitQt1::Identity &identity); + + /** + * \brief Function for setting the response given by the user. + * + * \param response Response from the user, typically a password + */ + void setResponse(const QString &response); + + /** + * \brief Function for providing response to requests received via request signal. + * + * \param request Request to enter the password from the authentication agent. + */ + void request(const QString &request, bool echo = false); + + /** + * \brief Function for marking the completion of the authentication process. + * \brief Either the subject is granted permissions or rejected. + * + * \param gainedAuthorization Whether privileges have been gained by the user. + */ + void completed(bool gainedAuthorization = false); + + /** + * \brief Function for showing the error code and error which may have occured during authentication. + * + * \param error Error message shown on the console + */ + void showError(const QString &error); + + /** + * \brief Function for providing information. + * + * \param info Information to be shown to the user. + */ + void showInfo(const QString &info) const; + +private: + Agent::Session *m_authenticationSession; + bool m_agentRegistered; +}; + +#endif // EXTERNALCOMMAND_AUTHAGENT_H + diff --git a/src/util/externalcommand_authagent.cpp b/src/util/externalcommand_authagent.cpp new file mode 100644 --- /dev/null +++ b/src/util/externalcommand_authagent.cpp @@ -0,0 +1,171 @@ +/************************************************************************* + * Copyright (C) 2019 by Shubham * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 3 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see .* + *************************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "util/externalcommand_authagent.h" + +using namespace PolkitQt1; + +Listener::Listener(QObject *parent) + : Agent::Listener(parent), + m_agentRegistered(false) +{ + qDebug() << "Registration of Authentication agent started"; +} + +Listener::~Listener() +{ + delete m_authenticationSession; +} + +bool Listener::registerListener(const Subject &subject, const QString &locale, const QString &objectPath) +{ + qDebug() << "Registering Agent Listener"; + return (m_agentRegistered = Authority::instance()->registerAuthenticationAgentSync(subject, locale, objectPath)); +} + +bool Listener::unregisterListener(const Subject &subject, const QString &objectPath) +{ + qDebug() << "Unregistering Agent Listener"; + return (m_agentRegistered = Authority::instance()->unregisterAuthenticationAgentSync(subject, objectPath)); +} + +void Listener::initiateAuthentication(const QString &actionId, + const QString &message, + const QString &iconName, + const Details &details, + const QString &cookie, + const Identity::List &identities, + Agent::AsyncResult *result) +{ + qDebug() << "initiateAuthentication for " << actionId << " with message " << message; + qDebug() << "iconName " << iconName; + qDebug() << details.keys(); + qDebug() << "cookie" << cookie; + + if (m_agentRegistered) { + for (const Identity &identity : identities) { + qDebug() << identity.toString(); + + m_authenticationSession = new Agent::Session(identity, cookie, result); + + connect(m_authenticationSession, &Agent::Session::request, this, &Listener::request); + connect(m_authenticationSession, &Agent::Session::request, this, &Listener::setResponse); + connect(m_authenticationSession, &Agent::Session::completed, this, &Listener::completed); + connect(m_authenticationSession, &Agent::Session::showError, this, &Listener::showError); + connect(m_authenticationSession, &Agent::Session::showInfo, this, &Listener::showInfo); + + m_authenticationSession->initiate(); + result->setCompleted(); + } + } +} + +void Listener::cancelAuthentication() +{ + qDebug() << "Cancelling the Authentication session"; + m_authenticationSession->cancel(); +} + +Authority::Result Listener::checkAuthorization(const QString &actionId, + const Subject &subject, + Authority::AuthorizationFlags flag /*= Authority::AllowUserInteraction*/) const +{ + return Authority::instance()->checkAuthorizationSync(actionId, subject, flag); +} + +Authority::ErrorCode Listener::pollError() const +{ + // Clear existing errors from buffer + Authority::instance()->clearError(); + + if (Authority::instance()->hasError()) { + return Authority::instance()->lastError(); + } + + return Authority::E_None; +} + +bool Listener::userSuccessfullyAuthenticated(const QString &cookie, const Identity &identity) +{ + QSignalSpy spy(sender(), SIGNAL(Agent::Session::completed(bool))/*&Agent::Session::completed*/); + + // Store the first signal that was emmitted + QList arguments = spy.takeFirst(); + + // Check if completed() signal is called with true ie. User was successfully authenticated. + if (spy.isValid()) { + if (arguments.at(0).toBool() == true) { + return Authority::instance()->authenticationAgentResponseSync(cookie, identity); + } + } + + return false; +} + +void Listener::setResponse(const QString &response) +{ + m_authenticationSession = static_cast(sender()); + + // Here the user provides their password to proceed further + m_authenticationSession->setResponse(response); +} + +void Listener::request(const QString &request, bool echo /*= false*/) +{ + m_authenticationSession = static_cast(sender()); + + qDebug() << "An application is attempting to perform an action that requires esacalated privileges. Please enter your Password to proceed."; + + if (echo) { + qDebug() << "PASSWORD: " << request; + } +} + +void Listener::completed(bool gainedAuthorization /*= false*/) +{ + qDebug() << "Authorization gained: " << gainedAuthorization; + + m_authenticationSession = static_cast(sender()); + m_authenticationSession->result()->setCompleted(); +} + +void Listener::showError(const QString &error) +{ + qDebug() << "Error: " << error; + + qDebug() << "Last error code:" << pollError(); + qDebug() << "Last error which occured:" << Authority::instance()->errorDetails(); + + m_authenticationSession->result()->setError(error); + m_authenticationSession->result()->setCompleted(); +} + +void Listener::showInfo(const QString &info) const +{ + qDebug() << "Information: " << info; +}