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, libssl-dev,
libmbedtls-dev, libmbedtls-dev,
libaws-iot-device-sdk-cpp, libaws-iot-device-sdk-cpp,
dbus-test-runner,
Package: guh Package: guh
Architecture: any 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 usr/lib/@DEB_HOST_MULTIARCH@/libguh-core.so.1.0.0
data/systemd/guhd.service /lib/systemd/system/ data/systemd/guhd.service /lib/systemd/system/
data/logrotate/guhd /etc/logrotate.d/ 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 cp -a $(CURDIR)/doc/html $(CURDIR)/debian/guh-doc/usr/share/doc/guh/ || true
override_dh_auto_test: 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: override_dh_strip:
dh_strip --dbg-package=guh-dbg 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 # define protocol versions
JSON_PROTOCOL_VERSION_MAJOR=0 JSON_PROTOCOL_VERSION_MAJOR=0
JSON_PROTOCOL_VERSION_MINOR=53 JSON_PROTOCOL_VERSION_MINOR=54
REST_API_VERSION=1 REST_API_VERSION=1
DEFINES += GUH_VERSION_STRING=\\\"$${GUH_VERSION_STRING}\\\" \ DEFINES += GUH_VERSION_STRING=\\\"$${GUH_VERSION_STRING}\\\" \

View File

