add pushbutton auth

This commit is contained in:
Michael Zanetti 2017-11-09 22:52:07 +01:00
parent 68a5798cf9
commit 99aa833ce8
39 changed files with 1163 additions and 187 deletions

View File

@ -0,0 +1,14 @@
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<policy user="root">
<allow own="io.guh.nymead"/>
<allow send_destination="io.guh.nymead"/>
</policy>
<policy context="default">
<allow send_destination="io.guh.nymead"/>
</policy>
</busconfig>

1
debian/control vendored
View File

@ -25,6 +25,7 @@ Build-Depends: debhelper (>= 9.0.0),
libssl-dev,
libmbedtls-dev,
libaws-iot-device-sdk-cpp,
dbus-test-runner,
Package: guh
Architecture: any

View File

@ -5,3 +5,4 @@ usr/lib/@DEB_HOST_MULTIARCH@/libguh-core.so.1.0
usr/lib/@DEB_HOST_MULTIARCH@/libguh-core.so.1.0.0
data/systemd/guhd.service /lib/systemd/system/
data/logrotate/guhd /etc/logrotate.d/
data/dbus-1/io.guh.nymead.conf /etc/dbus-1/system.d/

2
debian/rules vendored
View File

@ -43,7 +43,7 @@ override_dh_install: $(PREPROCESS_FILES:.in=)
cp -a $(CURDIR)/doc/html $(CURDIR)/debian/guh-doc/usr/share/doc/guh/ || true
override_dh_auto_test:
dh_auto_test -- -k TESTARGS="-o -,txt -o test-results.xml,xunitxml"
dh_auto_test -- -k TESTARGS="-m 120 -p -o -p -,txt -p -o -p test-results.xml,xunitxml" TESTRUNNER="dbus-test-runner --bus-type=system --task"
override_dh_strip:
dh_strip --dbg-package=guh-dbg

View File

@ -6,7 +6,7 @@ GUH_PLUGINS_PATH=/usr/lib/$$system('dpkg-architecture -q DEB_HOST_MULTIARCH')/gu
# define protocol versions
JSON_PROTOCOL_VERSION_MAJOR=0
JSON_PROTOCOL_VERSION_MINOR=53
JSON_PROTOCOL_VERSION_MINOR=54
REST_API_VERSION=1
DEFINES += GUH_VERSION_STRING=\\\"$${GUH_VERSION_STRING}\\\" \

View File

@ -438,6 +438,9 @@ void GuhCore::init() {
qCDebug(dcApplication) << "Creating Rule Engine";
m_ruleEngine = new RuleEngine(this);
qCDebug(dcApplication()) << "Creating User Manager";
m_userManager = new UserManager(this);
qCDebug(dcApplication) << "Creating Server Manager";
m_serverManager = new ServerManager(m_configuration, this);
@ -445,9 +448,6 @@ void GuhCore::init() {
qCDebug(dcApplication) << "Creating Network Manager";
m_networkManager = new NetworkManager(this);
qCDebug(dcApplication) << "Creating User Manager";
m_userManager = new UserManager(this);
qCDebug(dcApplication) << "Creating Cloud Manager";
m_cloudManager = new CloudManager(m_networkManager, this);
m_cloudManager->setDeviceId(m_configuration->serverUuid());

View File

@ -0,0 +1,61 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2017 Michael Zanetti <michael.zanetti@guh.io> *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "guhdbusservice.h"
namespace guhserver {
QDBusConnection GuhDBusService::s_connection = QDBusConnection::systemBus();
GuhDBusService::GuhDBusService(const QString &objectPath, UserManager *parent) : QObject(parent)
{
bool status = s_connection.registerService("io.guh.nymead");
if (!status) {
qCWarning(dcApplication) << "Failed to register D-Bus service.";
return;
}
status = s_connection.registerObject(objectPath, this, QDBusConnection::ExportScriptableContents);
if (!status) {
qCWarning(dcApplication()) << "Failed to register D-Bus object.";
return;
}
m_isValid = true;
}
bool GuhDBusService::isValid()
{
return m_isValid;
}
QDBusConnection GuhDBusService::connection() const
{
return s_connection;
}
void GuhDBusService::setBusType(QDBusConnection::BusType busType)
{
if (busType == QDBusConnection::SessionBus) {
s_connection = QDBusConnection::sessionBus();
} else {
s_connection = QDBusConnection::systemBus();
}
}
}

View File

@ -0,0 +1,53 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2017 Michael Zanetti <michael.zanetti@guh.io> *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef GUHDBUSSERVICE_H
#define GUHDBUSSERVICE_H
#include <QObject>
#include <QDBusConnection>
#include "guhcore.h"
namespace guhserver {
class GuhDBusService : public QObject, public QDBusContext
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "io.guh.nymead")
public:
explicit GuhDBusService(const QString &objectPath, UserManager *parent = nullptr);
static void setBusType(QDBusConnection::BusType busType);
bool isValid();
protected:
QDBusConnection connection() const;
private:
static QDBusConnection s_connection;
bool m_isValid = false;
};
}
#endif // GUHDBUSSERVICE_H

View File

@ -328,7 +328,7 @@ void JsonReply::setCommandId(int commandId)
/*! Start the timeout timer for this \l{JsonReply}. The default timeout is 15 seconds. */
void JsonReply::startWait()
{
m_timeout.start(15000);
m_timeout.start(30000);
}
void JsonReply::timeout()

View File

