Commit 89bea1b1 authored by Andrew den Exter's avatar Andrew den Exter

Merge branch 'jb40797' into 'master'

[devicelock] Allow backends to provide a reason for a lockout. Contributes to JB#40797

See merge request !32
parents acc349be e40cbfc3
......@@ -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;
......
......@@ -65,7 +65,7 @@ Authenticator::Methods CliAuthenticator::availableMethods() const
}
HostAuthenticationInput::Availability CliAuthenticator::availability() const
HostAuthenticationInput::Availability CliAuthenticator::availability(QVariantMap *) const
{
if (m_watcher->securityCodeSet()) {
const int maximum = maximumAttempts();
......@@ -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;
}
......
......@@ -51,14 +51,15 @@ public:
~CliAuthenticator();
Authenticator::Methods availableMethods() const override;
Availability availability() const override;
Availability availability(QVariantMap *feedbackData) const override;
int checkCode(const QString &code) override;
int setCode(const QString &oldCode, const QString &newCode) override;
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;
......
......@@ -54,7 +54,7 @@ CliDeviceLock::~CliDeviceLock()
{
}
HostAuthenticationInput::Availability CliDeviceLock::availability() const
HostAuthenticationInput::Availability CliDeviceLock::availability(QVariantMap *) const
{
if (m_watcher->securityCodeSet()) {
const int maximum = maximumAttempts();
......
......@@ -49,7 +49,7 @@ public:
CliDeviceLock(QObject *parent = nullptr);
~CliDeviceLock();
Availability availability() const override;
Availability availability(QVariantMap *data) const override;
int checkCode(const QString &code) override;
int setCode(const QString &oldCode, const QString &newCode) override;
......
......@@ -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);
}
}
void HostAuthenticationInput::authenticationUnavailable(
AuthenticationInput::Error error, uint authenticatingPid)
{
qCDebug(daemon, "Authentication unavailable");
if (pid != 0 && !m_inputStack.isEmpty()) {
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;
......@@ -344,29 +371,31 @@ void HostAuthenticationInput::feedback(
void HostAuthenticationInput::lockedOut()
{
lockedOut(availability(), &HostAuthenticationInput::abortAuthentication);
QVariantMap data;
lockedOut(availability(&data), &HostAuthenticationInput::abortAuthentication, data);
}
void HostAuthenticationInput::lockedOut(
Availability availability,
void (HostAuthenticationInput::*errorFunction)(AuthenticationInput::Error error))
void (HostAuthenticationInput::*errorFunction)(AuthenticationInput::Error error),
const QVariantMap &data)
{
switch (availability) {
case CodeEntryLockedRecoverable:
(this->*errorFunction)(AuthenticationInput::MaximumAttemptsExceeded);
feedback(AuthenticationInput::TemporarilyLocked, -1);
feedback(AuthenticationInput::TemporarilyLocked, data);
break;
case CodeEntryLockedPermanent:
(this->*errorFunction)(AuthenticationInput::MaximumAttemptsExceeded);
feedback(AuthenticationInput::PermanentlyLocked, -1);
feedback(AuthenticationInput::PermanentlyLocked, data);
break;
case ManagerLockedRecoverable:
(this->*errorFunction)(AuthenticationInput::LockedByManager);
feedback(AuthenticationInput::ContactSupport, -1);
feedback(AuthenticationInput::ContactSupport, data);
break;
case ManagerLockedPermanent:
(this->*errorFunction)(AuthenticationInput::LockedByManager);
feedback(AuthenticationInput::PermanentlyLocked, -1);
feedback(AuthenticationInput::PermanentlyLocked, data);
break;
default:
// Locked out but availability doesn't reflect this. This shouldn't be reachable
......@@ -440,4 +469,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:
......@@ -95,7 +96,7 @@ public:
QObject *parent = nullptr);
virtual ~HostAuthenticationInput();
virtual Availability availability() const = 0;
virtual Availability availability(QVariantMap *feedbackData = nullptr) const = 0;
virtual int checkCode(const QString &code) = 0;
virtual int setCode(const QString &oldCode, const QString &newCode) = 0;
......@@ -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,
......@@ -152,14 +164,15 @@ protected:
void lockedOut();
void lockedOut(
Availability availability,
void (HostAuthenticationInput::*errorFunction)(AuthenticationInput::Error error));
void (HostAuthenticationInput::*errorFunction)(AuthenticationInput::Error error),
const QVariantMap &data);
private:
friend class HostAuthenticationInputAdaptor;
struct Input
{
Input() {}
Input() = default;
Input(const QString &connection, const QString &path) : connection(connection), path(path) {}
QString connection;
......@@ -169,6 +182,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)
{
}
......@@ -132,18 +145,28 @@ void HostAuthenticator::authenticate(
m_state = Authenticating;
m_challengeCode = challengeCode;
const auto availability = this->availability();
QVariantMap feedbackData;
const auto availability = this->availability(&feedbackData);
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();
......@@ -154,7 +177,56 @@ void HostAuthenticator::authenticate(
case ManagerLockedRecoverable:
case ManagerLockedPermanent:
m_challengeCode.clear();
lockedOut(availability, &HostAuthenticationInput::authenticationUnavailable);
lockedOut(availability, &HostAuthenticationInput::authenticationUnavailable, feedbackData);
break;
}
}
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();
QVariantMap data = {
{ QStringLiteral("message"), message }
};
const auto availability = this->availability(&data);
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(availability, &HostAuthenticationInput::authenticationUnavailable, data);
break;
}
}
......@@ -233,11 +305,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 +415,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 +439,8 @@ void HostAuthenticator::setCodeFinished(int result)
m_currentCode.clear();
qCDebug(daemon, "Security code changed.");
securityCodeChanged(authenticateChallengeCode(m_challengeCode));