Commit 573c5c93 authored by Andrew den Exter's avatar Andrew den Exter

[devicelock] Add API for services to directly request permission for actions....

[devicelock] Add API for services to directly request permission for actions. Contributes to JB#40880

This allows a service to directly ask prompt the user to confirm it
should perform an action on behalf of an application. Or for a user
session service to act as an agent for another authentication service
such as polkit.
parent 82172d2f
......@@ -17,6 +17,9 @@
<method name="RequestSecurityCode">
<arg name="client" type="o" direction="in"/>
</method>
<method name="Authorize">
<arg name="client" type="o" direction="in"/>
</method>
<method name="Cancel">
<arg name="client" type="o" direction="in"/>
</method>
......
......@@ -8,6 +8,11 @@
<arg name="challenge_code" type="v" direction="in"/>
<arg name="methods" type="u" direction="in"/>
</method>
<method name="RequestPermission">
<arg name="client" type="o" direction="in"/>
<arg name="message" type="s" direction="in"/>
<arg name="methods" type="u" direction="in"/>
</method>
<method name="Cancel">
<arg name="client" type="o" direction="in"/>
</method>
......
......@@ -4,6 +4,8 @@
<interface name="org.nemomobile.devicelock.Authorization">
<method name="RequestChallenge">
<arg name="client" type="o" direction="in"/>
<arg name="requested_methods" type="u" direction="in"/>
<arg name="authenticating_pid" type="u" direction="in"/>
<arg name="challenge_code" type="v" direction="out"/>
<arg name="allowed_methods" type="u" direction="out"/>
</method>
......
......@@ -5,6 +5,9 @@
<method name="Authenticated">
<arg name="authentication_token" type="v" direction="in"/>
</method>
<method name="PermissionGranted">
<arg name="method" type="u" direction="in"/>
</method>
<method name="Aborted"/>
</interface>
</node>
......@@ -16,7 +16,6 @@ BuildRequires: pkgconfig(libsystemd-daemon)
BuildRequires: pkgconfig(mce)
BuildRequires: pkgconfig(nemodbus)
Obsoletes: nemo-qml-plugin-devicelock-default < 0.2.0
Requires: nemo-devicelock-daemon
%description
%{summary}.
......@@ -43,7 +42,6 @@ Requires: pkgconfig(nemodbus)
Summary: Development libraries for device lock daemons
Group: Development/Libraries
Requires: %{name}-devel = %{version}-%{release}
Requires: nemo-devicelock-daemon-cli = %{version}-%{release}
Requires: pkgconfig(keepalive)
Requires: pkgconfig(libsystemd-daemon)
Requires: pkgconfig(mce)
......
......@@ -434,6 +434,15 @@ void AuthenticationInput::requestSecurityCode()
call(QStringLiteral("RequestSecurityCode"), m_localPath);
}
/*!
Informs the security dialog that an action was authorized without authenticating.
*/
void AuthenticationInput::authorize()
{
call(QStringLiteral("Authorize"), m_localPath);
}
/*!
Sends a request to cancel authentication to the security daemon.
*/
......
......@@ -66,7 +66,7 @@ class NEMODEVICELOCK_EXPORT AuthenticationInput : public QObject, private Connec
Q_OBJECT
Q_PROPERTY(Authenticator::Methods utilizedMethods READ utilizedMethods NOTIFY utilizedMethodsChanged)
Q_PROPERTY(Status status READ status NOTIFY statusChanged)
Q_PROPERTY(bool authenticatingProcess READ authenticatingPid NOTIFY authenticatingPidChanged)
Q_PROPERTY(int authenticatingProcess READ authenticatingPid NOTIFY authenticatingPidChanged)
Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged)
Q_PROPERTY(bool registered READ isRegistered WRITE setRegistered NOTIFY registeredChanged)
Q_PROPERTY(int minimumCodeLength READ minimumCodeLength CONSTANT)
......@@ -76,6 +76,7 @@ class NEMODEVICELOCK_EXPORT AuthenticationInput : public QObject, private Connec
Q_PROPERTY(CodeGeneration codeGeneration READ codeGeneration NOTIFY codeGenerationChanged)
public:
enum Feedback {
Authorize,
EnterSecurityCode,
EnterNewSecurityCode,
RepeatNewSecurityCode,
......@@ -150,6 +151,7 @@ public:
Q_INVOKABLE void enterSecurityCode(const QString &code);
Q_INVOKABLE void requestSecurityCode();
Q_INVOKABLE void authorize();
Q_INVOKABLE void cancel();
signals:
......@@ -164,7 +166,7 @@ signals:
void codeGenerationChanged();
void codeInputIsKeyboardChanged();
void authenticationStarted(Feedback feedback, const QVariant &data);
void authenticationStarted(Feedback feedback, const QVariantMap &data);
void authenticationUnavailable(Error error);
void authenticationEvaluating();
void authenticationProgress(int current, int maximum);
......
......@@ -49,6 +49,11 @@ void AuthenticatorAdaptor::Authenticated(const QDBusVariant &authenticationToken
m_authenticator->handleAuthentication(authenticationToken.variant());
}
void AuthenticatorAdaptor::PermissionGranted(uint method)
{
m_authenticator->handlePermissionGranted(Authenticator::Method(method));
}
void AuthenticatorAdaptor::Aborted()
{
m_authenticator->handleAborted();
......@@ -142,6 +147,40 @@ void Authenticator::authenticate(const QVariant &challengeCode, Methods methods)
emit authenticatingChanged();
}
/*!
Requests the user grant permission for an action described by \a message and \a properties
using one of the given authentication \a methods.
The properties map may contain one of the following values:
\table
\header
\li Key
\li Value
\row
\li authenticatingPid
\li The PID of the application permissions are being requested for.
\endtable
*/
void Authenticator::requestPermission(
const QString &message, const QVariantMap &properties, Methods methods)
{
const auto response = call(
QStringLiteral("RequestPermission"), m_localPath, message, properties, uint(methods));
m_authenticating = true;
response->onError([this](const QDBusError &) {
m_authenticating = false;
emit aborted();
emit authenticatingChanged();
});
emit authenticatingChanged();
}
/*!
Cancels an active authentication request.
*/
......@@ -174,6 +213,23 @@ void Authenticator::handleAuthentication(const QVariant &authenticationToken)
}
}
/*!
\signal NemoDeviceLock::Authenticator::permissionGranted(Method method)
Signals that the user has successfully granted permission for the requested
action using the given authentication \a method.
*/
void Authenticator::handlePermissionGranted(Method method)
{
if (m_authenticating) {
m_authenticating = false;
emit permissionGranted(method);
emit authenticatingChanged();
}
}
/*!
\signal NemoDeviceLock::Authenticator::aborted()
......
......@@ -52,6 +52,7 @@ public:
public slots:
Q_NOREPLY void Authenticated(const QDBusVariant &authenticationToken);
Q_NOREPLY void PermissionGranted(uint method);
Q_NOREPLY void Aborted();
private:
......@@ -67,9 +68,11 @@ class NEMODEVICELOCK_EXPORT Authenticator : public QObject, private ConnectionCl
Q_FLAGS(Methods)
public:
enum Method {
NoAuthentication = 0x00,
SecurityCode = 0x01,
Fingerprint = 0x02
NoAuthentication = 0x000,
SecurityCode = 0x0001,
Fingerprint = 0x0002,
Confirmation = 0x1000,
AllAvailable = SecurityCode | Fingerprint | Confirmation
};
Q_DECLARE_FLAGS(Methods, Method)
......@@ -81,7 +84,9 @@ public:
bool isAuthenticating() const;
Q_INVOKABLE void authenticate(
const QVariant &challengeCode, Methods methods = Methods(SecurityCode | Fingerprint));
const QVariant &challengeCode, Methods methods = AllAvailable);
Q_INVOKABLE void requestPermission(
const QString &message, const QVariantMap &properties, Methods methods = AllAvailable);
Q_INVOKABLE void cancel();
signals:
......@@ -89,6 +94,7 @@ signals:
void authenticatingChanged();
void authenticated(const QVariant &authenticationToken);
void permissionGranted(Method method);
void aborted();
private:
......@@ -97,6 +103,7 @@ private:
inline void connected();
inline void handleAuthentication(const QVariant &authenticationToken);
inline void handlePermissionGranted(Method method);
inline void handleAborted();
AuthenticatorAdaptor m_adaptor;
......
......@@ -103,7 +103,7 @@ void CliAuthenticator::enterSecurityCode(const QString &code)
m_securityCode.clear();
}
QVariant CliAuthenticator::authenticateChallengeCode(const QVariant &)
QVariant CliAuthenticator::authenticateChallengeCode(const QVariant &, Authenticator::Method, uint)
{
return m_securityCode;
}
......
......@@ -58,7 +58,8 @@ public:
bool clearCode(const QString &code) override;
void enterSecurityCode(const QString &code);
QVariant authenticateChallengeCode(const QVariant &challengeCode);
QVariant authenticateChallengeCode(
const QVariant &challengeCode, Authenticator::Method method, uint authenticatingPid) override;
private:
QExplicitlySharedDataPointer<LockCodeWatcher> m_watcher;
......
......@@ -73,12 +73,17 @@ void HostAuthenticationInputAdaptor::Cancel(const QDBusObjectPath &path)
m_authenticationInput->handleCancel(path.path());
}
void HostAuthenticationInputAdaptor::Authorize(const QDBusObjectPath &path)
{
m_authenticationInput->handleAuthorize(path.path());
}
HostAuthenticationInput::HostAuthenticationInput(
const QString &path, Authenticator::Methods supportedMethods, QObject *parent)
: HostObject(path, parent)
, m_adaptor(this)
, m_settings(SettingsWatcher::instance())
, m_supportedMethods(supportedMethods)
, m_supportedMethods(supportedMethods | Authenticator::Confirmation) // Basic yes/no confirmation is always supported.
, m_activeMethods()
, m_authenticating(false)
{
......@@ -88,60 +93,81 @@ HostAuthenticationInput::~HostAuthenticationInput()
{
}
void HostAuthenticationInput::authorize()
{
}
void HostAuthenticationInput::authenticationStarted(
Authenticator::Methods methods,
uint authenticatingPid,
AuthenticationInput::Feedback)
{
Q_UNUSED(authenticatingPid);
qCDebug(daemon, "Authentication started");
m_authenticating = true;
m_activeMethods = methods & m_supportedMethods;
}
void HostAuthenticationInput::startAuthentication(
AuthenticationInput::Feedback feedback,
const QVariantMap &data,
Authenticator::Methods methods)
{
const uint pid = connectionPid(QDBusContext::connection());
if (pid != 0) {
startAuthentication(feedback, pid, data, methods);
}
}
void HostAuthenticationInput::startAuthentication(
AuthenticationInput::Feedback feedback,
uint authenticatingPid,
const QVariantMap &data,
Authenticator::Methods methods)
{
qCDebug(daemon, "Authentication started");
const uint pid = connectionPid(QDBusContext::connection());
if (pid != 0 && !m_inputStack.isEmpty()) {
authenticationStarted(methods, feedback);
if (!m_inputStack.isEmpty()) {
authenticationStarted(methods, authenticatingPid, feedback);
NemoDBus::send(
m_inputStack.last().connection,
m_inputStack.last().path,
clientInterface,
QStringLiteral("AuthenticationStarted"),
pid,
authenticatingPid,
uint(m_activeMethods),
uint(feedback),
data);
}
}
void HostAuthenticationInput::authenticationUnavailable(AuthenticationInput::Error error)
{
qCDebug(daemon, "Authentication unavailable");
const uint pid = connectionPid(QDBusContext::connection());
if (pid != 0) {
authenticationUnavailable(error, pid);
}
}
if (pid != 0 && !m_inputStack.isEmpty()) {
void HostAuthenticationInput::authenticationUnavailable(
AuthenticationInput::Error error, uint authenticatingPid)
{
qCDebug(daemon, "Authentication unavailable");
if (!m_inputStack.isEmpty()) {
NemoDBus::send(
m_inputStack.last().connection,
m_inputStack.last().path,
clientInterface,
QStringLiteral("AuthenticationUnavailable"),
pid,
authenticatingPid,
uint(error));
}
}
void HostAuthenticationInput::authenticationResumed(
AuthenticationInput::Feedback feedback,
const QVariantMap &data,
......@@ -213,6 +239,7 @@ void HostAuthenticationInput::authenticationEnded(bool confirmed)
void HostAuthenticationInput::setRegistered(const QString &path, bool registered)
{
const auto pid = connectionPid(QDBusContext::connection());
if (pid == 0 || !authorizeInput(pid)) {
QDBusContext::sendErrorReply(QDBusError::AccessDenied);
return;
......@@ -440,4 +467,14 @@ void HostAuthenticationInput::handleCancel(const QString &path)
}
}
void HostAuthenticationInput::handleAuthorize(const QString &path)
{
const auto connection = QDBusContext::connection().name();
if (!m_inputStack.isEmpty()
&& m_inputStack.last().connection == connection
&& m_inputStack.last().path == path) {
authorize();
}
}
}
......@@ -56,6 +56,7 @@ public slots:
void SetActive(const QDBusObjectPath &path, bool active);
void EnterSecurityCode(const QDBusObjectPath &path, const QString &code);
void RequestSecurityCode(const QDBusObjectPath &path);
void Authorize(const QDBusObjectPath &path);
void Cancel(const QDBusObjectPath &path);
private:
......@@ -110,17 +111,28 @@ public:
virtual void enterSecurityCode(const QString &code) = 0;
virtual void requestSecurityCode() = 0;
virtual void authorize();
void cancel() override = 0;
// Client
Authenticator::Methods activeMethods() const { return m_activeMethods; }
void startAuthentication(
AuthenticationInput::Feedback feedback,
const QVariantMap &data,
Authenticator::Methods methods);
void startAuthentication(
AuthenticationInput::Feedback feedback,
uint authenticatingPid,
const QVariantMap &data,
Authenticator::Methods methods);
virtual void authenticationStarted(
Authenticator::Methods methods,
uint authenticatingPid,
AuthenticationInput::Feedback feedback = AuthenticationInput::EnterSecurityCode);
void authenticationUnavailable(AuthenticationInput::Error error);
void authenticationUnavailable(AuthenticationInput::Error error, uint authenticatingPid);
void authenticationResumed(
AuthenticationInput::Feedback feedback,
const QVariantMap &data = QVariantMap(),
......@@ -132,9 +144,9 @@ public:
virtual void authenticationActive(Authenticator::Methods methods);
virtual void authenticationInactive();
virtual void confirmAuthentication() = 0;
virtual void confirmAuthentication(Authenticator::Method method) = 0;
virtual void abortAuthentication(AuthenticationInput::Error error);
\
// Signals
void feedback(
AuthenticationInput::Feedback feedback,
......@@ -159,7 +171,7 @@ private:
struct Input
{
Input() {}
Input() = default;
Input(const QString &connection, const QString &path) : connection(connection), path(path) {}
QString connection;
......@@ -169,6 +181,7 @@ private:
inline void handleEnterSecurityCode(const QString &client, const QString &code);
inline void handleRequestSecurityCode(const QString &path);
inline void handleCancel(const QString &client);
inline void handleAuthorize(const QString &client);
inline void setRegistered(const QString &path, bool registered);
inline void setActive(const QString &path, bool active);
......
......@@ -34,6 +34,12 @@
#include "settingswatcher.h"
#include <QDBusArgument>
#include <QDBusConnection>
#include <QDBusMetaType>
#include <unistd.h>
namespace NemoDeviceLock
{
......@@ -58,6 +64,12 @@ void HostAuthenticatorAdaptor::Authenticate(
path.path(), challengeCode.variant(), Authenticator::Methods(methods));
}
void HostAuthenticatorAdaptor::RequestPermission(
const QDBusObjectPath &path, const QString &message, const QVariantMap &properties, uint methods)
{
m_authenticator->requestPermission(path.path(), message, properties, Authenticator::Methods(methods));
}
void HostAuthenticatorAdaptor::Cancel(const QDBusObjectPath &path)
{
m_authenticator->handleCancel(path.path());
......@@ -99,6 +111,7 @@ HostAuthenticator::HostAuthenticator(Authenticator::Methods supportedMethods, QO
, m_adaptor(this)
, m_securityCodeAdaptor(this)
, m_repeatsRequired(0)
, m_authenticatingPid(0)
, m_state(Idle)
{
}
......@@ -135,15 +148,24 @@ void HostAuthenticator::authenticate(
const auto availability = this->availability();
switch (availability) {
case AuthenticationNotRequired:
qCDebug(daemon, "Authentication requested. Unsecured, authenticating immediately.");
confirmAuthentication();
if (methods & Authenticator::Confirmation) {
qCDebug(daemon, "Authentication requested. Requesting simple confirmation.");
startAuthentication(AuthenticationInput::Authorize, QVariantMap(), Authenticator::Confirmation);
} else {
qCDebug(daemon, "Authentication requested. Unsecured, authenticating immediately.");
confirmAuthentication(Authenticator::NoAuthentication);
}
break;
case CanAuthenticateSecurityCode:
methods &= Authenticator::SecurityCode;
methods &= Authenticator::SecurityCode | Authenticator::Confirmation;
// Fall through.
case CanAuthenticate:
qCDebug(daemon, "Authentication requested using methods %i.", int(methods));
startAuthentication(AuthenticationInput::EnterSecurityCode, QVariantMap(), methods);
if (methods == Authenticator::Confirmation) {
startAuthentication(AuthenticationInput::Authorize, QVariantMap(), Authenticator::Confirmation);
} else {
startAuthentication(AuthenticationInput::EnterSecurityCode, QVariantMap(), methods);
}
break;
case SecurityCodeRequired:
m_challengeCode.clear();
......@@ -159,6 +181,55 @@ void HostAuthenticator::authenticate(
}
}
void HostAuthenticator::requestPermission(
const QString &client,
const QString &message,
const QVariantMap &properties,
Authenticator::Methods methods)
{
cancel();
setActiveClient(client);
m_state = RequestingPermission;
const uint authenticatingPid = properties.value(
QStringLiteral("authenticatingPid"),
QVariant::fromValue(connectionPid(QDBusContext::connection()))).toUInt();
const QVariantMap data = {
{ QStringLiteral("message"), message }
};
const auto availability = this->availability();
switch (availability) {
case AuthenticationNotRequired:
qCDebug(daemon, "Authentication requested. Requesting simple confirmation.");
startAuthentication(AuthenticationInput::Authorize, authenticatingPid, data, Authenticator::Confirmation);
break;
case CanAuthenticateSecurityCode:
methods &= Authenticator::SecurityCode | Authenticator::Confirmation;
// Fall through.
case CanAuthenticate:
qCDebug(daemon, "Authentication requested using methods %i.", int(methods));
if (methods == Authenticator::Confirmation) {
startAuthentication(AuthenticationInput::Authorize, authenticatingPid, data, methods);
} else {
startAuthentication(AuthenticationInput::EnterSecurityCode, authenticatingPid, data, methods);
}
break;
case SecurityCodeRequired:
authenticationUnavailable(AuthenticationInput::FunctionUnavailable, authenticatingPid);
break;
case CodeEntryLockedRecoverable:
case CodeEntryLockedPermanent:
case ManagerLockedRecoverable:
case ManagerLockedPermanent:
lockedOut();
break;
}
}
void HostAuthenticator::handleChangeSecurityCode(const QString &client, const QVariant &challengeCode)
{
const auto pid = connectionPid(QDBusContext::connection());
......@@ -233,11 +304,12 @@ void HostAuthenticator::enterSecurityCode(const QString &code)
case Idle:
return;
case Authenticating:
case RequestingPermission:
qCDebug(daemon, "Security code entered for authentication.");
switch ((attempts = checkCode(code))) {
case Success:
case SecurityCodeExpired:
confirmAuthentication();
confirmAuthentication(Authenticator::SecurityCode);
return;
case LockedOut:
lockedOut();
......@@ -342,6 +414,23 @@ void HostAuthenticator::requestSecurityCode()
}
}
void HostAuthenticator::authorize()
{
switch (m_state) {
case Authenticating:
case RequestingPermission:
if (activeMethods() == Authenticator::Confirmation) {
qCDebug(daemon, "Action authorized without authentication.");
confirmAuthentication(Authenticator::Confirmation);
} else {
qCWarning(daemon, "Authorization requires authentication, rejecting.");
}
break;
default:
break;
}
}
void HostAuthenticator::setCodeFinished(int result)
{
switch (result) {
......@@ -349,7 +438,8 @@ void HostAuthenticator::setCodeFinished(int result)
m_currentCode.clear();
qCDebug(daemon, "Security code changed.");
securityCodeChanged(authenticateChallengeCode(m_challengeCode));
securityCodeChanged(authenticateChallengeCode(
m_challengeCode, Authenticator::SecurityCode, m_authenticatingPid));
break;
case SecurityCodeInHistory:
if (m_state == ChangeCanceled) {
......@@ -377,9 +467,14 @@ void HostAuthenticator::setCodeFinished(int result)
}
}
void HostAuthenticator::confirmAuthentication()
void HostAuthenticator::confirmAuthentication(Authenticator::Method method)
{
authenticated(authenticateChallengeCode(m_challengeCode));
if (m_state == RequestingPermission) {
sendToActiveClient(authenticatorInterface, QStringLiteral("PermissionGranted"), uint(method));
authenticationEnded(true);
} else {
authenticated(authenticateChallengeCode(m_challengeCode, method, m_authenticatingPid));
}
}
void HostAuthenticator::abortAuthentication(AuthenticationInput::Error error)
......@@ -388,6 +483,9 @@ void HostAuthenticator::abortAuthentication(AuthenticationInput::Error error)
case Authenticating:
m_state = AuthenticationError;
break;
case RequestingPermission:
m_state = AuthenticationError;
break;
case AuthenticatingForChange:
case EnteringNewSecurityCode:
case RepeatingNewSecurityCode:
......@@ -404,10 +502,19 @@ void HostAuthenticator::abortAuthentication(AuthenticationInput::Error error)
HostAuthenticationInput::abortAuthentication(error);
}
void HostAuthenticator::authenticationStarted(
Authenticator::Methods methods, uint authenticatingPid, AuthenticationInput::Feedback feedback)
{
m_authenticatingPid = authenticatingPid;
HostAuthenticationInput::authenticationStarted(methods, authenticatingPid, feedback);
}
void HostAuthenticator::authenticationEnded(bool confirmed)
{
clearActiveClient();
m_authenticatingPid = 0;
m_challengeCode.clear();
m_state = Idle;
m_currentCode.clear();
......@@ -421,6 +528,7 @@ void HostAuthenticator::cancel()
switch (m_state) {
case Authenticating:
case AuthenticationError:
case RequestingPermission:
aborted();
break;
case AuthenticatingForChange:
......
......@@ -34,10 +34,11 @@
#define NEMODEVICELOCK_HOSTAUTHENTICATOR_H
#include <QDBusAbstractAdaptor>
#include <QDBusContext>
#include <QDBusMessage>
#include <QDBusObjectPath>
#include <QDBusVariant>
#include <nemo-dbus/interface.h>
#include <nemo-devicelock/host/hostauthenticationinput.h>
#include <nemo-devicelock/host/hostobject.h>
......@@ -61,6 +62,8 @@ public:
public slots:
void Authenticate(const QDBusObjectPath &client, const QDBusVariant &challengeCode, uint methods);
void RequestPermission(
const QDBusObjectPath &path, const QString &message, const QVariantMap &properties, uint methods);
void Cancel(const QDBusObjectPath &client);
private:
......@@ -102,7 +105,8 @@ public:
// Authenticator
virtual Authenticator::Methods availableMethods() const = 0;
virtual QVariant authenticateChallengeCode(const QVariant &challengeCode) = 0;
virtual QVariant authenticateChallengeCode(
const QVariant &challengeCode, Authenticator::Method method, uint authenticatingPid) = 0;
// SecurityCodeSettings
virtual bool authorizeSecurityCodeSettings(unsigned long pid);
......@@ -116,10 +120,13 @@ public:
void enterSecurityCode(const QString &code) override;
void requestSecurityCode() override;
void authorize() override;
void cancel() override;
void confirmAuthentication() override;
void confirmAuthentication(Authenticator::Method method) override;
void abortAuthentication(AuthenticationInput::Error error) override;
void authenticationStarted(
Authenticator::Methods methods, uint authenticatingPid, AuthenticationInput::Feedback feedback) override;
void authenticationEnded(bool confirmed) override;
void setCodeFinished(int result);