@ -86,6 +86,7 @@ JsonRPCServer::JsonRPCServer(const QSslConfiguration &sslConfiguration, QObject
returns.insert("protocol version", JsonTypes::basicTypeToString(JsonTypes::String));
returns.insert("initialSetupRequired", JsonTypes::basicTypeToString(JsonTypes::Bool));
returns.insert("authenticationRequired", JsonTypes::basicTypeToString(JsonTypes::Bool));
returns.insert("pushButtonAuthAvailable", JsonTypes::basicTypeToString(JsonTypes::Bool));
setReturns("Hello", returns);
params.clear(); returns.clear();
@ -118,17 +119,40 @@ JsonRPCServer::JsonRPCServer(const QSslConfiguration &sslConfiguration, QObject
setReturns("CreateUser", returns);
params.clear(); returns.clear();
setDescription("Authenticate", "Authenticate a client to the api. This will return a new token to be used to authorize a client at the API.");
setDescription("Authenticate", "Authenticate a client to the api via user & password challenge. Provide "
"a device name which allows the user to identify the client and revoke the token in case "
"the device is lost or stolen. This will return a new token to be used to authorize a "
"client at the API.");
params.insert("username", JsonTypes::basicTypeToString(JsonTypes::String));
params.insert("password", JsonTypes::basicTypeToString(JsonTypes::String));
params.insert("deviceName", JsonTypes::basicTypeToString(JsonTypes::String));
setParams("Authenticate", params);
returns.insert("success", JsonTypes::basicTypeToString(JsonTypes::String));
returns.insert("success", JsonTypes::basicTypeToString(JsonTypes::Bool));
returns.insert("o:token", JsonTypes::basicTypeToString(JsonTypes::String));
setReturns("Authenticate", returns);
params.clear(); returns.clear();
setDescription("Tokens", "Return a list of if TokenInfo objects all the tokens for the current user.");
setDescription("RequestPushButtonAuth", "Authenticate a client to the api via Push Button method. "
"Provide a device name which allows the user to identify the client and revoke the "
"token in case the device is lost or stolen. If push button hardware is available, "
"this will return with success and start listening for push button presses. When the "
"push button is pressed, the PushButtonAuthFinished notification will be sent to the "
"requesting client. The procedure will be cancelled when the connection is interrupted. "
"If another client requests push button authentication while a procedure is still going "
"on, the second call will take over and the first one will be notified by the "
"PushButtonAuthFinished signal about the error. The application should make it clear "
"to the user to not press the button when the procedure fails as this can happen for 2 "
"reasons: a) a second user is trying to auth at the same time and only the currently "
"active user should press the button or b) it might indicate an attacker trying to take "
"over and snooping in for tokens.");
params.insert("deviceName", JsonTypes::basicTypeToString(JsonTypes::String));
setParams("RequestPushButtonAuth", params);
returns.insert("success", JsonTypes::basicTypeToString(JsonTypes::Bool));
returns.insert("transactionId", JsonTypes::basicTypeToString(JsonTypes::Int));
setReturns("RequestPushButtonAuth", returns);
params.clear(); returns.clear();
setDescription("Tokens", "Return a list of TokenInfo objects of all the tokens for the current user.");
setParams("Tokens", params);
returns.insert("tokenInfoList", QVariantList() << JsonTypes::tokenInfoRef());
setReturns("Tokens", returns);
@ -168,7 +192,16 @@ JsonRPCServer::JsonRPCServer(const QSslConfiguration &sslConfiguration, QObject
params.insert("connected", JsonTypes::basicTypeToString(JsonTypes::Bool));
setParams("CloudConnectedChanged", params);
params.clear();
setDescription("PushButtonAuthFinished", "Emitted when a push button authentication reaches final state. NOTE: This notification is special. It will only be emitted to connections that did actively request a push button authentication, but also it will be emitted regardless of the notification settings. ");
params.insert("status", JsonTypes::userErrorRef());
params.insert("transactionId", JsonTypes::basicTypeToString(JsonTypes::Int));
params.insert("o:token", JsonTypes::basicTypeToString(JsonTypes::String));
setParams("PushButtonAuthFinished", params);
QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection);
connect(GuhCore::instance()->userManager(), &UserManager::pushButtonAuthFinished, this, &JsonRPCServer::onPushButtonAuthFinished);
}
/*! Returns the \e namespace of \l{JsonHandler}. */
@ -218,9 +251,9 @@ JsonReply* JsonRPCServer::Version(const QVariantMap &params) const
JsonReply* JsonRPCServer::SetNotificationStatus(const QVariantMap &params)
{
QUuid clientId = this->property("clientId").toUuid();
m_clients[clientId] = params.value("enabled").toBool();
m_clientNotifications[clientId] = params.value("enabled").toBool();
QVariantMap returns;
returns.insert("enabled", m_clients[clientId]);
returns.insert("enabled", m_clientNotifications[clientId]);
return createReply(returns);
}
@ -251,6 +284,21 @@ JsonReply *JsonRPCServer::Authenticate(const QVariantMap &params)
return createReply(ret);
}
JsonReply *JsonRPCServer::RequestPushButtonAuth(const QVariantMap &params)
{
QString deviceName = params.value("deviceName").toString();
QUuid clientId = this->property("clientId").toUuid();
int transactionId = GuhCore::instance()->userManager()->requestPushButtonAuth(deviceName);
m_pushButtonTransactions.insert(transactionId, clientId);
QVariantMap data;
data.insert("transactionId", transactionId);
// TODO: return false if pushbutton auth is disabled in settings
data.insert("success", true);
return createReply(data);
}
JsonReply *JsonRPCServer::Tokens(const QVariantMap &params) const
{
Q_UNUSED(params)
@ -390,6 +438,7 @@ QVariantMap JsonRPCServer::createWelcomeMessage(TransportInterface *interface) c
handshake.insert("protocol version", JSON_PROTOCOL_VERSION);
handshake.insert("initialSetupRequired", (interface->configuration().authenticationEnabled ? GuhCore::instance()->userManager()->users().isEmpty() : false));
handshake.insert("authenticationRequired", interface->configuration().authenticationEnabled);
handshake.insert("pushButtonAuthAvailable", GuhCore::instance()->userManager()->pushButtonAuthAvailable());
return handshake;
}
@ -444,16 +493,18 @@ void JsonRPCServer::processData(const QUuid &clientId, const QByteArray &data)
// check if authentication is required for this transport
if (m_interfaces.value(interface)) {
// if there is no user in the system yet, let's fail unless this is a CreateUser, Introspect or Hello call
QByteArray token = message.value("token").toByteArray();
QStringList authExemptMethodsNoUser = {"Introspect", "Hello", "CreateUser", "RequestPushButtonAuth"};
QStringList authExemptMethodsWithUser = {"Introspect", "Hello", "Authenticate", "RequestPushButtonAuth"};
// if there is no user in the system yet, let's fail unless this is special method for authentication itself
if (GuhCore::instance()->userManager()->users().isEmpty()) {
if (!(targetNamespace == "JSONRPC" && (method == "CreateUser" || method == "Introspect" || method == "Hello"))) {
if (!(targetNamespace == "JSONRPC" && authExemptMethodsNoUser.contains(method)) && (token.isEmpty() || !GuhCore::instance()->userManager()->verifyToken(token))) {
sendUnauthorizedResponse(interface, clientId, commandId, "Initial setup required. Call CreateUser first.");
return;
}
} else {
// ok, we have a user. if there isn't a valid token, let's fail unless this is a Authenticate, Introspect or Hello call
QByteArray token = message.value("token").toByteArray();
if (!(targetNamespace == "JSONRPC" && (method == "Authenticate" || method == "Introspect" || method == "Hello")) && !GuhCore::instance()->userManager()->verifyToken(token)) {
// ok, we have a user. if there isn't a valid token, let's fail unless this is a Authenticate, Introspect Hello call
if (!(targetNamespace == "JSONRPC" && authExemptMethodsWithUser.contains(method)) && (token.isEmpty() || !GuhCore::instance()->userManager()->verifyToken(token))) {
sendUnauthorizedResponse(interface, clientId, commandId, "Forbidden: Invalid token.");
return;
}
@ -522,8 +573,8 @@ void JsonRPCServer::sendNotification(const QVariantMap &params)
notification.insert("notification", handler->name() + "." + method.name());
notification.insert("params", params);
foreach (TransportInterface *interface, m_interfaces.keys()) {
interface->sendData(m_clients.keys(true), QJsonDocument::fromVariant(notification).toJson(QJsonDocument::Compact));
foreach (const QUuid &clientId, m_clientNotifications.keys(true)) {
m_clientTransports.value(clientId)->sendData(clientId, QJsonDocument::fromVariant(notification).toJson(QJsonDocument::Compact));
}
}
@ -567,6 +618,35 @@ void JsonRPCServer::onCloudConnectedChanged(bool connected)
emit CloudConnectedChanged(params);
}
void JsonRPCServer::onPushButtonAuthFinished(int transactionId, bool success, const QByteArray &token)
{
QUuid clientId = m_pushButtonTransactions.take(transactionId);
if (clientId.isNull()) {
qCDebug(dcJsonRpc()) << "Received a PushButton reply but wasn't expecting it.";
return;
}
TransportInterface *transport = m_clientTransports.value(clientId);
if (!transport) {
qCWarning(dcJsonRpc()) << "No transport for given clientId";
return;
}
QVariantMap params;
params.insert("transactionId", transactionId);
params.insert("success", success);
if (success) {
params.insert("token", token);
}
QVariantMap notification;
notification.insert("id", transactionId);
notification.insert("notification", "JSONRPC.PushButtonAuthFinished");
notification.insert("params", params);
transport->sendData(clientId, QJsonDocument::fromVariant(notification).toJson(QJsonDocument::Compact));
}
void JsonRPCServer::registerHandler(JsonHandler *handler)
{
m_handlers.insert(handler->name(), handler);
@ -582,15 +662,21 @@ void JsonRPCServer::clientConnected(const QUuid &clientId)
{
TransportInterface *interface = qobject_cast<TransportInterface *>(sender());
m_clientTransports.insert(clientId, interface);
// If authentication is required, notifications are disabled by default. Clients must enable them with a valid token
m_clients.insert(clientId, !interface->configuration().authenticationEnabled);
m_clientNotifications.insert(clientId, !interface->configuration().authenticationEnabled);
interface->sendData(clientId, QJsonDocument::fromVariant(createWelcomeMessage(interface)).toJson(QJsonDocument::Compact));
}
void JsonRPCServer::clientDisconnected(const QUuid &clientId)
{
m_clients.remove(clientId);
m_clientTransports.remove(clientId);
m_clientNotifications.remove(clientId);
if (m_pushButtonTransactions.values().contains(clientId)) {
GuhCore::instance()->userManager()->cancelPushButtonAuth(m_pushButtonTransactions.key(clientId));
}
}
}

View File

@ -54,6 +54,7 @@ public:
Q_INVOKABLE JsonReply *CreateUser(const QVariantMap &params);
Q_INVOKABLE JsonReply *Authenticate(const QVariantMap &params);
Q_INVOKABLE JsonReply *RequestPushButtonAuth(const QVariantMap &params);
Q_INVOKABLE JsonReply *Tokens(const QVariantMap &params) const;
Q_INVOKABLE JsonReply *RemoveToken(const QVariantMap &params);
Q_INVOKABLE JsonReply *SetupRemoteAccess(const QVariantMap &params);
@ -62,8 +63,9 @@ public:
signals:
void CloudConnectedChanged(const QVariantMap &map);
void PushButtonAuthFinished(const QVariantMap &params);
// Internal
// Server API
public:
void registerTransportInterface(TransportInterface *interface, bool authenticationRequired);
void unregisterTransportInterface(TransportInterface *interface);
@ -90,14 +92,16 @@ private slots:
void pairingFinished(QString cognitoUserId, int status, const QString &message);
void onCloudConnectedChanged(bool connected);
void onPushButtonAuthFinished(int transactionId, bool success, const QByteArray &token);
private:
QMap<TransportInterface*, bool> m_interfaces;
QMap<TransportInterface*, bool> m_interfaces; // Interface, authenticationRequired
QHash<QString, JsonHandler *> m_handlers;
QHash<JsonReply *, TransportInterface *> m_asyncReplies;
// clientId, notificationsEnabled
QHash<QUuid, bool> m_clients;
QHash<QUuid, TransportInterface*> m_clientTransports;
QHash<QUuid, bool> m_clientNotifications;
QHash<int, QUuid> m_pushButtonTransactions;
QHash<QString, JsonReply*> m_pairingRequests;

View File

@ -75,6 +75,9 @@ HEADERS += guhcore.h \
cloudmanager.h \
MbedTLS/MbedTLSConnection.hpp \
janusconnector.h \
pushbuttondbusservice.h \
guhdbusservice.h
SOURCES += guhcore.cpp \
tcpserver.cpp \
@ -132,3 +135,5 @@ SOURCES += guhcore.cpp \
cloudmanager.cpp \
MbedTLS/MbedTLSConnection.cpp \
janusconnector.cpp \
pushbuttondbusservice.cpp \
guhdbusservice.cpp

View File

@ -0,0 +1,87 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2017 Michael Zanetti <michael.zanetti@guh.io> *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "pushbuttondbusservice.h"
#include "loggingcategories.h"
#include <QDBusConnection>
#include <QDebug>
#include <QDBusServiceWatcher>
namespace guhserver {
PushButtonDBusService::PushButtonDBusService(const QString &objectPath, UserManager *parent) : GuhDBusService(objectPath, parent),
m_userManager(parent)
{
if (!isValid()) {
qCWarning(dcUserManager()) << "Failed to register PushButton D-Bus service.";
return;
}
qCDebug(dcUserManager()) << "PushButton D-Bus service set up.";
}
bool PushButtonDBusService::agentAvailable() const
{
return m_registeredAgents.count() > 0;
}
void PushButtonDBusService::RegisterButtonAgent(const QDBusObjectPath &agentPath)
{
QDBusMessage msg = message();
connection().connect(QString(), QString(agentPath.path()), QString(), "PushButtonPressed", this, SIGNAL(pushButtonPressed()));
QDBusServiceWatcher *watcher = new QDBusServiceWatcher(msg.service(), connection(), QDBusServiceWatcher::WatchForUnregistration, this);
connect(watcher, &QDBusServiceWatcher::serviceUnregistered, this, &PushButtonDBusService::serviceUnregistered);
qCDebug(dcUserManager()) << "PushButton handler" << agentPath.path() << msg.service() << "registered.";
m_registeredAgents.append(msg.service());
}
void PushButtonDBusService::UnregisterButtonAgent()
{
QDBusMessage msg = message();
serviceUnregistered(msg.service());
}
QByteArray PushButtonDBusService::GenerateAuthToken(const QString &deviceName)
{
int transactionId = m_userManager->requestPushButtonAuth(deviceName);
bool success = false;
QByteArray token;
QMetaObject::Connection c = connect(m_userManager, &UserManager::pushButtonAuthFinished, this, [&] (int i, bool s, const QByteArray &t) {
if (transactionId == i) {
success = s;
token = t;
}
});
emit pushButtonPressed();
disconnect(c);
return token;
}
void PushButtonDBusService::serviceUnregistered(const QString &serviceName)
{
if (!m_registeredAgents.contains(serviceName)) {
qCWarning(dcUserManager()) << "PushButton agent" << serviceName << "not known. Cannot unregister.";
return;
}
m_registeredAgents.removeAll(serviceName);
qCDebug(dcUserManager()) << "PushButton agent" << serviceName << "unregistered";
}
}

View File

@ -0,0 +1,56 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2017 Michael Zanetti <michael.zanetti@guh.io> *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef PUSHBUTTONDBUSSERVICE_H
#define PUSHBUTTONDBUSSERVICE_H
#include <QObject>
#include "usermanager.h"
#include "guhdbusservice.h"
namespace guhserver {
class PushButtonDBusService : public GuhDBusService
{
Q_OBJECT
public:
explicit PushButtonDBusService(const QString &objectPath, UserManager *parent);
bool agentAvailable() const;
Q_SCRIPTABLE void RegisterButtonAgent(const QDBusObjectPath &agentPath);
Q_SCRIPTABLE void UnregisterButtonAgent();
Q_SCRIPTABLE QByteArray GenerateAuthToken(const QString &deviceName);
signals:
void pushButtonPressed();
private slots:
void serviceUnregistered(const QString &serviceName);
private:
UserManager *m_userManager;
QStringList m_registeredAgents;
};
}
#endif // PUSHBUTTONDBUSSERVICE_H

View File

@ -81,6 +81,8 @@ void TcpServer::sendData(const QUuid &clientId, const QByteArray &data)
client = m_clientList.value(clientId);
if (client) {
client->write(data + '\n');
} else {
qWarning(dcTcpServer()) << "Client" << clientId << "unknown to this transport";
}
}

View File

@ -22,6 +22,7 @@
#include "guhsettings.h"
#include "loggingcategories.h"
#include "guhcore.h"
#include "pushbuttondbusservice.h"
#include <QUuid>
#include <QCryptographicHash>
@ -44,6 +45,10 @@ UserManager::UserManager(QObject *parent) : QObject(parent)
return;
}
initDB();
m_pushButtonDBusService = new PushButtonDBusService("/io/guh/nymead/UserManager", this);
connect(m_pushButtonDBusService, &PushButtonDBusService::pushButtonPressed, this, &UserManager::onPushButtonPressed);
m_pushButtonTransaction = qMakePair<int, QString>(-1, QString());
}
QStringList UserManager::users() const
@ -105,6 +110,11 @@ UserManager::UserError UserManager::removeUser(const QString &username)
return UserErrorNoError;
}
bool UserManager::pushButtonAuthAvailable() const
{
return m_pushButtonDBusService->agentAvailable();
}
QByteArray UserManager::authenticate(const QString &username, const QString &password, const QString &deviceName)
{
if (!validateUsername(username)) {
@ -142,6 +152,35 @@ QByteArray UserManager::authenticate(const QString &username, const QString &pas
return token;
}
int UserManager::requestPushButtonAuth(const QString &deviceName)
{
if (m_pushButtonTransaction.first != -1) {
qCWarning(dcUserManager()) << "PushButton authentication already in progress for device" << m_pushButtonTransaction.second << ". Cancelling...";
cancelPushButtonAuth(m_pushButtonTransaction.first);
}
qCDebug(dcUserManager()) << "Starting PushButton authentication for device" << deviceName;
int transactionId = ++m_pushButtonTransactionIdCounter;
m_pushButtonTransaction = qMakePair<int, QString>(transactionId, deviceName);
return transactionId;
}
void UserManager::cancelPushButtonAuth(int transactionId)
{
if (m_pushButtonTransaction.first == -1) {
qCWarning(dcUserManager()) << "No PushButton transaction in progress. Nothing to cancel.";
return;
}
if (m_pushButtonTransaction.first != transactionId) {
qCWarning(dcUserManager()) << "PushButton transaction" << transactionId << "not in progress. Cannot cancel.";
return;
}
qCDebug(dcUserManager()) << "Cancelling PushButton transaction for device:" << m_pushButtonTransaction.second;
emit pushButtonAuthFinished(m_pushButtonTransaction.first, false, QByteArray());
m_pushButtonTransaction.first = -1;
}
QString UserManager::userForToken(const QByteArray &token) const
{
if (!validateToken(token)) {
@ -220,7 +259,7 @@ bool UserManager::verifyToken(const QByteArray &token)
qCDebug(dcUserManager) << "Authorisation failed for token" << token;
return false;
}
qCDebug(dcUserManager) << "Token authorized for user" << result.value("username").toString();
//qCDebug(dcUserManager) << "Token authorized for user" << result.value("username").toString();
return true;
}
@ -246,4 +285,30 @@ bool UserManager::validateToken(const QByteArray &token) const
return validator.exactMatch(token);
}
void UserManager::onPushButtonPressed()
{
if (m_pushButtonTransaction.first == -1) {
qCDebug(dcUserManager()) << "PushButton pressed but don't have a transaction waiting for it.";
return;
}
QByteArray token = QCryptographicHash::hash(QUuid::createUuid().toByteArray(), QCryptographicHash::Sha256).toBase64();
QString storeTokenQuery = QString("INSERT INTO tokens(id, username, token, creationdate, devicename) VALUES(\"%1\", \"%2\", \"%3\", \"%4\", \"%5\");")
.arg(QUuid::createUuid().toString())
.arg("")
.arg(QString::fromUtf8(token))
.arg(GuhCore::instance()->timeManager()->currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
.arg(m_pushButtonTransaction.second);
m_db.exec(storeTokenQuery);
if (m_db.lastError().type() != QSqlError::NoError) {
qCWarning(dcUserManager) << "Error storing token in DB:" << m_db.lastError().databaseText() << m_db.lastError().driverText();
emit pushButtonAuthFinished(m_pushButtonTransaction.first, false, QByteArray());
}
qCDebug(dcUserManager()) << "PushButton Auth succeeded";
emit pushButtonAuthFinished(m_pushButtonTransaction.first, true, token);
m_pushButtonTransaction.first = -1;
}
}

View File

@ -28,6 +28,8 @@
namespace guhserver {
class PushButtonDBusService;
class UserManager : public QObject
{
Q_OBJECT
@ -50,20 +52,33 @@ public:
UserError createUser(const QString &username, const QString &password);
UserError removeUser(const QString &username);
bool pushButtonAuthAvailable() const;
QByteArray authenticate(const QString &username, const QString &password, const QString &deviceName);
int requestPushButtonAuth(const QString &deviceName);
void cancelPushButtonAuth(int transactionId);
QString userForToken(const QByteArray &token) const;
QList<TokenInfo> tokens(const QString &username) const;
guhserver::UserManager::UserError removeToken(const QUuid &tokenId);
bool verifyToken(const QByteArray &token);
signals:
void pushButtonAuthFinished(int transactionId, bool success, const QByteArray &token);
private:
void initDB();
bool validateUsername(const QString &username) const;
bool validateToken(const QByteArray &token) const;
private slots:
void onPushButtonPressed();
private:
QSqlDatabase m_db;
PushButtonDBusService *m_pushButtonDBusService = nullptr;
int m_pushButtonTransactionIdCounter = 0;
QPair<int, QString> m_pushButtonTransaction;
};

View File

@ -93,6 +93,8 @@ void WebSocketServer::sendData(const QUuid &clientId, const QByteArray &data)
if (client) {
qCDebug(dcWebSocketServerTraffic()) << "Sending data to client" << data;
client->sendTextMessage(data + '\n');
} else {
qCWarning(dcWebSocketServer()) << "Client" << clientId << "unknown to this transport";
}
}

View File

@ -12,230 +12,230 @@
<context>
<name>MockDevice</name>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="100"/>
<location filename="../plugininfo.h" line="100"/>
<source>guh</source>
<extracomment>The name of the vendor (2062d64d-3232-433c-88bc-0d33c0ba2ba6)</extracomment>
<translation>guh</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="103"/>
<location filename="../plugininfo.h" line="103"/>
<source>Mock Device</source>
<extracomment>The name of the DeviceClass (753f0d32-0468-4d08-82ed-1964aab03298)</extracomment>
<translation>Mock Gerät</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="106"/>
<location filename="../plugininfo.h" line="106"/>
<source>http port</source>
<extracomment>The name of the paramType (d4f06047-125e-4479-9810-b54c189917f5) of Mock Device</extracomment>
<translation>HTTP Port</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="142"/>
<location filename="../plugininfo.h" line="142"/>
<source>Mock Action 3 (async)</source>
<extracomment>The name of the ActionType fbae06d3-7666-483e-a39e-ec50fe89054e of deviceClass Mock Device</extracomment>
<translation>Mock Aktion 3 (async)</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="145"/>
<location filename="../plugininfo.h" line="145"/>
<source>Mock Action 4 (broken)</source>
<extracomment>The name of the ActionType df3cf33d-26d5-4577-9132-9823bd33fad0 of deviceClass Mock Device</extracomment>
<translation>Mock Aktion 4 (kaputt)</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="148"/>
<location filename="../plugininfo.h" line="148"/>
<source>Mock Action 5 (async, broken)</source>
<extracomment>The name of the ActionType bfe89a1d-3497-4121-8318-e77c37537219 of deviceClass Mock Device</extracomment>
<translation>Mock Aktion 5 (async, kaputt)</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="154"/>
<location filename="../plugininfo.h" line="154"/>
<source>Mock Device (Auto created)</source>
<extracomment>The name of the DeviceClass (ab4257b3-7548-47ee-9bd4-7dc3004fd197)</extracomment>
<translation>Mock Gerät (Auto erstellt)</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="166"/>
<location filename="../plugininfo.h" line="166"/>
<source>Mock Device (Push Button)</source>
<extracomment>The name of the DeviceClass (9e03144c-e436-4eea-82d9-ccb33ef778db)</extracomment>
<translation>Mock Gerät (Drückknopf)</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="163"/>
<location filename="../plugininfo.h" line="163"/>
<source>Wait 3 second before you continue, the push button will be pressed automatically.</source>
<extracomment>The pairing info of deviceClass Mock Device (Push Button)</extracomment>
<translation>Warte 3 Sekunden bevor du fortfährst, the Knopf wird automatisch gerückt.</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="94"/>
<location filename="../plugininfo.h" line="94"/>
<source>configParamInt</source>
<extracomment>The name of the paramType (e1f72121-a426-45e2-b475-8262b5cdf103) of Mock Devices</extracomment>
<translation>configParamInt</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="97"/>
<location filename="../plugininfo.h" line="97"/>
<source>configParamBool</source>
<extracomment>The name of the paramType (c75723b6-ea4f-4982-9751-6c5e39c88145) of Mock Devices</extracomment>
<translation>configParamBool</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="109"/>
<location filename="../plugininfo.h" line="109"/>
<source>async</source>
<extracomment>The name of the paramType (f2977061-4dd0-4ef5-85aa-3b7134743be3) of Mock Device</extracomment>
<translation>async</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="112"/>
<location filename="../plugininfo.h" line="112"/>
<source>broken</source>
<extracomment>The name of the paramType (ae8f8901-f2c1-42a5-8111-6d2fc8e4c1e4) of Mock Device</extracomment>
<translation>kaputt</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="115"/>
<location filename="../plugininfo.h" line="115"/>
<source>resultCount</source>
<extracomment>The name of the paramType (d222adb4-2f9c-4c3f-8655-76400d0fb6ce) of Mock Device</extracomment>
<translation>Resultat Anzahl</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="118"/>
<location filename="../plugininfo.h" line="118"/>
<source>Dummy int state changed</source>
<extracomment>The name of the autocreated EventType (80baec19-54de-4948-ac46-31eabfaceb83)</extracomment>
<translation>Int Zustand verändert</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="121"/>
<location filename="../plugininfo.h" line="121"/>
<source>Dummy int state</source>
<extracomment>The name of the ParamType of StateType (80baec19-54de-4948-ac46-31eabfaceb83) of DeviceClass Mock Device</extracomment>
<translation>Dummy Int Zustand</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="124"/>
<location filename="../plugininfo.h" line="124"/>
<source>Dummy bool state changed</source>
<extracomment>The name of the autocreated EventType (9dd6a97c-dfd1-43dc-acbd-367932742310)</extracomment>
<translation>Bool Zustand verändert</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="127"/>
<location filename="../plugininfo.h" line="127"/>
<source>Dummy bool state</source>
<extracomment>The name of the ParamType of StateType (9dd6a97c-dfd1-43dc-acbd-367932742310) of DeviceClass Mock Device</extracomment>
<translation>Dummy Bool Zustand</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="130"/>
<location filename="../plugininfo.h" line="130"/>
<source>Mock Action 1 (with params)</source>
<extracomment>The name of the ActionType dea0f4e1-65e3-4981-8eaa-2701c53a9185 of deviceClass Mock Device</extracomment>
<translation>Mock Aktion 1 (Mit Parametern)</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="133"/>
<location filename="../plugininfo.h" line="133"/>
<source>mockActionParam1</source>
<extracomment>The name of the paramType (a2d3a256-a551-4712-a65b-ecd5a436a1cb) of Mock Device</extracomment>
<translation>mockActionParam1</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="136"/>
<location filename="../plugininfo.h" line="136"/>
<source>mockActionParam2</source>
<extracomment>The name of the paramType (304a4899-18be-4e3b-94f4-d03be52f3233) of Mock Device</extracomment>
<translation>mockActionParam2</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="139"/>
<location filename="../plugininfo.h" line="139"/>
<source>Mock Action 2 (without params)</source>
<extracomment>The name of the ActionType defd3ed6-1a0d-400b-8879-a0202cf39935 of deviceClass Mock Device</extracomment>
<translation>Mock Aktion (ohne Parameter</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="151"/>
<location filename="../plugininfo.h" line="151"/>
<source>mockParamInt</source>
<extracomment>The name of the paramType (0550e16d-60b9-4ba5-83f4-4d3cee656121) of Mock Device</extracomment>
<translation>mockParamInt</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="157"/>
<location filename="../plugininfo.h" line="157"/>
<source>Mock Event 1</source>
<extracomment>The name of the EventType 45bf3752-0fc6-46b9-89fd-ffd878b5b22b of deviceClass Mock Device (Auto created)</extracomment>
<translation>Mock Event 1</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="160"/>
<location filename="../plugininfo.h" line="160"/>
<source>Mock Event 2</source>
<extracomment>The name of the EventType 863d5920-b1cf-4eb9-88bd-8f7b8583b1cf of deviceClass Mock Device (Auto created)</extracomment>
<translation>Mock Event 2</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="169"/>
<location filename="../plugininfo.h" line="169"/>
<source>color changed</source>
<extracomment>The name of the autocreated EventType (20dc7c22-c50e-42db-837c-2bbced939f8e)</extracomment>
<translation>Farbe geändert</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="172"/>
<location filename="../plugininfo.h" line="172"/>
<source>color</source>
<extracomment>The name of the ParamType of StateType (20dc7c22-c50e-42db-837c-2bbced939f8e) of DeviceClass Mock Device (Push Button)</extracomment>
<translation>Farbe</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="175"/>
<location filename="../plugininfo.h" line="175"/>
<source>Set color</source>
<extracomment>The name of the autocreated ActionType (20dc7c22-c50e-42db-837c-2bbced939f8e)</extracomment>
<translation>Setze Farbe</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="178"/>
<location filename="../plugininfo.h" line="178"/>
<source>percentage changed</source>
<extracomment>The name of the autocreated EventType (72981c04-267a-4ba0-a59e-9921d2f3af9c)</extracomment>
<translation>Prozent gändert</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="181"/>
<location filename="../plugininfo.h" line="181"/>
<source>percentage</source>
<extracomment>The name of the ParamType of StateType (72981c04-267a-4ba0-a59e-9921d2f3af9c) of DeviceClass Mock Device (Push Button)</extracomment>
<translation>Prozent</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="184"/>
<location filename="../plugininfo.h" line="184"/>
<source>Set percentage</source>
<extracomment>The name of the autocreated ActionType (72981c04-267a-4ba0-a59e-9921d2f3af9c)</extracomment>
<translation>Setze Prozentwert</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="187"/>
<location filename="../plugininfo.h" line="187"/>
<source>allowed values changed</source>
<extracomment>The name of the autocreated EventType (05f63f9c-f61e-4dcf-ad55-3f13fde2765b)</extracomment>
<translation>Erlaubter Wert geändert</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="190"/>
<location filename="../plugininfo.h" line="190"/>
<source>allowed values</source>
<extracomment>The name of the ParamType of StateType (05f63f9c-f61e-4dcf-ad55-3f13fde2765b) of DeviceClass Mock Device (Push Button)</extracomment>
<translation>Erlaubte Werte</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="193"/>
<location filename="../plugininfo.h" line="193"/>
<source>Set allowed values</source>
<extracomment>The name of the autocreated ActionType (05f63f9c-f61e-4dcf-ad55-3f13fde2765b)</extracomment>
<translation>Setze erlaubten Wert</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="196"/>
<location filename="../plugininfo.h" line="196"/>
<source>double value changed</source>
<extracomment>The name of the autocreated EventType (53cd7c55-49b7-441b-b970-9048f20f0e2c)</extracomment>
<translation>Double Wert geändert</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="199"/>
<location filename="../plugininfo.h" line="199"/>
<source>double value</source>
<extracomment>The name of the ParamType of StateType (53cd7c55-49b7-441b-b970-9048f20f0e2c) of DeviceClass Mock Device (Push Button)</extracomment>
<translation>Double Wert</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="202"/>
<location filename="../plugininfo.h" line="202"/>
<source>Set double value</source>
<extracomment>The name of the autocreated ActionType (53cd7c55-49b7-441b-b970-9048f20f0e2c)</extracomment>
<translation>Setze double Wert</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="211"/>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="235"/>
<location filename="../plugininfo.h" line="211"/>
<location filename="../plugininfo.h" line="235"/>
<source>Set bool value</source>
<extracomment>The name of the autocreated ActionType (e680f7a4-b39e-46da-be41-fa3170fe3768)
----------
@ -243,20 +243,20 @@ The name of the autocreated ActionType (d24ede5f-4064-4898-bb84-cfb533b1fbc0)</e
<translation>Setze boll Wert</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="214"/>
<location filename="../plugininfo.h" line="214"/>
<source>Timeout action</source>
<extracomment>The name of the ActionType 54646e7c-bc54-4895-81a2-590d72d120f9 of deviceClass Mock Device (Push Button)</extracomment>
<translation>Timeout Aktion</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="220"/>
<location filename="../plugininfo.h" line="220"/>
<source>Mock Device (Display Pin)</source>
<extracomment>The name of the DeviceClass (296f1fd4-e893-46b2-8a42-50d1bceb8730)</extracomment>
<translation>Mock Gerät (Pin anzeigen)</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="205"/>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="229"/>
<location filename="../plugininfo.h" line="205"/>
<location filename="../plugininfo.h" line="229"/>
<source>bool value changed</source>
<extracomment>The name of the autocreated EventType (e680f7a4-b39e-46da-be41-fa3170fe3768)
----------
@ -264,8 +264,8 @@ The name of the autocreated EventType (d24ede5f-4064-4898-bb84-cfb533b1fbc0)</ex
<translation>Bool Wert geändert</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="208"/>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="232"/>
<location filename="../plugininfo.h" line="208"/>
<location filename="../plugininfo.h" line="232"/>
<source>bool value</source>
<extracomment>The name of the ParamType of StateType (e680f7a4-b39e-46da-be41-fa3170fe3768) of DeviceClass Mock Device (Push Button)
----------
@ -273,97 +273,97 @@ The name of the ParamType of StateType (d24ede5f-4064-4898-bb84-cfb533b1fbc0) of
<translation>Bool Wert</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="247"/>
<location filename="../plugininfo.h" line="247"/>
<source>Text line</source>
<extracomment>The name of the paramType (e6acf0c7-4b8e-4296-ac62-855d20deb816) of Mock Device (InputTypes)</extracomment>
<translation>Textzeile</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="250"/>
<location filename="../plugininfo.h" line="250"/>
<source>Text area</source>
<extracomment>The name of the paramType (716f0994-bc01-42b0-b64d-59236f7320d2) of Mock Device (InputTypes)</extracomment>
<translation>Textfeld</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="253"/>
<location filename="../plugininfo.h" line="253"/>
<source>Password text</source>
<extracomment>The name of the paramType (e5c0d14b-c9f1-4aca-a56e-85bfa6977150) of Mock Device (InputTypes)</extracomment>
<translation>Passwort Text</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="256"/>
<location filename="../plugininfo.h" line="256"/>
<source>Search text</source>
<extracomment>The name of the paramType (22add8c9-ee4f-43ad-8931-58e999313ac3) of Mock Device (InputTypes)</extracomment>
<translation>Suchtext</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="259"/>
<location filename="../plugininfo.h" line="259"/>
<source>Mail address</source>
<extracomment>The name of the paramType (a8494faf-3a0f-4cf3-84b7-4b39148a838d) of Mock Device (InputTypes)</extracomment>
<translation>Mail Adresse</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="262"/>
<location filename="../plugininfo.h" line="262"/>
<source>IPv4 address</source>
<extracomment>The name of the paramType (9e5f86a0-4bb3-4892-bff8-3fc4032af6e2) of Mock Device (InputTypes)</extracomment>
<translation>IPv4 Adresse</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="265"/>
<location filename="../plugininfo.h" line="265"/>
<source>IPv6 address</source>
<extracomment>The name of the paramType (43bf3832-dd48-4090-a836-656e8b60216e) of Mock Device (InputTypes)</extracomment>
<translation>IPv6 Adresse</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="268"/>
<location filename="../plugininfo.h" line="268"/>
<source>URL</source>
<extracomment>The name of the paramType (fa67229f-fcef-496f-b671-59a4b48f3ab5) of Mock Device (InputTypes)</extracomment>
<translation>URL</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="271"/>
<location filename="../plugininfo.h" line="271"/>
<source>Mac address</source>
<extracomment>The name of the paramType (e93db587-7919-48f3-8c88-1651de63c765) of Mock Device (InputTypes)</extracomment>
<translation>Mac Adresse</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="217"/>
<location filename="../plugininfo.h" line="217"/>
<source>Please enter the secret which normaly will be displayed on the device. For the mockdevice the pin is 243681.</source>
<extracomment>The pairing info of deviceClass Mock Device (Display Pin)</extracomment>
<translation>Bitte geben sie den Pincode ein der normalerweise auf dem Gerät angezeit werden würde. In diesem fall lautet der Pincode 243681.</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="91"/>
<location filename="../plugininfo.h" line="91"/>
<source>Mock Devices</source>
<extracomment>The name of the plugin Mock Devices (727a4a9a-c187-446f-aadf-f1b2220607d1)</extracomment>
<translation>Mock Gerät</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="223"/>
<location filename="../plugininfo.h" line="223"/>
<source>pin</source>
<extracomment>The name of the paramType (da820e07-22dc-4173-9c07-2f49a4e265f9) of Mock Device (Display Pin)</extracomment>
<translation>Pin</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="226"/>
<location filename="../plugininfo.h" line="226"/>
<source>Mock Device (Parent)</source>
<extracomment>The name of the DeviceClass (a71fbde9-9a38-4bf8-beab-c8aade2608ba)</extracomment>
<translation>Mock Gerät (Elternteil)</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="238"/>
<location filename="../plugininfo.h" line="238"/>
<source>Mock Device (Child)</source>
<extracomment>The name of the DeviceClass (40893c9f-bc47-40c1-8bf7-b390c7c1b4fc)</extracomment>
<translation>Mock Gerät (Kind)</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="241"/>
<location filename="../plugininfo.h" line="241"/>
<source>parent uuid</source>
<extracomment>The name of the paramType (104b5288-404e-42d3-bf38-e40682e75681) of Mock Device (Child)</extracomment>
<translation>Elternteil Uuid</translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="244"/>
<location filename="../plugininfo.h" line="244"/>
<source>Mock Device (InputTypes)</source>
<extracomment>The name of the DeviceClass (515ffdf1-55e5-498d-9abc-4e2fe768f3a9)</extracomment>
<translation>Mock Gerät (Eingabemethoden)</translation>

View File

@ -12,230 +12,230 @@
<context>
<name>MockDevice</name>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="100"/>
<location filename="../plugininfo.h" line="100"/>
<source>guh</source>
<extracomment>The name of the vendor (2062d64d-3232-433c-88bc-0d33c0ba2ba6)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="103"/>
<location filename="../plugininfo.h" line="103"/>
<source>Mock Device</source>
<extracomment>The name of the DeviceClass (753f0d32-0468-4d08-82ed-1964aab03298)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="106"/>
<location filename="../plugininfo.h" line="106"/>
<source>http port</source>
<extracomment>The name of the paramType (d4f06047-125e-4479-9810-b54c189917f5) of Mock Device</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="142"/>
<location filename="../plugininfo.h" line="142"/>
<source>Mock Action 3 (async)</source>
<extracomment>The name of the ActionType fbae06d3-7666-483e-a39e-ec50fe89054e of deviceClass Mock Device</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="145"/>
<location filename="../plugininfo.h" line="145"/>
<source>Mock Action 4 (broken)</source>
<extracomment>The name of the ActionType df3cf33d-26d5-4577-9132-9823bd33fad0 of deviceClass Mock Device</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="148"/>
<location filename="../plugininfo.h" line="148"/>
<source>Mock Action 5 (async, broken)</source>
<extracomment>The name of the ActionType bfe89a1d-3497-4121-8318-e77c37537219 of deviceClass Mock Device</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="154"/>
<location filename="../plugininfo.h" line="154"/>
<source>Mock Device (Auto created)</source>
<extracomment>The name of the DeviceClass (ab4257b3-7548-47ee-9bd4-7dc3004fd197)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="166"/>
<location filename="../plugininfo.h" line="166"/>
<source>Mock Device (Push Button)</source>
<extracomment>The name of the DeviceClass (9e03144c-e436-4eea-82d9-ccb33ef778db)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="163"/>
<location filename="../plugininfo.h" line="163"/>
<source>Wait 3 second before you continue, the push button will be pressed automatically.</source>
<extracomment>The pairing info of deviceClass Mock Device (Push Button)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="94"/>
<location filename="../plugininfo.h" line="94"/>
<source>configParamInt</source>
<extracomment>The name of the paramType (e1f72121-a426-45e2-b475-8262b5cdf103) of Mock Devices</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="97"/>
<location filename="../plugininfo.h" line="97"/>
<source>configParamBool</source>
<extracomment>The name of the paramType (c75723b6-ea4f-4982-9751-6c5e39c88145) of Mock Devices</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="109"/>
<location filename="../plugininfo.h" line="109"/>
<source>async</source>
<extracomment>The name of the paramType (f2977061-4dd0-4ef5-85aa-3b7134743be3) of Mock Device</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="112"/>
<location filename="../plugininfo.h" line="112"/>
<source>broken</source>
<extracomment>The name of the paramType (ae8f8901-f2c1-42a5-8111-6d2fc8e4c1e4) of Mock Device</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="115"/>
<location filename="../plugininfo.h" line="115"/>
<source>resultCount</source>
<extracomment>The name of the paramType (d222adb4-2f9c-4c3f-8655-76400d0fb6ce) of Mock Device</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="118"/>
<location filename="../plugininfo.h" line="118"/>
<source>Dummy int state changed</source>
<extracomment>The name of the autocreated EventType (80baec19-54de-4948-ac46-31eabfaceb83)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="121"/>
<location filename="../plugininfo.h" line="121"/>
<source>Dummy int state</source>
<extracomment>The name of the ParamType of StateType (80baec19-54de-4948-ac46-31eabfaceb83) of DeviceClass Mock Device</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="124"/>
<location filename="../plugininfo.h" line="124"/>
<source>Dummy bool state changed</source>
<extracomment>The name of the autocreated EventType (9dd6a97c-dfd1-43dc-acbd-367932742310)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="127"/>
<location filename="../plugininfo.h" line="127"/>
<source>Dummy bool state</source>
<extracomment>The name of the ParamType of StateType (9dd6a97c-dfd1-43dc-acbd-367932742310) of DeviceClass Mock Device</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="130"/>
<location filename="../plugininfo.h" line="130"/>
<source>Mock Action 1 (with params)</source>
<extracomment>The name of the ActionType dea0f4e1-65e3-4981-8eaa-2701c53a9185 of deviceClass Mock Device</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="133"/>
<location filename="../plugininfo.h" line="133"/>
<source>mockActionParam1</source>
<extracomment>The name of the paramType (a2d3a256-a551-4712-a65b-ecd5a436a1cb) of Mock Device</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="136"/>
<location filename="../plugininfo.h" line="136"/>
<source>mockActionParam2</source>
<extracomment>The name of the paramType (304a4899-18be-4e3b-94f4-d03be52f3233) of Mock Device</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="139"/>
<location filename="../plugininfo.h" line="139"/>
<source>Mock Action 2 (without params)</source>
<extracomment>The name of the ActionType defd3ed6-1a0d-400b-8879-a0202cf39935 of deviceClass Mock Device</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="151"/>
<location filename="../plugininfo.h" line="151"/>
<source>mockParamInt</source>
<extracomment>The name of the paramType (0550e16d-60b9-4ba5-83f4-4d3cee656121) of Mock Device</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="157"/>
<location filename="../plugininfo.h" line="157"/>
<source>Mock Event 1</source>
<extracomment>The name of the EventType 45bf3752-0fc6-46b9-89fd-ffd878b5b22b of deviceClass Mock Device (Auto created)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="160"/>
<location filename="../plugininfo.h" line="160"/>
<source>Mock Event 2</source>
<extracomment>The name of the EventType 863d5920-b1cf-4eb9-88bd-8f7b8583b1cf of deviceClass Mock Device (Auto created)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="169"/>
<location filename="../plugininfo.h" line="169"/>
<source>color changed</source>
<extracomment>The name of the autocreated EventType (20dc7c22-c50e-42db-837c-2bbced939f8e)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="172"/>
<location filename="../plugininfo.h" line="172"/>
<source>color</source>
<extracomment>The name of the ParamType of StateType (20dc7c22-c50e-42db-837c-2bbced939f8e) of DeviceClass Mock Device (Push Button)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="175"/>
<location filename="../plugininfo.h" line="175"/>
<source>Set color</source>
<extracomment>The name of the autocreated ActionType (20dc7c22-c50e-42db-837c-2bbced939f8e)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="178"/>
<location filename="../plugininfo.h" line="178"/>
<source>percentage changed</source>
<extracomment>The name of the autocreated EventType (72981c04-267a-4ba0-a59e-9921d2f3af9c)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="181"/>
<location filename="../plugininfo.h" line="181"/>
<source>percentage</source>
<extracomment>The name of the ParamType of StateType (72981c04-267a-4ba0-a59e-9921d2f3af9c) of DeviceClass Mock Device (Push Button)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="184"/>
<location filename="../plugininfo.h" line="184"/>
<source>Set percentage</source>
<extracomment>The name of the autocreated ActionType (72981c04-267a-4ba0-a59e-9921d2f3af9c)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="187"/>
<location filename="../plugininfo.h" line="187"/>
<source>allowed values changed</source>
<extracomment>The name of the autocreated EventType (05f63f9c-f61e-4dcf-ad55-3f13fde2765b)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="190"/>
<location filename="../plugininfo.h" line="190"/>
<source>allowed values</source>
<extracomment>The name of the ParamType of StateType (05f63f9c-f61e-4dcf-ad55-3f13fde2765b) of DeviceClass Mock Device (Push Button)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="193"/>
<location filename="../plugininfo.h" line="193"/>
<source>Set allowed values</source>
<extracomment>The name of the autocreated ActionType (05f63f9c-f61e-4dcf-ad55-3f13fde2765b)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="196"/>
<location filename="../plugininfo.h" line="196"/>
<source>double value changed</source>
<extracomment>The name of the autocreated EventType (53cd7c55-49b7-441b-b970-9048f20f0e2c)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="199"/>
<location filename="../plugininfo.h" line="199"/>
<source>double value</source>
<extracomment>The name of the ParamType of StateType (53cd7c55-49b7-441b-b970-9048f20f0e2c) of DeviceClass Mock Device (Push Button)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="202"/>
<location filename="../plugininfo.h" line="202"/>
<source>Set double value</source>
<extracomment>The name of the autocreated ActionType (53cd7c55-49b7-441b-b970-9048f20f0e2c)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="211"/>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="235"/>
<location filename="../plugininfo.h" line="211"/>
<location filename="../plugininfo.h" line="235"/>
<source>Set bool value</source>
<extracomment>The name of the autocreated ActionType (e680f7a4-b39e-46da-be41-fa3170fe3768)
----------
@ -243,20 +243,20 @@ The name of the autocreated ActionType (d24ede5f-4064-4898-bb84-cfb533b1fbc0)</e
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="214"/>
<location filename="../plugininfo.h" line="214"/>
<source>Timeout action</source>
<extracomment>The name of the ActionType 54646e7c-bc54-4895-81a2-590d72d120f9 of deviceClass Mock Device (Push Button)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="220"/>
<location filename="../plugininfo.h" line="220"/>
<source>Mock Device (Display Pin)</source>
<extracomment>The name of the DeviceClass (296f1fd4-e893-46b2-8a42-50d1bceb8730)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="205"/>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="229"/>
<location filename="../plugininfo.h" line="205"/>
<location filename="../plugininfo.h" line="229"/>
<source>bool value changed</source>
<extracomment>The name of the autocreated EventType (e680f7a4-b39e-46da-be41-fa3170fe3768)
----------
@ -264,8 +264,8 @@ The name of the autocreated EventType (d24ede5f-4064-4898-bb84-cfb533b1fbc0)</ex
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="208"/>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="232"/>
<location filename="../plugininfo.h" line="208"/>
<location filename="../plugininfo.h" line="232"/>
<source>bool value</source>
<extracomment>The name of the ParamType of StateType (e680f7a4-b39e-46da-be41-fa3170fe3768) of DeviceClass Mock Device (Push Button)
----------
@ -273,97 +273,97 @@ The name of the ParamType of StateType (d24ede5f-4064-4898-bb84-cfb533b1fbc0) of
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="247"/>
<location filename="../plugininfo.h" line="247"/>
<source>Text line</source>
<extracomment>The name of the paramType (e6acf0c7-4b8e-4296-ac62-855d20deb816) of Mock Device (InputTypes)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="250"/>
<location filename="../plugininfo.h" line="250"/>
<source>Text area</source>
<extracomment>The name of the paramType (716f0994-bc01-42b0-b64d-59236f7320d2) of Mock Device (InputTypes)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="253"/>
<location filename="../plugininfo.h" line="253"/>
<source>Password text</source>
<extracomment>The name of the paramType (e5c0d14b-c9f1-4aca-a56e-85bfa6977150) of Mock Device (InputTypes)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="256"/>
<location filename="../plugininfo.h" line="256"/>
<source>Search text</source>
<extracomment>The name of the paramType (22add8c9-ee4f-43ad-8931-58e999313ac3) of Mock Device (InputTypes)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="259"/>
<location filename="../plugininfo.h" line="259"/>
<source>Mail address</source>
<extracomment>The name of the paramType (a8494faf-3a0f-4cf3-84b7-4b39148a838d) of Mock Device (InputTypes)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="262"/>
<location filename="../plugininfo.h" line="262"/>
<source>IPv4 address</source>
<extracomment>The name of the paramType (9e5f86a0-4bb3-4892-bff8-3fc4032af6e2) of Mock Device (InputTypes)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="265"/>
<location filename="../plugininfo.h" line="265"/>
<source>IPv6 address</source>
<extracomment>The name of the paramType (43bf3832-dd48-4090-a836-656e8b60216e) of Mock Device (InputTypes)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="268"/>
<location filename="../plugininfo.h" line="268"/>
<source>URL</source>
<extracomment>The name of the paramType (fa67229f-fcef-496f-b671-59a4b48f3ab5) of Mock Device (InputTypes)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="271"/>
<location filename="../plugininfo.h" line="271"/>
<source>Mac address</source>
<extracomment>The name of the paramType (e93db587-7919-48f3-8c88-1651de63c765) of Mock Device (InputTypes)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="217"/>
<location filename="../plugininfo.h" line="217"/>
<source>Please enter the secret which normaly will be displayed on the device. For the mockdevice the pin is 243681.</source>
<extracomment>The pairing info of deviceClass Mock Device (Display Pin)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="91"/>
<location filename="../plugininfo.h" line="91"/>
<source>Mock Devices</source>
<extracomment>The name of the plugin Mock Devices (727a4a9a-c187-446f-aadf-f1b2220607d1)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="223"/>
<location filename="../plugininfo.h" line="223"/>
<source>pin</source>
<extracomment>The name of the paramType (da820e07-22dc-4173-9c07-2f49a4e265f9) of Mock Device (Display Pin)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="226"/>
<location filename="../plugininfo.h" line="226"/>
<source>Mock Device (Parent)</source>
<extracomment>The name of the DeviceClass (a71fbde9-9a38-4bf8-beab-c8aade2608ba)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="238"/>
<location filename="../plugininfo.h" line="238"/>
<source>Mock Device (Child)</source>
<extracomment>The name of the DeviceClass (40893c9f-bc47-40c1-8bf7-b390c7c1b4fc)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="241"/>
<location filename="../plugininfo.h" line="241"/>
<source>parent uuid</source>
<extracomment>The name of the paramType (104b5288-404e-42d3-bf38-e40682e75681) of Mock Device (Child)</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../../build-guh-Desktop-Debug/plugins/mock/plugininfo.h" line="244"/>
<location filename="../plugininfo.h" line="244"/>
<source>Mock Device (InputTypes)</source>
<extracomment>The name of the DeviceClass (515ffdf1-55e5-498d-9abc-4e2fe768f3a9)</extracomment>
<translation type="unfinished"></translation>

View File

@ -38,6 +38,7 @@
#include "guhcore.h"
#include "guhservice.h"
#include "guhsettings.h"
#include "guhdbusservice.h"
#include "guhapplication.h"
#include "loggingcategories.h"
@ -195,6 +196,9 @@ int main(int argc, char *argv[])
QCommandLineOption logOption({"l", "log"}, QCoreApplication::translate("main", "Specify a log file to write to, If this option is not specified, logs will be printed to the standard output."), "logfile", "/var/log/guhd.log");
parser.addOption(logOption);
QCommandLineOption dbusOption(QStringList() << "session", QCoreApplication::translate("main", "If specified, all D-Bus interfaces will be bound to the session bus instead of the system bus."));
parser.addOption(dbusOption);
parser.process(application);
// Open the logfile, if any specified
@ -234,6 +238,10 @@ int main(int argc, char *argv[])
}
QLoggingCategory::installFilter(loggingCategoryFilter);
if (parser.isSet(dbusOption)) {
GuhDBusService::setBusType(QDBusConnection::SessionBus);
}
bool startForeground = parser.isSet(foregroundOption);
if (startForeground) {
// inform about userid

View File

@ -1,4 +1,4 @@
0.53
0.54
{
"methods": {
"Actions.ExecuteAction": {
@ -403,7 +403,7 @@
}
},
"JSONRPC.Authenticate": {
"description": "Authenticate a client to the api. This will return a new token to be used to authorize a client at the API.",
"description": "Authenticate a client to the api via user & password challenge. Provide a device name which allows the user to identify the client and revoke the token in case the device is lost or stolen. This will return a new token to be used to authorize a client at the API.",
"params": {
"deviceName": "String",
"password": "String",
@ -411,7 +411,7 @@
},
"returns": {
"o:token": "String",
"success": "String"
"success": "Bool"
}
},
"JSONRPC.CreateUser": {
@ -435,6 +435,7 @@
"language": "String",
"name": "String",
"protocol version": "String",
"pushButtonAuthAvailable": "Bool",
"server": "String",
"uuid": "Uuid",
"version": "String"
@ -475,6 +476,16 @@
"error": "$ref:UserError"
}
},
"JSONRPC.RequestPushButtonAuth": {
"description": "Authenticate a client to the api via Push Button method. Provide a device name which allows the user to identify the client and revoke the token in case the device is lost or stolen. If push button hardware is available, this will return with success and start listening for push button presses. When the push button is pressed, the PushButtonAuthFinished notification will be sent to the requesting client. The procedure will be cancelled when the connection is interrupted. If another client requests push button authentication while a procedure is still going on, the second call will take over and the first one will be notified by the PushButtonAuthFinished signal about the error. The application should make it clear to the user to not press the button when the procedure fails as this can happen for 2 reasons: a) a second user is trying to auth at the same time and only the currently active user should press the button or b) it might indicate an attacker trying to take over and snooping in for tokens.",
"params": {
"deviceName": "String"
},
"returns": {
"success": "Bool",
"transactionId": "Int"
}
},
"JSONRPC.SetNotificationStatus": {
"description": "Enable/Disable notifications for this connections.",
"params": {
@ -496,7 +507,7 @@
}
},
"JSONRPC.Tokens": {
"description": "Return a list of if TokenInfo objects all the tokens for the current user.",
"description": "Return a list of TokenInfo objects of all the tokens for the current user.",
"params": {
},
"returns": {
@ -850,6 +861,14 @@
"connected": "Bool"
}
},
"JSONRPC.PushButtonAuthFinished": {
"description": "Emitted when a push button authentication reaches final state. NOTE: This notification is special. It will only be emitted to connections that did actively request a push button authentication, but also it will be emitted regardless of the notification settings. ",
"params": {
"o:token": "String",
"status": "$ref:UserError",
"transactionId": "Int"
}
},
"Logging.LogDatabaseUpdated": {
"description": "Emitted whenever the database was updated. The database will be updated when a log entry was deleted. A log entry will be deleted when the corresponding device or a rule will be removed, or when the oldest entry of the database was deleted to keep to database in the size limits.",
"params": {

View File

@ -199,6 +199,7 @@ void GuhTestBase::initTestCase()
// Add the mockdevice
m_mockTcpServer = MockTcpServer::servers().first();
m_clientId = QUuid::createUuid();
m_mockTcpServer->clientConnected(m_clientId);
createMockDevice();
@ -223,7 +224,7 @@ void GuhTestBase::cleanup()
}
}
QVariant GuhTestBase::injectAndWait(const QString &method, const QVariantMap &params)
QVariant GuhTestBase::injectAndWait(const QString &method, const QVariantMap &params, const QUuid &clientId)
{
QVariantMap call;
call.insert("id", m_commandId);
@ -234,7 +235,7 @@ QVariant GuhTestBase::injectAndWait(const QString &method, const QVariantMap &pa
QJsonDocument jsonDoc = QJsonDocument::fromVariant(call);
QSignalSpy spy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
m_mockTcpServer->injectData(m_clientId, jsonDoc.toJson(QJsonDocument::Compact));
m_mockTcpServer->injectData(clientId.isNull() ? m_clientId : clientId, jsonDoc.toJson(QJsonDocument::Compact));
if (spy.count() == 0) {
spy.wait();
@ -499,6 +500,7 @@ void GuhTestBase::restartServer()
QSignalSpy spy(GuhCore::instance()->deviceManager(), SIGNAL(loaded()));
spy.wait();
m_mockTcpServer = MockTcpServer::servers().first();
m_mockTcpServer->clientConnected(m_clientId);
}
void GuhTestBase::clearLoggingDatabase()

View File

@ -104,7 +104,7 @@ protected slots:
void cleanup();
protected:
QVariant injectAndWait(const QString &method, const QVariantMap &params = QVariantMap());
QVariant injectAndWait(const QString &method, const QVariantMap &params = QVariantMap(), const QUuid &clientId = QUuid());
QVariant checkNotification(const QSignalSpy &spy, const QString &notification);
QVariantList checkNotifications(const QSignalSpy &spy, const QString &notification);

View File

@ -2,4 +2,7 @@ include(../../../guh.pri)
include(../autotests.pri)
TARGET = testjsonrpc
SOURCES += testjsonrpc.cpp
SOURCES += testjsonrpc.cpp \
../../utils/pushbuttonagent.cpp
HEADERS += ../../utils/pushbuttonagent.h

View File

@ -23,6 +23,7 @@
#include "guhcore.h"
#include "devicemanager.h"
#include "mocktcpserver.h"
#include "../../utils/pushbuttonagent.h"
#include <QtTest/QtTest>
#include <QCoreApplication>
@ -65,6 +66,39 @@ private slots:
void pluginConfigChangeEmitsNotification();
/*
Cases for push button auth:
Case 1: regular pushbutton
- alice sends JSONRPC.RequestPushButtonAuth, gets "OK" back (if push button hardware is available)
- alice pushes the hardware button and gets a notification on jsonrpc containing the token for local auth
*/
void testPushButtonAuth();
/*
Case 2: if we have an attacker in the network, he could try to call requestPushButtonAuth and
hope someone would eventually press the button and give him a token. In order to prevent this
any previous attempt for a push button auth needs to be cancelled when a new request comes in:
* Mallory does RequestPushButtonAuth, gets OK back
* Alice does RequestPushButtonAuth,
* Mallory receives a "PushButtonFailed" notification
* Alice receives OK
* alice presses the hardware button
* Alice reveices a notification with token, mallory receives nothing
Case 3: Mallory tries to hijack it back again
* Mallory does RequestPushButtonAuth, gets OK back
* Alice does RequestPusButtonAuth,
* Alice gets ok reply, Mallory gets failed notification
* Mallory quickly does RequestPushButtonAuth again to win the fight
* Alice gets failed notification and can instruct the user to _not_ press the button now until procedure is restarted
*/
void testPushButtonAuthInterrupt();
void testPushButtonAuthConnectionDrop();
private:
QStringList extractRefs(const QVariant &variant);
@ -109,12 +143,22 @@ void TestJSONRPC::testHandshake()
QString guhVersionString(GUH_VERSION_STRING);
QVERIFY2(handShake.value("version").toString() == guhVersionString, "Handshake version doesn't match Guh version.");
// Check whether pushButtonAuth is disabled
QCOMPARE(handShake.value("pushButtonAuthAvailable").toBool(), false);
// Now register push button agent
PushButtonAgent pushButtonAgent;
pushButtonAgent.init();
// And now check if it is sent again when calling JSONRPC.Hello
handShake = injectAndWait("JSONRPC.Hello").toMap();
QCOMPARE(handShake.value("params").toMap().value("version").toString(), guhVersionString);
m_mockTcpServer->clientDisconnected(newClientId);
// Check whether pushButtonAuth is now
QCOMPARE(handShake.value("params").toMap().value("pushButtonAuthAvailable").toBool(), true);
// And now check if it is sent again when calling JSONRPC.Hello
handShake = injectAndWait("JSONRPC.Hello").toMap();
QCOMPARE(handShake.value("params").toMap().value("version").toString(), guhVersionString);
@ -876,6 +920,188 @@ void TestJSONRPC::pluginConfigChangeEmitsNotification()
QCOMPARE(notificationData.first().toMap().value("notification").toString() == "Devices.PluginConfigurationChanged", true);
}
void TestJSONRPC::testPushButtonAuth()
{
PushButtonAgent pushButtonAgent;
pushButtonAgent.init();
QVariantMap params;
params.insert("deviceName", "pbtestdevice");
QVariant response = injectAndWait("JSONRPC.RequestPushButtonAuth", params);
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true);
int transactionId = response.toMap().value("params").toMap().value("transactionId").toInt();
// Setup connection to mock client
QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
pushButtonAgent.sendButtonPressed();
if (clientSpy.count() == 0) {
clientSpy.wait();
}
QVariantMap rsp = checkNotification(clientSpy, "JSONRPC.PushButtonAuthFinished").toMap();
QCOMPARE(rsp.value("params").toMap().value("transactionId").toInt(), transactionId);
QVERIFY2(!rsp.value("params").toMap().value("token").toByteArray().isEmpty(), "Token not in push button auth notification");
}
void TestJSONRPC::testPushButtonAuthInterrupt()
{
PushButtonAgent pushButtonAgent;
pushButtonAgent.init();
// m_clientId is registered in gutTestbase already, just using it here to improve readability of the test
QUuid aliceId = m_clientId;
// Create a new clientId for mallory and connect it to the server
QUuid malloryId = QUuid::createUuid();
m_mockTcpServer->clientConnected(malloryId);
// Snoop in on everything the TCP server sends to its clients.
QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
// request push button auth for client 1 (alice) and check for OK reply
QVariantMap params;
params.insert("deviceName", "alice");
QVariant response = injectAndWait("JSONRPC.RequestPushButtonAuth", params, aliceId);
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true);
int transactionId1 = response.toMap().value("params").toMap().value("transactionId").toInt();
// Request push button auth for client 2 (mallory)
clientSpy.clear();
params.clear();
params.insert("deviceName", "mallory");
response = injectAndWait("JSONRPC.RequestPushButtonAuth", params, malloryId);
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true);
int transactionId2 = response.toMap().value("params").toMap().value("transactionId").toInt();
// Both clients should receive something. Wait for it
if (clientSpy.count() < 2) {
clientSpy.wait();
}
// spy.at(0) should be the failed notification for alice
// spy.at(1) shpuld be the OK reply for mallory
// alice should have received a failed notification. She knows something's wrong.
QVariantMap notification = QJsonDocument::fromJson(clientSpy.first().at(1).toByteArray()).toVariant().toMap();
QCOMPARE(clientSpy.first().first().toUuid(), aliceId);
QCOMPARE(notification.value("notification").toString(), QLatin1String("JSONRPC.PushButtonAuthFinished"));
QCOMPARE(notification.value("params").toMap().value("transactionId").toInt(), transactionId1);
QCOMPARE(notification.value("params").toMap().value("success").toBool(), false);
// Mallory instead should have received an OK
QVariantMap reply = QJsonDocument::fromJson(clientSpy.at(1).at(1).toByteArray()).toVariant().toMap();
QCOMPARE(clientSpy.at(1).first().toUuid(), malloryId);
QCOMPARE(reply.value("params").toMap().value("success").toBool(), true);
// Alice tries once more
clientSpy.clear();
params.clear();
params.insert("deviceName", "alice");
response = injectAndWait("JSONRPC.RequestPushButtonAuth", params, aliceId);
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true);
int transactionId3 = response.toMap().value("params").toMap().value("transactionId").toInt();
// Both clients should receive something. Wait for it
if (clientSpy.count() < 2) {
clientSpy.wait();
}
// spy.at(0) should be the failed notification for mallory
// spy.at(1) shpuld be the OK reply for alice
// mallory should have received a failed notification. She knows something's wrong.
notification = QJsonDocument::fromJson(clientSpy.first().at(1).toByteArray()).toVariant().toMap();
QCOMPARE(clientSpy.first().first().toUuid(), malloryId);
QCOMPARE(notification.value("notification").toString(), QLatin1String("JSONRPC.PushButtonAuthFinished"));
QCOMPARE(notification.value("params").toMap().value("transactionId").toInt(), transactionId2);
QCOMPARE(notification.value("params").toMap().value("success").toBool(), false);
// Alice instead should have received an OK
reply = QJsonDocument::fromJson(clientSpy.at(1).at(1).toByteArray()).toVariant().toMap();
QCOMPARE(clientSpy.at(1).first().toUuid(), aliceId);
QCOMPARE(reply.value("params").toMap().value("success").toBool(), true);
clientSpy.clear();
// do the button press
pushButtonAgent.sendButtonPressed();
// Wait for things to happen
if (clientSpy.count() == 0) {
clientSpy.wait();
}
// There should have been only exactly one message sent, the token for alice
// Mallory should not have received anything
QCOMPARE(clientSpy.count(), 1);
notification = QJsonDocument::fromJson(clientSpy.first().at(1).toByteArray()).toVariant().toMap();
QCOMPARE(clientSpy.first().first().toUuid(), aliceId);
QCOMPARE(notification.value("notification").toString(), QLatin1String("JSONRPC.PushButtonAuthFinished"));
QCOMPARE(notification.value("params").toMap().value("transactionId").toInt(), transactionId3);
QCOMPARE(notification.value("params").toMap().value("success").toBool(), true);
QVERIFY2(!notification.value("params").toMap().value("token").toByteArray().isEmpty(), "Token is empty while it shouldn't be");
}
void TestJSONRPC::testPushButtonAuthConnectionDrop()
{
PushButtonAgent pushButtonAgent;
pushButtonAgent.init();
// Snoop in on everything the TCP server sends to its clients.
QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
// Create a new clientId for alice and connect it to the server
QUuid aliceId = QUuid::createUuid();
m_mockTcpServer->clientConnected(aliceId);
// request push button auth for client 1 (alice) and check for OK reply
QVariantMap params;
params.insert("deviceName", "alice");
QVariant response = injectAndWait("JSONRPC.RequestPushButtonAuth", params, aliceId);
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true);
// Disconnect alice
m_mockTcpServer->clientDisconnected(aliceId);
// Now try with bob
// Create a new clientId for bob and connect it to the server
QUuid bobId = QUuid::createUuid();
m_mockTcpServer->clientConnected(bobId);
// request push button auth for client 1 (alice) and check for OK reply
params.clear();
params.insert("deviceName", "bob");
response = injectAndWait("JSONRPC.RequestPushButtonAuth", params, bobId);
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true);
int transactionId = response.toMap().value("params").toMap().value("transactionId").toInt();
clientSpy.clear();
pushButtonAgent.sendButtonPressed();
// Wait for things to happen
if (clientSpy.count() == 0) {
clientSpy.wait();
}
// There should have been only exactly one message sent, the token for bob
QCOMPARE(clientSpy.count(), 1);
QVariantMap notification = QJsonDocument::fromJson(clientSpy.first().at(1).toByteArray()).toVariant().toMap();
QCOMPARE(clientSpy.first().first().toUuid(), bobId);
QCOMPARE(notification.value("notification").toString(), QLatin1String("JSONRPC.PushButtonAuthFinished"));
QCOMPARE(notification.value("params").toMap().value("transactionId").toInt(), transactionId);
QCOMPARE(notification.value("params").toMap().value("success").toBool(), true);
QVERIFY2(!notification.value("params").toMap().value("token").toByteArray().isEmpty(), "Token is empty while it shouldn't be");
}
#include "testjsonrpc.moc"
QTEST_MAIN(TestJSONRPC)

