Advance Wayland and KDE package bring-up
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Nicola Gigante <nicola.gigante@gmail.com>
|
||||
SPDX-FileCopyrightText: 2009-2010 Dario Freddi <drf@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include "DBusHelperProxy.h"
|
||||
#include "BackendsManager.h"
|
||||
#include "kauthdebug.h"
|
||||
#include "kf6authadaptor.h"
|
||||
|
||||
#include <QDBusConnectionInterface>
|
||||
#include <QDBusMessage>
|
||||
#include <QDBusMetaType>
|
||||
#include <QDBusUnixFileDescriptor>
|
||||
#include <QMap>
|
||||
#include <QMetaMethod>
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
#include <qplugin.h>
|
||||
|
||||
extern Q_CORE_EXPORT const QtPrivate::QMetaTypeInterface *qMetaTypeGuiHelper;
|
||||
|
||||
namespace KAuth
|
||||
{
|
||||
static void debugMessageReceived(int t, const QString &message);
|
||||
|
||||
DBusHelperProxy::DBusHelperProxy()
|
||||
: responder(nullptr)
|
||||
, m_stopRequest(false)
|
||||
, m_busConnection(QDBusConnection::systemBus())
|
||||
{
|
||||
qDBusRegisterMetaType<QMap<QString, QDBusUnixFileDescriptor>>();
|
||||
}
|
||||
|
||||
DBusHelperProxy::DBusHelperProxy(const QDBusConnection &busConnection)
|
||||
: responder(nullptr)
|
||||
, m_stopRequest(false)
|
||||
, m_busConnection(busConnection)
|
||||
{
|
||||
qDBusRegisterMetaType<QMap<QString, QDBusUnixFileDescriptor>>();
|
||||
}
|
||||
|
||||
DBusHelperProxy::~DBusHelperProxy()
|
||||
{
|
||||
}
|
||||
|
||||
void DBusHelperProxy::stopAction(const QString &action, const QString &helperID)
|
||||
{
|
||||
QDBusMessage message;
|
||||
message = QDBusMessage::createMethodCall(helperID, QLatin1String("/"), QLatin1String("org.kde.kf6auth"), QLatin1String("stopAction"));
|
||||
|
||||
QList<QVariant> args;
|
||||
args << action;
|
||||
message.setArguments(args);
|
||||
|
||||
m_busConnection.asyncCall(message);
|
||||
}
|
||||
|
||||
void DBusHelperProxy::executeAction(const QString &action, const QString &helperID, const DetailsMap &details, const QVariantMap &arguments, int timeout)
|
||||
{
|
||||
QMap<QString, QDBusUnixFileDescriptor> fds;
|
||||
QVariantMap nonFds;
|
||||
for (auto [key, value] : arguments.asKeyValueRange()) {
|
||||
if (value.metaType() == QMetaType::fromType<QDBusUnixFileDescriptor>()) {
|
||||
fds.insert(key, value.value<QDBusUnixFileDescriptor>());
|
||||
} else {
|
||||
nonFds.insert(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray blob;
|
||||
{
|
||||
QDataStream stream(&blob, QIODevice::WriteOnly);
|
||||
stream << nonFds;
|
||||
}
|
||||
|
||||
// on unit tests we won't have a service, but the service will already be running
|
||||
const auto reply = m_busConnection.interface()->startService(helperID);
|
||||
if (!reply.isValid() && !m_busConnection.interface()->isServiceRegistered(helperID)) {
|
||||
ActionReply errorReply = ActionReply::DBusErrorReply();
|
||||
errorReply.setErrorDescription(tr("DBus Backend error: service start %1 failed: %2").arg(helperID, reply.error().message()));
|
||||
Q_EMIT actionPerformed(action, errorReply);
|
||||
return;
|
||||
}
|
||||
|
||||
const bool connected = m_busConnection.connect(helperID,
|
||||
QLatin1String("/"),
|
||||
QLatin1String("org.kde.kf6auth"),
|
||||
QLatin1String("remoteSignal"),
|
||||
this,
|
||||
SLOT(remoteSignalReceived(int, QString, QByteArray)));
|
||||
|
||||
// if already connected reply will be false but we won't have an error or a reason to fail
|
||||
if (!connected && m_busConnection.lastError().isValid()) {
|
||||
ActionReply errorReply = ActionReply::DBusErrorReply();
|
||||
errorReply.setErrorDescription(tr("DBus Backend error: connection to helper failed. %1\n(application: %2 helper: %3)")
|
||||
.arg(m_busConnection.lastError().message(), qApp->applicationName(), helperID));
|
||||
Q_EMIT actionPerformed(action, errorReply);
|
||||
return;
|
||||
}
|
||||
|
||||
QDBusMessage message;
|
||||
message = QDBusMessage::createMethodCall(helperID, QLatin1String("/"), QLatin1String("org.kde.kf6auth"), QLatin1String("performAction"));
|
||||
|
||||
QList<QVariant> args;
|
||||
args << action << BackendsManager::authBackend()->callerID() << BackendsManager::authBackend()->backendDetails(details) << blob << QVariant::fromValue(fds);
|
||||
message.setArguments(args);
|
||||
|
||||
m_actionsInProgress.push_back(action);
|
||||
|
||||
QDBusPendingCall pendingCall = m_busConnection.asyncCall(message, timeout);
|
||||
|
||||
auto watcher = new QDBusPendingCallWatcher(pendingCall, this);
|
||||
|
||||
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, action, args, message, watcher, timeout]() mutable {
|
||||
watcher->deleteLater();
|
||||
|
||||
QDBusMessage reply = watcher->reply();
|
||||
|
||||
if (reply.type() == QDBusMessage::ErrorMessage) {
|
||||
if (watcher->error().type() == QDBusError::InvalidArgs) {
|
||||
// For backwards compatibility if helper binary was built with older KAuth version.
|
||||
args.removeAt(args.count() - 2); // remove backend details
|
||||
message.setArguments(args);
|
||||
reply = m_busConnection.call(message, QDBus::Block, timeout);
|
||||
if (reply.type() != QDBusMessage::ErrorMessage) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
ActionReply r = ActionReply::DBusErrorReply();
|
||||
r.setErrorDescription(tr("DBus Backend error: could not contact the helper. "
|
||||
"Connection error: %1. Message error: %2")
|
||||
.arg(reply.errorMessage(), m_busConnection.lastError().message()));
|
||||
qCWarning(KAUTH) << reply.errorMessage();
|
||||
|
||||
Q_EMIT actionPerformed(action, r);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool DBusHelperProxy::initHelper(const QString &name)
|
||||
{
|
||||
new Kf6authAdaptor(this);
|
||||
|
||||
if (!m_busConnection.registerService(name)) {
|
||||
qCWarning(KAUTH) << "Error registering helper DBus service" << name << m_busConnection.lastError().message();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_busConnection.registerObject(QLatin1String("/"), this)) {
|
||||
qCWarning(KAUTH) << "Error registering helper DBus object:" << m_busConnection.lastError().message();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_name = name;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DBusHelperProxy::setHelperResponder(QObject *o)
|
||||
{
|
||||
responder = o;
|
||||
}
|
||||
|
||||
void DBusHelperProxy::remoteSignalReceived(int t, const QString &action, QByteArray blob)
|
||||
{
|
||||
SignalType type = static_cast<SignalType>(t);
|
||||
QDataStream stream(&blob, QIODevice::ReadOnly);
|
||||
|
||||
if (type == ActionStarted) {
|
||||
Q_EMIT actionStarted(action);
|
||||
} else if (type == ActionPerformed) {
|
||||
ActionReply reply = ActionReply::deserialize(blob);
|
||||
|
||||
m_actionsInProgress.removeOne(action);
|
||||
Q_EMIT actionPerformed(action, reply);
|
||||
} else if (type == DebugMessage) {
|
||||
int level;
|
||||
QString message;
|
||||
|
||||
stream >> level >> message;
|
||||
|
||||
debugMessageReceived(level, message);
|
||||
} else if (type == ProgressStepIndicator) {
|
||||
int step;
|
||||
stream >> step;
|
||||
|
||||
Q_EMIT progressStep(action, step);
|
||||
} else if (type == ProgressStepData) {
|
||||
QVariantMap data;
|
||||
stream >> data;
|
||||
Q_EMIT progressStepData(action, data);
|
||||
}
|
||||
}
|
||||
|
||||
void DBusHelperProxy::stopAction(const QString &action)
|
||||
{
|
||||
Q_UNUSED(action)
|
||||
//#warning FIXME: The stop request should be action-specific rather than global
|
||||
m_stopRequest = true;
|
||||
}
|
||||
|
||||
bool DBusHelperProxy::hasToStopAction()
|
||||
{
|
||||
QEventLoop loop;
|
||||
loop.processEvents(QEventLoop::AllEvents);
|
||||
|
||||
return m_stopRequest;
|
||||
}
|
||||
|
||||
bool DBusHelperProxy::isCallerAuthorized(const QString &action, const QByteArray &callerID, const QVariantMap &details)
|
||||
{
|
||||
Q_UNUSED(callerID); // this only exists for the benefit of the mac backend. We obtain our callerID from dbus!
|
||||
return BackendsManager::authBackend()->isCallerAuthorized(action, message().service().toUtf8(), details);
|
||||
}
|
||||
|
||||
QByteArray DBusHelperProxy::performAction(const QString &action,
|
||||
const QByteArray &callerID,
|
||||
const QVariantMap &details,
|
||||
QByteArray arguments,
|
||||
const QMap<QString, QDBusUnixFileDescriptor> &fdArguments)
|
||||
{
|
||||
if (!responder) {
|
||||
return ActionReply::NoResponderReply().serialized();
|
||||
}
|
||||
|
||||
if (!m_currentAction.isEmpty()) {
|
||||
return ActionReply::HelperBusyReply().serialized();
|
||||
}
|
||||
|
||||
// Make sure we don't try restoring gui variants, in particular QImage/QPixmap/QIcon are super dangerous
|
||||
// since they end up calling the image loaders and thus are a vector for crashing → executing code
|
||||
auto origMetaTypeGuiHelper = qMetaTypeGuiHelper;
|
||||
qMetaTypeGuiHelper = nullptr;
|
||||
|
||||
QVariantMap args;
|
||||
QDataStream s(&arguments, QIODevice::ReadOnly);
|
||||
s >> args;
|
||||
|
||||
for (auto [key, value] : fdArguments.asKeyValueRange()) {
|
||||
args.insert(key, QVariant::fromValue(value));
|
||||
}
|
||||
|
||||
qMetaTypeGuiHelper = origMetaTypeGuiHelper;
|
||||
|
||||
m_currentAction = action;
|
||||
Q_EMIT remoteSignal(ActionStarted, action, QByteArray());
|
||||
QEventLoop e;
|
||||
e.processEvents(QEventLoop::AllEvents);
|
||||
|
||||
ActionReply retVal;
|
||||
|
||||
QTimer *timer = responder->property("__KAuth_Helper_Shutdown_Timer").value<QTimer *>();
|
||||
timer->stop();
|
||||
|
||||
if (isCallerAuthorized(action, callerID, details)) {
|
||||
QString slotname = action;
|
||||
if (slotname.startsWith(m_name + QLatin1Char('.'))) {
|
||||
slotname = slotname.right(slotname.length() - m_name.length() - 1);
|
||||
}
|
||||
|
||||
slotname.replace(QLatin1Char('.'), QLatin1Char('_'));
|
||||
|
||||
// For legacy reasons we could be dealing with ActionReply types (i.e.
|
||||
// `using namespace KAuth`). Since Qt type names are verbatim this would
|
||||
// mismatch a return type that is called 'KAuth::ActionReply' and
|
||||
// vice versa. This effectively required client code to always 'use' the
|
||||
// namespace as otherwise we'd not be able to call into it.
|
||||
// To support both scenarios we now dynamically determine what kind of return type
|
||||
// we deal with and call Q_RETURN_ARG either with or without namespace.
|
||||
const auto metaObj = responder->metaObject();
|
||||
const QString slotSignature(slotname + QStringLiteral("(QVariantMap)"));
|
||||
const QMetaMethod method = metaObj->method(metaObj->indexOfMethod(qPrintable(slotSignature)));
|
||||
if (method.isValid()) {
|
||||
const auto needle = "KAuth::";
|
||||
bool success = false;
|
||||
if (strncmp(needle, method.typeName(), strlen(needle)) == 0) {
|
||||
success = method.invoke(responder, Qt::DirectConnection, Q_RETURN_ARG(KAuth::ActionReply, retVal), Q_ARG(QVariantMap, args));
|
||||
} else {
|
||||
success = method.invoke(responder, Qt::DirectConnection, Q_RETURN_ARG(ActionReply, retVal), Q_ARG(QVariantMap, args));
|
||||
}
|
||||
if (!success) {
|
||||
retVal = ActionReply::NoSuchActionReply();
|
||||
}
|
||||
} else {
|
||||
retVal = ActionReply::NoSuchActionReply();
|
||||
}
|
||||
} else {
|
||||
retVal = ActionReply::AuthorizationDeniedReply();
|
||||
}
|
||||
|
||||
timer->start();
|
||||
|
||||
Q_EMIT remoteSignal(ActionPerformed, action, retVal.serialized());
|
||||
e.processEvents(QEventLoop::AllEvents);
|
||||
m_currentAction.clear();
|
||||
m_stopRequest = false;
|
||||
|
||||
return retVal.serialized();
|
||||
}
|
||||
|
||||
void DBusHelperProxy::sendDebugMessage(int level, const char *msg)
|
||||
{
|
||||
QByteArray blob;
|
||||
QDataStream stream(&blob, QIODevice::WriteOnly);
|
||||
|
||||
stream << level << QString::fromLocal8Bit(msg);
|
||||
|
||||
Q_EMIT remoteSignal(DebugMessage, m_currentAction, blob);
|
||||
}
|
||||
|
||||
void DBusHelperProxy::sendProgressStep(int step)
|
||||
{
|
||||
QByteArray blob;
|
||||
QDataStream stream(&blob, QIODevice::WriteOnly);
|
||||
|
||||
stream << step;
|
||||
|
||||
Q_EMIT remoteSignal(ProgressStepIndicator, m_currentAction, blob);
|
||||
}
|
||||
|
||||
void DBusHelperProxy::sendProgressStepData(const QVariantMap &data)
|
||||
{
|
||||
QByteArray blob;
|
||||
QDataStream stream(&blob, QIODevice::WriteOnly);
|
||||
|
||||
stream << data;
|
||||
|
||||
Q_EMIT remoteSignal(ProgressStepData, m_currentAction, blob);
|
||||
}
|
||||
|
||||
void debugMessageReceived(int t, const QString &message)
|
||||
{
|
||||
QtMsgType type = static_cast<QtMsgType>(t);
|
||||
switch (type) {
|
||||
case QtDebugMsg:
|
||||
qDebug("Debug message from helper: %s", message.toLatin1().data());
|
||||
break;
|
||||
case QtInfoMsg:
|
||||
qInfo("Info message from helper: %s", message.toLatin1().data());
|
||||
break;
|
||||
case QtWarningMsg:
|
||||
qWarning("Warning from helper: %s", message.toLatin1().data());
|
||||
break;
|
||||
case QtCriticalMsg:
|
||||
qCritical("Critical warning from helper: %s", message.toLatin1().data());
|
||||
break;
|
||||
case QtFatalMsg:
|
||||
qFatal("Fatal error from helper: %s", message.toLatin1().data());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int DBusHelperProxy::callerUid() const
|
||||
{
|
||||
QDBusConnectionInterface *iface = connection().interface();
|
||||
if (!iface) {
|
||||
return -1;
|
||||
}
|
||||
return iface->serviceUid(message().service());
|
||||
}
|
||||
|
||||
} // namespace KAuth
|
||||
|
||||
#include "moc_DBusHelperProxy.cpp"
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Nicola Gigante <nicola.gigante@gmail.com>
|
||||
SPDX-FileCopyrightText: 2009 Dario Freddi <drf@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#ifndef DBUS_HELPER_PROXY_H
|
||||
#define DBUS_HELPER_PROXY_H
|
||||
|
||||
#include "HelperProxy.h"
|
||||
#include "actionreply.h"
|
||||
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusContext>
|
||||
#include <QDBusUnixFileDescriptor>
|
||||
#include <QVariant>
|
||||
|
||||
namespace KAuth
|
||||
{
|
||||
class DBusHelperProxy : public HelperProxy, protected QDBusContext
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID "org.kde.DBusHelperProxy")
|
||||
Q_INTERFACES(KAuth::HelperProxy)
|
||||
|
||||
QObject *responder;
|
||||
QString m_name;
|
||||
QString m_currentAction;
|
||||
bool m_stopRequest;
|
||||
QList<QString> m_actionsInProgress;
|
||||
QDBusConnection m_busConnection;
|
||||
|
||||
enum SignalType {
|
||||
ActionStarted, // The blob argument is empty
|
||||
ActionPerformed, // The blob argument contains the ActionReply
|
||||
DebugMessage, // The blob argument contains the debug level and the message (in this order)
|
||||
ProgressStepIndicator, // The blob argument contains the step indicator
|
||||
ProgressStepData, // The blob argument contains the QVariantMap
|
||||
};
|
||||
|
||||
public:
|
||||
DBusHelperProxy();
|
||||
DBusHelperProxy(const QDBusConnection &busConnection);
|
||||
|
||||
~DBusHelperProxy() override;
|
||||
|
||||
virtual void
|
||||
executeAction(const QString &action, const QString &helperID, const DetailsMap &details, const QVariantMap &arguments, int timeout = -1) override;
|
||||
void stopAction(const QString &action, const QString &helperID) override;
|
||||
|
||||
bool initHelper(const QString &name) override;
|
||||
void setHelperResponder(QObject *o) override;
|
||||
bool hasToStopAction() override;
|
||||
void sendDebugMessage(int level, const char *msg) override;
|
||||
void sendProgressStep(int step) override;
|
||||
void sendProgressStepData(const QVariantMap &data) override;
|
||||
|
||||
int callerUid() const override;
|
||||
|
||||
public Q_SLOTS:
|
||||
void stopAction(const QString &action);
|
||||
QByteArray performAction(const QString &action,
|
||||
const QByteArray &callerID,
|
||||
const QVariantMap &details,
|
||||
QByteArray arguments,
|
||||
const QMap<QString, QDBusUnixFileDescriptor> &fdArguments);
|
||||
|
||||
Q_SIGNALS:
|
||||
void remoteSignal(int type, const QString &action, const QByteArray &blob); // This signal is sent from the helper to the app
|
||||
|
||||
private Q_SLOTS:
|
||||
void remoteSignalReceived(int type, const QString &action, QByteArray blob);
|
||||
|
||||
private:
|
||||
bool isCallerAuthorized(const QString &action, const QByteArray &callerID, const QVariantMap &details);
|
||||
};
|
||||
|
||||
} // namespace Auth
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE busconfig PUBLIC
|
||||
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
||||
<busconfig>
|
||||
|
||||
<!-- Only user root can own the foo helper -->
|
||||
<policy user="@HELPER_USER@">
|
||||
<allow own="@HELPER_ID@"/>
|
||||
</policy>
|
||||
|
||||
<policy context="default">
|
||||
<allow send_destination="@HELPER_ID@"/>
|
||||
</policy>
|
||||
|
||||
</busconfig>
|
||||
@@ -0,0 +1,4 @@
|
||||
[D-BUS Service]
|
||||
Name=@HELPER_ID@
|
||||
Exec=@KAUTH_HELPER_INSTALL_ABSOLUTE_DIR@/@HELPER_TARGET@
|
||||
User=@HELPER_USER@
|
||||
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE busconfig PUBLIC
|
||||
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
||||
<busconfig>
|
||||
|
||||
<!-- Allow anyone to call into the service - we'll reject callers using Polkit -->
|
||||
<policy context="default">
|
||||
<allow send_interface="org.kde.kf6auth"/>
|
||||
<allow receive_sender="org.kde.kf6auth"/>
|
||||
<allow receive_interface="org.kde.kf6auth"/>
|
||||
</policy>
|
||||
|
||||
</busconfig>
|
||||
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
<node>
|
||||
<interface name="org.kde.kf6auth">
|
||||
<method name="performAction" >
|
||||
<arg name="action" type="s" direction="in" />
|
||||
<arg name="callerID" type="ay" direction="in" />
|
||||
<arg name="details" type="a{sv}" direction="in" />
|
||||
<arg name="arguments" type="ay" direction="in" />
|
||||
<arg name="fdArguments" type="a{sh}" direction="in" />
|
||||
<arg name="r" type="ay" direction="out" />
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In2" value="QVariantMap"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In4" value="QMap<QString,QDBusUnixFileDescriptor>"/>
|
||||
</method>
|
||||
<method name="stopAction" >
|
||||
<arg name="action" type="s" direction="in" />
|
||||
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
|
||||
</method>
|
||||
<signal name="remoteSignal" >
|
||||
<arg name="type" type="i" />
|
||||
<arg name="action" type="s" />
|
||||
<arg name="blob" type="ay" />
|
||||
</signal>
|
||||
</interface>
|
||||
</node>
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Nicola Gigante <nicola.gigante@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include "FakeBackend.h"
|
||||
|
||||
namespace KAuth
|
||||
{
|
||||
FakeBackend::FakeBackend()
|
||||
: AuthBackend()
|
||||
{
|
||||
setCapabilities(NoCapability);
|
||||
}
|
||||
|
||||
Action::AuthStatus FakeBackend::authorizeAction(const QString &action)
|
||||
{
|
||||
Q_UNUSED(action)
|
||||
return Action::DeniedStatus;
|
||||
}
|
||||
|
||||
void FakeBackend::setupAction(const QString &action)
|
||||
{
|
||||
Q_UNUSED(action)
|
||||
}
|
||||
|
||||
Action::AuthStatus FakeBackend::actionStatus(const QString &action)
|
||||
{
|
||||
Q_UNUSED(action)
|
||||
return Action::DeniedStatus;
|
||||
}
|
||||
|
||||
QByteArray FakeBackend::callerID() const
|
||||
{
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
bool FakeBackend::isCallerAuthorized(const QString &action, const QByteArray &callerID, const QVariantMap &details)
|
||||
{
|
||||
Q_UNUSED(action)
|
||||
Q_UNUSED(callerID)
|
||||
Q_UNUSED(details)
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Auth
|
||||
|
||||
#include "moc_FakeBackend.cpp"
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Nicola Gigante <nicola.gigante@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#ifndef FAKE_BACKEND_H
|
||||
#define FAKE_BACKEND_H
|
||||
|
||||
#include "AuthBackend.h"
|
||||
#include <QHash>
|
||||
|
||||
class QByteArray;
|
||||
|
||||
namespace KAuth
|
||||
{
|
||||
class FakeBackend : public AuthBackend
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(KAuth::AuthBackend)
|
||||
|
||||
public:
|
||||
FakeBackend();
|
||||
void setupAction(const QString &) override;
|
||||
Action::AuthStatus authorizeAction(const QString &) override;
|
||||
Action::AuthStatus actionStatus(const QString &) override;
|
||||
QByteArray callerID() const override;
|
||||
bool isCallerAuthorized(const QString &action, const QByteArray &callerID, const QVariantMap &details) override;
|
||||
};
|
||||
|
||||
} // namespace Auth
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Nicola Gigante <nicola.gigante@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include <auth/policy-gen/policy-gen.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTextStream>
|
||||
#include <cstdio>
|
||||
|
||||
const char header[] =
|
||||
""
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
|
||||
"<!DOCTYPE policyconfig PUBLIC\n"
|
||||
"\"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN\"\n"
|
||||
"\"http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd\">\n"
|
||||
"<policyconfig>\n";
|
||||
|
||||
const char policy_tag[] =
|
||||
""
|
||||
" <defaults>\n"
|
||||
" <allow_inactive>no</allow_inactive>\n"
|
||||
" <allow_active>%1</allow_active>\n"
|
||||
" </defaults>\n";
|
||||
|
||||
const char dent[] = " ";
|
||||
|
||||
void output(const QList<Action> &actions, const QMap<QString, QString> &domain)
|
||||
{
|
||||
Q_UNUSED(domain)
|
||||
|
||||
QTextStream out(stdout);
|
||||
out << header;
|
||||
|
||||
for (const Action &action : std::as_const(actions)) {
|
||||
out << dent << "<action id=\"" << action.name << "\" >\n";
|
||||
|
||||
const auto lstKeys = action.descriptions.keys();
|
||||
for (const QString &lang : lstKeys) {
|
||||
out << dent << dent << "<description";
|
||||
if (lang != "en") {
|
||||
out << " xml:lang=\"" << lang << '"';
|
||||
}
|
||||
out << '>' << action.messages.value(lang) << "</description>\n";
|
||||
}
|
||||
|
||||
const auto lstMessagesKeys = action.messages.keys();
|
||||
for (const QString &lang : lstMessagesKeys) {
|
||||
out << dent << dent << "<message";
|
||||
if (lang != "en") {
|
||||
out << " xml:lang=\"" << lang << '"';
|
||||
}
|
||||
out << '>' << action.descriptions.value(lang) << "</message>\n";
|
||||
}
|
||||
|
||||
QString policy = action.policy;
|
||||
if (!action.persistence.isEmpty()) {
|
||||
policy += "_keep_" + action.persistence;
|
||||
}
|
||||
|
||||
out << QString(policy_tag).arg(policy);
|
||||
|
||||
out << dent << "</action>\n";
|
||||
}
|
||||
|
||||
out << "</policyconfig>\n";
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2010 Dario Freddi <drf@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include "FakeHelperProxy.h"
|
||||
|
||||
namespace KAuth
|
||||
{
|
||||
FakeHelperProxy::FakeHelperProxy()
|
||||
: HelperProxy()
|
||||
{
|
||||
}
|
||||
|
||||
FakeHelperProxy::~FakeHelperProxy()
|
||||
{
|
||||
}
|
||||
|
||||
void FakeHelperProxy::sendProgressStepData(const QVariantMap &step)
|
||||
{
|
||||
Q_UNUSED(step)
|
||||
}
|
||||
|
||||
void FakeHelperProxy::sendProgressStep(int step)
|
||||
{
|
||||
Q_UNUSED(step)
|
||||
}
|
||||
|
||||
void FakeHelperProxy::sendDebugMessage(int level, const char *msg)
|
||||
{
|
||||
Q_UNUSED(level)
|
||||
Q_UNUSED(msg)
|
||||
}
|
||||
|
||||
bool FakeHelperProxy::hasToStopAction()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void FakeHelperProxy::setHelperResponder(QObject *o)
|
||||
{
|
||||
Q_UNUSED(o)
|
||||
}
|
||||
|
||||
bool FakeHelperProxy::initHelper(const QString &name)
|
||||
{
|
||||
Q_UNUSED(name)
|
||||
return false;
|
||||
}
|
||||
|
||||
void FakeHelperProxy::stopAction(const QString &action, const QString &helperID)
|
||||
{
|
||||
Q_UNUSED(action)
|
||||
Q_UNUSED(helperID)
|
||||
}
|
||||
|
||||
void FakeHelperProxy::executeAction(const QString &action, const QString &helperID, const DetailsMap &details, const QVariantMap &arguments, int timeout)
|
||||
{
|
||||
Q_UNUSED(helperID)
|
||||
Q_UNUSED(details)
|
||||
Q_UNUSED(arguments)
|
||||
Q_UNUSED(timeout)
|
||||
Q_EMIT actionPerformed(action, KAuth::ActionReply::NoSuchActionReply());
|
||||
}
|
||||
|
||||
int FakeHelperProxy::callerUid() const
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "moc_FakeHelperProxy.cpp"
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2010 Dario Freddi <drf@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#ifndef FAKEHELPERPROXY_H
|
||||
#define FAKEHELPERPROXY_H
|
||||
|
||||
#include "HelperProxy.h"
|
||||
|
||||
namespace KAuth
|
||||
{
|
||||
class FakeHelperProxy : public HelperProxy
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(KAuth::HelperProxy)
|
||||
|
||||
public:
|
||||
FakeHelperProxy();
|
||||
~FakeHelperProxy() override;
|
||||
|
||||
void sendProgressStepData(const QVariantMap &step) override;
|
||||
void sendProgressStep(int step) override;
|
||||
void sendDebugMessage(int level, const char *msg) override;
|
||||
bool hasToStopAction() override;
|
||||
void setHelperResponder(QObject *o) override;
|
||||
bool initHelper(const QString &name) override;
|
||||
void stopAction(const QString &action, const QString &helperID) override;
|
||||
void executeAction(const QString &action, const QString &helperID, const DetailsMap &details, const QVariantMap &arguments, int timeout = -1) override;
|
||||
int callerUid() const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FAKEHELPERPROXY_H
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Nicola Gigante <nicola.gigante@gmail.com>
|
||||
SPDX-FileCopyrightText: 2014, 2016 René Bertin <rjvbertin@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include "AuthServicesBackend.h"
|
||||
|
||||
#include <qplugin.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(KAUTH_OSX)
|
||||
// logging category for this backend, default: log stuff >= warning
|
||||
Q_LOGGING_CATEGORY(KAUTH_OSX, "kf.auth.apple", QtWarningMsg)
|
||||
|
||||
namespace KAuth
|
||||
{
|
||||
static AuthorizationRef s_authRef = NULL;
|
||||
|
||||
AuthorizationRef authRef()
|
||||
{
|
||||
if (!s_authRef) {
|
||||
AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &s_authRef);
|
||||
}
|
||||
|
||||
return s_authRef;
|
||||
}
|
||||
|
||||
// GetActionRights return codes:
|
||||
// errAuthorizationSuccess = 0,
|
||||
// errAuthorizationInvalidSet = -60001, /* The authorization rights are invalid. */
|
||||
// errAuthorizationInvalidRef = -60002, /* The authorization reference is invalid. */
|
||||
// errAuthorizationInvalidTag = -60003, /* The authorization tag is invalid. */
|
||||
// errAuthorizationInvalidPointer = -60004, /* The returned authorization is invalid. */
|
||||
// errAuthorizationDenied = -60005, /* The authorization was denied. */
|
||||
// errAuthorizationCanceled = -60006, /* The authorization was cancelled by the user. */
|
||||
// errAuthorizationInteractionNotAllowed = -60007, /* The authorization was denied since no user interaction was possible. */
|
||||
// errAuthorizationInternal = -60008, /* Unable to obtain authorization for this operation. */
|
||||
// errAuthorizationExternalizeNotAllowed = -60009, /* The authorization is not allowed to be converted to an external format. */
|
||||
// errAuthorizationInternalizeNotAllowed = -60010, /* The authorization is not allowed to be created from an external format. */
|
||||
// errAuthorizationInvalidFlags = -60011, /* The provided option flag(s) are invalid for this authorization operation. */
|
||||
// errAuthorizationToolExecuteFailure = -60031, /* The specified program could not be executed. */
|
||||
// errAuthorizationToolEnvironmentError = -60032, /* An invalid status was returned during execution of a privileged tool. */
|
||||
// errAuthorizationBadAddress = -60033, /* The requested socket address is invalid (must be 0-1023 inclusive). */
|
||||
static OSStatus GetActionRights(const QString &action, AuthorizationFlags flags, AuthorizationRef auth)
|
||||
{
|
||||
AuthorizationItem item;
|
||||
item.name = action.toUtf8().constData();
|
||||
item.valueLength = 0;
|
||||
item.value = NULL;
|
||||
item.flags = 0;
|
||||
|
||||
AuthorizationRights rights;
|
||||
rights.count = 1;
|
||||
rights.items = &item;
|
||||
|
||||
OSStatus result = AuthorizationCopyRights(auth, &rights, kAuthorizationEmptyEnvironment, flags, NULL);
|
||||
return result;
|
||||
}
|
||||
|
||||
// On OS X we avoid using a helper but grab privilege from here, the client.
|
||||
AuthServicesBackend::AuthServicesBackend()
|
||||
: AuthBackend()
|
||||
{
|
||||
setCapabilities(AuthorizeFromClientCapability);
|
||||
}
|
||||
|
||||
AuthServicesBackend::~AuthServicesBackend()
|
||||
{
|
||||
if (s_authRef) {
|
||||
OSStatus err = AuthorizationFree(s_authRef, kAuthorizationFlagDefaults);
|
||||
qCDebug(KAUTH_OSX) << "AuthorizationFree(" << s_authRef << ") returned" << err;
|
||||
s_authRef = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void AuthServicesBackend::setupAction(const QString &)
|
||||
{
|
||||
// Nothing to do here...
|
||||
}
|
||||
|
||||
Action::AuthStatus AuthServicesBackend::authorizeAction(const QString &action)
|
||||
{
|
||||
Action::AuthStatus retval;
|
||||
OSStatus result = GetActionRights(action, kAuthorizationFlagExtendRights | kAuthorizationFlagInteractionAllowed, authRef());
|
||||
qCDebug(KAUTH_OSX) << "AuthServicesBackend::authorizeAction(" << action << ") AuthorizationCopyRights returned" << result;
|
||||
switch (result) {
|
||||
case errAuthorizationSuccess:
|
||||
retval = Action::AuthorizedStatus;
|
||||
break;
|
||||
case errAuthorizationCanceled:
|
||||
retval = Action::UserCancelledStatus;
|
||||
break;
|
||||
case errAuthorizationInteractionNotAllowed:
|
||||
case errAuthorizationDenied:
|
||||
retval = Action::DeniedStatus;
|
||||
break;
|
||||
case errAuthorizationInternal:
|
||||
// does this make sense?
|
||||
retval = Action::AuthRequiredStatus;
|
||||
break;
|
||||
case errAuthorizationExternalizeNotAllowed:
|
||||
case errAuthorizationInternalizeNotAllowed:
|
||||
case errAuthorizationToolExecuteFailure:
|
||||
case errAuthorizationToolEnvironmentError:
|
||||
case errAuthorizationBadAddress:
|
||||
retval = Action::ErrorStatus;
|
||||
break;
|
||||
default:
|
||||
retval = Action::InvalidStatus;
|
||||
break;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
Action::AuthStatus AuthServicesBackend::actionStatus(const QString &action)
|
||||
{
|
||||
Action::AuthStatus retval;
|
||||
OSStatus result = GetActionRights(action, kAuthorizationFlagExtendRights | kAuthorizationFlagPreAuthorize, authRef());
|
||||
qCDebug(KAUTH_OSX) << "AuthServicesBackend::actionStatus(" << action << ") AuthorizationCopyRights returned" << result;
|
||||
// this function has a simpler return code parser:
|
||||
switch (result) {
|
||||
case errAuthorizationSuccess:
|
||||
retval = Action::AuthorizedStatus;
|
||||
break;
|
||||
case errAuthorizationCanceled:
|
||||
retval = Action::UserCancelledStatus;
|
||||
break;
|
||||
case errAuthorizationInteractionNotAllowed:
|
||||
retval = Action::AuthRequiredStatus;
|
||||
break;
|
||||
default:
|
||||
retval = Action::DeniedStatus;
|
||||
break;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
QByteArray AuthServicesBackend::callerID() const
|
||||
{
|
||||
AuthorizationExternalForm ext;
|
||||
AuthorizationMakeExternalForm(authRef(), &ext);
|
||||
QByteArray id((const char *)&ext, sizeof(ext));
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
bool AuthServicesBackend::isCallerAuthorized(const QString &action, const QByteArray &callerID, const QVariantMap &details)
|
||||
{
|
||||
Q_UNUSED(details);
|
||||
|
||||
AuthorizationExternalForm ext;
|
||||
memcpy(&ext, callerID.data(), sizeof(ext));
|
||||
|
||||
AuthorizationRef auth;
|
||||
|
||||
if (AuthorizationCreateFromExternalForm(&ext, &auth) != noErr) {
|
||||
qCWarning(KAUTH_OSX()) << "AuthorizationCreateFromExternalForm(" << action << "," << callerID.constData() << ") failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
OSStatus result = GetActionRights(action, kAuthorizationFlagExtendRights | kAuthorizationFlagInteractionAllowed, auth);
|
||||
|
||||
AuthorizationFree(auth, kAuthorizationFlagDefaults);
|
||||
qCDebug(KAUTH_OSX) << "AuthServicesBackend::isCallerAuthorized(" << action << "," << callerID.constData() << ") AuthorizationCopyRights returned" << result;
|
||||
|
||||
return result == errAuthorizationSuccess;
|
||||
}
|
||||
|
||||
}; // namespace KAuth
|
||||
|
||||
#include "moc_AuthServicesBackend.cpp"
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Nicola Gigante <nicola.gigante@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#ifndef AUTHSERVICES_BACKEND_H
|
||||
#define AUTHSERVICES_BACKEND_H
|
||||
|
||||
#include "AuthBackend.h"
|
||||
#include <Security/Security.h>
|
||||
|
||||
namespace KAuth
|
||||
{
|
||||
class AuthServicesBackend : public AuthBackend
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID "org.kde.AuthServicesBackend")
|
||||
Q_INTERFACES(KAuth::AuthBackend)
|
||||
|
||||
public:
|
||||
AuthServicesBackend();
|
||||
virtual ~AuthServicesBackend();
|
||||
virtual void setupAction(const QString &);
|
||||
virtual Action::AuthStatus authorizeAction(const QString &);
|
||||
virtual Action::AuthStatus actionStatus(const QString &);
|
||||
virtual QByteArray callerID() const;
|
||||
virtual bool isCallerAuthorized(const QString &action, const QByteArray &callerID, const QVariantMap &details);
|
||||
};
|
||||
|
||||
} // namespace KAuth
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Nicola Gigante <nicola.gigante@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include "../../policy-gen/policy-gen.h"
|
||||
|
||||
#include <Security/Security.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
using namespace std;
|
||||
|
||||
void output(const QList<Action> &actions, const QMap<QString, QString> &domain)
|
||||
{
|
||||
AuthorizationRef auth;
|
||||
AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &auth);
|
||||
|
||||
OSStatus err;
|
||||
|
||||
for (const Action &action : std::as_const(actions)) {
|
||||
err = AuthorizationRightGet(action.name.toLatin1().constData(), NULL);
|
||||
|
||||
if (err != errAuthorizationSuccess) {
|
||||
QString rule;
|
||||
|
||||
if (action.policy == QLatin1String("yes")) {
|
||||
rule = QString::fromLatin1(kAuthorizationRuleClassAllow);
|
||||
} else if (action.policy == QLatin1String("no")) {
|
||||
rule = QString::fromLatin1(kAuthorizationRuleClassDeny);
|
||||
} else if (action.policy == QLatin1String("auth_self")) {
|
||||
rule = QString::fromLatin1(kAuthorizationRuleAuthenticateAsSessionUser);
|
||||
} else if (action.policy == QLatin1String("auth_admin")) {
|
||||
rule = QString::fromLatin1(kAuthorizationRuleAuthenticateAsAdmin);
|
||||
}
|
||||
|
||||
CFStringRef cfRule = CFStringCreateWithCString(NULL, rule.toLatin1().constData(), kCFStringEncodingASCII);
|
||||
CFStringRef cfPrompt =
|
||||
CFStringCreateWithCString(NULL, action.descriptions.value(QLatin1String("en")).toLatin1().constData(), kCFStringEncodingASCII);
|
||||
|
||||
err = AuthorizationRightSet(auth, action.name.toLatin1().constData(), cfRule, cfPrompt, NULL, NULL);
|
||||
if (err != noErr) {
|
||||
cerr << "You don't have the right to edit the security database (try to run cmake with sudo): " << err << endl;
|
||||
exit(1);
|
||||
} else {
|
||||
qInfo() << "Created or updated rule" << rule << "for right entry" << action.name << "policy" << action.policy << "; domain=" << domain;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Nicola Gigante <nicola.gigante@gmail.com>
|
||||
SPDX-FileCopyrightText: 2009 Radek Novacek <rnovacek@redhat.com>
|
||||
SPDX-FileCopyrightText: 2009-2010 Dario Freddi <drf@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
SPDX-FileCopyrightText: 2023 Kai Uwe Broulik <kde@broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include "Polkit1Backend.h"
|
||||
#include "kauthdebug.h"
|
||||
|
||||
#include <KWaylandExtras>
|
||||
#include <KWindowSystem>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QTimer>
|
||||
#include <qplugin.h>
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QWindow>
|
||||
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusConnectionInterface>
|
||||
#include <QDBusPendingCallWatcher>
|
||||
#include <QDBusPendingReply>
|
||||
|
||||
#include <PolkitQt1/Subject>
|
||||
#include <polkitqt1-version.h>
|
||||
|
||||
constexpr QLatin1String c_kdeAgentService{"org.kde.polkit-kde-authentication-agent-1"};
|
||||
constexpr QLatin1String c_kdeAgentPath{"/org/kde/Polkit1AuthAgent"};
|
||||
constexpr QLatin1String c_kdeAgentInterface{"org.kde.Polkit1AuthAgent"};
|
||||
|
||||
namespace KAuth
|
||||
{
|
||||
|
||||
Polkit1Backend::Polkit1Backend()
|
||||
: AuthBackend()
|
||||
{
|
||||
setCapabilities(AuthorizeFromHelperCapability | PreAuthActionCapability);
|
||||
|
||||
// Setup useful signals
|
||||
connect(PolkitQt1::Authority::instance(), &PolkitQt1::Authority::configChanged, this, &KAuth::Polkit1Backend::checkForResultChanged);
|
||||
connect(PolkitQt1::Authority::instance(), &PolkitQt1::Authority::consoleKitDBChanged, this, &KAuth::Polkit1Backend::checkForResultChanged);
|
||||
}
|
||||
|
||||
Polkit1Backend::~Polkit1Backend()
|
||||
{
|
||||
}
|
||||
|
||||
void Polkit1Backend::preAuthAction(const QString &action, QWindow *parentWindow)
|
||||
{
|
||||
// If a parent was not specified, skip this
|
||||
if (!parentWindow) {
|
||||
qCDebug(KAUTH) << "Parent widget does not exist, skipping";
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we actually are entitled to use GUI capabilities
|
||||
if (!qGuiApp) {
|
||||
qCDebug(KAUTH) << "Not streaming parent as we are on a TTY application";
|
||||
return;
|
||||
}
|
||||
|
||||
// Are we running our KDE auth agent?
|
||||
if (QDBusConnection::sessionBus().interface()->isServiceRegistered(QLatin1String("org.kde.polkit-kde-authentication-agent-1"))) {
|
||||
if (KWindowSystem::isPlatformWayland()) {
|
||||
KWaylandExtras::exportWindow(parentWindow);
|
||||
connect(
|
||||
KWaylandExtras::self(),
|
||||
&KWaylandExtras::windowExported,
|
||||
this,
|
||||
[this, action, parentWindow](QWindow *window, const QString &handle) {
|
||||
if (window == parentWindow) {
|
||||
sendWindowHandle(action, handle);
|
||||
}
|
||||
},
|
||||
Qt::SingleShotConnection);
|
||||
|
||||
// Generate and send an XDG Activation token.
|
||||
sendActivationToken(action, parentWindow);
|
||||
} else {
|
||||
// Retrieve the dialog root window Id
|
||||
const qulonglong wId = parentWindow->winId();
|
||||
|
||||
sendWindowHandle(action, QString::number(wId));
|
||||
|
||||
// Call the old method for compatibility.
|
||||
QDBusMessage methodCall = QDBusMessage::createMethodCall(c_kdeAgentService, c_kdeAgentPath, c_kdeAgentInterface, QLatin1String("setWIdForAction"));
|
||||
methodCall << action;
|
||||
methodCall << wId;
|
||||
|
||||
// Legacy call has to be blocking, old agent doesn't handle it coming in delayed.
|
||||
const auto reply = QDBusConnection::sessionBus().call(methodCall);
|
||||
if (reply.type() != QDBusMessage::ReplyMessage) {
|
||||
qWarning() << "Failed to set window id" << wId << "for" << action << reply.errorMessage();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qCDebug(KAUTH) << "KDE polkit agent appears too old or not registered on the bus";
|
||||
}
|
||||
}
|
||||
|
||||
void Polkit1Backend::sendWindowHandle(const QString &action, const QString &handle)
|
||||
{
|
||||
// Send it over the bus to our agent
|
||||
QDBusMessage methodCall = QDBusMessage::createMethodCall(c_kdeAgentService, c_kdeAgentPath, c_kdeAgentInterface, QLatin1String("setWindowHandleForAction"));
|
||||
methodCall << action;
|
||||
methodCall << handle;
|
||||
|
||||
const auto reply = QDBusConnection::sessionBus().asyncCall(methodCall);
|
||||
auto *watcher = new QDBusPendingCallWatcher(reply, this);
|
||||
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, watcher, handle, action] {
|
||||
watcher->deleteLater();
|
||||
|
||||
QDBusPendingReply<> reply = *watcher;
|
||||
if (reply.isError()) {
|
||||
qCWarning(KAUTH) << "Failed to set window handle" << handle << "for" << action << reply.error().message();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Polkit1Backend::sendActivationToken(const QString &action, QWindow *window)
|
||||
{
|
||||
const auto requestedSerial = KWaylandExtras::lastInputSerial(window);
|
||||
connect(
|
||||
KWaylandExtras::self(),
|
||||
&KWaylandExtras::xdgActivationTokenArrived,
|
||||
this,
|
||||
[this, requestedSerial, action](quint32 serial, const QString &token) {
|
||||
if (serial != requestedSerial || token.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
QDBusMessage methodCall =
|
||||
QDBusMessage::createMethodCall(c_kdeAgentService, c_kdeAgentPath, c_kdeAgentInterface, QLatin1String("setActivationTokenForAction"));
|
||||
methodCall << action;
|
||||
methodCall << token;
|
||||
|
||||
const auto reply = QDBusConnection::sessionBus().asyncCall(methodCall);
|
||||
auto *watcher = new QDBusPendingCallWatcher(reply, this);
|
||||
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, watcher, token, action] {
|
||||
watcher->deleteLater();
|
||||
|
||||
QDBusPendingReply<> reply = *watcher;
|
||||
if (reply.isError()) {
|
||||
qCWarning(KAUTH) << "Failed to set activation token" << token << "for" << action << reply.error().message();
|
||||
}
|
||||
});
|
||||
},
|
||||
Qt::SingleShotConnection);
|
||||
KWaylandExtras::requestXdgActivationToken(window, requestedSerial, {});
|
||||
}
|
||||
|
||||
Action::AuthStatus Polkit1Backend::authorizeAction(const QString &action)
|
||||
{
|
||||
Q_UNUSED(action)
|
||||
// Always return Yes here, we'll authorize inside isCallerAuthorized
|
||||
return Action::AuthorizedStatus;
|
||||
}
|
||||
|
||||
void Polkit1Backend::setupAction(const QString &action)
|
||||
{
|
||||
m_cachedResults[action] = actionStatus(action);
|
||||
}
|
||||
|
||||
Action::AuthStatus Polkit1Backend::actionStatus(const QString &action)
|
||||
{
|
||||
PolkitQt1::SystemBusNameSubject subject(QString::fromUtf8(callerID()));
|
||||
auto authority = PolkitQt1::Authority::instance();
|
||||
PolkitQt1::Authority::Result r = authority->checkAuthorizationSync(action, subject, PolkitQt1::Authority::None);
|
||||
|
||||
if (authority->hasError()) {
|
||||
qCDebug(KAUTH) << "Encountered error while checking action status, error code:" << authority->lastError() << authority->errorDetails();
|
||||
authority->clearError();
|
||||
return Action::InvalidStatus;
|
||||
}
|
||||
|
||||
switch (r) {
|
||||
case PolkitQt1::Authority::Yes:
|
||||
return Action::AuthorizedStatus;
|
||||
case PolkitQt1::Authority::No:
|
||||
case PolkitQt1::Authority::Unknown:
|
||||
return Action::DeniedStatus;
|
||||
default:
|
||||
return Action::AuthRequiredStatus;
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray Polkit1Backend::callerID() const
|
||||
{
|
||||
return QDBusConnection::systemBus().baseService().toUtf8();
|
||||
}
|
||||
|
||||
bool Polkit1Backend::isCallerAuthorized(const QString &action, const QByteArray &callerID, const QVariantMap &details)
|
||||
{
|
||||
PolkitQt1::SystemBusNameSubject subject(QString::fromUtf8(callerID));
|
||||
PolkitQt1::Authority *authority = PolkitQt1::Authority::instance();
|
||||
QMap<QString, QString> polkit1Details;
|
||||
for (auto it = details.cbegin(); it != details.cend(); ++it) {
|
||||
polkit1Details.insert(it.key(), it.value().toString());
|
||||
}
|
||||
|
||||
PolkitQt1::Authority::Result result;
|
||||
QEventLoop e;
|
||||
connect(authority, &PolkitQt1::Authority::checkAuthorizationFinished, &e, [&result, &e](PolkitQt1::Authority::Result _result) {
|
||||
result = _result;
|
||||
e.quit();
|
||||
});
|
||||
|
||||
#if POLKITQT1_IS_VERSION(0, 113, 0)
|
||||
authority->checkAuthorizationWithDetails(action, subject, PolkitQt1::Authority::AllowUserInteraction, polkit1Details);
|
||||
#else
|
||||
authority->checkAuthorization(action, subject, PolkitQt1::Authority::AllowUserInteraction);
|
||||
#endif
|
||||
e.exec();
|
||||
|
||||
if (authority->hasError()) {
|
||||
qCDebug(KAUTH) << "Encountered error while checking authorization, error code:" << authority->lastError() << authority->errorDetails();
|
||||
authority->clearError();
|
||||
}
|
||||
|
||||
switch (result) {
|
||||
case PolkitQt1::Authority::Yes:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Polkit1Backend::checkForResultChanged()
|
||||
{
|
||||
for (auto it = m_cachedResults.begin(); it != m_cachedResults.end(); ++it) {
|
||||
const QString action = it.key();
|
||||
if (it.value() != actionStatus(action)) {
|
||||
*it = actionStatus(action);
|
||||
Q_EMIT actionStatusChanged(action, *it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVariantMap Polkit1Backend::backendDetails(const DetailsMap &details)
|
||||
{
|
||||
QVariantMap backendDetails;
|
||||
for (auto it = details.cbegin(); it != details.cend(); ++it) {
|
||||
switch (it.key()) {
|
||||
case Action::AuthDetail::DetailMessage:
|
||||
backendDetails.insert(QStringLiteral("polkit.message"), it.value());
|
||||
break;
|
||||
case Action::AuthDetail::DetailOther:
|
||||
default:
|
||||
backendDetails.insert(QStringLiteral("other_details"), it.value());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return backendDetails;
|
||||
}
|
||||
|
||||
} // namespace Auth
|
||||
|
||||
#include "Polkit1Backend.moc"
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Nicola Gigante <nicola.gigante@gmail.com>
|
||||
SPDX-FileCopyrightText: 2009 Radek Novacek <rnovacek@redhat.com>
|
||||
SPDX-FileCopyrightText: 2009-2010 Dario Freddi <drf@kde.org>
|
||||
SPDX-FileCopyrightText: 2023 Kai Uwe Broulik <kde@broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#ifndef POLKIT1BACKEND_H
|
||||
#define POLKIT1BACKEND_H
|
||||
|
||||
#include "AuthBackend.h"
|
||||
|
||||
#include <QEventLoop>
|
||||
#include <QHash>
|
||||
#include <QStringList>
|
||||
|
||||
#include <PolkitQt1/Authority>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class QByteArray;
|
||||
|
||||
namespace KAuth
|
||||
{
|
||||
class Polkit1Backend : public AuthBackend
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID "org.kde.Polkit1Backend")
|
||||
Q_INTERFACES(KAuth::AuthBackend)
|
||||
|
||||
public:
|
||||
Polkit1Backend();
|
||||
~Polkit1Backend() override;
|
||||
void setupAction(const QString &) override;
|
||||
void preAuthAction(const QString &action, QWindow *parent) override;
|
||||
Action::AuthStatus authorizeAction(const QString &) override;
|
||||
Action::AuthStatus actionStatus(const QString &) override;
|
||||
QByteArray callerID() const override;
|
||||
bool isCallerAuthorized(const QString &action, const QByteArray &callerID, const QVariantMap &details) override;
|
||||
QVariantMap backendDetails(const DetailsMap &details) override;
|
||||
|
||||
private Q_SLOTS:
|
||||
void checkForResultChanged();
|
||||
|
||||
private:
|
||||
void sendWindowHandle(const QString &action, const QString &handle);
|
||||
void sendActivationToken(const QString &action, QWindow *window);
|
||||
|
||||
QHash<QString, Action::AuthStatus> m_cachedResults;
|
||||
};
|
||||
|
||||
} // namespace Auth
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Nicola Gigante <nicola.gigante@gmail.com>
|
||||
SPDX-FileCopyrightText: 2009-2010 Dario Freddi <drf@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include <policy-gen/policy-gen.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTextStream>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
const char header[] =
|
||||
""
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
|
||||
"<!DOCTYPE policyconfig PUBLIC\n"
|
||||
"\"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN\"\n"
|
||||
"\"http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd\">\n"
|
||||
"<policyconfig>\n";
|
||||
|
||||
const char policy_tag[] =
|
||||
""
|
||||
" <defaults>\n"
|
||||
" <allow_inactive>%1</allow_inactive>\n"
|
||||
" <allow_active>%2</allow_active>\n"
|
||||
" </defaults>\n";
|
||||
|
||||
const char dent[] = " ";
|
||||
|
||||
void output(const QList<Action> &actions, const QMap<QString, QString> &domain)
|
||||
{
|
||||
QTextStream out(stdout);
|
||||
out << header;
|
||||
|
||||
if (domain.contains(QLatin1String("vendor"))) {
|
||||
out << "<vendor>" << domain[QStringLiteral("vendor")].toHtmlEscaped() << "</vendor>\n";
|
||||
}
|
||||
if (domain.contains(QLatin1String("vendorurl"))) {
|
||||
out << "<vendor_url>" << domain[QStringLiteral("vendorurl")] << "</vendor_url>\n";
|
||||
}
|
||||
if (domain.contains(QLatin1String("icon"))) {
|
||||
out << "<icon_name>" << domain[QStringLiteral("icon")] << "</icon_name>\n";
|
||||
}
|
||||
|
||||
for (const Action &action : actions) {
|
||||
out << dent << "<action id=\"" << action.name << "\" >\n";
|
||||
|
||||
// Not a typo, messages and descriptions are actually inverted
|
||||
for (auto i = action.messages.cbegin(); i != action.messages.cend(); ++i) {
|
||||
out << dent << dent << "<description";
|
||||
if (i.key() != QLatin1String("en")) {
|
||||
out << " xml:lang=\"" << i.key() << '"';
|
||||
}
|
||||
|
||||
out << '>' << i.value().toHtmlEscaped() << "</description>\n";
|
||||
}
|
||||
|
||||
for (auto i = action.descriptions.cbegin(); i != action.descriptions.cend(); ++i) {
|
||||
out << dent << dent << "<message";
|
||||
if (i.key() != QLatin1String("en")) {
|
||||
out << " xml:lang=\"" << i.key() << '"';
|
||||
}
|
||||
|
||||
out << '>' << i.value().toHtmlEscaped() << "</message>\n";
|
||||
}
|
||||
|
||||
QString policy = action.policy;
|
||||
QString policyInactive = action.policyInactive.isEmpty() ? QLatin1String("no") : action.policyInactive;
|
||||
if (!action.persistence.isEmpty() && policy != QLatin1String("yes") && policy != QLatin1String("no")) {
|
||||
policy += QLatin1String("_keep");
|
||||
}
|
||||
if (!action.persistence.isEmpty() && policyInactive != QLatin1String("yes") && policyInactive != QLatin1String("no")) {
|
||||
policyInactive += QLatin1String("_keep");
|
||||
}
|
||||
|
||||
out << QString(QLatin1String(policy_tag)).arg(policyInactive, policy);
|
||||
|
||||
out << dent << "</action>\n";
|
||||
}
|
||||
|
||||
out << "</policyconfig>\n";
|
||||
}
|
||||
Reference in New Issue
Block a user