@ -438,6 +438,9 @@ void GuhCore::init() {
qCDebug(dcApplication) << "Creating Rule Engine"; qCDebug(dcApplication) << "Creating Rule Engine";
m_ruleEngine = new RuleEngine(this); m_ruleEngine = new RuleEngine(this);
qCDebug(dcApplication()) << "Creating User Manager";
m_userManager = new UserManager(this);
qCDebug(dcApplication) << "Creating Server Manager"; qCDebug(dcApplication) << "Creating Server Manager";
m_serverManager = new ServerManager(m_configuration, this); m_serverManager = new ServerManager(m_configuration, this);
@ -445,9 +448,6 @@ void GuhCore::init() {
qCDebug(dcApplication) << "Creating Network Manager"; qCDebug(dcApplication) << "Creating Network Manager";
m_networkManager = new NetworkManager(this); m_networkManager = new NetworkManager(this);
qCDebug(dcApplication) << "Creating User Manager";
m_userManager = new UserManager(this);
qCDebug(dcApplication) << "Creating Cloud Manager"; qCDebug(dcApplication) << "Creating Cloud Manager";
m_cloudManager = new CloudManager(m_networkManager, this); m_cloudManager = new CloudManager(m_networkManager, this);
m_cloudManager->setDeviceId(m_configuration->serverUuid()); 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. */ /*! Start the timeout timer for this \l{JsonReply}. The default timeout is 15 seconds. */
void JsonReply::startWait() void JsonReply::startWait()
{ {
m_timeout.start(15000); m_timeout.start(30000);
} }
void JsonReply::timeout() 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("protocol version", JsonTypes::basicTypeToString(JsonTypes::String));
returns.insert("initialSetupRequired", JsonTypes::basicTypeToString(JsonTypes::Bool)); returns.insert("initialSetupRequired", JsonTypes::basicTypeToString(JsonTypes::Bool));
returns.insert("authenticationRequired", JsonTypes::basicTypeToString(JsonTypes::Bool)); returns.insert("authenticationRequired", JsonTypes::basicTypeToString(JsonTypes::Bool));
returns.insert("pushButtonAuthAvailable", JsonTypes::basicTypeToString(JsonTypes::Bool));
setReturns("Hello", returns); setReturns("Hello", returns);
params.clear(); returns.clear(); params.clear(); returns.clear();
@ -118,17 +119,40 @@ JsonRPCServer::JsonRPCServer(const QSslConfiguration &sslConfiguration, QObject
setReturns("CreateUser", returns); setReturns("CreateUser", returns);
params.clear(); returns.clear(); 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("username", JsonTypes::basicTypeToString(JsonTypes::String));
params.insert("password", JsonTypes::basicTypeToString(JsonTypes::String)); params.insert("password", JsonTypes::basicTypeToString(JsonTypes::String));
params.insert("deviceName", JsonTypes::basicTypeToString(JsonTypes::String)); params.insert("deviceName", JsonTypes::basicTypeToString(JsonTypes::String));
setParams("Authenticate", params); 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)); returns.insert("o:token", JsonTypes::basicTypeToString(JsonTypes::String));
setReturns("Authenticate", returns); setReturns("Authenticate", returns);
params.clear(); returns.clear(); 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); setParams("Tokens", params);
returns.insert("tokenInfoList", QVariantList() << JsonTypes::tokenInfoRef()); returns.insert("tokenInfoList", QVariantList() << JsonTypes::tokenInfoRef());
setReturns("Tokens", returns); setReturns("Tokens", returns);
@ -168,7 +192,16 @@ JsonRPCServer::JsonRPCServer(const QSslConfiguration &sslConfiguration, QObject
params.insert("connected", JsonTypes::basicTypeToString(JsonTypes::Bool)); params.insert("connected", JsonTypes::basicTypeToString(JsonTypes::Bool));
setParams("CloudConnectedChanged", params); 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); QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection);
connect(GuhCore::instance()->userManager(), &UserManager::pushButtonAuthFinished, this, &JsonRPCServer::onPushButtonAuthFinished);
} }
/*! Returns the \e namespace of \l{JsonHandler}. */ /*! Returns the \e namespace of \l{JsonHandler}. */
@ -218,9 +251,9 @@ JsonReply* JsonRPCServer::Version(const QVariantMap &params) const
JsonReply* JsonRPCServer::SetNotificationStatus(const QVariantMap &params) JsonReply* JsonRPCServer::SetNotificationStatus(const QVariantMap &params)
{ {
QUuid clientId = this->property("clientId").toUuid(); QUuid clientId = this->property("clientId").toUuid();
m_clients[clientId] = params.value("enabled").toBool(); m_clientNotifications[clientId] = params.value("enabled").toBool();
QVariantMap returns; QVariantMap returns;
returns.insert("enabled", m_clients[clientId]); returns.insert("enabled", m_clientNotifications[clientId]);
return createReply(returns); return createReply(returns);
} }
@ -251,6 +284,21 @@ JsonReply *JsonRPCServer::Authenticate(const QVariantMap &params)
return createReply(ret); 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 JsonReply *JsonRPCServer::Tokens(const QVariantMap &params) const
{ {
Q_UNUSED(params) Q_UNUSED(params)
@ -390,6 +438,7 @@ QVariantMap JsonRPCServer::createWelcomeMessage(TransportInterface *interface) c
handshake.insert("protocol version", JSON_PROTOCOL_VERSION); handshake.insert("protocol version", JSON_PROTOCOL_VERSION);
handshake.insert("initialSetupRequired", (interface->configuration().authenticationEnabled ? GuhCore::instance()->userManager()->users().isEmpty() : false)); handshake.insert("initialSetupRequired", (interface->configuration().authenticationEnabled ? GuhCore::instance()->userManager()->users().isEmpty() : false));
handshake.insert("authenticationRequired", interface->configuration().authenticationEnabled); handshake.insert("authenticationRequired", interface->configuration().authenticationEnabled);
handshake.insert("pushButtonAuthAvailable", GuhCore::instance()->userManager()->pushButtonAuthAvailable());
return handshake; return handshake;
} }
@ -444,16 +493,18 @@ void JsonRPCServer::processData(const QUuid &clientId, const QByteArray &data)
// check if authentication is required for this transport // check if authentication is required for this transport
if (m_interfaces.value(interface)) { 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 (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."); sendUnauthorizedResponse(interface, clientId, commandId, "Initial setup required. Call CreateUser first.");
return; return;
} }
} else { } 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 // ok, we have a user. if there isn't a valid token, let's fail unless this is a Authenticate, Introspect Hello call
QByteArray token = message.value("token").toByteArray(); if (!(targetNamespace == "JSONRPC" && authExemptMethodsWithUser.contains(method)) && (token.isEmpty() || !GuhCore::instance()->userManager()->verifyToken(token))) {
if (!(targetNamespace == "JSONRPC" && (method == "Authenticate" || method == "Introspect" || method == "Hello")) && !GuhCore::instance()->userManager()->verifyToken(token)) {
sendUnauthorizedResponse(interface, clientId, commandId, "Forbidden: Invalid token."); sendUnauthorizedResponse(interface, clientId, commandId, "Forbidden: Invalid token.");
return; return;
} }
@ -522,8 +573,8 @@ void JsonRPCServer::sendNotification(const QVariantMap &params)
notification.insert("notification", handler->name() + "." + method.name()); notification.insert("notification", handler->name() + "." + method.name());
notification.insert("params", params); notification.insert("params", params);
foreach (TransportInterface *interface, m_interfaces.keys()) { foreach (const QUuid &clientId, m_clientNotifications.keys(true)) {
interface->sendData(m_clients.keys(true), QJsonDocument::fromVariant(notification).toJson(QJsonDocument::Compact)); m_clientTransports.value(clientId)->sendData(clientId, QJsonDocument::fromVariant(notification).toJson(QJsonDocument::Compact));
} }
} }
@ -567,6 +618,35 @@ void JsonRPCServer::onCloudConnectedChanged(bool connected)
emit CloudConnectedChanged(params); 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) void JsonRPCServer::registerHandler(JsonHandler *handler)
{ {
m_handlers.insert(handler->name(), handler); m_handlers.insert(handler->name(), handler);
@ -582,15 +662,21 @@ void JsonRPCServer::clientConnected(const QUuid &clientId)
{ {
TransportInterface *interface = qobject_cast<TransportInterface *>(sender()); 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 // 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)); interface->sendData(clientId, QJsonDocument::fromVariant(createWelcomeMessage(interface)).toJson(QJsonDocument::Compact));
} }
void JsonRPCServer::clientDisconnected(const QUuid &clientId) 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 *CreateUser(const QVariantMap &params);
Q_INVOKABLE JsonReply *Authenticate(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 *Tokens(const QVariantMap &params) const;
Q_INVOKABLE JsonReply *RemoveToken(const QVariantMap &params); Q_INVOKABLE JsonReply *RemoveToken(const QVariantMap &params);
Q_INVOKABLE JsonReply *SetupRemoteAccess(const QVariantMap &params); Q_INVOKABLE JsonReply *SetupRemoteAccess(const QVariantMap &params);
@ -62,8 +63,9 @@ public:
signals: signals:
void CloudConnectedChanged(const QVariantMap &map); void CloudConnectedChanged(const QVariantMap &map);
void PushButtonAuthFinished(const QVariantMap &params);
// Internal // Server API
public: public:
void registerTransportInterface(TransportInterface *interface, bool authenticationRequired); void registerTransportInterface(TransportInterface *interface, bool authenticationRequired);
void unregisterTransportInterface(TransportInterface *interface); void unregisterTransportInterface(TransportInterface *interface);
@ -90,14 +92,16 @@ private slots:
void pairingFinished(QString cognitoUserId, int status, const QString &message); void pairingFinished(QString cognitoUserId, int status, const QString &message);
void onCloudConnectedChanged(bool connected); void onCloudConnectedChanged(bool connected);
void onPushButtonAuthFinished(int transactionId, bool success, const QByteArray &token);
private: private:
QMap<TransportInterface*, bool> m_interfaces; QMap<TransportInterface*, bool> m_interfaces; // Interface, authenticationRequired
QHash<QString, JsonHandler *> m_handlers; QHash<QString, JsonHandler *> m_handlers;
QHash<JsonReply *, TransportInterface *> m_asyncReplies; QHash<JsonReply *, TransportInterface *> m_asyncReplies;
// clientId, notificationsEnabled QHash<QUuid, TransportInterface*> m_clientTransports;
QHash<QUuid, bool> m_clients; QHash<QUuid, bool> m_clientNotifications;
QHash<int, QUuid> m_pushButtonTransactions;
QHash<QString, JsonReply*> m_pairingRequests; QHash<QString, JsonReply*> m_pairingRequests;

View File

@ -75,6 +75,9 @@ HEADERS += guhcore.h \
cloudmanager.h \ cloudmanager.h \
MbedTLS/MbedTLSConnection.hpp \ MbedTLS/MbedTLSConnection.hpp \
janusconnector.h \ janusconnector.h \
pushbuttondbusservice.h \
guhdbusservice.h
SOURCES += guhcore.cpp \ SOURCES += guhcore.cpp \
tcpserver.cpp \ tcpserver.cpp \
@ -132,3 +135,5 @@ SOURCES += guhcore.cpp \
cloudmanager.cpp \ cloudmanager.cpp \
MbedTLS/MbedTLSConnection.cpp \ MbedTLS/MbedTLSConnection.cpp \
janusconnector.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); client = m_clientList.value(clientId);
if (client) { if (client) {
client->write(data + '\n'); client->write(data + '\n');
} else {
qWarning(dcTcpServer()) << "Client" << clientId << "unknown to this transport";
} }
} }