View File

@ -1,19 +0,0 @@
include(../../guh.pri)
include(../../server/server.pri)
TARGET = guh-core
TEMPLATE = lib
QT += sql
DEFINES += TESTING_ENABLED
INCLUDEPATH += $$top_srcdir/server/ \
$$top_srcdir/server/jsonrpc \
$$top_srcdir/libguh \
$$top_srcdir/tests/auto/
LIBS += -L$$top_builddir/libguh/ -lguh -lssl -lcrypto -laws-iot-sdk-cpp -lmbedtls -lmbedx509 -lmbedcrypto
target.path = /usr/lib/$$system('dpkg-architecture -q DEB_HOST_MULTIARCH')
INSTALLS += target

7
tests/scripts/getconfig.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
if [ -z $1 ]; then
echo "usage $0 host"
else
(echo '{"id":1, "method":"Configuration.GetConfigurations"}'; sleep 1) | nc $1 2222
fi

7
tests/scripts/hello.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
if [ -z $1 ]; then
echo "usage: $0 host"
else
(echo '{"id":1, "method": "JSONRPC.Hello"}'; sleep 1) | nc $1 2222
fi

View File

@ -0,0 +1,7 @@
#!/bin/bash
if [ -z $2 ]; then
echo "usage: $0 host devicename"
else
(echo '{"id":1, "method": "JSONRPC.RequestPushButtonAuth", "params": { "deviceName": "'$2'"}}'; sleep 1) | nc $1 2222
fi

