diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -152,6 +152,10 @@ KF5::DBusAddons # KDEInitInterface ) +if (UNIX) + target_link_libraries(KF5KIOCore PRIVATE KF5::Auth) #SlaveBase uses KAuth::Action +endif() + if(ACL_FOUND) target_link_libraries(KF5KIOCore PRIVATE ${ACL_LIBS}) endif() diff --git a/src/core/job.cpp b/src/core/job.cpp --- a/src/core/job.cpp +++ b/src/core/job.cpp @@ -33,7 +33,6 @@ #include #include "slave.h" #include "scheduler.h" -#include "slavebase.h" using namespace KIO; @@ -263,97 +262,74 @@ return d_func()->m_outgoingMetaData; } -PrivilegeOperationStatus JobPrivate::tryAskPrivilegeOpConfirmation() +QByteArray JobPrivate::privilegeOperationData() { - if (m_confirmationAsked) { - return OperationAllowed; - } + PrivilegeOperationStatus status = OperationNotAllowed; + QByteArray jobData; if (m_parentJob) { - if (!m_parentJob->d_func()->m_privilegeExecutionEnabled) { - return OperationNotAllowed; - } + jobData = m_parentJob->d_func()->privilegeOperationData(); + // Copy meta-data from parent job + m_incomingMetaData.insert(QStringLiteral("TestData"), m_parentJob->queryMetaData(QStringLiteral("TestData"))); + return jobData; + } else { + if (m_privilegeExecutionEnabled) { + status = OperationAllowed; + switch (m_operationType) { + case ChangeAttr: + m_caption = i18n("Change Attribute"); + m_message = i18n("Root privileges are required to change file attributes. " + "Do you want to continue?"); + break; + case Copy: + m_caption = i18n("Copy Files"); + m_message = i18n("Root privileges are required to complete the copy operation. " + "Do you want to continue?"); + break; + case Delete: + m_caption = i18n("Delete Files"); + m_message = i18n("Root privileges are required to complete the delete operation. " + "However, doing so may damage your system. Do you want to continue?"); + break; + case MkDir: + m_caption = i18n("Create Folder"); + m_message = i18n("Root privileges are required to create this folder. " + "Do you want to continue?"); + break; + case Move: + m_caption = i18n("Move Items"); + m_message = i18n("Root privileges are required to complete the move operation. " + "Do you want to continue?"); + break; + case Rename: + m_caption = i18n("Rename"); + m_message = i18n("Root privileges are required to complete renaming. " + "Do you want to continue?"); + break; + case Symlink: + m_caption = i18n("Create Symlink"); + m_message = i18n("Root privileges are required to create a symlink. " + "Do you want to continue?"); + break; + case Transfer: + m_caption = i18n("Transfer data"); + m_message = i18n("Root privileges are required to complete transferring data. " + "Do you want to continue?"); + default: + break; + } - if (!m_parentJob->d_func()->m_confirmationAsked) { - PrivilegeOperationStatus opStatus = m_parentJob->d_func()->tryAskPrivilegeOpConfirmation(); - if (opStatus == OperationAllowed) { - // Copy meta-data from parent job - m_incomingMetaData.insert(QStringLiteral("TestData"), m_parentJob->queryMetaData(QStringLiteral("TestData"))); - m_confirmationAsked = true; + if (m_outgoingMetaData.value(QStringLiteral("UnitTesting")) == QLatin1String("true")) { + // Set meta-data for the top-level job + m_incomingMetaData.insert(QStringLiteral("TestData"), QStringLiteral("PrivilegeOperationAllowed")); } - return opStatus; - } else { - return OperationAllowed; - } - } else { - // In case of SimpleJob like chmod, chown, etc. which don't accept JobFlags - if (!m_privilegeExecutionEnabled) { - return OperationNotAllowed; } } - switch (m_operationType) { - case ChangeAttr: - m_caption = i18n("Change Attribute"); - m_message = i18n("Root privileges are required to change file attributes. " - "Do you want to continue?"); - break; - case Copy: - m_caption = i18n("Copy Files"); - m_message = i18n("Root privileges are required to complete the copy operation. " - "Do you want to continue?"); - break; - case Delete: - m_caption = i18n("Delete Files"); - m_message = i18n("Root privileges are required to complete the delete operation. " - "However, doing so may damage your system. Do you want to continue?"); - break; - case MkDir: - m_caption = i18n("Create Folder"); - m_message = i18n("Root privileges are required to create this folder. " - "Do you want to continue?"); - break; - case Move: - m_caption = i18n("Move Items"); - m_message = i18n("Root privileges are required to complete the move operation. " - "Do you want to continue?"); - break; - case Rename: - m_caption = i18n("Rename"); - m_message = i18n("Root privileges are required to complete renaming. " - "Do you want to continue?"); - break; - case Symlink: - m_caption = i18n("Create Symlink"); - m_message = i18n("Root privileges are required to create a symlink. " - "Do you want to continue?"); - break; - case Transfer: - m_caption = i18n("Transfer data"); - m_message = i18n("Root privileges are required to complete transferring data. " - "Do you want to continue?"); - default: - break; - } + QDataStream ds(&jobData, QIODevice::WriteOnly); + ds << status << m_caption << m_message; - if (m_outgoingMetaData.value(QStringLiteral("UnitTesting")) == QLatin1String("true")) { - // Set meta-data for the top-level job - m_incomingMetaData.insert(QStringLiteral("TestData"), QStringLiteral("PrivilegeOperationAllowed")); - return OperationAllowed; - } - - if (!m_uiDelegateExtension) { - return OperationNotAllowed; - } - - int status = m_uiDelegateExtension->requestMessageBox(JobUiDelegateExtension::WarningContinueCancel, - m_message, m_caption, i18n("Continue"), i18n("Cancel")); - m_confirmationAsked = true; - - if (status == SlaveBase::Cancel) { - return OperationCanceled; - } - return OperationAllowed; + return jobData; } ////////////////////////// diff --git a/src/core/job_p.h b/src/core/job_p.h --- a/src/core/job_p.h +++ b/src/core/job_p.h @@ -49,7 +49,7 @@ JobPrivate() : m_parentJob(nullptr), m_extraFlags(0), m_uiDelegateExtension(KIO::defaultJobUiDelegateExtension()), - m_privilegeExecutionEnabled(false), m_confirmationAsked(false) + m_privilegeExecutionEnabled(false) { } @@ -89,11 +89,10 @@ Job *q_ptr; // For privilege operation bool m_privilegeExecutionEnabled; - bool m_confirmationAsked; QString m_caption, m_message; FileOperationType m_operationType; - PrivilegeOperationStatus tryAskPrivilegeOpConfirmation(); + QByteArray privilegeOperationData(); void slotSpeed(KJob *job, unsigned long speed); static void emitMoving(KIO::Job *, const QUrl &src, const QUrl &dest); diff --git a/src/core/simplejob.cpp b/src/core/simplejob.cpp --- a/src/core/simplejob.cpp +++ b/src/core/simplejob.cpp @@ -338,7 +338,7 @@ void SimpleJobPrivate::slotPrivilegeOperationRequested() { - m_slave->send(MSG_PRIVILEGE_EXEC, QByteArray::number(tryAskPrivilegeOpConfirmation())); + m_slave->send(MSG_PRIVILEGE_EXEC, privilegeOperationData()); } ////////// diff --git a/src/core/slavebase.h b/src/core/slavebase.h --- a/src/core/slavebase.h +++ b/src/core/slavebase.h @@ -943,6 +943,15 @@ */ PrivilegeOperationStatus requestPrivilegeOperation(); + /** + * Adds @p action to the list of PolicyKit actions which the + * slave is authorized to perform. + * + * @param action the PolicyKit action + * @since 5.45 + */ + void addTemporaryAuthorization(const QString &action); + protected: /** * Name of the protocol supported by this slave diff --git a/src/core/slavebase.cpp b/src/core/slavebase.cpp --- a/src/core/slavebase.cpp +++ b/src/core/slavebase.cpp @@ -59,6 +59,10 @@ #endif #endif +#ifdef Q_OS_UNIX +#include +#endif + extern "C" { Q_NORETURN static void sigsegv_handler(int sig); static void sigpipe_handler(int sig); @@ -80,7 +84,8 @@ { public: SlaveBase *q; - SlaveBasePrivate(SlaveBase *owner): q(owner), nextTimeoutMsecs(0), m_passwdServerClient(nullptr) + SlaveBasePrivate(SlaveBase *owner): q(owner), nextTimeoutMsecs(0), m_passwdServerClient(nullptr), + m_confirmationAsked(false), m_privilegeOperationStatus(OperationNotAllowed) { if (!qEnvironmentVariableIsEmpty("KIOSLAVE_ENABLE_TESTMODE")) { QStandardPaths::enableTestMode(true); @@ -121,6 +126,43 @@ KPasswdServerClient *m_passwdServerClient; bool m_rootEntryListed = false; + QSet m_TempAuth; + QString m_warningCaption; + QString m_warningMessage; + bool m_confirmationAsked; + int m_privilegeOperationStatus; + + PrivilegeOperationStatus askConfirmation() + { + if (!m_confirmationAsked) { + int status = q->messageBox(SlaveBase::WarningContinueCancel, m_warningMessage, m_warningCaption, QStringLiteral("Continue"), QStringLiteral("Cancel")); + if (status == SlaveBase::Cancel) { + return OperationCanceled; + } + m_confirmationAsked = true; + } + return OperationAllowed; + } + + bool hasTempAuth(bool revoke = false) + { +#ifdef Q_OS_UNIX + foreach (const QString &actId, m_TempAuth) { + KAuth::Action action(actId); + if (revoke) { + action.revokeAuthorization(); + } + if (action.status() != KAuth::Action::AuthorizedStatus) { + m_TempAuth.remove(actId); + } + } + return !m_TempAuth.isEmpty(); +#else + return false; +#endif + + } + // Reconstructs configGroup from configData and mIncomingMetaData void rebuildConfig() { @@ -452,6 +494,8 @@ //reset d->totalSize = 0; d->inOpenLoop = false; + d->m_confirmationAsked = false; + d->m_privilegeOperationStatus = OperationNotAllowed; } void SlaveBase::connected() @@ -494,6 +538,8 @@ d->totalSize = 0; d->inOpenLoop = false; d->m_rootEntryListed = false; + d->m_confirmationAsked = false; + d->m_privilegeOperationStatus = OperationNotAllowed; } void SlaveBase::needSubUrlData() @@ -505,7 +551,7 @@ { qint64 pid = getpid(); qint8 b = connected ? 1 : 0; - KIO_DATA << pid << mProtocol << host << b; + KIO_DATA << pid << mProtocol << host << b << d->hasTempAuth(true); if (d->onHold) { stream << d->onHoldUrl; } @@ -1461,8 +1507,22 @@ PrivilegeOperationStatus SlaveBase::requestPrivilegeOperation() { - QByteArray buffer; - send(MSG_PRIVILEGE_EXEC); - waitForAnswer(MSG_PRIVILEGE_EXEC, 0, buffer); - return KIO::PrivilegeOperationStatus(buffer.toInt()); + if (d->m_privilegeOperationStatus == OperationNotAllowed) { + QByteArray buffer; + send(MSG_PRIVILEGE_EXEC); + waitForAnswer(MSG_PRIVILEGE_EXEC, 0, buffer); + QDataStream ds(buffer); + ds >> d->m_privilegeOperationStatus >> d->m_warningCaption >> d-> m_warningMessage; + } + + if (d->m_privilegeOperationStatus == OperationAllowed) { + d->m_privilegeOperationStatus = d->askConfirmation(); + } + + return KIO::PrivilegeOperationStatus(d->m_privilegeOperationStatus); +} + +void SlaveBase::addTemporaryAuthorization(const QString &action) +{ + d->m_TempAuth.insert(action); }