View File

@ -22,6 +22,7 @@
#include "guhsettings.h" #include "guhsettings.h"
#include "loggingcategories.h" #include "loggingcategories.h"
#include "guhcore.h" #include "guhcore.h"
#include "pushbuttondbusservice.h"
#include <QUuid> #include <QUuid>
#include <QCryptographicHash> #include <QCryptographicHash>
@ -44,6 +45,10 @@ UserManager::UserManager(QObject *parent) : QObject(parent)
return; return;
} }
initDB(); 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 QStringList UserManager::users() const
@ -105,6 +110,11 @@ UserManager::UserError UserManager::removeUser(const QString &username)
return UserErrorNoError; return UserErrorNoError;
} }
bool UserManager::pushButtonAuthAvailable() const
{
return m_pushButtonDBusService->agentAvailable();
}
QByteArray UserManager::authenticate(const QString &username, const QString &password, const QString &deviceName) QByteArray UserManager::authenticate(const QString &username, const QString &password, const QString &deviceName)
{ {
if (!validateUsername(username)) { if (!validateUsername(username)) {
@ -142,6 +152,35 @@ QByteArray UserManager::authenticate(const QString &username, const QString &pas
return token; 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 QString UserManager::userForToken(const QByteArray &token) const
{ {
if (!validateToken(token)) { if (!validateToken(token)) {
@ -220,7 +259,7 @@ bool UserManager::verifyToken(const QByteArray &token)
qCDebug(dcUserManager) << "Authorisation failed for token" << token; qCDebug(dcUserManager) << "Authorisation failed for token" << token;
return false; return false;
} }
qCDebug(dcUserManager) << "Token authorized for user" << result.value("username").toString(); //qCDebug(dcUserManager) << "Token authorized for user" << result.value("username").toString();
return true; return true;
} }
@ -246,4 +285,30 @@ bool UserManager::validateToken(const QByteArray &token) const
return validator.exactMatch(token); 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 { namespace guhserver {
class PushButtonDBusService;
class UserManager : public QObject class UserManager : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -50,20 +52,33 @@ public:
UserError createUser(const QString &username, const QString &password); UserError createUser(const QString &username, const QString &password);
UserError removeUser(const QString &username); UserError removeUser(const QString &username);
bool pushButtonAuthAvailable() const;
QByteArray authenticate(const QString &username, const QString &password, const QString &deviceName); 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; QString userForToken(const QByteArray &token) const;
QList<TokenInfo> tokens(const QString &username) const; QList<TokenInfo> tokens(const QString &username) const;
guhserver::UserManager::UserError removeToken(const QUuid &tokenId); guhserver::UserManager::UserError removeToken(const QUuid &tokenId);
bool verifyToken(const QByteArray &token); bool verifyToken(const QByteArray &token);
signals:
void pushButtonAuthFinished(int transactionId, bool success, const QByteArray &token);
private: private:
void initDB(); void initDB();
bool validateUsername(const QString &username) const; bool validateUsername(const QString &username) const;
bool validateToken(const QByteArray &token) const; bool validateToken(const QByteArray &token) const;
private slots:
void onPushButtonPressed();
private: private:
QSqlDatabase m_db; 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) { if (client) {
qCDebug(dcWebSocketServerTraffic()) << "Sending data to client" << data; qCDebug(dcWebSocketServerTraffic()) << "Sending data to client" << data;
client->sendTextMessage(data + '\n'); client->sendTextMessage(data + '\n');
} else {
qCWarning(dcWebSocketServer()) << "Client" << clientId << "unknown to this transport";
} }
} }

View File

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

View File

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

View File

@ -38,6 +38,7 @@
#include "guhcore.h" #include "guhcore.h"
#include "guhservice.h" #include "guhservice.h"
#include "guhsettings.h" #include "guhsettings.h"
#include "guhdbusservice.h"
#include "guhapplication.h" #include "guhapplication.h"
#include "loggingcategories.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"); 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); 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); parser.process(application);
// Open the logfile, if any specified // Open the logfile, if any specified
@ -234,6 +238,10 @@ int main(int argc, char *argv[])
} }
QLoggingCategory::installFilter(loggingCategoryFilter); QLoggingCategory::installFilter(loggingCategoryFilter);
if (parser.isSet(dbusOption)) {
GuhDBusService::setBusType(QDBusConnection::SessionBus);
}
bool startForeground = parser.isSet(foregroundOption); bool startForeground = parser.isSet(foregroundOption);
if (startForeground) { if (startForeground) {
// inform about userid // inform about userid

View File

@ -1,4 +1,4 @@
0.53 0.54
{ {
"methods": { "methods": {
"Actions.ExecuteAction": { "Actions.ExecuteAction": {
@ -403,7 +403,7 @@
} }
}, },
"JSONRPC.Authenticate": { "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": { "params": {
"deviceName": "String", "deviceName": "String",
"password": "String", "password": "String",
@ -411,7 +411,7 @@
}, },
"returns": { "returns": {
"o:token": "String", "o:token": "String",
"success": "String" "success": "Bool"
} }
}, },
"JSONRPC.CreateUser": { "JSONRPC.CreateUser": {
@ -435,6 +435,7 @@
"language": "String", "language": "String",
"name": "String", "name": "String",
"protocol version": "String", "protocol version": "String",
"pushButtonAuthAvailable": "Bool",
"server": "String", "server": "String",
"uuid": "Uuid", "uuid": "Uuid",
"version": "String" "version": "String"
@ -475,6 +476,16 @@
"error": "$ref:UserError" "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": { "JSONRPC.SetNotificationStatus": {
"description": "Enable/Disable notifications for this connections.", "description": "Enable/Disable notifications for this connections.",
"params": { "params": {
@ -496,7 +507,7 @@
} }
}, },
"JSONRPC.Tokens": { "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": { "params": {
}, },
"returns": { "returns": {
@ -850,6 +861,14 @@
"connected": "Bool" "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": { "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.", "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": { "params": {

View File

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

View File

@ -104,7 +104,7 @@ protected slots:
void cleanup(); void cleanup();
protected: 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); QVariant checkNotification(const QSignalSpy &spy, const QString &notification);
QVariantList checkNotifications(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) include(../autotests.pri)
TARGET = testjsonrpc 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 "guhcore.h"
#include "devicemanager.h" #include "devicemanager.h"
#include "mocktcpserver.h" #include "mocktcpserver.h"
#include "../../utils/pushbuttonagent.h"
#include <QtTest/QtTest> #include <QtTest/QtTest>
#include <QCoreApplication> #include <QCoreApplication>
@ -65,6 +66,39 @@ private slots:
void pluginConfigChangeEmitsNotification(); 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: private:
QStringList extractRefs(const QVariant &variant); QStringList extractRefs(const QVariant &variant);
@ -109,12 +143,22 @@ void TestJSONRPC::testHandshake()
QString guhVersionString(GUH_VERSION_STRING); QString guhVersionString(GUH_VERSION_STRING);
QVERIFY2(handShake.value("version").toString() == guhVersionString, "Handshake version doesn't match Guh version."); 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 // And now check if it is sent again when calling JSONRPC.Hello
handShake = injectAndWait("JSONRPC.Hello").toMap(); handShake = injectAndWait("JSONRPC.Hello").toMap();
QCOMPARE(handShake.value("params").toMap().value("version").toString(), guhVersionString); QCOMPARE(handShake.value("params").toMap().value("version").toString(), guhVersionString);
m_mockTcpServer->clientDisconnected(newClientId); 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 // And now check if it is sent again when calling JSONRPC.Hello
handShake = injectAndWait("JSONRPC.Hello").toMap(); handShake = injectAndWait("JSONRPC.Hello").toMap();
QCOMPARE(handShake.value("params").toMap().value("version").toString(), guhVersionString); 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); 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" #include "testjsonrpc.moc"
QTEST_MAIN(TestJSONRPC) 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 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> <context>
<name>main</name> <name>main</name>
<message> <message>
<location filename="../server/main.cpp" line="158"/> <location filename="../server/main.cpp" line="159"/>
<source> <source>
guh ( /[guːh]/ ) is an open source IoT (Internet of Things) server, guh ( /[guːh]/ ) is an open source IoT (Internet of Things) server,
which allows to control a lot of different devices from many different which allows to control a lot of different devices from many different
@ -23,12 +23,12 @@ Szenen undVerhaltensweisen des Systems festzulegen.
</translation> </translation>
</message> </message>
<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> <source>Run guhd in the foreground, not as daemon.</source>
<translation>Starte guhd im Vordergrund, nicht als Service.</translation> <translation>Starte guhd im Vordergrund, nicht als Service.</translation>
</message> </message>
<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;. <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> Categories are:</source>
@ -36,17 +36,22 @@ Categories are:</source>
Es gibt folgende Kategorien:</translation> Es gibt folgende Kategorien:</translation>
</message> </message>
<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> <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> <translation>Aktiviere alle Debug-Kategorien. Dieser Parameter überschreibt alle anderen Debug-Kategorien Parameter.</translation>
</message> </message>
<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> <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> <translation type="unfinished"></translation>
</message> </message>
<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> <source>No such debug category:</source>
<translation>Diese Debug-Kategorie existiert nicht:</translation> <translation>Diese Debug-Kategorie existiert nicht:</translation>
</message> </message>

View File

@ -4,7 +4,7 @@
<context> <context>
<name>main</name> <name>main</name>
<message> <message>
<location filename="../server/main.cpp" line="158"/> <location filename="../server/main.cpp" line="159"/>
<source> <source>
guh ( /[guːh]/ ) is an open source IoT (Internet of Things) server, guh ( /[guːh]/ ) is an open source IoT (Internet of Things) server,
which allows to control a lot of different devices from many different which allows to control a lot of different devices from many different
@ -23,12 +23,12 @@ for your environment.
</translation> </translation>
</message> </message>
<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> <source>Run guhd in the foreground, not as daemon.</source>
<translation>Run guhd in the foreground, not as daemon.</translation> <translation>Run guhd in the foreground, not as daemon.</translation>
</message> </message>
<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;. <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> Categories are:</source>
@ -37,17 +37,22 @@ Categories are:</source>
Categories are:</translation> Categories are:</translation>
</message> </message>
<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> <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> <translation>Enables all debug categories. This parameter overrides all debug category parameters.</translation>
</message> </message>
<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> <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> <translation type="unfinished"></translation>
</message> </message>
<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> <source>No such debug category:</source>
<translation>No such debug category:</translation> <translation>No such debug category:</translation>
</message> </message>