View File

@ -1,3 +1,3 @@
TEMPLATE = subdirs
SUBDIRS = auto
SUBDIRS = auto tools/simplepushbuttonhandler

View File

@ -0,0 +1,40 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2017 Michael Zanetti <michael.zanetti@guh.io> *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "inputwatcher.h"
#include <QTextStream>
#include <QDebug>
#include <QSocketNotifier>
#include <stdio.h>
InputWatcher::InputWatcher(QObject *parent) : QObject(parent)
{
m_notifier = new QSocketNotifier(fileno(stdin), QSocketNotifier::Read, this);
connect(m_notifier, SIGNAL(activated(int)), this, SLOT(readyRead()));
}
void InputWatcher::readyRead()
{
QTextStream qin(stdin);
qin.readLine();
emit enterPressed();
}

View File

@ -0,0 +1,44 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2017 Michael Zanetti <michael.zanetti@guh.io> *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef INPUTWATCHER_H
#define INPUTWATCHER_H
#include <QObject>
class QSocketNotifier;
class InputWatcher : public QObject
{
Q_OBJECT
public:
explicit InputWatcher(QObject *parent = nullptr);
private slots:
void readyRead();
signals:
void enterPressed();
private:
QSocketNotifier *m_notifier;
};
#endif // INPUTWATCHER_H

View File

@ -0,0 +1,47 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2017 Michael Zanetti <michael.zanetti@guh.io> *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <QCoreApplication>
#include <QDebug>
#include <QCommandLineParser>
#include <QCommandLineOption>
#include "../../utils/pushbuttonagent.h"
#include "inputwatcher.h"
int main(int argc, char *argv[])
{
QCommandLineParser parser;
parser.addHelpOption();
QCommandLineOption dbusOption(QStringList() << "session", QCoreApplication::translate("main", "If specified, all D-Bus interfaces will be bound to the session bus instead of the system bus."));
parser.addOption(dbusOption);
QCoreApplication a(argc, argv);
PushButtonAgent agent;
if (!agent.init(parser.isSet(dbusOption) ? QDBusConnection::SessionBus : QDBusConnection::SystemBus)) {
return -1;
}
InputWatcher inputWatcher;
QObject::connect(&inputWatcher, &InputWatcher::enterPressed, &agent, &PushButtonAgent::sendButtonPressed);
qDebug() << "Use the Enter key as push button.";
return a.exec();
}

View File

@ -0,0 +1,24 @@
QT -= gui
QT += dbus
CONFIG += c++11 console
CONFIG -= app_bundle
# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += main.cpp \
../../utils/pushbuttonagent.cpp \
inputwatcher.cpp
HEADERS += \
../../utils/pushbuttonagent.h \
inputwatcher.h

View File

@ -0,0 +1,57 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2017 Michael Zanetti <michael.zanetti@guh.io> *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "pushbuttonagent.h"
#include <QDBusMessage>
#include <QDBusObjectPath>
#include <QDebug>
#include <QTimer>
PushButtonAgent::PushButtonAgent(QObject *parent) : QObject(parent)
{
}
bool PushButtonAgent::init(QDBusConnection::BusType busType)
{
QDBusConnection bus = busType == QDBusConnection::SessionBus ? QDBusConnection::sessionBus() : QDBusConnection::systemBus();
bool result = bus.registerObject("/guh/pushbuttonhandler", this, QDBusConnection::ExportScriptableContents);
if (!result) {
qDebug() << "Error registering PushButton agent on D-Bus.";
return false;
}
QDBusMessage message = QDBusMessage::createMethodCall("io.guh.nymead", "/io/guh/nymead/UserManager", QString(), "RegisterButtonAgent");
message << qVariantFromValue(QDBusObjectPath("/guh/pushbuttonhandler"));
QDBusMessage reply = bus.call(message);
if (!reply.errorName().isEmpty()) {
qDebug() << "Error registering PushButton agent:" << reply.errorMessage();
return false;
}
qDebug() << "PushButton agent registered.";
return true;
}
void PushButtonAgent::sendButtonPressed()
{
qDebug() << "Sending button pressed event.";
emit PushButtonPressed();
}

View File

@ -0,0 +1,42 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2017 Michael Zanetti <michael.zanetti@guh.io> *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef PUSHBUTTONAGENT_H
#define PUSHBUTTONAGENT_H
#include <QObject>
#include <QDBusConnection>
class PushButtonAgent : public QObject
{
Q_OBJECT
public:
explicit PushButtonAgent(QObject *parent = nullptr);
bool init(QDBusConnection::BusType busType = QDBusConnection::SystemBus);
signals:
Q_SCRIPTABLE void PushButtonPressed();
public slots:
void sendButtonPressed();
};
#endif // PUSHBUTTONAGENT_H

View File

@ -4,7 +4,7 @@
<context>
<name>main</name>
<message>
<location filename="../server/main.cpp" line="158"/>
<location filename="../server/main.cpp" line="159"/>
<source>
guh ( /[guːh]/ ) is an open source IoT (Internet of Things) server,
which allows to control a lot of different devices from many different
@ -23,12 +23,12 @@ Szenen undVerhaltensweisen des Systems festzulegen.
</translation>
</message>
<message>
<location filename="../server/main.cpp" line="170"/>
<location filename="../server/main.cpp" line="171"/>
<source>Run guhd in the foreground, not as daemon.</source>
<translation>Starte guhd im Vordergrund, nicht als Service.</translation>
</message>
<message>
<location filename="../server/main.cpp" line="173"/>
<location filename="../server/main.cpp" line="174"/>
<source>Debug categories to enable. Prefix with &quot;No&quot; to disable. Warnings from all categories will be printed unless explicitly muted with &quot;NoWarnings&quot;.
Categories are:</source>
@ -36,17 +36,22 @@ Categories are:</source>
Es gibt folgende Kategorien:</translation>
</message>
<message>
<location filename="../server/main.cpp" line="190"/>
<location filename="../server/main.cpp" line="191"/>
<source>Enables all debug categories. This parameter overrides all debug category parameters.</source>
<translation>Aktiviere alle Debug-Kategorien. Dieser Parameter überschreibt alle anderen Debug-Kategorien Parameter.</translation>
</message>
<message>
<location filename="../server/main.cpp" line="195"/>
<location filename="../server/main.cpp" line="196"/>
<source>Specify a log file to write to, If this option is not specified, logs will be printed to the standard output.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../server/main.cpp" line="232"/>
<location filename="../server/main.cpp" line="199"/>
<source>If specified, all D-Bus interfaces will be bound to the session bus instead of the system bus.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../server/main.cpp" line="236"/>
<source>No such debug category:</source>
<translation>Diese Debug-Kategorie existiert nicht:</translation>
</message>

View File

@ -4,7 +4,7 @@
<context>
<name>main</name>
<message>
<location filename="../server/main.cpp" line="158"/>
<location filename="../server/main.cpp" line="159"/>
<source>
guh ( /[guːh]/ ) is an open source IoT (Internet of Things) server,
which allows to control a lot of different devices from many different
@ -23,12 +23,12 @@ for your environment.
</translation>
</message>
<message>
<location filename="../server/main.cpp" line="170"/>
<location filename="../server/main.cpp" line="171"/>
<source>Run guhd in the foreground, not as daemon.</source>
<translation>Run guhd in the foreground, not as daemon.</translation>
</message>
<message>
<location filename="../server/main.cpp" line="173"/>
<location filename="../server/main.cpp" line="174"/>
<source>Debug categories to enable. Prefix with &quot;No&quot; to disable. Warnings from all categories will be printed unless explicitly muted with &quot;NoWarnings&quot;.
Categories are:</source>
@ -37,17 +37,22 @@ Categories are:</source>
Categories are:</translation>
</message>
<message>
<location filename="../server/main.cpp" line="190"/>
<location filename="../server/main.cpp" line="191"/>
<source>Enables all debug categories. This parameter overrides all debug category parameters.</source>
<translation>Enables all debug categories. This parameter overrides all debug category parameters.</translation>
</message>
<message>
<location filename="../server/main.cpp" line="195"/>
<location filename="../server/main.cpp" line="196"/>
<source>Specify a log file to write to, If this option is not specified, logs will be printed to the standard output.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../server/main.cpp" line="232"/>
<location filename="../server/main.cpp" line="199"/>
<source>If specified, all D-Bus interfaces will be bound to the session bus instead of the system bus.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../server/main.cpp" line="236"/>
<source>No such debug category:</source>
<translation>No such debug category:</translation>
</message>