Merge PR #425: Add support fur user permissions
This commit is contained in:
commit
fca9ecee20
@ -1,187 +0,0 @@
|
|||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
||||||
*
|
|
||||||
* Copyright 2013 - 2020, nymea GmbH
|
|
||||||
* Contact: contact@nymea.io
|
|
||||||
*
|
|
||||||
* This file is part of nymea.
|
|
||||||
* This project including source code and documentation is protected by
|
|
||||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
|
||||||
* reproduction, publication, editing and translation, are reserved. The use of
|
|
||||||
* this project is subject to the terms of a license agreement to be concluded
|
|
||||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
|
||||||
* under https://nymea.io/license
|
|
||||||
*
|
|
||||||
* GNU General Public License Usage
|
|
||||||
* Alternatively, this project may be redistributed and/or modified under the
|
|
||||||
* terms of the GNU General Public License as published by the Free Software
|
|
||||||
* Foundation, GNU version 3. This project 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
|
|
||||||
* this project. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
* For any further details and any questions please contact us under
|
|
||||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
|
||||||
* https://nymea.io/license/faq
|
|
||||||
*
|
|
||||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
||||||
|
|
||||||
#include "actionhandler.h"
|
|
||||||
#include "devicehandler.h"
|
|
||||||
|
|
||||||
#include "nymeacore.h"
|
|
||||||
#include "integrations/thingmanager.h"
|
|
||||||
#include "integrations/thingactioninfo.h"
|
|
||||||
#include "integrations/browseractioninfo.h"
|
|
||||||
#include "integrations/browseritemactioninfo.h"
|
|
||||||
#include "types/action.h"
|
|
||||||
#include "types/actiontype.h"
|
|
||||||
#include "loggingcategories.h"
|
|
||||||
|
|
||||||
namespace nymeaserver {
|
|
||||||
|
|
||||||
ActionHandler::ActionHandler(QObject *parent) :
|
|
||||||
JsonHandler(parent)
|
|
||||||
{
|
|
||||||
// Enums
|
|
||||||
registerEnum<Types::InputType>();
|
|
||||||
registerEnum<Types::Unit>();
|
|
||||||
|
|
||||||
// Objects
|
|
||||||
registerObject<ParamType, ParamTypes>();
|
|
||||||
registerObject<Param, ParamList>();
|
|
||||||
registerObject<ActionType>();
|
|
||||||
registerObject<Action>();
|
|
||||||
|
|
||||||
// Methods
|
|
||||||
QString description; QVariantMap params; QVariantMap returns;
|
|
||||||
description = "Execute a single action.";
|
|
||||||
params.insert("actionTypeId", enumValueName(Uuid));
|
|
||||||
params.insert("deviceId", enumValueName(Uuid));
|
|
||||||
params.insert("o:params", objectRef<ParamList>());
|
|
||||||
returns.insert("deviceError", enumRef<Device::DeviceError>());
|
|
||||||
returns.insert("o:displayMessage", enumValueName(String));
|
|
||||||
registerMethod("ExecuteAction", description, params, returns, "Please use Integrations.ExecuteAction instead.");
|
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
|
||||||
description = "Get the ActionType for the given ActionTypeId.";
|
|
||||||
params.insert("actionTypeId", enumValueName(Uuid));
|
|
||||||
returns.insert("deviceError", enumRef<Device::DeviceError>());
|
|
||||||
returns.insert("o:actionType", objectRef<ActionType>());
|
|
||||||
registerMethod("GetActionType", description, params, returns, "Please use the Integrations namespace instead.");
|
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
|
||||||
description = "Execute the item identified by itemId on the given device.";
|
|
||||||
params.insert("deviceId", enumValueName(Uuid));
|
|
||||||
params.insert("itemId", enumValueName(String));
|
|
||||||
returns.insert("deviceError", enumRef<Device::DeviceError>());
|
|
||||||
registerMethod("ExecuteBrowserItem", description, params, returns, "Please use Integrations.ExecuteBrowserItem instead.");
|
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
|
||||||
description = "Execute the action for the browser item identified by actionTypeId and the itemId on the given device.";
|
|
||||||
params.insert("deviceId", enumValueName(Uuid));
|
|
||||||
params.insert("itemId", enumValueName(String));
|
|
||||||
params.insert("actionTypeId", enumValueName(Uuid));
|
|
||||||
params.insert("o:params", objectRef<ParamList>());
|
|
||||||
returns.insert("deviceError", enumRef<Device::DeviceError>());
|
|
||||||
registerMethod("ExecuteBrowserItemAction", description, params, returns, "Please use Integrations.ExecuteBrowserItem instead.");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
QString ActionHandler::name() const
|
|
||||||
{
|
|
||||||
return "Actions";
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonReply* ActionHandler::ExecuteAction(const QVariantMap ¶ms, const JsonContext &context)
|
|
||||||
{
|
|
||||||
ThingId thingId(params.value("deviceId").toString());
|
|
||||||
ActionTypeId actionTypeId(params.value("actionTypeId").toString());
|
|
||||||
ParamList actionParams = unpack<ParamList>(params.value("params"));
|
|
||||||
QLocale locale = context.locale();
|
|
||||||
|
|
||||||
Action action(actionTypeId, thingId);
|
|
||||||
action.setParams(actionParams);
|
|
||||||
|
|
||||||
JsonReply *jsonReply = createAsyncReply("ExecuteAction");
|
|
||||||
|
|
||||||
ThingActionInfo *info = NymeaCore::instance()->thingManager()->executeAction(action);
|
|
||||||
connect(info, &ThingActionInfo::finished, jsonReply, [info, jsonReply, locale](){
|
|
||||||
QVariantMap data;
|
|
||||||
data.insert("deviceError", enumValueName(info->status()).replace("Thing", "Device"));
|
|
||||||
if (!info->displayMessage().isEmpty()) {
|
|
||||||
data.insert("displayMessage", info->translatedDisplayMessage(locale));
|
|
||||||
}
|
|
||||||
jsonReply->setData(data);
|
|
||||||
jsonReply->finished();
|
|
||||||
});
|
|
||||||
|
|
||||||
return jsonReply;
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonReply *ActionHandler::GetActionType(const QVariantMap ¶ms, const JsonContext &context) const
|
|
||||||
{
|
|
||||||
QLocale locale = context.locale();
|
|
||||||
qCDebug(dcJsonRpc) << "asked for action type" << params;
|
|
||||||
ActionTypeId actionTypeId(params.value("actionTypeId").toString());
|
|
||||||
foreach (const ThingClass &deviceClass, NymeaCore::instance()->thingManager()->supportedThings()) {
|
|
||||||
foreach (const ActionType &actionType, deviceClass.actionTypes()) {
|
|
||||||
if (actionType.id() == actionTypeId) {
|
|
||||||
ActionType translatedActionType = actionType;
|
|
||||||
translatedActionType.setDisplayName(NymeaCore::instance()->thingManager()->translate(deviceClass.pluginId(), actionType.displayName(), locale));
|
|
||||||
|
|
||||||
QVariantMap data;
|
|
||||||
data.insert("deviceError", enumValueName<Thing::ThingError>(Thing::ThingErrorNoError).replace("ThingError", "DeviceError"));
|
|
||||||
data.insert("actionType", pack(translatedActionType));
|
|
||||||
return createReply(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QVariantMap data;
|
|
||||||
data.insert("deviceError", enumValueName<Thing::ThingError>(Thing::ThingErrorActionTypeNotFound).replace("ThingError", "DeviceError"));
|
|
||||||
return createReply(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonReply *ActionHandler::ExecuteBrowserItem(const QVariantMap ¶ms)
|
|
||||||
{
|
|
||||||
ThingId thingId = ThingId(params.value("deviceId").toString());
|
|
||||||
QString itemId = params.value("itemId").toString();
|
|
||||||
BrowserAction action(thingId, itemId);
|
|
||||||
|
|
||||||
JsonReply *jsonReply = createAsyncReply("ExecuteBrowserItem");
|
|
||||||
|
|
||||||
BrowserActionInfo *info = NymeaCore::instance()->executeBrowserItem(action);
|
|
||||||
connect(info, &BrowserActionInfo::finished, jsonReply, [info, jsonReply](){
|
|
||||||
QVariantMap data;
|
|
||||||
data.insert("deviceError", enumValueName<Thing::ThingError>(info->status()).replace("ThingError", "DeviceError"));
|
|
||||||
jsonReply->setData(data);
|
|
||||||
jsonReply->finished();
|
|
||||||
});
|
|
||||||
|
|
||||||
return jsonReply;
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonReply *ActionHandler::ExecuteBrowserItemAction(const QVariantMap ¶ms)
|
|
||||||
{
|
|
||||||
ThingId thingId = ThingId(params.value("deviceId").toString());
|
|
||||||
QString itemId = params.value("itemId").toString();
|
|
||||||
ActionTypeId actionTypeId = ActionTypeId(params.value("actionTypeId").toString());
|
|
||||||
ParamList paramList = unpack<ParamList>(params.value("params"));
|
|
||||||
BrowserItemAction browserItemAction(thingId, itemId, actionTypeId, paramList);
|
|
||||||
|
|
||||||
JsonReply *jsonReply = createAsyncReply("ExecuteBrowserItemAction");
|
|
||||||
|
|
||||||
BrowserItemActionInfo *info = NymeaCore::instance()->executeBrowserItemAction(browserItemAction);
|
|
||||||
connect(info, &BrowserItemActionInfo::finished, jsonReply, [info, jsonReply](){
|
|
||||||
QVariantMap data;
|
|
||||||
data.insert("deviceError", enumValueName<Thing::ThingError>(info->status()).replace("Thing", "Device"));
|
|
||||||
jsonReply->setData(data);
|
|
||||||
jsonReply->finished();
|
|
||||||
});
|
|
||||||
|
|
||||||
return jsonReply;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,57 +0,0 @@
|
|||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
||||||
*
|
|
||||||
* Copyright 2013 - 2020, nymea GmbH
|
|
||||||
* Contact: contact@nymea.io
|
|
||||||
*
|
|
||||||
* This file is part of nymea.
|
|
||||||
* This project including source code and documentation is protected by
|
|
||||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
|
||||||
* reproduction, publication, editing and translation, are reserved. The use of
|
|
||||||
* this project is subject to the terms of a license agreement to be concluded
|
|
||||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
|
||||||
* under https://nymea.io/license
|
|
||||||
*
|
|
||||||
* GNU General Public License Usage
|
|
||||||
* Alternatively, this project may be redistributed and/or modified under the
|
|
||||||
* terms of the GNU General Public License as published by the Free Software
|
|
||||||
* Foundation, GNU version 3. This project 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
|
|
||||||
* this project. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
* For any further details and any questions please contact us under
|
|
||||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
|
||||||
* https://nymea.io/license/faq
|
|
||||||
*
|
|
||||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
||||||
|
|
||||||
#ifndef ACTIONHANDLER_H
|
|
||||||
#define ACTIONHANDLER_H
|
|
||||||
|
|
||||||
#include "jsonrpc/jsonhandler.h"
|
|
||||||
#include "integrations/thingmanager.h"
|
|
||||||
|
|
||||||
namespace nymeaserver {
|
|
||||||
|
|
||||||
class ActionHandler : public JsonHandler
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit ActionHandler(QObject *parent = nullptr);
|
|
||||||
|
|
||||||
QString name() const override;
|
|
||||||
|
|
||||||
Q_INVOKABLE JsonReply *ExecuteAction(const QVariantMap ¶ms, const JsonContext &context);
|
|
||||||
Q_INVOKABLE JsonReply *GetActionType(const QVariantMap ¶ms, const JsonContext &context) const;
|
|
||||||
|
|
||||||
Q_INVOKABLE JsonReply *ExecuteBrowserItem(const QVariantMap ¶ms);
|
|
||||||
Q_INVOKABLE JsonReply *ExecuteBrowserItemAction(const QVariantMap ¶ms);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // ACTIONHANDLER_H
|
|
||||||
@ -92,12 +92,12 @@ ConfigurationHandler::ConfigurationHandler(QObject *parent):
|
|||||||
QString description; QVariantMap params; QVariantMap returns;
|
QString description; QVariantMap params; QVariantMap returns;
|
||||||
description = "Get the list of available timezones.";
|
description = "Get the list of available timezones.";
|
||||||
returns.insert("timeZones", QVariantList() << enumValueName(String));
|
returns.insert("timeZones", QVariantList() << enumValueName(String));
|
||||||
registerMethod("GetTimeZones", description, params, returns, "Use System.GetTimeZones instead.");
|
registerMethod("GetTimeZones", description, params, returns, Types::PermissionScopeNone, "Use System.GetTimeZones instead.");
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Returns a list of locale codes available for the server. i.e. en_US, de_AT";
|
description = "Returns a list of locale codes available for the server. i.e. en_US, de_AT";
|
||||||
returns.insert("languages", QVariantList() << enumValueName(String));
|
returns.insert("languages", QVariantList() << enumValueName(String));
|
||||||
registerMethod("GetAvailableLanguages", description, params, returns, "Use the locale property in the Handshake message instead.");
|
registerMethod("GetAvailableLanguages", description, params, returns, Types::PermissionScopeNone, "Use the locale property in the Handshake message instead.");
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Get all configuration parameters of the server.";
|
description = "Get all configuration parameters of the server.";
|
||||||
@ -135,13 +135,13 @@ ConfigurationHandler::ConfigurationHandler(QObject *parent):
|
|||||||
description = "Set the time zone of the server. See also: \"GetTimeZones\"";
|
description = "Set the time zone of the server. See also: \"GetTimeZones\"";
|
||||||
params.insert("timeZone", enumValueName(String));
|
params.insert("timeZone", enumValueName(String));
|
||||||
returns.insert("configurationError", enumRef<NymeaConfiguration::ConfigurationError>());
|
returns.insert("configurationError", enumRef<NymeaConfiguration::ConfigurationError>());
|
||||||
registerMethod("SetTimeZone", description, params, returns, "Use System.SetTimeZone instead.");
|
registerMethod("SetTimeZone", description, params, returns, Types::PermissionScopeAdmin, "Use System.SetTimeZone instead.");
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Sets the server language to the given language. See also: \"GetAvailableLanguages\"";
|
description = "Sets the server language to the given language. See also: \"GetAvailableLanguages\"";
|
||||||
params.insert("language", enumValueName(String));
|
params.insert("language", enumValueName(String));
|
||||||
returns.insert("configurationError", enumRef<NymeaConfiguration::ConfigurationError>());
|
returns.insert("configurationError", enumRef<NymeaConfiguration::ConfigurationError>());
|
||||||
registerMethod("SetLanguage", description, params, returns, "Use the locale property in the Handshake message instead.");
|
registerMethod("SetLanguage", description, params, returns, Types::PermissionScopeAdmin, "Use the locale property in the Handshake message instead.");
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Enable or disable the debug server.";
|
description = "Enable or disable the debug server.";
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,210 +0,0 @@
|
|||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
||||||
*
|
|
||||||
* Copyright 2013 - 2020, nymea GmbH
|
|
||||||
* Contact: contact@nymea.io
|
|
||||||
*
|
|
||||||
* This file is part of nymea.
|
|
||||||
* This project including source code and documentation is protected by
|
|
||||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
|
||||||
* reproduction, publication, editing and translation, are reserved. The use of
|
|
||||||
* this project is subject to the terms of a license agreement to be concluded
|
|
||||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
|
||||||
* under https://nymea.io/license
|
|
||||||
*
|
|
||||||
* GNU General Public License Usage
|
|
||||||
* Alternatively, this project may be redistributed and/or modified under the
|
|
||||||
* terms of the GNU General Public License as published by the Free Software
|
|
||||||
* Foundation, GNU version 3. This project 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
|
|
||||||
* this project. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
* For any further details and any questions please contact us under
|
|
||||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
|
||||||
* https://nymea.io/license/faq
|
|
||||||
*
|
|
||||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
||||||
|
|
||||||
#ifndef DEVICEHANDLER_H
|
|
||||||
#define DEVICEHANDLER_H
|
|
||||||
|
|
||||||
#include "jsonrpc/jsonhandler.h"
|
|
||||||
#include "integrations/thingmanager.h"
|
|
||||||
#include "integrations/thing.h"
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
DECLARE_TYPE_ID(DeviceClass)
|
|
||||||
DECLARE_TYPE_ID(Device)
|
|
||||||
|
|
||||||
namespace nymeaserver {
|
|
||||||
|
|
||||||
// Device has been renamed to Thing. As we need to keep compatibility with the Devices API for a bit,
|
|
||||||
// let's create them here
|
|
||||||
|
|
||||||
class DevicePlugin: public IntegrationPlugin
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
};
|
|
||||||
|
|
||||||
class DevicePlugins: public IntegrationPlugins
|
|
||||||
{
|
|
||||||
Q_GADGET
|
|
||||||
};
|
|
||||||
|
|
||||||
class DeviceClass: public ThingClass
|
|
||||||
{
|
|
||||||
Q_GADGET
|
|
||||||
|
|
||||||
public:
|
|
||||||
DeviceClass(): ThingClass() {}
|
|
||||||
DeviceClass(const ThingClass &other);
|
|
||||||
};
|
|
||||||
|
|
||||||
class DeviceClasses: public ThingClasses
|
|
||||||
{
|
|
||||||
Q_GADGET
|
|
||||||
};
|
|
||||||
|
|
||||||
class Device: public Thing
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
Q_PROPERTY(QUuid deviceClassId READ deviceClassId)
|
|
||||||
public:
|
|
||||||
enum DeviceError {
|
|
||||||
DeviceErrorNoError,
|
|
||||||
DeviceErrorPluginNotFound,
|
|
||||||
DeviceErrorVendorNotFound,
|
|
||||||
DeviceErrorDeviceNotFound,
|
|
||||||
DeviceErrorDeviceClassNotFound,
|
|
||||||
DeviceErrorActionTypeNotFound,
|
|
||||||
DeviceErrorStateTypeNotFound,
|
|
||||||
DeviceErrorEventTypeNotFound,
|
|
||||||
DeviceErrorDeviceDescriptorNotFound,
|
|
||||||
DeviceErrorMissingParameter,
|
|
||||||
DeviceErrorInvalidParameter,
|
|
||||||
DeviceErrorSetupFailed,
|
|
||||||
DeviceErrorDuplicateUuid,
|
|
||||||
DeviceErrorCreationMethodNotSupported,
|
|
||||||
DeviceErrorSetupMethodNotSupported,
|
|
||||||
DeviceErrorHardwareNotAvailable,
|
|
||||||
DeviceErrorHardwareFailure,
|
|
||||||
DeviceErrorAuthenticationFailure,
|
|
||||||
DeviceErrorDeviceInUse,
|
|
||||||
DeviceErrorDeviceInRule,
|
|
||||||
DeviceErrorDeviceIsChild,
|
|
||||||
DeviceErrorPairingTransactionIdNotFound,
|
|
||||||
DeviceErrorParameterNotWritable,
|
|
||||||
DeviceErrorItemNotFound,
|
|
||||||
DeviceErrorItemNotExecutable,
|
|
||||||
DeviceErrorUnsupportedFeature,
|
|
||||||
DeviceErrorTimeout,
|
|
||||||
};
|
|
||||||
Q_ENUM(DeviceError)
|
|
||||||
|
|
||||||
enum DeviceSetupStatus {
|
|
||||||
DeviceSetupStatusNone,
|
|
||||||
DeviceSetupStatusInProgress,
|
|
||||||
DeviceSetupStatusComplete,
|
|
||||||
DeviceSetupStatusFailed,
|
|
||||||
};
|
|
||||||
Q_ENUM(DeviceSetupStatus)
|
|
||||||
|
|
||||||
DeviceClassId deviceClassId() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Devices: public Things
|
|
||||||
{
|
|
||||||
Q_GADGET
|
|
||||||
};
|
|
||||||
|
|
||||||
class DeviceDescriptor: public ThingDescriptor
|
|
||||||
{
|
|
||||||
Q_GADGET
|
|
||||||
Q_PROPERTY(QUuid deviceId READ thingId USER true)
|
|
||||||
Q_PROPERTY(ParamList deviceParams READ params)
|
|
||||||
};
|
|
||||||
|
|
||||||
class DeviceDescriptors: public ThingDescriptors
|
|
||||||
{
|
|
||||||
Q_GADGET
|
|
||||||
};
|
|
||||||
|
|
||||||
class DeviceHandler : public JsonHandler
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit DeviceHandler(QObject *parent = nullptr);
|
|
||||||
|
|
||||||
QString name() const override;
|
|
||||||
QHash<QString, QString> cacheHashes() const override;
|
|
||||||
|
|
||||||
QVariantMap translateNotification(const QString ¬ification, const QVariantMap ¶ms, const QLocale &locale) override;
|
|
||||||
|
|
||||||
Q_INVOKABLE JsonReply *GetSupportedVendors(const QVariantMap ¶ms, const JsonContext &context) const;
|
|
||||||
Q_INVOKABLE JsonReply *GetSupportedDevices(const QVariantMap ¶ms, const JsonContext &context) const;
|
|
||||||
Q_INVOKABLE JsonReply *GetDiscoveredDevices(const QVariantMap ¶ms, const JsonContext &context) const;
|
|
||||||
Q_INVOKABLE JsonReply *GetPlugins(const QVariantMap ¶ms, const JsonContext &context) const;
|
|
||||||
Q_INVOKABLE JsonReply *GetPluginConfiguration(const QVariantMap ¶ms) const;
|
|
||||||
Q_INVOKABLE JsonReply *SetPluginConfiguration(const QVariantMap ¶ms);
|
|
||||||
|
|
||||||
Q_INVOKABLE JsonReply *AddConfiguredDevice(const QVariantMap ¶ms, const JsonContext &context);
|
|
||||||
Q_INVOKABLE JsonReply *PairDevice(const QVariantMap ¶ms, const JsonContext &context);
|
|
||||||
Q_INVOKABLE JsonReply *ConfirmPairing(const QVariantMap ¶ms, const JsonContext &context);
|
|
||||||
Q_INVOKABLE JsonReply *GetConfiguredDevices(const QVariantMap ¶ms, const JsonContext &context) const;
|
|
||||||
Q_INVOKABLE JsonReply *ReconfigureDevice(const QVariantMap ¶ms, const JsonContext &context);
|
|
||||||
Q_INVOKABLE JsonReply *EditDevice(const QVariantMap ¶ms);
|
|
||||||
Q_INVOKABLE JsonReply *RemoveConfiguredDevice(const QVariantMap ¶ms);
|
|
||||||
Q_INVOKABLE JsonReply *SetDeviceSettings(const QVariantMap ¶ms);
|
|
||||||
|
|
||||||
Q_INVOKABLE JsonReply *GetEventTypes(const QVariantMap ¶ms, const JsonContext &context) const;
|
|
||||||
Q_INVOKABLE JsonReply *GetActionTypes(const QVariantMap ¶ms, const JsonContext &context) const;
|
|
||||||
Q_INVOKABLE JsonReply *GetStateTypes(const QVariantMap ¶ms, const JsonContext &context) const;
|
|
||||||
Q_INVOKABLE JsonReply *GetStateValue(const QVariantMap ¶ms) const;
|
|
||||||
Q_INVOKABLE JsonReply *GetStateValues(const QVariantMap ¶ms) const;
|
|
||||||
|
|
||||||
Q_INVOKABLE JsonReply *BrowseDevice(const QVariantMap ¶ms, const JsonContext &context) const;
|
|
||||||
Q_INVOKABLE JsonReply *GetBrowserItem(const QVariantMap ¶ms, const JsonContext &context) const;
|
|
||||||
|
|
||||||
Q_INVOKABLE JsonReply *ExecuteAction(const QVariantMap ¶ms, const JsonContext &context);
|
|
||||||
Q_INVOKABLE JsonReply *ExecuteBrowserItem(const QVariantMap ¶ms, const JsonContext &context);
|
|
||||||
Q_INVOKABLE JsonReply *ExecuteBrowserItemAction(const QVariantMap ¶ms, const JsonContext &context);
|
|
||||||
|
|
||||||
static QVariantMap packBrowserItem(const BrowserItem &item);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void PluginConfigurationChanged(const QVariantMap ¶ms);
|
|
||||||
void StateChanged(const QVariantMap ¶ms);
|
|
||||||
void DeviceRemoved(const QVariantMap ¶ms);
|
|
||||||
void DeviceAdded(const QVariantMap ¶ms);
|
|
||||||
void DeviceChanged(const QVariantMap ¶ms);
|
|
||||||
void DeviceSettingChanged(const QVariantMap ¶ms);
|
|
||||||
void EventTriggered(const QVariantMap ¶ms);
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void pluginConfigChanged(const PluginId &id, const ParamList &config);
|
|
||||||
|
|
||||||
void deviceStateChanged(Thing *device, const QUuid &stateTypeId, const QVariant &value);
|
|
||||||
|
|
||||||
void deviceRemovedNotification(const QUuid &deviceId);
|
|
||||||
|
|
||||||
void deviceAddedNotification(Thing *thing);
|
|
||||||
|
|
||||||
void deviceChangedNotification(Thing *thing);
|
|
||||||
|
|
||||||
void deviceSettingChangedNotification(const ThingId &thingId, const ParamTypeId ¶mTypeId, const QVariant &value);
|
|
||||||
|
|
||||||
private:
|
|
||||||
QVariantMap statusToReply(Device::ThingError status) const;
|
|
||||||
|
|
||||||
QHash<QString, QString> m_cacheHashes;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
Q_DECLARE_METATYPE(nymeaserver::DeviceClass)
|
|
||||||
Q_DECLARE_METATYPE(nymeaserver::Device::DeviceError)
|
|
||||||
|
|
||||||
#endif // DEVICEHANDLER_H
|
|
||||||
@ -1,100 +0,0 @@
|
|||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
||||||
*
|
|
||||||
* Copyright 2013 - 2020, nymea GmbH
|
|
||||||
* Contact: contact@nymea.io
|
|
||||||
*
|
|
||||||
* This file is part of nymea.
|
|
||||||
* This project including source code and documentation is protected by
|
|
||||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
|
||||||
* reproduction, publication, editing and translation, are reserved. The use of
|
|
||||||
* this project is subject to the terms of a license agreement to be concluded
|
|
||||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
|
||||||
* under https://nymea.io/license
|
|
||||||
*
|
|
||||||
* GNU General Public License Usage
|
|
||||||
* Alternatively, this project may be redistributed and/or modified under the
|
|
||||||
* terms of the GNU General Public License as published by the Free Software
|
|
||||||
* Foundation, GNU version 3. This project 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
|
|
||||||
* this project. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
* For any further details and any questions please contact us under
|
|
||||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
|
||||||
* https://nymea.io/license/faq
|
|
||||||
*
|
|
||||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
||||||
|
|
||||||
#include "eventhandler.h"
|
|
||||||
#include "nymeacore.h"
|
|
||||||
#include "loggingcategories.h"
|
|
||||||
#include "devicehandler.h"
|
|
||||||
|
|
||||||
namespace nymeaserver {
|
|
||||||
|
|
||||||
/*! Constructs a new \l EventHandler with the given \a parent. */
|
|
||||||
EventHandler::EventHandler(QObject *parent) :
|
|
||||||
JsonHandler(parent)
|
|
||||||
{
|
|
||||||
registerEnum<Types::InputType>();
|
|
||||||
registerEnum<Types::Unit>();
|
|
||||||
// Objects
|
|
||||||
registerObject<Param, ParamList>();
|
|
||||||
registerObject<Event>();
|
|
||||||
registerObject<ParamType, ParamTypes>();
|
|
||||||
registerObject<EventType>();
|
|
||||||
|
|
||||||
// Methods
|
|
||||||
QString description; QVariantMap params; QVariantMap returns;
|
|
||||||
description = "Get the EventType for the given eventTypeId.";
|
|
||||||
params.insert("eventTypeId", enumValueName(Uuid));
|
|
||||||
returns.insert("deviceError", enumRef<Device::DeviceError>());
|
|
||||||
returns.insert("o:eventType", objectRef<EventType>());
|
|
||||||
registerMethod("GetEventType", description, params, returns, "Please use the Devices namespace instead.");
|
|
||||||
|
|
||||||
// Notifications
|
|
||||||
params.clear(); returns.clear();
|
|
||||||
description = "Emitted whenever an Event is triggered.";
|
|
||||||
params.insert("event", objectRef<Event>());
|
|
||||||
registerNotification("EventTriggered", description, params, "Please use Devices.EventTriggered instead.");
|
|
||||||
connect(NymeaCore::instance(), &NymeaCore::eventTriggered, this, &EventHandler::eventTriggered);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Returns the name of the \l{EventHandler}. In this case \b Events.*/
|
|
||||||
QString EventHandler::name() const
|
|
||||||
{
|
|
||||||
return "Events";
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventHandler::eventTriggered(const Event &event)
|
|
||||||
{
|
|
||||||
QVariantMap params;
|
|
||||||
params.insert("event", pack(event));
|
|
||||||
emit EventTriggered(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonReply* EventHandler::GetEventType(const QVariantMap ¶ms, const JsonContext &context) const
|
|
||||||
{
|
|
||||||
qCDebug(dcJsonRpc) << "asked for event type" << params;
|
|
||||||
EventTypeId eventTypeId(params.value("eventTypeId").toString());
|
|
||||||
foreach (const ThingClass &deviceClass, NymeaCore::instance()->thingManager()->supportedThings()) {
|
|
||||||
foreach (const EventType &eventType, deviceClass.eventTypes()) {
|
|
||||||
if (eventType.id() == eventTypeId) {
|
|
||||||
EventType translatedEventType = eventType;
|
|
||||||
translatedEventType.setDisplayName(NymeaCore::instance()->thingManager()->translate(deviceClass.pluginId(), eventType.displayName(), context.locale()));
|
|
||||||
QVariantMap data;
|
|
||||||
data.insert("deviceError", enumValueName<Thing::ThingError>(Thing::ThingErrorNoError).replace("ThingError", "DeviceError"));
|
|
||||||
data.insert("eventType", pack(translatedEventType));
|
|
||||||
return createReply(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QVariantMap data;
|
|
||||||
data.insert("deviceError", enumValueName<Thing::ThingError>(Thing::ThingErrorEventTypeNotFound).replace("ThingError", "DeviceError"));
|
|
||||||
return createReply(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,58 +0,0 @@
|
|||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
||||||
*
|
|
||||||
* Copyright 2013 - 2020, nymea GmbH
|
|
||||||
* Contact: contact@nymea.io
|
|
||||||
*
|
|
||||||
* This file is part of nymea.
|
|
||||||
* This project including source code and documentation is protected by
|
|
||||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
|
||||||
* reproduction, publication, editing and translation, are reserved. The use of
|
|
||||||
* this project is subject to the terms of a license agreement to be concluded
|
|
||||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
|
||||||
* under https://nymea.io/license
|
|
||||||
*
|
|
||||||
* GNU General Public License Usage
|
|
||||||
* Alternatively, this project may be redistributed and/or modified under the
|
|
||||||
* terms of the GNU General Public License as published by the Free Software
|
|
||||||
* Foundation, GNU version 3. This project 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
|
|
||||||
* this project. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
* For any further details and any questions please contact us under
|
|
||||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
|
||||||
* https://nymea.io/license/faq
|
|
||||||
*
|
|
||||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
||||||
|
|
||||||
#ifndef EVENTHANDLER_H
|
|
||||||
#define EVENTHANDLER_H
|
|
||||||
|
|
||||||
#include "jsonrpc/jsonhandler.h"
|
|
||||||
|
|
||||||
#include "types/event.h"
|
|
||||||
|
|
||||||
namespace nymeaserver {
|
|
||||||
|
|
||||||
class EventHandler : public JsonHandler
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit EventHandler(QObject *parent = nullptr);
|
|
||||||
QString name() const override;
|
|
||||||
|
|
||||||
Q_INVOKABLE JsonReply *GetEventType(const QVariantMap ¶ms, const JsonContext &context) const;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void EventTriggered(const QVariantMap ¶ms);
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void eventTriggered(const Event &event);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // EVENTHANDLER_H
|
|
||||||
@ -102,7 +102,7 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
|
|||||||
QString description; QVariantMap returns; QVariantMap params;
|
QString description; QVariantMap returns; QVariantMap params;
|
||||||
description = "Returns a list of supported Vendors.";
|
description = "Returns a list of supported Vendors.";
|
||||||
returns.insert("vendors", objectRef<Vendors>());
|
returns.insert("vendors", objectRef<Vendors>());
|
||||||
registerMethod("GetVendors", description, params, returns);
|
registerMethod("GetVendors", description, params, returns, Types::PermissionScopeNone);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Returns a list of supported thing classes, optionally filtered by vendorId or by a list of thing class ids.";
|
description = "Returns a list of supported thing classes, optionally filtered by vendorId or by a list of thing class ids.";
|
||||||
@ -110,26 +110,26 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
|
|||||||
params.insert("o:thingClassIds", QVariantList() << enumValueName(Uuid));
|
params.insert("o:thingClassIds", QVariantList() << enumValueName(Uuid));
|
||||||
returns.insert("thingError", enumRef<Thing::ThingError>());
|
returns.insert("thingError", enumRef<Thing::ThingError>());
|
||||||
returns.insert("o:thingClasses", objectRef<ThingClasses>());
|
returns.insert("o:thingClasses", objectRef<ThingClasses>());
|
||||||
registerMethod("GetThingClasses", description, params, returns);
|
registerMethod("GetThingClasses", description, params, returns, Types::PermissionScopeNone);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Returns a list of loaded plugins.";
|
description = "Returns a list of loaded plugins.";
|
||||||
returns.insert("plugins", objectRef<IntegrationPlugins>());
|
returns.insert("plugins", objectRef<IntegrationPlugins>());
|
||||||
registerMethod("GetPlugins", description, params, returns);
|
registerMethod("GetPlugins", description, params, returns, Types::PermissionScopeNone);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Get a plugin's params.";
|
description = "Get a plugin's params.";
|
||||||
params.insert("pluginId", enumValueName(Uuid));
|
params.insert("pluginId", enumValueName(Uuid));
|
||||||
returns.insert("thingError", enumRef<Thing::ThingError>());
|
returns.insert("thingError", enumRef<Thing::ThingError>());
|
||||||
returns.insert("o:configuration", objectRef<ParamList>());
|
returns.insert("o:configuration", objectRef<ParamList>());
|
||||||
registerMethod("GetPluginConfiguration", description, params, returns);
|
registerMethod("GetPluginConfiguration", description, params, returns, Types::PermissionScopeConfigureThings);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Set a plugin's params.";
|
description = "Set a plugin's params.";
|
||||||
params.insert("pluginId", enumValueName(Uuid));
|
params.insert("pluginId", enumValueName(Uuid));
|
||||||
params.insert("configuration", objectRef<ParamList>());
|
params.insert("configuration", objectRef<ParamList>());
|
||||||
returns.insert("thingError", enumRef<Thing::ThingError>());
|
returns.insert("thingError", enumRef<Thing::ThingError>());
|
||||||
registerMethod("SetPluginConfiguration", description, params, returns);
|
registerMethod("SetPluginConfiguration", description, params, returns, Types::PermissionScopeConfigureThings);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Add a new thing to the system. "
|
description = "Add a new thing to the system. "
|
||||||
@ -146,7 +146,7 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
|
|||||||
returns.insert("thingError", enumRef<Thing::ThingError>());
|
returns.insert("thingError", enumRef<Thing::ThingError>());
|
||||||
returns.insert("o:thingId", enumValueName(Uuid));
|
returns.insert("o:thingId", enumValueName(Uuid));
|
||||||
returns.insert("o:displayMessage", enumValueName(String));
|
returns.insert("o:displayMessage", enumValueName(String));
|
||||||
registerMethod("AddThing", description, params, returns);
|
registerMethod("AddThing", description, params, returns, Types::PermissionScopeConfigureThings);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Pair a new thing. "
|
description = "Pair a new thing. "
|
||||||
@ -178,7 +178,7 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
|
|||||||
returns.insert("o:displayMessage", enumValueName(String));
|
returns.insert("o:displayMessage", enumValueName(String));
|
||||||
returns.insert("o:oAuthUrl", enumValueName(String));
|
returns.insert("o:oAuthUrl", enumValueName(String));
|
||||||
returns.insert("o:pin", enumValueName(String));
|
returns.insert("o:pin", enumValueName(String));
|
||||||
registerMethod("PairThing", description, params, returns);
|
registerMethod("PairThing", description, params, returns, Types::PermissionScopeConfigureThings);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Confirm an ongoing pairing. For SetupMethodUserAndPassword, provide the username in the \"username\" field "
|
description = "Confirm an ongoing pairing. For SetupMethodUserAndPassword, provide the username in the \"username\" field "
|
||||||
@ -191,14 +191,14 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
|
|||||||
returns.insert("thingError", enumRef<Thing::ThingError>());
|
returns.insert("thingError", enumRef<Thing::ThingError>());
|
||||||
returns.insert("o:displayMessage", enumValueName(String));
|
returns.insert("o:displayMessage", enumValueName(String));
|
||||||
returns.insert("o:thingId", enumValueName(Uuid));
|
returns.insert("o:thingId", enumValueName(Uuid));
|
||||||
registerMethod("ConfirmPairing", description, params, returns);
|
registerMethod("ConfirmPairing", description, params, returns, Types::PermissionScopeConfigureThings);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Returns a list of configured things, optionally filtered by thingId.";
|
description = "Returns a list of configured things, optionally filtered by thingId.";
|
||||||
params.insert("o:thingId", enumValueName(Uuid));
|
params.insert("o:thingId", enumValueName(Uuid));
|
||||||
returns.insert("o:things", objectRef<Things>());
|
returns.insert("o:things", objectRef<Things>());
|
||||||
returns.insert("thingError", enumRef<Thing::ThingError>());
|
returns.insert("thingError", enumRef<Thing::ThingError>());
|
||||||
registerMethod("GetThings", description, params, returns);
|
registerMethod("GetThings", description, params, returns, Types::PermissionScopeNone);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Performs a thing discovery for things of the given thingClassId and returns the results. "
|
description = "Performs a thing discovery for things of the given thingClassId and returns the results. "
|
||||||
@ -211,7 +211,7 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
|
|||||||
returns.insert("thingError", enumRef<Thing::ThingError>());
|
returns.insert("thingError", enumRef<Thing::ThingError>());
|
||||||
returns.insert("o:displayMessage", enumValueName(String));
|
returns.insert("o:displayMessage", enumValueName(String));
|
||||||
returns.insert("o:thingDescriptors", objectRef<ThingDescriptors>());
|
returns.insert("o:thingDescriptors", objectRef<ThingDescriptors>());
|
||||||
registerMethod("DiscoverThings", description, params, returns);
|
registerMethod("DiscoverThings", description, params, returns, Types::PermissionScopeConfigureThings);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Reconfigure a thing. This comes down to removing and recreating a thing with new parameters "
|
description = "Reconfigure a thing. This comes down to removing and recreating a thing with new parameters "
|
||||||
@ -226,21 +226,21 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
|
|||||||
params.insert("o:thingParams", objectRef<ParamList>());
|
params.insert("o:thingParams", objectRef<ParamList>());
|
||||||
returns.insert("thingError", enumRef<Thing::ThingError>());
|
returns.insert("thingError", enumRef<Thing::ThingError>());
|
||||||
returns.insert("o:displayMessage", enumValueName(String));
|
returns.insert("o:displayMessage", enumValueName(String));
|
||||||
registerMethod("ReconfigureThing", description, params, returns);
|
registerMethod("ReconfigureThing", description, params, returns, Types::PermissionScopeConfigureThings);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Edit the name of a thing.";
|
description = "Edit the name of a thing.";
|
||||||
params.insert("thingId", enumValueName(Uuid));
|
params.insert("thingId", enumValueName(Uuid));
|
||||||
params.insert("name", enumValueName(String));
|
params.insert("name", enumValueName(String));
|
||||||
returns.insert("thingError", enumRef<Thing::ThingError>());
|
returns.insert("thingError", enumRef<Thing::ThingError>());
|
||||||
registerMethod("EditThing", description, params, returns);
|
registerMethod("EditThing", description, params, returns, Types::PermissionScopeConfigureThings);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Change the settings of a thing.";
|
description = "Change the settings of a thing.";
|
||||||
params.insert("thingId", enumValueName(Uuid));
|
params.insert("thingId", enumValueName(Uuid));
|
||||||
params.insert("settings", objectRef<ParamList>());
|
params.insert("settings", objectRef<ParamList>());
|
||||||
returns.insert("thingError", enumRef<Thing::ThingError>());
|
returns.insert("thingError", enumRef<Thing::ThingError>());
|
||||||
registerMethod("SetThingSettings", description, params, returns);
|
registerMethod("SetThingSettings", description, params, returns, Types::PermissionScopeConfigureThings);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Enable/disable logging for the given event type on the given thing.";
|
description = "Enable/disable logging for the given event type on the given thing.";
|
||||||
@ -248,7 +248,7 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
|
|||||||
params.insert("eventTypeId", enumValueName(Uuid));
|
params.insert("eventTypeId", enumValueName(Uuid));
|
||||||
params.insert("enabled", enumValueName(Bool));
|
params.insert("enabled", enumValueName(Bool));
|
||||||
returns.insert("thingError", enumRef<Thing::ThingError>());
|
returns.insert("thingError", enumRef<Thing::ThingError>());
|
||||||
registerMethod("SetEventLogging", description, params, returns);
|
registerMethod("SetEventLogging", description, params, returns, Types::PermissionScopeConfigureThings);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Set the filter for the given state on the given thing.";
|
description = "Set the filter for the given state on the given thing.";
|
||||||
@ -256,7 +256,7 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
|
|||||||
params.insert("stateTypeId", enumValueName(Uuid));
|
params.insert("stateTypeId", enumValueName(Uuid));
|
||||||
params.insert("filter", enumRef<Types::StateValueFilter>());
|
params.insert("filter", enumRef<Types::StateValueFilter>());
|
||||||
returns.insert("thingError", enumRef<Thing::ThingError>());
|
returns.insert("thingError", enumRef<Thing::ThingError>());
|
||||||
registerMethod("SetStateFilter", description, params, returns);
|
registerMethod("SetStateFilter", description, params, returns, Types::PermissionScopeConfigureThings);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Remove a thing from the system.";
|
description = "Remove a thing from the system.";
|
||||||
@ -270,25 +270,25 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
|
|||||||
params.insert("o:removePolicyList", removePolicyList);
|
params.insert("o:removePolicyList", removePolicyList);
|
||||||
returns.insert("thingError", enumRef<Thing::ThingError>());
|
returns.insert("thingError", enumRef<Thing::ThingError>());
|
||||||
returns.insert("o:ruleIds", QVariantList() << enumValueName(Uuid));
|
returns.insert("o:ruleIds", QVariantList() << enumValueName(Uuid));
|
||||||
registerMethod("RemoveThing", description, params, returns);
|
registerMethod("RemoveThing", description, params, returns, Types::PermissionScopeConfigureThings);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Get event types for a specified thingClassId.";
|
description = "Get event types for a specified thingClassId.";
|
||||||
params.insert("thingClassId", enumValueName(Uuid));
|
params.insert("thingClassId", enumValueName(Uuid));
|
||||||
returns.insert("eventTypes", objectRef<EventTypes>());
|
returns.insert("eventTypes", objectRef<EventTypes>());
|
||||||
registerMethod("GetEventTypes", description, params, returns);
|
registerMethod("GetEventTypes", description, params, returns, Types::PermissionScopeNone);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Get action types for a specified thingClassId.";
|
description = "Get action types for a specified thingClassId.";
|
||||||
params.insert("thingClassId", enumValueName(Uuid));
|
params.insert("thingClassId", enumValueName(Uuid));
|
||||||
returns.insert("actionTypes", objectRef<ActionTypes>());
|
returns.insert("actionTypes", objectRef<ActionTypes>());
|
||||||
registerMethod("GetActionTypes", description, params, returns);
|
registerMethod("GetActionTypes", description, params, returns, Types::PermissionScopeNone);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Get state types for a specified thingClassId.";
|
description = "Get state types for a specified thingClassId.";
|
||||||
params.insert("thingClassId", enumValueName(Uuid));
|
params.insert("thingClassId", enumValueName(Uuid));
|
||||||
returns.insert("stateTypes", objectRef<StateTypes>());
|
returns.insert("stateTypes", objectRef<StateTypes>());
|
||||||
registerMethod("GetStateTypes", description, params, returns);
|
registerMethod("GetStateTypes", description, params, returns, Types::PermissionScopeNone);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Get the value of the given thing and the given stateType";
|
description = "Get the value of the given thing and the given stateType";
|
||||||
@ -296,14 +296,14 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
|
|||||||
params.insert("stateTypeId", enumValueName(Uuid));
|
params.insert("stateTypeId", enumValueName(Uuid));
|
||||||
returns.insert("thingError", enumRef<Thing::ThingError>());
|
returns.insert("thingError", enumRef<Thing::ThingError>());
|
||||||
returns.insert("o:value", enumValueName(Variant));
|
returns.insert("o:value", enumValueName(Variant));
|
||||||
registerMethod("GetStateValue", description, params, returns);
|
registerMethod("GetStateValue", description, params, returns, Types::PermissionScopeNone);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Get all the state values of the given thing.";
|
description = "Get all the state values of the given thing.";
|
||||||
params.insert("thingId", enumValueName(Uuid));
|
params.insert("thingId", enumValueName(Uuid));
|
||||||
returns.insert("thingError", enumRef<Thing::ThingError>());
|
returns.insert("thingError", enumRef<Thing::ThingError>());
|
||||||
returns.insert("o:values", objectRef<States>());
|
returns.insert("o:values", objectRef<States>());
|
||||||
registerMethod("GetStateValues", description, params, returns);
|
registerMethod("GetStateValues", description, params, returns, Types::PermissionScopeNone);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Browse a thing. "
|
description = "Browse a thing. "
|
||||||
@ -318,7 +318,7 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
|
|||||||
returns.insert("thingError", enumRef<Thing::ThingError>());
|
returns.insert("thingError", enumRef<Thing::ThingError>());
|
||||||
returns.insert("o:displayMessage", enumValueName(String));
|
returns.insert("o:displayMessage", enumValueName(String));
|
||||||
returns.insert("items", QVariantList() << objectRef("BrowserItem"));
|
returns.insert("items", QVariantList() << objectRef("BrowserItem"));
|
||||||
registerMethod("BrowseThing", description, params, returns);
|
registerMethod("BrowseThing", description, params, returns, Types::PermissionScopeNone);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Get a single item from the browser. "
|
description = "Get a single item from the browser. "
|
||||||
@ -332,7 +332,7 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
|
|||||||
returns.insert("thingError", enumRef<Thing::ThingError>());
|
returns.insert("thingError", enumRef<Thing::ThingError>());
|
||||||
returns.insert("o:displayMessage", enumValueName(String));
|
returns.insert("o:displayMessage", enumValueName(String));
|
||||||
returns.insert("o:item", objectRef("BrowserItem"));
|
returns.insert("o:item", objectRef("BrowserItem"));
|
||||||
registerMethod("GetBrowserItem", description, params, returns);
|
registerMethod("GetBrowserItem", description, params, returns, Types::PermissionScopeNone);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Execute a single action.";
|
description = "Execute a single action.";
|
||||||
@ -341,7 +341,7 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
|
|||||||
params.insert("o:params", objectRef<ParamList>());
|
params.insert("o:params", objectRef<ParamList>());
|
||||||
returns.insert("thingError", enumRef<Thing::ThingError>());
|
returns.insert("thingError", enumRef<Thing::ThingError>());
|
||||||
returns.insert("o:displayMessage", enumValueName(String));
|
returns.insert("o:displayMessage", enumValueName(String));
|
||||||
registerMethod("ExecuteAction", description, params, returns);
|
registerMethod("ExecuteAction", description, params, returns, Types::PermissionScopeControlThings);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Execute the item identified by itemId on the given thing.\n"
|
description = "Execute the item identified by itemId on the given thing.\n"
|
||||||
@ -352,7 +352,7 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
|
|||||||
params.insert("itemId", enumValueName(String));
|
params.insert("itemId", enumValueName(String));
|
||||||
returns.insert("thingError", enumRef<Thing::ThingError>());
|
returns.insert("thingError", enumRef<Thing::ThingError>());
|
||||||
returns.insert("o:displayMessage", enumValueName(String));
|
returns.insert("o:displayMessage", enumValueName(String));
|
||||||
registerMethod("ExecuteBrowserItem", description, params, returns);
|
registerMethod("ExecuteBrowserItem", description, params, returns, Types::PermissionScopeControlThings);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Execute the action for the browser item identified by actionTypeId and the itemId on the given thing.\n"
|
description = "Execute the action for the browser item identified by actionTypeId and the itemId on the given thing.\n"
|
||||||
@ -365,13 +365,13 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
|
|||||||
params.insert("o:params", objectRef<ParamList>());
|
params.insert("o:params", objectRef<ParamList>());
|
||||||
returns.insert("thingError", enumRef<Thing::ThingError>());
|
returns.insert("thingError", enumRef<Thing::ThingError>());
|
||||||
returns.insert("o:displayMessage", enumValueName(String));
|
returns.insert("o:displayMessage", enumValueName(String));
|
||||||
registerMethod("ExecuteBrowserItemAction", description, params, returns);
|
registerMethod("ExecuteBrowserItemAction", description, params, returns, Types::PermissionScopeControlThings);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Fetch IO connections. Optionally filtered by thingId and stateTypeId.";
|
description = "Fetch IO connections. Optionally filtered by thingId and stateTypeId.";
|
||||||
params.insert("o:thingId", enumValueName(Uuid));
|
params.insert("o:thingId", enumValueName(Uuid));
|
||||||
returns.insert("ioConnections", objectRef<IOConnections>());
|
returns.insert("ioConnections", objectRef<IOConnections>());
|
||||||
registerMethod("GetIOConnections", description, params, returns);
|
registerMethod("GetIOConnections", description, params, returns, Types::PermissionScopeNone);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Connect two generic IO states. Input and output need to be compatible, that is, either a digital input "
|
description = "Connect two generic IO states. Input and output need to be compatible, that is, either a digital input "
|
||||||
@ -383,13 +383,13 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
|
|||||||
params.insert("o:inverted", enumValueName(Bool));
|
params.insert("o:inverted", enumValueName(Bool));
|
||||||
returns.insert("thingError", enumRef<Thing::ThingError>());
|
returns.insert("thingError", enumRef<Thing::ThingError>());
|
||||||
returns.insert("o:ioConnectionId", enumValueName(Uuid));
|
returns.insert("o:ioConnectionId", enumValueName(Uuid));
|
||||||
registerMethod("ConnectIO", description, params, returns);
|
registerMethod("ConnectIO", description, params, returns, Types::PermissionScopeConfigureThings);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Disconnect an existing IO connection.";
|
description = "Disconnect an existing IO connection.";
|
||||||
params.insert("ioConnectionId", enumValueName(Uuid));
|
params.insert("ioConnectionId", enumValueName(Uuid));
|
||||||
returns.insert("thingError", enumRef<Thing::ThingError>());
|
returns.insert("thingError", enumRef<Thing::ThingError>());
|
||||||
registerMethod("DisconnectIO", description, params, returns);
|
registerMethod("DisconnectIO", description, params, returns, Types::PermissionScopeConfigureThings);
|
||||||
|
|
||||||
|
|
||||||
// Notifications
|
// Notifications
|
||||||
|
|||||||
@ -60,14 +60,10 @@
|
|||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "cloud/cloudmanager.h"
|
#include "cloud/cloudmanager.h"
|
||||||
|
|
||||||
#include "devicehandler.h"
|
|
||||||
#include "integrationshandler.h"
|
#include "integrationshandler.h"
|
||||||
#include "actionhandler.h"
|
|
||||||
#include "ruleshandler.h"
|
#include "ruleshandler.h"
|
||||||
#include "scriptshandler.h"
|
#include "scriptshandler.h"
|
||||||
#include "eventhandler.h"
|
|
||||||
#include "logginghandler.h"
|
#include "logginghandler.h"
|
||||||
#include "statehandler.h"
|
|
||||||
#include "configurationhandler.h"
|
#include "configurationhandler.h"
|
||||||
#include "networkmanagerhandler.h"
|
#include "networkmanagerhandler.h"
|
||||||
#include "tagshandler.h"
|
#include "tagshandler.h"
|
||||||
@ -95,6 +91,7 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration
|
|||||||
registerEnum<BasicType>();
|
registerEnum<BasicType>();
|
||||||
registerEnum<UserManager::UserError>();
|
registerEnum<UserManager::UserError>();
|
||||||
registerEnum<CloudManager::CloudConnectionState>();
|
registerEnum<CloudManager::CloudConnectionState>();
|
||||||
|
registerFlag<Types::PermissionScope, Types::PermissionScopes>();
|
||||||
|
|
||||||
// Objects
|
// Objects
|
||||||
registerObject<TokenInfo>();
|
registerObject<TokenInfo>();
|
||||||
@ -122,7 +119,9 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration
|
|||||||
"like initialSetupRequired might change if the setup has been performed in the meantime.\n "
|
"like initialSetupRequired might change if the setup has been performed in the meantime.\n "
|
||||||
"The field cacheHashes may contain a map of methods and MD5 hashes. As long as the hash for "
|
"The field cacheHashes may contain a map of methods and MD5 hashes. As long as the hash for "
|
||||||
"a method does not change, a client may use a previously cached copy of the call instead of "
|
"a method does not change, a client may use a previously cached copy of the call instead of "
|
||||||
"fetching the content again.";
|
"fetching the content again. While the Hello call doesn't necessarily require a token, this "
|
||||||
|
"can be called with a token. If a token is provided, it will be verified and the reply contains "
|
||||||
|
"information about the tokens validity and the user and permissions for the given token.";
|
||||||
params.insert("o:locale", enumValueName(String));
|
params.insert("o:locale", enumValueName(String));
|
||||||
returns.insert("server", enumValueName(String));
|
returns.insert("server", enumValueName(String));
|
||||||
returns.insert("name", enumValueName(String));
|
returns.insert("name", enumValueName(String));
|
||||||
@ -136,14 +135,17 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration
|
|||||||
returns.insert("pushButtonAuthAvailable", enumValueName(Bool));
|
returns.insert("pushButtonAuthAvailable", enumValueName(Bool));
|
||||||
returns.insert("o:experiences", QVariantList() << objectRef("Experience"));
|
returns.insert("o:experiences", QVariantList() << objectRef("Experience"));
|
||||||
returns.insert("o:cacheHashes", QVariantList() << objectRef("CacheHash"));
|
returns.insert("o:cacheHashes", QVariantList() << objectRef("CacheHash"));
|
||||||
registerMethod("Hello", description, params, returns);
|
returns.insert("o:authenticated", enumValueName(Bool));
|
||||||
|
returns.insert("o:permissionScopes", flagRef<Types::PermissionScopes>());
|
||||||
|
returns.insert("o:username", enumValueName(String));
|
||||||
|
registerMethod("Hello", description, params, returns, Types::PermissionScopeNone);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Introspect this API.";
|
description = "Introspect this API.";
|
||||||
returns.insert("methods", enumValueName(Object));
|
returns.insert("methods", enumValueName(Object));
|
||||||
returns.insert("notifications", enumValueName(Object));
|
returns.insert("notifications", enumValueName(Object));
|
||||||
returns.insert("types", enumValueName(Object));
|
returns.insert("types", enumValueName(Object));
|
||||||
registerMethod("Introspect", description, params, returns);
|
registerMethod("Introspect", description, params, returns, Types::PermissionScopeNone);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Version of this nymea/JSONRPC interface.";
|
description = "Version of this nymea/JSONRPC interface.";
|
||||||
@ -151,7 +153,7 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration
|
|||||||
returns.insert("protocol version", enumValueName(String));
|
returns.insert("protocol version", enumValueName(String));
|
||||||
returns.insert("qtVersion", enumValueName(String));
|
returns.insert("qtVersion", enumValueName(String));
|
||||||
returns.insert("qtBuildVersion", enumValueName(String));
|
returns.insert("qtBuildVersion", enumValueName(String));
|
||||||
registerMethod("Version", description, params, returns);
|
registerMethod("Version", description, params, returns, Types::PermissionScopeNone);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Enable/Disable notifications for this connections. Either \"enabled\" or """
|
description = "Enable/Disable notifications for this connections. Either \"enabled\" or """
|
||||||
@ -166,14 +168,18 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration
|
|||||||
params.insert("d:o:enabled", enumValueName(Bool));
|
params.insert("d:o:enabled", enumValueName(Bool));
|
||||||
returns.insert("namespaces", enumValueName(StringList));
|
returns.insert("namespaces", enumValueName(StringList));
|
||||||
returns.insert("d:enabled", enumValueName(Bool));
|
returns.insert("d:enabled", enumValueName(Bool));
|
||||||
registerMethod("SetNotificationStatus", description, params, returns);
|
registerMethod("SetNotificationStatus", description, params, returns, Types::PermissionScopeNone);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Create a new user in the API. Currently this is only allowed to be called once when a new nymea instance is set up. Call Authenticate after this to obtain a device token for this user.";
|
description = "Create a new user in the API. This is only allowed to be called when the initial setup is required. "
|
||||||
|
"To create additional users, use Users.CreateUser instead. Call Authenticate after this to obtain a "
|
||||||
|
"device token for the newly created user.";
|
||||||
params.insert("username", enumValueName(String));
|
params.insert("username", enumValueName(String));
|
||||||
params.insert("password", enumValueName(String));
|
params.insert("password", enumValueName(String));
|
||||||
|
params.insert("o:displayName", enumValueName(String));
|
||||||
|
params.insert("o:email", enumValueName(String));
|
||||||
returns.insert("error", enumRef<UserManager::UserError>());
|
returns.insert("error", enumRef<UserManager::UserError>());
|
||||||
registerMethod("CreateUser", description, params, returns, "Use Users.CreateUser instead.");
|
registerMethod("CreateUser", description, params, returns, Types::PermissionScopeAdmin);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Authenticate a client to the api via user & password challenge. Provide "
|
description = "Authenticate a client to the api via user & password challenge. Provide "
|
||||||
@ -185,7 +191,9 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration
|
|||||||
params.insert("deviceName", enumValueName(String));
|
params.insert("deviceName", enumValueName(String));
|
||||||
returns.insert("success", enumValueName(Bool));
|
returns.insert("success", enumValueName(Bool));
|
||||||
returns.insert("o:token", enumValueName(String));
|
returns.insert("o:token", enumValueName(String));
|
||||||
registerMethod("Authenticate", description, params, returns, "Use Users.Authenticate instead.");
|
returns.insert("o:username", enumValueName(String));
|
||||||
|
returns.insert("o:scopes", flagRef<Types::PermissionScopes>());
|
||||||
|
registerMethod("Authenticate", description, params, returns, Types::PermissionScopeNone);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Authenticate a client to the api via Push Button method. "
|
description = "Authenticate a client to the api via Push Button method. "
|
||||||
@ -204,18 +212,7 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration
|
|||||||
params.insert("deviceName", enumValueName(String));
|
params.insert("deviceName", enumValueName(String));
|
||||||
returns.insert("success", enumValueName(Bool));
|
returns.insert("success", enumValueName(Bool));
|
||||||
returns.insert("transactionId", enumValueName(Int));
|
returns.insert("transactionId", enumValueName(Int));
|
||||||
registerMethod("RequestPushButtonAuth", description, params, returns, "Use Users.RequestPushButtonAuth instead.");
|
registerMethod("RequestPushButtonAuth", description, params, returns, Types::PermissionScopeNone);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
|
||||||
description = "Return a list of TokenInfo objects of all the tokens for the current user.";
|
|
||||||
returns.insert("tokenInfoList", QVariantList() << objectRef("TokenInfo"));
|
|
||||||
registerMethod("Tokens", description, params, returns, "Use Users.GetTokens instead.");
|
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
|
||||||
description = "Revoke access for a given token.";
|
|
||||||
params.insert("tokenId", enumValueName(Uuid));
|
|
||||||
returns.insert("error", enumRef<UserManager::UserError>());
|
|
||||||
registerMethod("RemoveToken", description, params, returns, "Use Users.RemoveToken instead.");
|
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Sets up the cloud connection by deploying a certificate and its configuration.";
|
description = "Sets up the cloud connection by deploying a certificate and its configuration.";
|
||||||
@ -246,7 +243,7 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration
|
|||||||
params.insert("sessionId", enumValueName(String));
|
params.insert("sessionId", enumValueName(String));
|
||||||
returns.insert("success", enumValueName(Bool));
|
returns.insert("success", enumValueName(Bool));
|
||||||
returns.insert("sessionId", enumValueName(String));
|
returns.insert("sessionId", enumValueName(String));
|
||||||
registerMethod("KeepAlive", description, params, returns);
|
registerMethod("KeepAlive", description, params, returns, Types::PermissionScopeNone);
|
||||||
|
|
||||||
// Notifications
|
// Notifications
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
@ -260,11 +257,14 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration
|
|||||||
params.insert("success", enumValueName(Bool));
|
params.insert("success", enumValueName(Bool));
|
||||||
params.insert("transactionId", enumValueName(Int));
|
params.insert("transactionId", enumValueName(Int));
|
||||||
params.insert("o:token", enumValueName(String));
|
params.insert("o:token", enumValueName(String));
|
||||||
registerNotification("PushButtonAuthFinished", description, params, "Use Users.PushButtonAuthFinished instead.");
|
registerNotification("PushButtonAuthFinished", description, params);
|
||||||
|
|
||||||
QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection);
|
QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection);
|
||||||
|
|
||||||
connect(NymeaCore::instance()->userManager(), &UserManager::pushButtonAuthFinished, this, &JsonRPCServerImplementation::onPushButtonAuthFinished);
|
connect(NymeaCore::instance()->userManager(), &UserManager::pushButtonAuthFinished, this, &JsonRPCServerImplementation::onPushButtonAuthFinished);
|
||||||
|
|
||||||
|
m_connectionLockdownTimer.setSingleShot(true);
|
||||||
|
m_connectionLockdownTimer.setInterval(3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Returns the \e namespace of \l{JsonHandler}. */
|
/*! Returns the \e namespace of \l{JsonHandler}. */
|
||||||
@ -290,7 +290,67 @@ JsonReply *JsonRPCServerImplementation::Hello(const QVariantMap ¶ms, const J
|
|||||||
delete m_newConnectionWaitTimers.take(clientId);
|
delete m_newConnectionWaitTimers.take(clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return createReply(createWelcomeMessage(interface, clientId));
|
// Compose the reply
|
||||||
|
QVariantMap handshake;
|
||||||
|
handshake.insert("server", "nymea");
|
||||||
|
handshake.insert("name", NymeaCore::instance()->configuration()->serverName());
|
||||||
|
handshake.insert("version", NYMEA_VERSION_STRING);
|
||||||
|
handshake.insert("uuid", NymeaCore::instance()->configuration()->serverUuid().toString());
|
||||||
|
// "language" is deprecated
|
||||||
|
handshake.insert("language", m_clientLocales.value(clientId).name());
|
||||||
|
handshake.insert("locale", m_clientLocales.value(clientId).name());
|
||||||
|
handshake.insert("protocol version", JSON_PROTOCOL_VERSION);
|
||||||
|
handshake.insert("initialSetupRequired", (interface->configuration().authenticationEnabled ? NymeaCore::instance()->userManager()->initRequired() : false));
|
||||||
|
handshake.insert("authenticationRequired", interface->configuration().authenticationEnabled);
|
||||||
|
handshake.insert("pushButtonAuthAvailable", NymeaCore::instance()->userManager()->pushButtonAuthAvailable());
|
||||||
|
if (!m_experiences.isEmpty()) {
|
||||||
|
QVariantList experiences;
|
||||||
|
foreach (JsonHandler* handler, m_experiences.keys()) {
|
||||||
|
QVariantMap experience;
|
||||||
|
experience.insert("name", handler->name());
|
||||||
|
experience.insert("version", m_experiences.value(handler));
|
||||||
|
experiences.append(experience);
|
||||||
|
}
|
||||||
|
handshake.insert("experiences", experiences);
|
||||||
|
}
|
||||||
|
QVariantList cacheHashes;
|
||||||
|
foreach (const QString &handlerName, m_handlers.keys()) {
|
||||||
|
QHash<QString, QString> hashes = m_handlers.value(handlerName)->cacheHashes();
|
||||||
|
foreach (const QString &hashName, hashes.keys()) {
|
||||||
|
QVariantMap cacheHash;
|
||||||
|
cacheHash.insert("method", handlerName + "." + hashName);
|
||||||
|
cacheHash.insert("hash", hashes.value(hashName));
|
||||||
|
cacheHashes.append(cacheHash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!cacheHashes.isEmpty()) {
|
||||||
|
handshake.insert("cacheHashes", cacheHashes);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool badToken = false;
|
||||||
|
if (!context.token().isEmpty()) {
|
||||||
|
TokenInfo tokenInfo = NymeaCore::instance()->userManager()->tokenInfo(context.token());
|
||||||
|
UserInfo userInfo = NymeaCore::instance()->userManager()->userInfo(tokenInfo.username());
|
||||||
|
badToken = tokenInfo.id().isNull();
|
||||||
|
handshake.insert("authenticated", !badToken);
|
||||||
|
handshake.insert("permissionScopes", Types::scopesToStringList(userInfo.scopes()));
|
||||||
|
handshake.insert("username", userInfo.username());
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the connection is locked down already (because of a previous failed attempt) and authentication failed
|
||||||
|
// again, drop the client. He won't be able to reconnect until the lockdown timer runs out.
|
||||||
|
// This will give at max 2 attempts to present a valid token per lockdown period.
|
||||||
|
if (m_connectionLockdownTimer.isActive() && badToken) {
|
||||||
|
qCWarning(dcJsonRpc()) << "Dropping client because of repeated bad token authentication.";
|
||||||
|
interface->terminateClientConnection(clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (badToken) {
|
||||||
|
qCWarning(dcJsonRpc()) << "Staring connection lockdown timer";
|
||||||
|
m_connectionLockdownTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
return createReply(handshake);;
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonReply* JsonRPCServerImplementation::Introspect(const QVariantMap ¶ms) const
|
JsonReply* JsonRPCServerImplementation::Introspect(const QVariantMap ¶ms) const
|
||||||
@ -342,15 +402,17 @@ JsonReply *JsonRPCServerImplementation::CreateUser(const QVariantMap ¶ms)
|
|||||||
{
|
{
|
||||||
QString username = params.value("username").toString();
|
QString username = params.value("username").toString();
|
||||||
QString password = params.value("password").toString();
|
QString password = params.value("password").toString();
|
||||||
|
QString email = params.value("email").toString();
|
||||||
|
QString displayName = params.value("displayName").toString();
|
||||||
|
|
||||||
UserManager::UserError status = NymeaCore::instance()->userManager()->createUser(username, password);
|
UserManager::UserError status = NymeaCore::instance()->userManager()->createUser(username, password, email, displayName, Types::PermissionScopeAdmin);
|
||||||
|
|
||||||
QVariantMap returns;
|
QVariantMap returns;
|
||||||
returns.insert("error", enumValueName<UserManager::UserError>(status));
|
returns.insert("error", enumValueName<UserManager::UserError>(status));
|
||||||
return createReply(returns);
|
return createReply(returns);
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonReply *JsonRPCServerImplementation::Authenticate(const QVariantMap ¶ms)
|
JsonReply *JsonRPCServerImplementation::Authenticate(const QVariantMap ¶ms, const JsonContext &context)
|
||||||
{
|
{
|
||||||
QString username = params.value("username").toString();
|
QString username = params.value("username").toString();
|
||||||
QString password = params.value("password").toString();
|
QString password = params.value("password").toString();
|
||||||
@ -361,7 +423,26 @@ JsonReply *JsonRPCServerImplementation::Authenticate(const QVariantMap ¶ms)
|
|||||||
ret.insert("success", !token.isEmpty());
|
ret.insert("success", !token.isEmpty());
|
||||||
if (!token.isEmpty()) {
|
if (!token.isEmpty()) {
|
||||||
ret.insert("token", token);
|
ret.insert("token", token);
|
||||||
|
TokenInfo tokenInfo = NymeaCore::instance()->userManager()->tokenInfo(token);
|
||||||
|
UserInfo userInfo = NymeaCore::instance()->userManager()->userInfo(tokenInfo.username());
|
||||||
|
ret.insert("username", userInfo.username());
|
||||||
|
ret.insert("scopes", Types::scopesToStringList(userInfo.scopes()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the connection is locked down already (because of a previous failed attempt) and authentication failed
|
||||||
|
// again, drop the client. He won't be able to reconnect until the lockdown timer runs out.
|
||||||
|
// This will give at max 2 attempts to present a valid token per lockdown period.
|
||||||
|
if (m_connectionLockdownTimer.isActive() && token.isEmpty()) {
|
||||||
|
qCWarning(dcJsonRpc()) << "Dropping client because of repeated bad user/password authentication.";
|
||||||
|
TransportInterface *interface = reinterpret_cast<TransportInterface*>(property("transportInterface").toLongLong());
|
||||||
|
interface->terminateClientConnection(context.clientId());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token.isEmpty()) {
|
||||||
|
qCWarning(dcJsonRpc()) << "Staring connection lockdown timer";
|
||||||
|
m_connectionLockdownTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
return createReply(ret);
|
return createReply(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -380,30 +461,6 @@ JsonReply *JsonRPCServerImplementation::RequestPushButtonAuth(const QVariantMap
|
|||||||
return createReply(data);
|
return createReply(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonReply *JsonRPCServerImplementation::Tokens(const QVariantMap ¶ms, const JsonContext &context) const
|
|
||||||
{
|
|
||||||
Q_UNUSED(params)
|
|
||||||
|
|
||||||
TokenInfo tokenInfo = NymeaCore::instance()->userManager()->tokenInfo(context.token());
|
|
||||||
QList<TokenInfo> tokens = NymeaCore::instance()->userManager()->tokens(tokenInfo.username());
|
|
||||||
QVariantList retList;
|
|
||||||
foreach (const TokenInfo &tokenInfo, tokens) {
|
|
||||||
retList << pack(tokenInfo);
|
|
||||||
}
|
|
||||||
QVariantMap retMap;
|
|
||||||
retMap.insert("tokenInfoList", retList);
|
|
||||||
return createReply(retMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonReply *JsonRPCServerImplementation::RemoveToken(const QVariantMap ¶ms)
|
|
||||||
{
|
|
||||||
QUuid tokenId = params.value("tokenId").toUuid();
|
|
||||||
UserManager::UserError error = NymeaCore::instance()->userManager()->removeToken(tokenId);
|
|
||||||
QVariantMap ret;
|
|
||||||
ret.insert("error", enumValueName<UserManager::UserError>(error));
|
|
||||||
return createReply(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonReply *JsonRPCServerImplementation::SetupCloudConnection(const QVariantMap ¶ms)
|
JsonReply *JsonRPCServerImplementation::SetupCloudConnection(const QVariantMap ¶ms)
|
||||||
{
|
{
|
||||||
if (NymeaCore::instance()->cloudManager()->connectionState() != CloudManager::CloudConnectionStateUnconfigured) {
|
if (NymeaCore::instance()->cloudManager()->connectionState() != CloudManager::CloudConnectionStateUnconfigured) {
|
||||||
@ -543,56 +600,12 @@ void JsonRPCServerImplementation::sendUnauthorizedResponse(TransportInterface *i
|
|||||||
interface->sendData(clientId, data);
|
interface->sendData(clientId, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap JsonRPCServerImplementation::createWelcomeMessage(TransportInterface *interface, const QUuid &clientId) const
|
|
||||||
{
|
|
||||||
QVariantMap handshake;
|
|
||||||
handshake.insert("server", "nymea");
|
|
||||||
handshake.insert("name", NymeaCore::instance()->configuration()->serverName());
|
|
||||||
handshake.insert("version", NYMEA_VERSION_STRING);
|
|
||||||
handshake.insert("uuid", NymeaCore::instance()->configuration()->serverUuid().toString());
|
|
||||||
// "language" is deprecated
|
|
||||||
handshake.insert("language", m_clientLocales.value(clientId).name());
|
|
||||||
handshake.insert("locale", m_clientLocales.value(clientId).name());
|
|
||||||
handshake.insert("protocol version", JSON_PROTOCOL_VERSION);
|
|
||||||
handshake.insert("initialSetupRequired", (interface->configuration().authenticationEnabled ? NymeaCore::instance()->userManager()->initRequired() : false));
|
|
||||||
handshake.insert("authenticationRequired", interface->configuration().authenticationEnabled);
|
|
||||||
handshake.insert("pushButtonAuthAvailable", NymeaCore::instance()->userManager()->pushButtonAuthAvailable());
|
|
||||||
if (!m_experiences.isEmpty()) {
|
|
||||||
QVariantList experiences;
|
|
||||||
foreach (JsonHandler* handler, m_experiences.keys()) {
|
|
||||||
QVariantMap experience;
|
|
||||||
experience.insert("name", handler->name());
|
|
||||||
experience.insert("version", m_experiences.value(handler));
|
|
||||||
experiences.append(experience);
|
|
||||||
}
|
|
||||||
handshake.insert("experiences", experiences);
|
|
||||||
}
|
|
||||||
QVariantList cacheHashes;
|
|
||||||
foreach (const QString &handlerName, m_handlers.keys()) {
|
|
||||||
QHash<QString, QString> hashes = m_handlers.value(handlerName)->cacheHashes();
|
|
||||||
foreach (const QString &hashName, hashes.keys()) {
|
|
||||||
QVariantMap cacheHash;
|
|
||||||
cacheHash.insert("method", handlerName + "." + hashName);
|
|
||||||
cacheHash.insert("hash", hashes.value(hashName));
|
|
||||||
cacheHashes.append(cacheHash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!cacheHashes.isEmpty()) {
|
|
||||||
handshake.insert("cacheHashes", cacheHashes);
|
|
||||||
}
|
|
||||||
return handshake;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JsonRPCServerImplementation::setup()
|
void JsonRPCServerImplementation::setup()
|
||||||
{
|
{
|
||||||
registerHandler(this);
|
registerHandler(this);
|
||||||
registerHandler(new IntegrationsHandler(NymeaCore::instance()->thingManager(), this));
|
registerHandler(new IntegrationsHandler(NymeaCore::instance()->thingManager(), this));
|
||||||
registerHandler(new DeviceHandler(this));
|
|
||||||
registerHandler(new ActionHandler(this));
|
|
||||||
registerHandler(new RulesHandler(this));
|
registerHandler(new RulesHandler(this));
|
||||||
registerHandler(new EventHandler(this));
|
|
||||||
registerHandler(new LoggingHandler(this));
|
registerHandler(new LoggingHandler(this));
|
||||||
registerHandler(new StateHandler(this));
|
|
||||||
registerHandler(new ConfigurationHandler(this));
|
registerHandler(new ConfigurationHandler(this));
|
||||||
registerHandler(new NetworkManagerHandler(NymeaCore::instance()->networkManager(), this));
|
registerHandler(new NetworkManagerHandler(NymeaCore::instance()->networkManager(), this));
|
||||||
registerHandler(new TagsHandler(this));
|
registerHandler(new TagsHandler(this));
|
||||||
@ -654,7 +667,8 @@ void JsonRPCServerImplementation::processJsonPacket(TransportInterface *interfac
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList commandList = message.value("method").toString().split('.');
|
QString methodString = message.value("method").toString();
|
||||||
|
QStringList commandList = methodString.split('.');
|
||||||
if (commandList.count() != 2) {
|
if (commandList.count() != 2) {
|
||||||
qCWarning(dcJsonRpc) << "Error parsing method.\nGot:" << message.value("method").toString() << "\nExpected: \"Namespace.method\"";
|
qCWarning(dcJsonRpc) << "Error parsing method.\nGot:" << message.value("method").toString() << "\nExpected: \"Namespace.method\"";
|
||||||
sendErrorResponse(interface, clientId, commandId, QString("Error parsing method. Got: '%1'', Expected: 'Namespace.method'").arg(message.value("method").toString()));
|
sendErrorResponse(interface, clientId, commandId, QString("Error parsing method. Got: '%1'', Expected: 'Namespace.method'").arg(message.value("method").toString()));
|
||||||
@ -666,23 +680,38 @@ void JsonRPCServerImplementation::processJsonPacket(TransportInterface *interfac
|
|||||||
// 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)) {
|
||||||
QByteArray token = message.value("token").toByteArray();
|
QByteArray token = message.value("token").toByteArray();
|
||||||
QStringList authExemptMethodsNoUser = {"JSONRPC.Introspect", "JSONRPC.Hello", "JSONRPC.RequestPushButtonAuth", "JSONRPC.CreateUser", "Users.RequestPushButtonAuth", "Users.CreateUser"};
|
QStringList authExemptMethodsNoUser = {"JSONRPC.Introspect", "JSONRPC.Hello", "JSONRPC.RequestPushButtonAuth", "JSONRPC.CreateUser"};
|
||||||
QStringList authExemptMethodsWithUser = {"JSONRPC.Introspect", "JSONRPC.Hello", "JSONRPC.Authenticate", "JSONRPC.RequestPushButtonAuth", "Users.Authenticate", "Users.RequestPushButtonAuth"};
|
QStringList authExemptMethodsWithUser = {"JSONRPC.Introspect", "JSONRPC.Hello", "JSONRPC.Authenticate", "JSONRPC.RequestPushButtonAuth"};
|
||||||
// if there is no user in the system yet, let's fail unless this is special method for authentication itself
|
// if there is no user in the system yet, let's fail unless this is a special method for authentication itself
|
||||||
if (NymeaCore::instance()->userManager()->initRequired()) {
|
if (NymeaCore::instance()->userManager()->initRequired()) {
|
||||||
if (!authExemptMethodsNoUser.contains(targetNamespace + "." + method) && (token.isEmpty() || !NymeaCore::instance()->userManager()->verifyToken(token))) {
|
if (!authExemptMethodsNoUser.contains(methodString) && (token.isEmpty() || !NymeaCore::instance()->userManager()->verifyToken(token))) {
|
||||||
sendUnauthorizedResponse(interface, clientId, commandId, "Initial setup required. Call Users.CreateUser first.");
|
sendUnauthorizedResponse(interface, clientId, commandId, "Initial setup required. Call Users.CreateUser first.");
|
||||||
qCWarning(dcJsonRpc()) << "Initial setup required but client does not call the setup. Dropping connection.";
|
qCWarning(dcJsonRpc()) << "Initial setup required but client does not call the setup. Dropping connection.";
|
||||||
interface->terminateClientConnection(clientId);
|
interface->terminateClientConnection(clientId);
|
||||||
|
qCWarning(dcJsonRpc()) << "Staring connection lockdown timer";
|
||||||
|
m_connectionLockdownTimer.start();
|
||||||
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 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
|
||||||
if (!authExemptMethodsWithUser.contains(targetNamespace + "." + method) && (token.isEmpty() || !NymeaCore::instance()->userManager()->verifyToken(token))) {
|
if (!authExemptMethodsWithUser.contains(methodString)) {
|
||||||
sendUnauthorizedResponse(interface, clientId, commandId, "Forbidden: Invalid token.");
|
if (token.isEmpty() || !NymeaCore::instance()->userManager()->verifyToken(token)) {
|
||||||
qCWarning(dcJsonRpc()) << "Client did not not present a valid token. Dropping connection.";
|
sendUnauthorizedResponse(interface, clientId, commandId, "Forbidden: Invalid token.");
|
||||||
interface->terminateClientConnection(clientId);
|
qCWarning(dcJsonRpc()) << "Client did not not present a valid token. Dropping connection.";
|
||||||
return;
|
interface->terminateClientConnection(clientId);
|
||||||
|
qCWarning(dcJsonRpc()) << "Staring connection lockdown timer";
|
||||||
|
m_connectionLockdownTimer.start();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Check if the user has the required permissions
|
||||||
|
TokenInfo tokenInfo = NymeaCore::instance()->userManager()->tokenInfo(token);
|
||||||
|
UserInfo userInfo = NymeaCore::instance()->userManager()->userInfo(tokenInfo.username());
|
||||||
|
Types::PermissionScope methodScope = Types::scopeFromString(m_api.value("methods").toMap().value(methodString).toMap().value("permissionScope").toString());
|
||||||
|
if (methodScope != Types::PermissionScopeNone && !userInfo.scopes().testFlag(Types::PermissionScopeAdmin) && !userInfo.scopes().testFlag(methodScope)) {
|
||||||
|
qCWarning(dcJsonRpc()) << "Method" << methodString << "requires" << Types::scopeToString(methodScope) << "but client token has:" << Types::scopesToStringList(userInfo.scopes());
|
||||||
|
sendErrorResponse(interface, clientId, commandId, "Permission denied.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1025,6 +1054,12 @@ void JsonRPCServerImplementation::clientConnected(const QUuid &clientId)
|
|||||||
qCDebug(dcJsonRpc()) << "Client connected with uuid" << clientId.toString();
|
qCDebug(dcJsonRpc()) << "Client connected with uuid" << clientId.toString();
|
||||||
TransportInterface *interface = qobject_cast<TransportInterface *>(sender());
|
TransportInterface *interface = qobject_cast<TransportInterface *>(sender());
|
||||||
|
|
||||||
|
if (m_connectionLockdownTimer.isActive()) {
|
||||||
|
qCWarning(dcJsonRpc()) << "Connection is locked down. Rejecting new client connection.";
|
||||||
|
interface->terminateClientConnection(clientId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
m_clientTransports.insert(clientId, interface);
|
m_clientTransports.insert(clientId, interface);
|
||||||
|
|
||||||
// Initialize the connection locale to the settings default
|
// Initialize the connection locale to the settings default
|
||||||
|
|||||||
@ -63,10 +63,8 @@ public:
|
|||||||
Q_INVOKABLE JsonReply *SetNotificationStatus(const QVariantMap ¶ms, const JsonContext &context);
|
Q_INVOKABLE JsonReply *SetNotificationStatus(const QVariantMap ¶ms, const JsonContext &context);
|
||||||
|
|
||||||
Q_INVOKABLE JsonReply *CreateUser(const QVariantMap ¶ms);
|
Q_INVOKABLE JsonReply *CreateUser(const QVariantMap ¶ms);
|
||||||
Q_INVOKABLE JsonReply *Authenticate(const QVariantMap ¶ms);
|
Q_INVOKABLE JsonReply *Authenticate(const QVariantMap ¶ms, const JsonContext &context);
|
||||||
Q_INVOKABLE JsonReply *RequestPushButtonAuth(const QVariantMap ¶ms, const JsonContext &context);
|
Q_INVOKABLE JsonReply *RequestPushButtonAuth(const QVariantMap ¶ms, const JsonContext &context);
|
||||||
Q_INVOKABLE JsonReply *Tokens(const QVariantMap ¶ms, const JsonContext &context) const;
|
|
||||||
Q_INVOKABLE JsonReply *RemoveToken(const QVariantMap ¶ms);
|
|
||||||
Q_INVOKABLE JsonReply *SetupCloudConnection(const QVariantMap ¶ms);
|
Q_INVOKABLE JsonReply *SetupCloudConnection(const QVariantMap ¶ms);
|
||||||
Q_INVOKABLE JsonReply *SetupRemoteAccess(const QVariantMap ¶ms);
|
Q_INVOKABLE JsonReply *SetupRemoteAccess(const QVariantMap ¶ms);
|
||||||
Q_INVOKABLE JsonReply *IsCloudConnected(const QVariantMap ¶ms);
|
Q_INVOKABLE JsonReply *IsCloudConnected(const QVariantMap ¶ms);
|
||||||
@ -90,7 +88,6 @@ private:
|
|||||||
void sendResponse(TransportInterface *interface, const QUuid &clientId, int commandId, const QVariantMap ¶ms = QVariantMap(), const QString &deprecationWarning = QString());
|
void sendResponse(TransportInterface *interface, const QUuid &clientId, int commandId, const QVariantMap ¶ms = QVariantMap(), const QString &deprecationWarning = QString());
|
||||||
void sendErrorResponse(TransportInterface *interface, const QUuid &clientId, int commandId, const QString &error);
|
void sendErrorResponse(TransportInterface *interface, const QUuid &clientId, int commandId, const QString &error);
|
||||||
void sendUnauthorizedResponse(TransportInterface *interface, const QUuid &clientId, int commandId, const QString &error);
|
void sendUnauthorizedResponse(TransportInterface *interface, const QUuid &clientId, int commandId, const QString &error);
|
||||||
QVariantMap createWelcomeMessage(TransportInterface *interface, const QUuid &clientId) const;
|
|
||||||
|
|
||||||
void processJsonPacket(TransportInterface *interface, const QUuid &clientId, const QByteArray &data);
|
void processJsonPacket(TransportInterface *interface, const QUuid &clientId, const QByteArray &data);
|
||||||
|
|
||||||
@ -129,6 +126,8 @@ private:
|
|||||||
|
|
||||||
int m_notificationId;
|
int m_notificationId;
|
||||||
|
|
||||||
|
QTimer m_connectionLockdownTimer;
|
||||||
|
|
||||||
QString formatAssertion(const QString &targetNamespace, const QString &method, QMetaMethod::MethodType methodType, JsonHandler *handler, const QVariantMap &data) const;
|
QString formatAssertion(const QString &targetNamespace, const QString &method, QMetaMethod::MethodType methodType, JsonHandler *handler, const QVariantMap &data) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -179,7 +179,7 @@ JsonValidator::Result JsonValidator::validateEntry(const QVariant &value, const
|
|||||||
|
|
||||||
QVariantList enumList = refDefinition.toList();
|
QVariantList enumList = refDefinition.toList();
|
||||||
if (!enumList.contains(value.toString())) {
|
if (!enumList.contains(value.toString())) {
|
||||||
return Result(false, "Expected enum " + refName + " but got " + value.toJsonDocument().toJson());
|
return Result(false, "Expected enum value for" + refName + " but got " + value.toString());
|
||||||
}
|
}
|
||||||
return Result(true);
|
return Result(true);
|
||||||
}
|
}
|
||||||
@ -187,7 +187,7 @@ JsonValidator::Result JsonValidator::validateEntry(const QVariant &value, const
|
|||||||
QVariantMap flags = api.value("flags").toMap();
|
QVariantMap flags = api.value("flags").toMap();
|
||||||
if (flags.contains(refName)) {
|
if (flags.contains(refName)) {
|
||||||
QVariant refDefinition = flags.value(refName);
|
QVariant refDefinition = flags.value(refName);
|
||||||
if (value.type() != QVariant::StringList) {
|
if (value.type() != QVariant::List && value.type() != QVariant::StringList) {
|
||||||
return Result(false, "Expected flags " + refName + " but got " + value.toString());
|
return Result(false, "Expected flags " + refName + " but got " + value.toString());
|
||||||
}
|
}
|
||||||
QString flagEnum = refDefinition.toList().first().toString();
|
QString flagEnum = refDefinition.toList().first().toString();
|
||||||
|
|||||||
@ -76,7 +76,6 @@ LoggingHandler::LoggingHandler(QObject *parent) :
|
|||||||
params.insert("o:eventTypes", QVariantList() << enumRef<Logging::LoggingEventType>());
|
params.insert("o:eventTypes", QVariantList() << enumRef<Logging::LoggingEventType>());
|
||||||
params.insert("o:typeIds", QVariantList() << enumValueName(Uuid));
|
params.insert("o:typeIds", QVariantList() << enumValueName(Uuid));
|
||||||
params.insert("o:thingIds", QVariantList() << enumValueName(Uuid));
|
params.insert("o:thingIds", QVariantList() << enumValueName(Uuid));
|
||||||
params.insert("d:o:deviceIds", QVariantList() << enumValueName(Uuid));
|
|
||||||
params.insert("o:values", QVariantList() << enumValueName(Variant));
|
params.insert("o:values", QVariantList() << enumValueName(Variant));
|
||||||
params.insert("o:limit", enumValueName(Int));
|
params.insert("o:limit", enumValueName(Int));
|
||||||
params.insert("o:offset", enumValueName(Int));
|
params.insert("o:offset", enumValueName(Int));
|
||||||
@ -189,7 +188,6 @@ QVariantMap LoggingHandler::packLogEntry(const LogEntry &logEntry)
|
|||||||
logEntryMap.insert("typeId", logEntry.typeId());
|
logEntryMap.insert("typeId", logEntry.typeId());
|
||||||
}
|
}
|
||||||
logEntryMap.insert("thingId", logEntry.thingId());
|
logEntryMap.insert("thingId", logEntry.thingId());
|
||||||
logEntryMap.insert("deviceId", logEntry.thingId()); // DEPRECATED
|
|
||||||
logEntryMap.insert("value", LogValueTool::convertVariantToString(logEntry.value()));
|
logEntryMap.insert("value", LogValueTool::convertVariantToString(logEntry.value()));
|
||||||
break;
|
break;
|
||||||
case Logging::LoggingSourceSystem:
|
case Logging::LoggingSourceSystem:
|
||||||
@ -251,13 +249,6 @@ LogFilter LoggingHandler::unpackLogFilter(const QVariantMap &logFilterMap)
|
|||||||
filter.addThingId(ThingId(thingId.toString()));
|
filter.addThingId(ThingId(thingId.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// DEPRECATED
|
|
||||||
if (logFilterMap.contains("deviceIds")) {
|
|
||||||
QVariantList deviceIds = logFilterMap.value("deviceIds").toList();
|
|
||||||
foreach (const QVariant &deviceId, deviceIds) {
|
|
||||||
filter.addThingId(ThingId(deviceId.toString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (logFilterMap.contains("values")) {
|
if (logFilterMap.contains("values")) {
|
||||||
QVariantList values = logFilterMap.value("values").toList();
|
QVariantList values = logFilterMap.value("values").toList();
|
||||||
foreach (const QVariant &value, values) {
|
foreach (const QVariant &value, values) {
|
||||||
|
|||||||
@ -107,7 +107,7 @@ RulesHandler::RulesHandler(QObject *parent) :
|
|||||||
description = "Get the descriptions of all configured rules. If you need more information about a specific rule use the "
|
description = "Get the descriptions of all configured rules. If you need more information about a specific rule use the "
|
||||||
"method Rules.GetRuleDetails.";
|
"method Rules.GetRuleDetails.";
|
||||||
returns.insert("ruleDescriptions", QVariantList() << objectRef("RuleDescription"));
|
returns.insert("ruleDescriptions", QVariantList() << objectRef("RuleDescription"));
|
||||||
registerMethod("GetRules", description, params, returns);
|
registerMethod("GetRules", description, params, returns, Types::PermissionScopeConfigureRules);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Get details for the rule identified by ruleId";
|
description = "Get details for the rule identified by ruleId";
|
||||||
@ -166,8 +166,7 @@ RulesHandler::RulesHandler(QObject *parent) :
|
|||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Find a list of rules containing any of the given parameters.";
|
description = "Find a list of rules containing any of the given parameters.";
|
||||||
params.insert("o:thingId", enumValueName(Uuid)); // TODO: remove "o:" from thingId once we drop deviceId support
|
params.insert("thingId", enumValueName(Uuid));
|
||||||
params.insert("d:o:deviceId", enumValueName(Uuid));
|
|
||||||
returns.insert("ruleIds", QVariantList() << enumValueName(Uuid));
|
returns.insert("ruleIds", QVariantList() << enumValueName(Uuid));
|
||||||
registerMethod("FindRules", description, params, returns);
|
registerMethod("FindRules", description, params, returns);
|
||||||
|
|
||||||
@ -301,10 +300,7 @@ JsonReply* RulesHandler::RemoveRule(const QVariantMap ¶ms)
|
|||||||
|
|
||||||
JsonReply *RulesHandler::FindRules(const QVariantMap ¶ms)
|
JsonReply *RulesHandler::FindRules(const QVariantMap ¶ms)
|
||||||
{
|
{
|
||||||
ThingId thingId = ThingId(params.value("deviceId").toString()); // DEPRECATED
|
ThingId thingId = ThingId(params.value("thingId").toString());
|
||||||
if (params.contains("thingId")) {
|
|
||||||
thingId = ThingId(params.value("thingId").toString());
|
|
||||||
}
|
|
||||||
QList<RuleId> rules = NymeaCore::instance()->ruleEngine()->findRules(thingId);
|
QList<RuleId> rules = NymeaCore::instance()->ruleEngine()->findRules(thingId);
|
||||||
|
|
||||||
QVariantList rulesList;
|
QVariantList rulesList;
|
||||||
|
|||||||
@ -1,97 +0,0 @@
|
|||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
||||||
*
|
|
||||||
* Copyright 2013 - 2020, nymea GmbH
|
|
||||||
* Contact: contact@nymea.io
|
|
||||||
*
|
|
||||||
* This file is part of nymea.
|
|
||||||
* This project including source code and documentation is protected by
|
|
||||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
|
||||||
* reproduction, publication, editing and translation, are reserved. The use of
|
|
||||||
* this project is subject to the terms of a license agreement to be concluded
|
|
||||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
|
||||||
* under https://nymea.io/license
|
|
||||||
*
|
|
||||||
* GNU General Public License Usage
|
|
||||||
* Alternatively, this project may be redistributed and/or modified under the
|
|
||||||
* terms of the GNU General Public License as published by the Free Software
|
|
||||||
* Foundation, GNU version 3. This project 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
|
|
||||||
* this project. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
* For any further details and any questions please contact us under
|
|
||||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
|
||||||
* https://nymea.io/license/faq
|
|
||||||
*
|
|
||||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\class nymeaserver::StateHandler
|
|
||||||
\brief This subclass of \l{JsonHandler} processes the JSON requests for the \tt States namespace of the JSON-RPC API.
|
|
||||||
|
|
||||||
\ingroup json
|
|
||||||
\inmodule core
|
|
||||||
|
|
||||||
This \l{JsonHandler} will be created in the \l{JsonRPCServer} and used to handle JSON-RPC requests
|
|
||||||
for the \tt {States} namespace of the API.
|
|
||||||
|
|
||||||
\sa State, JsonHandler, JsonRPCServer
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "statehandler.h"
|
|
||||||
#include "nymeacore.h"
|
|
||||||
#include "loggingcategories.h"
|
|
||||||
|
|
||||||
#include "devicehandler.h"
|
|
||||||
|
|
||||||
namespace nymeaserver {
|
|
||||||
|
|
||||||
/*! Constructs a new \l{StateHandler} with the given \a parent. */
|
|
||||||
StateHandler::StateHandler(QObject *parent) :
|
|
||||||
JsonHandler(parent)
|
|
||||||
{
|
|
||||||
registerEnum<Types::Unit>();
|
|
||||||
registerEnum<Types::IOType>();
|
|
||||||
registerObject<State>();
|
|
||||||
registerObject<StateType>();
|
|
||||||
|
|
||||||
// Methods
|
|
||||||
QString description; QVariantMap params; QVariantMap returns;
|
|
||||||
description = "Get the StateType for the given stateTypeId.";
|
|
||||||
params.insert("stateTypeId", enumValueName(Uuid));
|
|
||||||
returns.insert("deviceError", enumRef<Device::DeviceError>());
|
|
||||||
returns.insert("o:stateType", objectRef<StateType>());
|
|
||||||
registerMethod("GetStateType", description, params, returns, "Please use the Integrations namespace instead.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Returns the name of the \l{StateHandler}. In this case \b States.*/
|
|
||||||
QString StateHandler::name() const
|
|
||||||
{
|
|
||||||
return "States";
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonReply* StateHandler::GetStateType(const QVariantMap ¶ms, const JsonContext &context) const
|
|
||||||
{
|
|
||||||
qCDebug(dcJsonRpc) << "asked for state type" << params;
|
|
||||||
StateTypeId stateTypeId(params.value("stateTypeId").toString());
|
|
||||||
foreach (const ThingClass &deviceClass, NymeaCore::instance()->thingManager()->supportedThings()) {
|
|
||||||
foreach (const StateType &stateType, deviceClass.stateTypes()) {
|
|
||||||
if (stateType.id() == stateTypeId) {
|
|
||||||
QVariantMap data;
|
|
||||||
data.insert("deviceError", enumValueName<Thing::ThingError>(Thing::ThingErrorNoError).replace("ThingError", "DeviceError"));
|
|
||||||
StateType translatedStateType = stateType;
|
|
||||||
translatedStateType.setDisplayName(NymeaCore::instance()->thingManager()->translate(deviceClass.pluginId(), stateType.displayName(), context.locale()));
|
|
||||||
data.insert("stateType", pack(translatedStateType));
|
|
||||||
return createReply(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QVariantMap data;
|
|
||||||
data.insert("deviceError", enumValueName<Thing::ThingError>(Thing::ThingErrorStateTypeNotFound).replace("ThingError", "DeviceError"));
|
|
||||||
return createReply(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
||||||
*
|
|
||||||
* Copyright 2013 - 2020, nymea GmbH
|
|
||||||
* Contact: contact@nymea.io
|
|
||||||
*
|
|
||||||
* This file is part of nymea.
|
|
||||||
* This project including source code and documentation is protected by
|
|
||||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
|
||||||
* reproduction, publication, editing and translation, are reserved. The use of
|
|
||||||
* this project is subject to the terms of a license agreement to be concluded
|
|
||||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
|
||||||
* under https://nymea.io/license
|
|
||||||
*
|
|
||||||
* GNU General Public License Usage
|
|
||||||
* Alternatively, this project may be redistributed and/or modified under the
|
|
||||||
* terms of the GNU General Public License as published by the Free Software
|
|
||||||
* Foundation, GNU version 3. This project 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
|
|
||||||
* this project. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
* For any further details and any questions please contact us under
|
|
||||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
|
||||||
* https://nymea.io/license/faq
|
|
||||||
*
|
|
||||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
||||||
|
|
||||||
#ifndef STATEHANDLER_H
|
|
||||||
#define STATEHANDLER_H
|
|
||||||
|
|
||||||
#include "jsonrpc/jsonhandler.h"
|
|
||||||
|
|
||||||
namespace nymeaserver {
|
|
||||||
|
|
||||||
class StateHandler : public JsonHandler
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit StateHandler(QObject *parent = nullptr);
|
|
||||||
QString name() const override;
|
|
||||||
|
|
||||||
Q_INVOKABLE JsonReply *GetStateType(const QVariantMap ¶ms, const JsonContext &context) const;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // EVENTHANDLER_H
|
|
||||||
@ -49,7 +49,6 @@ TagsHandler::TagsHandler(QObject *parent) : JsonHandler(parent)
|
|||||||
"Tags can be filtered by a thingID, a ruleId, an appId, a tagId or a combination of any (however, "
|
"Tags can be filtered by a thingID, a ruleId, an appId, a tagId or a combination of any (however, "
|
||||||
"combining thingId and ruleId will return an empty result set).";
|
"combining thingId and ruleId will return an empty result set).";
|
||||||
params.insert("o:thingId", enumValueName(Uuid));
|
params.insert("o:thingId", enumValueName(Uuid));
|
||||||
params.insert("d:o:deviceId", enumValueName(Uuid));
|
|
||||||
params.insert("o:ruleId", enumValueName(Uuid));
|
params.insert("o:ruleId", enumValueName(Uuid));
|
||||||
params.insert("o:appId", enumValueName(String));
|
params.insert("o:appId", enumValueName(String));
|
||||||
params.insert("o:tagId", enumValueName(String));
|
params.insert("o:tagId", enumValueName(String));
|
||||||
@ -108,10 +107,6 @@ JsonReply *TagsHandler::GetTags(const QVariantMap ¶ms) const
|
|||||||
if (params.contains("thingId") && params.value("thingId").toUuid() != tag.thingId()) {
|
if (params.contains("thingId") && params.value("thingId").toUuid() != tag.thingId()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (params.contains("deviceId") && params.value("deviceId").toUuid() != tag.thingId()) {
|
|
||||||
// nymea < 0.19
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (params.contains("ruleId") && params.value("ruleId").toUuid() != tag.ruleId()) {
|
if (params.contains("ruleId") && params.value("ruleId").toUuid() != tag.ruleId()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,50 +40,23 @@ UsersHandler::UsersHandler(UserManager *userManager, QObject *parent):
|
|||||||
JsonHandler(parent),
|
JsonHandler(parent),
|
||||||
m_userManager(userManager)
|
m_userManager(userManager)
|
||||||
{
|
{
|
||||||
registerObject<UserInfo>();
|
registerFlag<Types::PermissionScope, Types::PermissionScopes>();
|
||||||
|
registerObject<UserInfo, UserInfoList>();
|
||||||
registerObject<TokenInfo, TokenInfoList>();
|
registerObject<TokenInfo, TokenInfoList>();
|
||||||
|
|
||||||
QVariantMap params, returns;
|
QVariantMap params, returns;
|
||||||
QString description;
|
QString description;
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Create a new user in the API. Currently this is only allowed to be called once when a new nymea instance is set up. Call Authenticate after this to obtain a device token for this user.";
|
description = "Create a new user in the API with the given username and password. Use scopes to define the permissions for the new user. If no scopes are given, this user will be an admin user. Call Authenticate after this to obtain a device token for this user.";
|
||||||
params.insert("username", enumValueName(String));
|
params.insert("username", enumValueName(String));
|
||||||
params.insert("password", enumValueName(String));
|
params.insert("password", enumValueName(String));
|
||||||
|
params.insert("o:email", enumValueName(String));
|
||||||
|
params.insert("o:displayName", enumValueName(String));
|
||||||
|
params.insert("o:scopes", flagRef<Types::PermissionScopes>());
|
||||||
returns.insert("error", enumRef<UserManager::UserError>());
|
returns.insert("error", enumRef<UserManager::UserError>());
|
||||||
registerMethod("CreateUser", description, params, returns);
|
registerMethod("CreateUser", description, params, returns);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
|
||||||
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.insert("username", enumValueName(String));
|
|
||||||
params.insert("password", enumValueName(String));
|
|
||||||
params.insert("deviceName", enumValueName(String));
|
|
||||||
returns.insert("success", enumValueName(Bool));
|
|
||||||
returns.insert("o:token", enumValueName(String));
|
|
||||||
registerMethod("Authenticate", description, params, returns);
|
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
|
||||||
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.insert("deviceName", enumValueName(String));
|
|
||||||
returns.insert("success", enumValueName(Bool));
|
|
||||||
returns.insert("transactionId", enumValueName(Int));
|
|
||||||
registerMethod("RequestPushButtonAuth", description, params, returns);
|
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Change the password for the currently logged in user.";
|
description = "Change the password for the currently logged in user.";
|
||||||
params.insert("newPassword", enumValueName(String));
|
params.insert("newPassword", enumValueName(String));
|
||||||
@ -94,7 +67,7 @@ UsersHandler::UsersHandler(UserManager *userManager, QObject *parent):
|
|||||||
description = "Get info about the current token (the currently logged in user).";
|
description = "Get info about the current token (the currently logged in user).";
|
||||||
returns.insert("o:userInfo", objectRef<UserInfo>());
|
returns.insert("o:userInfo", objectRef<UserInfo>());
|
||||||
returns.insert("error", enumRef<UserManager::UserError>());
|
returns.insert("error", enumRef<UserManager::UserError>());
|
||||||
registerMethod("GetUserInfo", description, params, returns);
|
registerMethod("GetUserInfo", description, params, returns, Types::PermissionScopeNone);
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Get all the tokens for the current user.";
|
description = "Get all the tokens for the current user.";
|
||||||
@ -108,7 +81,48 @@ UsersHandler::UsersHandler(UserManager *userManager, QObject *parent):
|
|||||||
returns.insert("error", enumRef<UserManager::UserError>());
|
returns.insert("error", enumRef<UserManager::UserError>());
|
||||||
registerMethod("RemoveToken", description, params, returns);
|
registerMethod("RemoveToken", description, params, returns);
|
||||||
|
|
||||||
|
params.clear(); returns.clear();
|
||||||
|
description = "Return a list of all users in the system.";
|
||||||
|
returns.insert("users", objectRef<UserInfoList>());
|
||||||
|
registerMethod("GetUsers", description, params, returns);
|
||||||
|
|
||||||
|
params.clear(); returns.clear();
|
||||||
|
description = "Remove a user from the system.";
|
||||||
|
params.insert("username", enumValueName(String));
|
||||||
|
returns.insert("error", enumRef<UserManager::UserError>());
|
||||||
|
registerMethod("RemoveUser", description, params, returns);
|
||||||
|
|
||||||
|
params.clear(); returns.clear();
|
||||||
|
description = "Set the permissions (scopes) for a given user.";
|
||||||
|
params.insert("username", enumValueName(String));
|
||||||
|
params.insert("scopes", flagRef<Types::PermissionScopes>());
|
||||||
|
returns.insert("error", enumRef<UserManager::UserError>());
|
||||||
|
registerMethod("SetUserScopes", description, params, returns);
|
||||||
|
|
||||||
|
params.clear(); returns.clear();
|
||||||
|
description = "Change user info. If username is given, info for the respective user is changed, otherwise the current user info is edited. Requires admin permissions to edit user info other than the own.";
|
||||||
|
params.insert("o:username", enumValueName(String));
|
||||||
|
params.insert("o:displayName", enumValueName(String));
|
||||||
|
params.insert("o:email", enumValueName(String));
|
||||||
|
returns.insert("error", enumRef<UserManager::UserError>());
|
||||||
|
registerMethod("SetUserInfo", description, params, returns);
|
||||||
|
|
||||||
// Notifications
|
// Notifications
|
||||||
|
params.clear();
|
||||||
|
description = "Emitted when a user is added to the system.";
|
||||||
|
params.insert("userInfo", objectRef<UserInfo>());
|
||||||
|
registerNotification("UserAdded", description, params);
|
||||||
|
|
||||||
|
params.clear();
|
||||||
|
description = "Emitted when a user is removed from the system.";
|
||||||
|
params.insert("username", enumValueName(String));
|
||||||
|
registerNotification("UserRemoved", description, params);
|
||||||
|
|
||||||
|
params.clear();
|
||||||
|
description = "Emitted whenever a user is changed.";
|
||||||
|
params.insert("userInfo", objectRef<UserInfo>());
|
||||||
|
registerNotification("UserChanged", description, params);
|
||||||
|
|
||||||
params.clear();
|
params.clear();
|
||||||
description = "Emitted when a push button authentication reaches final state. NOTE: This notification is "
|
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 "
|
"special. It will only be emitted to connections that did actively request a push button "
|
||||||
@ -118,7 +132,21 @@ UsersHandler::UsersHandler(UserManager *userManager, QObject *parent):
|
|||||||
params.insert("o:token", enumValueName(String));
|
params.insert("o:token", enumValueName(String));
|
||||||
registerNotification("PushButtonAuthFinished", description, params);
|
registerNotification("PushButtonAuthFinished", description, params);
|
||||||
|
|
||||||
connect(m_userManager, &UserManager::pushButtonAuthFinished, this, &UsersHandler::onPushButtonAuthFinished);
|
connect(m_userManager, &UserManager::userAdded, this, [this](const QString &username){
|
||||||
|
QVariantMap params;
|
||||||
|
params.insert("userInfo", pack(m_userManager->userInfo(username)));
|
||||||
|
emit UserAdded(params);
|
||||||
|
});
|
||||||
|
connect(m_userManager, &UserManager::userChanged, this, [this](const QString &username){
|
||||||
|
QVariantMap params;
|
||||||
|
params.insert("userInfo", pack(m_userManager->userInfo(username)));
|
||||||
|
emit UserChanged(params);
|
||||||
|
});
|
||||||
|
connect(m_userManager, &UserManager::userRemoved, this, [this](const QString &username){
|
||||||
|
QVariantMap params;
|
||||||
|
params.insert("username", username);
|
||||||
|
emit UserRemoved(params);
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,8 +159,12 @@ JsonReply *UsersHandler::CreateUser(const QVariantMap ¶ms)
|
|||||||
{
|
{
|
||||||
QString username = params.value("username").toString();
|
QString username = params.value("username").toString();
|
||||||
QString password = params.value("password").toString();
|
QString password = params.value("password").toString();
|
||||||
|
QString email = params.value("email").toString();
|
||||||
|
QString displayName = params.value("displayName").toString();
|
||||||
|
QStringList scopesList = params.value("scopes", Types::scopesToStringList(Types::PermissionScopeAdmin)).toStringList();
|
||||||
|
Types::PermissionScopes scopes = Types::scopesFromStringList(scopesList);
|
||||||
|
|
||||||
UserManager::UserError status = m_userManager->createUser(username, password);
|
UserManager::UserError status = m_userManager->createUser(username, password, email, displayName, scopes);
|
||||||
|
|
||||||
QVariantMap returns;
|
QVariantMap returns;
|
||||||
returns.insert("error", enumValueName<UserManager::UserError>(status));
|
returns.insert("error", enumValueName<UserManager::UserError>(status));
|
||||||
@ -157,42 +189,14 @@ JsonReply *UsersHandler::ChangePassword(const QVariantMap ¶ms, const JsonCon
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString newPassword = params.value("newPassword").toString();
|
QString newPassword = params.value("newPassword").toString();
|
||||||
QString username = m_userManager->userInfo(currentToken).username();
|
|
||||||
|
|
||||||
UserManager::UserError status = m_userManager->changePassword(username, newPassword);
|
TokenInfo tokenInfo = m_userManager->tokenInfo(currentToken);
|
||||||
|
|
||||||
|
UserManager::UserError status = m_userManager->changePassword(tokenInfo.username(), newPassword);
|
||||||
ret.insert("error", enumValueName<UserManager::UserError>(status));
|
ret.insert("error", enumValueName<UserManager::UserError>(status));
|
||||||
return createReply(ret);
|
return createReply(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonReply *UsersHandler::Authenticate(const QVariantMap ¶ms)
|
|
||||||
{
|
|
||||||
QString username = params.value("username").toString();
|
|
||||||
QString password = params.value("password").toString();
|
|
||||||
QString deviceName = params.value("deviceName").toString();
|
|
||||||
|
|
||||||
QByteArray token = m_userManager->authenticate(username, password, deviceName);
|
|
||||||
QVariantMap ret;
|
|
||||||
ret.insert("success", !token.isEmpty());
|
|
||||||
if (!token.isEmpty()) {
|
|
||||||
ret.insert("token", token);
|
|
||||||
}
|
|
||||||
return createReply(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonReply *UsersHandler::RequestPushButtonAuth(const QVariantMap ¶ms, const JsonContext &context)
|
|
||||||
{
|
|
||||||
QString deviceName = params.value("deviceName").toString();
|
|
||||||
|
|
||||||
int transactionId = m_userManager->requestPushButtonAuth(deviceName);
|
|
||||||
m_pushButtonTransactions.insert(transactionId, context.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 *UsersHandler::GetUserInfo(const QVariantMap ¶ms, const JsonContext &context)
|
JsonReply *UsersHandler::GetUserInfo(const QVariantMap ¶ms, const JsonContext &context)
|
||||||
{
|
{
|
||||||
Q_UNUSED(params)
|
Q_UNUSED(params)
|
||||||
@ -200,7 +204,7 @@ JsonReply *UsersHandler::GetUserInfo(const QVariantMap ¶ms, const JsonContex
|
|||||||
|
|
||||||
QByteArray currentToken = context.token();
|
QByteArray currentToken = context.token();
|
||||||
if (currentToken.isEmpty()) {
|
if (currentToken.isEmpty()) {
|
||||||
qCWarning(dcJsonRpc()) << "Cannot get user info form an unauthenticated connection";
|
qCWarning(dcJsonRpc()) << "Cannot get user info from an unauthenticated connection";
|
||||||
ret.insert("error", enumValueName<UserManager::UserError>(UserManager::UserErrorPermissionDenied));
|
ret.insert("error", enumValueName<UserManager::UserError>(UserManager::UserErrorPermissionDenied));
|
||||||
return createReply(ret);
|
return createReply(ret);
|
||||||
}
|
}
|
||||||
@ -211,7 +215,9 @@ JsonReply *UsersHandler::GetUserInfo(const QVariantMap ¶ms, const JsonContex
|
|||||||
return createReply(ret);
|
return createReply(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
UserInfo userInfo = m_userManager->userInfo(currentToken);
|
TokenInfo tokenInfo = m_userManager->tokenInfo(currentToken);
|
||||||
|
|
||||||
|
UserInfo userInfo = m_userManager->userInfo(tokenInfo.username());
|
||||||
ret.insert("userInfo", pack(userInfo));
|
ret.insert("userInfo", pack(userInfo));
|
||||||
ret.insert("error", enumValueName<UserManager::UserError>(UserManager::UserErrorNoError));
|
ret.insert("error", enumValueName<UserManager::UserError>(UserManager::UserErrorNoError));
|
||||||
return createReply(ret);
|
return createReply(ret);
|
||||||
@ -224,7 +230,7 @@ JsonReply *UsersHandler::GetTokens(const QVariantMap ¶ms, const JsonContext
|
|||||||
|
|
||||||
QByteArray currentToken = context.token();
|
QByteArray currentToken = context.token();
|
||||||
if (currentToken.isEmpty()) {
|
if (currentToken.isEmpty()) {
|
||||||
qCWarning(dcJsonRpc()) << "Cannot fetch tokens form an unauthenticated connection";
|
qCWarning(dcJsonRpc()) << "Cannot fetch tokens for an unauthenticated connection";
|
||||||
ret.insert("error", enumValueName<UserManager::UserError>(UserManager::UserErrorPermissionDenied));
|
ret.insert("error", enumValueName<UserManager::UserError>(UserManager::UserErrorPermissionDenied));
|
||||||
return createReply(ret);
|
return createReply(ret);
|
||||||
}
|
}
|
||||||
@ -236,7 +242,7 @@ JsonReply *UsersHandler::GetTokens(const QVariantMap ¶ms, const JsonContext
|
|||||||
}
|
}
|
||||||
|
|
||||||
TokenInfo tokenInfo = m_userManager->tokenInfo(currentToken);
|
TokenInfo tokenInfo = m_userManager->tokenInfo(currentToken);
|
||||||
qCDebug(dcJsonRpc()) << "Fetching tokens for user" << tokenInfo.username();
|
qCDebug(dcJsonRpc()) << "Fetching tokens for user" << currentToken << tokenInfo.username();
|
||||||
QList<TokenInfo> tokens = m_userManager->tokens(tokenInfo.username());
|
QList<TokenInfo> tokens = m_userManager->tokens(tokenInfo.username());
|
||||||
QVariantList retList;
|
QVariantList retList;
|
||||||
foreach (const TokenInfo &tokenInfo, tokens) {
|
foreach (const TokenInfo &tokenInfo, tokens) {
|
||||||
@ -285,24 +291,70 @@ JsonReply *UsersHandler::RemoveToken(const QVariantMap ¶ms, const JsonContex
|
|||||||
return createReply(ret);
|
return createReply(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UsersHandler::onPushButtonAuthFinished(int transactionId, bool success, const QByteArray &token)
|
JsonReply *UsersHandler::GetUsers(const QVariantMap ¶ms)
|
||||||
{
|
{
|
||||||
Q_UNUSED(success)
|
Q_UNUSED(params)
|
||||||
Q_UNUSED(token)
|
QVariantMap reply;
|
||||||
QUuid clientId = m_pushButtonTransactions.take(transactionId);
|
reply.insert("users", pack(m_userManager->users()));
|
||||||
if (clientId.isNull()) {
|
return createReply(reply);
|
||||||
qCDebug(dcJsonRpc()) << "Received a PushButton reply but wasn't expecting it.";
|
}
|
||||||
return;
|
|
||||||
|
JsonReply *UsersHandler::RemoveUser(const QVariantMap ¶ms, const JsonContext &context)
|
||||||
|
{
|
||||||
|
Q_UNUSED(context)
|
||||||
|
QString username = params.value("username").toString();
|
||||||
|
QVariantMap returns;
|
||||||
|
UserManager::UserError error = m_userManager->removeUser(username);
|
||||||
|
returns.insert("error", enumValueName<UserManager::UserError>(error));
|
||||||
|
return createReply(returns);
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonReply *UsersHandler::SetUserScopes(const QVariantMap ¶ms, const JsonContext &context)
|
||||||
|
{
|
||||||
|
Q_UNUSED(context)
|
||||||
|
QString username = params.value("username").toString();
|
||||||
|
Types::PermissionScopes scopes = Types::scopesFromStringList(params.value("scopes").toStringList());
|
||||||
|
UserManager::UserError error = m_userManager->setUserScopes(username, scopes);
|
||||||
|
QVariantMap returns;
|
||||||
|
returns.insert("error", enumValueName<UserManager::UserError>(error));
|
||||||
|
return createReply(returns);
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonReply *UsersHandler::SetUserInfo(const QVariantMap ¶ms, const JsonContext &context)
|
||||||
|
{
|
||||||
|
QVariantMap ret;
|
||||||
|
|
||||||
|
TokenInfo callingTokenInfo = m_userManager->tokenInfo(context.token());
|
||||||
|
QString username;
|
||||||
|
|
||||||
|
if (params.contains("username")) {
|
||||||
|
username = params.value("username").toString();
|
||||||
|
} else {
|
||||||
|
username = callingTokenInfo.username();
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap params;
|
if (callingTokenInfo.username() != username && !m_userManager->userInfo(callingTokenInfo.username()).scopes().testFlag(Types::PermissionScopeAdmin)) {
|
||||||
params.insert("transactionId", transactionId);
|
ret.insert("error", enumValueName(UserManager::UserErrorPermissionDenied));
|
||||||
params.insert("success", success);
|
return createReply(ret);
|
||||||
if (success) {
|
|
||||||
params.insert("token", token);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
emit PushButtonAuthFinished(clientId, params);
|
UserInfo changedUserInfo = m_userManager->userInfo(username);
|
||||||
|
|
||||||
|
QString email;
|
||||||
|
if (params.contains("email")) {
|
||||||
|
email = params.value("email").toString();
|
||||||
|
} else {
|
||||||
|
email = changedUserInfo.email();
|
||||||
|
}
|
||||||
|
QString displayName;
|
||||||
|
if (params.contains("displayName")) {
|
||||||
|
displayName = params.value("displayName").toString();
|
||||||
|
} else {
|
||||||
|
displayName = changedUserInfo.displayName();
|
||||||
|
}
|
||||||
|
UserManager::UserError status = m_userManager->setUserInfo(username, email, displayName);
|
||||||
|
ret.insert("error", enumValueName(status));
|
||||||
|
return createReply(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,17 +49,18 @@ public:
|
|||||||
|
|
||||||
Q_INVOKABLE JsonReply *CreateUser(const QVariantMap ¶ms);
|
Q_INVOKABLE JsonReply *CreateUser(const QVariantMap ¶ms);
|
||||||
Q_INVOKABLE JsonReply *ChangePassword(const QVariantMap ¶ms, const JsonContext &context);
|
Q_INVOKABLE JsonReply *ChangePassword(const QVariantMap ¶ms, const JsonContext &context);
|
||||||
Q_INVOKABLE JsonReply *Authenticate(const QVariantMap ¶ms);
|
|
||||||
Q_INVOKABLE JsonReply *RequestPushButtonAuth(const QVariantMap ¶ms, const JsonContext &context);
|
|
||||||
Q_INVOKABLE JsonReply *GetUserInfo(const QVariantMap ¶ms, const JsonContext &context);
|
Q_INVOKABLE JsonReply *GetUserInfo(const QVariantMap ¶ms, const JsonContext &context);
|
||||||
Q_INVOKABLE JsonReply *GetTokens(const QVariantMap ¶ms, const JsonContext &context);
|
Q_INVOKABLE JsonReply *GetTokens(const QVariantMap ¶ms, const JsonContext &context);
|
||||||
Q_INVOKABLE JsonReply *RemoveToken(const QVariantMap ¶ms, const JsonContext &context);
|
Q_INVOKABLE JsonReply *RemoveToken(const QVariantMap ¶ms, const JsonContext &context);
|
||||||
|
Q_INVOKABLE JsonReply *GetUsers(const QVariantMap ¶ms);
|
||||||
|
Q_INVOKABLE JsonReply *RemoveUser(const QVariantMap ¶ms, const JsonContext &context);
|
||||||
|
Q_INVOKABLE JsonReply *SetUserScopes(const QVariantMap ¶ms, const JsonContext &context);
|
||||||
|
Q_INVOKABLE JsonReply *SetUserInfo(const QVariantMap ¶ms, const JsonContext &context);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void PushButtonAuthFinished(const QUuid &clientId, const QVariantMap ¶ms);
|
void UserAdded(const QVariantMap ¶ms);
|
||||||
|
void UserRemoved(const QVariantMap ¶ms);
|
||||||
private slots:
|
void UserChanged(const QVariantMap ¶ms);
|
||||||
void onPushButtonAuthFinished(int transactionId, bool success, const QByteArray &token);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
UserManager *m_userManager = nullptr;
|
UserManager *m_userManager = nullptr;
|
||||||
|
|||||||
@ -103,11 +103,7 @@ HEADERS += nymeacore.h \
|
|||||||
jsonrpc/jsonrpcserverimplementation.h \
|
jsonrpc/jsonrpcserverimplementation.h \
|
||||||
jsonrpc/jsonvalidator.h \
|
jsonrpc/jsonvalidator.h \
|
||||||
jsonrpc/integrationshandler.h \
|
jsonrpc/integrationshandler.h \
|
||||||
jsonrpc/devicehandler.h \
|
|
||||||
jsonrpc/ruleshandler.h \
|
jsonrpc/ruleshandler.h \
|
||||||
jsonrpc/actionhandler.h \
|
|
||||||
jsonrpc/eventhandler.h \
|
|
||||||
jsonrpc/statehandler.h \
|
|
||||||
jsonrpc/logginghandler.h \
|
jsonrpc/logginghandler.h \
|
||||||
jsonrpc/configurationhandler.h \
|
jsonrpc/configurationhandler.h \
|
||||||
jsonrpc/networkmanagerhandler.h \
|
jsonrpc/networkmanagerhandler.h \
|
||||||
@ -122,6 +118,7 @@ HEADERS += nymeacore.h \
|
|||||||
logging/logentry.h \
|
logging/logentry.h \
|
||||||
logging/logvaluetool.h \
|
logging/logvaluetool.h \
|
||||||
time/timemanager.h \
|
time/timemanager.h \
|
||||||
|
usermanager/userautorizer.h \
|
||||||
usermanager/userinfo.h \
|
usermanager/userinfo.h \
|
||||||
usermanager/usermanager.h \
|
usermanager/usermanager.h \
|
||||||
usermanager/tokeninfo.h \
|
usermanager/tokeninfo.h \
|
||||||
@ -197,11 +194,7 @@ SOURCES += nymeacore.cpp \
|
|||||||
jsonrpc/jsonrpcserverimplementation.cpp \
|
jsonrpc/jsonrpcserverimplementation.cpp \
|
||||||
jsonrpc/jsonvalidator.cpp \
|
jsonrpc/jsonvalidator.cpp \
|
||||||
jsonrpc/integrationshandler.cpp \
|
jsonrpc/integrationshandler.cpp \
|
||||||
jsonrpc/devicehandler.cpp \
|
|
||||||
jsonrpc/ruleshandler.cpp \
|
jsonrpc/ruleshandler.cpp \
|
||||||
jsonrpc/actionhandler.cpp \
|
|
||||||
jsonrpc/eventhandler.cpp \
|
|
||||||
jsonrpc/statehandler.cpp \
|
|
||||||
jsonrpc/logginghandler.cpp \
|
jsonrpc/logginghandler.cpp \
|
||||||
jsonrpc/configurationhandler.cpp \
|
jsonrpc/configurationhandler.cpp \
|
||||||
jsonrpc/networkmanagerhandler.cpp \
|
jsonrpc/networkmanagerhandler.cpp \
|
||||||
@ -215,6 +208,7 @@ SOURCES += nymeacore.cpp \
|
|||||||
logging/logentry.cpp \
|
logging/logentry.cpp \
|
||||||
logging/logvaluetool.cpp \
|
logging/logvaluetool.cpp \
|
||||||
time/timemanager.cpp \
|
time/timemanager.cpp \
|
||||||
|
usermanager/userautorizer.cpp \
|
||||||
usermanager/userinfo.cpp \
|
usermanager/userinfo.cpp \
|
||||||
usermanager/usermanager.cpp \
|
usermanager/usermanager.cpp \
|
||||||
usermanager/tokeninfo.cpp \
|
usermanager/tokeninfo.cpp \
|
||||||
|
|||||||
@ -48,7 +48,6 @@ class LogEntry
|
|||||||
Q_PROPERTY(Logging::LoggingSource source READ source)
|
Q_PROPERTY(Logging::LoggingSource source READ source)
|
||||||
Q_PROPERTY(QUuid typeId READ typeId USER true)
|
Q_PROPERTY(QUuid typeId READ typeId USER true)
|
||||||
Q_PROPERTY(QUuid thingId READ thingId USER true)
|
Q_PROPERTY(QUuid thingId READ thingId USER true)
|
||||||
Q_PROPERTY(QUuid deviceId READ thingId USER true REVISION 1)
|
|
||||||
Q_PROPERTY(QVariant value READ value USER true)
|
Q_PROPERTY(QVariant value READ value USER true)
|
||||||
Q_PROPERTY(bool active READ active USER true)
|
Q_PROPERTY(bool active READ active USER true)
|
||||||
Q_PROPERTY(Logging::LoggingEventType eventType READ eventType USER true)
|
Q_PROPERTY(Logging::LoggingEventType eventType READ eventType USER true)
|
||||||
|
|||||||
@ -40,7 +40,6 @@ class LIBNYMEA_EXPORT RuleAction
|
|||||||
{
|
{
|
||||||
Q_GADGET
|
Q_GADGET
|
||||||
Q_PROPERTY(QUuid thingId READ thingId WRITE setThingId USER true)
|
Q_PROPERTY(QUuid thingId READ thingId WRITE setThingId USER true)
|
||||||
Q_PROPERTY(QUuid deviceId READ thingId WRITE setThingId USER true REVISION 1)
|
|
||||||
Q_PROPERTY(QUuid actionTypeId READ actionTypeId WRITE setActionTypeId USER true)
|
Q_PROPERTY(QUuid actionTypeId READ actionTypeId WRITE setActionTypeId USER true)
|
||||||
Q_PROPERTY(QString interface READ interface WRITE setInterface USER true)
|
Q_PROPERTY(QString interface READ interface WRITE setInterface USER true)
|
||||||
Q_PROPERTY(QString interfaceAction READ interfaceAction WRITE setInterfaceAction USER true)
|
Q_PROPERTY(QString interfaceAction READ interfaceAction WRITE setInterfaceAction USER true)
|
||||||
|
|||||||
@ -48,7 +48,6 @@ class LIBNYMEA_EXPORT RuleActionParam
|
|||||||
Q_PROPERTY(QUuid eventTypeId READ eventTypeId WRITE setEventTypeId USER true)
|
Q_PROPERTY(QUuid eventTypeId READ eventTypeId WRITE setEventTypeId USER true)
|
||||||
Q_PROPERTY(QUuid eventParamTypeId READ eventParamTypeId WRITE setEventParamTypeId USER true)
|
Q_PROPERTY(QUuid eventParamTypeId READ eventParamTypeId WRITE setEventParamTypeId USER true)
|
||||||
Q_PROPERTY(QUuid stateThingId READ stateThingId WRITE setStateThingId USER true)
|
Q_PROPERTY(QUuid stateThingId READ stateThingId WRITE setStateThingId USER true)
|
||||||
Q_PROPERTY(QUuid stateDeviceId READ stateThingId WRITE setStateThingId USER true REVISION 1)
|
|
||||||
Q_PROPERTY(QUuid stateTypeId READ stateTypeId WRITE setStateTypeId USER true)
|
Q_PROPERTY(QUuid stateTypeId READ stateTypeId WRITE setStateTypeId USER true)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@ -57,12 +57,9 @@ QtMessageHandler ScriptEngine::s_upstreamMessageHandler;
|
|||||||
QLoggingCategory::CategoryFilter ScriptEngine::s_oldCategoryFilter = nullptr;
|
QLoggingCategory::CategoryFilter ScriptEngine::s_oldCategoryFilter = nullptr;
|
||||||
QMutex ScriptEngine::s_loggerMutex;
|
QMutex ScriptEngine::s_loggerMutex;
|
||||||
|
|
||||||
ScriptEngine::ScriptEngine(ThingManager *deviceManager, QObject *parent) : QObject(parent),
|
ScriptEngine::ScriptEngine(ThingManager *thingManager, QObject *parent) : QObject(parent),
|
||||||
m_deviceManager(deviceManager)
|
m_thingManager(thingManager)
|
||||||
{
|
{
|
||||||
qmlRegisterType<ScriptEvent>("nymea", 1, 0, "DeviceEvent");
|
|
||||||
qmlRegisterType<ScriptAction>("nymea", 1, 0, "DeviceAction");
|
|
||||||
qmlRegisterType<ScriptState>("nymea", 1, 0, "DeviceState");
|
|
||||||
qmlRegisterType<ScriptEvent>("nymea", 1, 0, "ThingEvent");
|
qmlRegisterType<ScriptEvent>("nymea", 1, 0, "ThingEvent");
|
||||||
qmlRegisterType<ScriptAction>("nymea", 1, 0, "ThingAction");
|
qmlRegisterType<ScriptAction>("nymea", 1, 0, "ThingAction");
|
||||||
qmlRegisterType<ScriptState>("nymea", 1, 0, "ThingState");
|
qmlRegisterType<ScriptState>("nymea", 1, 0, "ThingState");
|
||||||
@ -71,7 +68,7 @@ ScriptEngine::ScriptEngine(ThingManager *deviceManager, QObject *parent) : QObje
|
|||||||
qmlRegisterType<ScriptAlarm>("nymea", 1, 0, "Alarm");
|
qmlRegisterType<ScriptAlarm>("nymea", 1, 0, "Alarm");
|
||||||
|
|
||||||
m_engine = new QQmlEngine(this);
|
m_engine = new QQmlEngine(this);
|
||||||
m_engine->setProperty("thingManager", reinterpret_cast<quint64>(m_deviceManager));
|
m_engine->setProperty("thingManager", reinterpret_cast<quint64>(m_thingManager));
|
||||||
|
|
||||||
// Don't automatically print script warnings (that is, runtime errors, *not* console.warn() messages)
|
// Don't automatically print script warnings (that is, runtime errors, *not* console.warn() messages)
|
||||||
// to stdout as they'd end up on the "default" logging category.
|
// to stdout as they'd end up on the "default" logging category.
|
||||||
|
|||||||
@ -75,7 +75,7 @@ public:
|
|||||||
QByteArray content;
|
QByteArray content;
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit ScriptEngine(ThingManager *deviceManager, QObject *parent = nullptr);
|
explicit ScriptEngine(ThingManager *thingManager, QObject *parent = nullptr);
|
||||||
~ScriptEngine();
|
~ScriptEngine();
|
||||||
|
|
||||||
Scripts scripts();
|
Scripts scripts();
|
||||||
@ -102,7 +102,7 @@ private:
|
|||||||
|
|
||||||
void onScriptMessage(QtMsgType type, const QMessageLogContext &context, const QString &message);
|
void onScriptMessage(QtMsgType type, const QMessageLogContext &context, const QString &message);
|
||||||
private:
|
private:
|
||||||
ThingManager *m_deviceManager = nullptr;
|
ThingManager *m_thingManager = nullptr;
|
||||||
QQmlEngine *m_engine = nullptr;
|
QQmlEngine *m_engine = nullptr;
|
||||||
|
|
||||||
QHash<QUuid, Script*> m_scripts;
|
QHash<QUuid, Script*> m_scripts;
|
||||||
|
|||||||
@ -116,6 +116,7 @@ void TcpServer::terminateClientConnection(const QUuid &clientId)
|
|||||||
{
|
{
|
||||||
QTcpSocket *client = m_clientList.value(clientId);
|
QTcpSocket *client = m_clientList.value(clientId);
|
||||||
if (client) {
|
if (client) {
|
||||||
|
client->flush();
|
||||||
client->close();
|
client->close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -121,6 +121,7 @@ void WebSocketServer::terminateClientConnection(const QUuid &clientId)
|
|||||||
{
|
{
|
||||||
QWebSocket *client = m_clientList.value(clientId);
|
QWebSocket *client = m_clientList.value(clientId);
|
||||||
if (client) {
|
if (client) {
|
||||||
|
client->flush();
|
||||||
client->close();
|
client->close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,7 +44,6 @@ class Tag
|
|||||||
Q_PROPERTY(QString appId READ appId WRITE setAppId)
|
Q_PROPERTY(QString appId READ appId WRITE setAppId)
|
||||||
Q_PROPERTY(QString tagId READ tagId WRITE setTagId)
|
Q_PROPERTY(QString tagId READ tagId WRITE setTagId)
|
||||||
Q_PROPERTY(QUuid thingId READ thingId WRITE setThingId USER true)
|
Q_PROPERTY(QUuid thingId READ thingId WRITE setThingId USER true)
|
||||||
Q_PROPERTY(QUuid deviceId READ thingId WRITE setThingId USER true REVISION 1)
|
|
||||||
Q_PROPERTY(QUuid ruleId READ ruleId WRITE setRuleId USER true)
|
Q_PROPERTY(QUuid ruleId READ ruleId WRITE setRuleId USER true)
|
||||||
Q_PROPERTY(QString value READ value WRITE setValue USER true)
|
Q_PROPERTY(QString value READ value WRITE setValue USER true)
|
||||||
public:
|
public:
|
||||||
|
|||||||
6
libnymea-core/usermanager/userautorizer.cpp
Normal file
6
libnymea-core/usermanager/userautorizer.cpp
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#include "userautorizer.h"
|
||||||
|
|
||||||
|
UserAutorizer::UserAutorizer(QObject *parent) : QObject(parent)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
16
libnymea-core/usermanager/userautorizer.h
Normal file
16
libnymea-core/usermanager/userautorizer.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef USERAUTORIZER_H
|
||||||
|
#define USERAUTORIZER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class UserAutorizer : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit UserAutorizer(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // USERAUTORIZER_H
|
||||||
@ -1,5 +1,9 @@
|
|||||||
#include "userinfo.h"
|
#include "userinfo.h"
|
||||||
|
|
||||||
|
#include <QMetaEnum>
|
||||||
|
|
||||||
|
namespace nymeaserver {
|
||||||
|
|
||||||
UserInfo::UserInfo()
|
UserInfo::UserInfo()
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -20,3 +24,45 @@ void UserInfo::setUsername(const QString &username)
|
|||||||
{
|
{
|
||||||
m_username = username;
|
m_username = username;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString UserInfo::email()
|
||||||
|
{
|
||||||
|
return m_email;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserInfo::setEmail(const QString &email)
|
||||||
|
{
|
||||||
|
m_email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString UserInfo::displayName() const
|
||||||
|
{
|
||||||
|
return m_displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserInfo::setDisplayName(const QString &displayName)
|
||||||
|
{
|
||||||
|
m_displayName = displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
Types::PermissionScopes UserInfo::scopes() const
|
||||||
|
{
|
||||||
|
return m_scopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserInfo::setScopes(Types::PermissionScopes scopes)
|
||||||
|
{
|
||||||
|
m_scopes = scopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant UserInfoList::get(int index) const
|
||||||
|
{
|
||||||
|
return QVariant::fromValue(at(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserInfoList::put(const QVariant &variant)
|
||||||
|
{
|
||||||
|
append(variant.value<UserInfo>());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -33,11 +33,18 @@
|
|||||||
|
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QVariant>
|
||||||
|
#include "typeutils.h"
|
||||||
|
|
||||||
|
namespace nymeaserver {
|
||||||
|
|
||||||
class UserInfo
|
class UserInfo
|
||||||
{
|
{
|
||||||
Q_GADGET
|
Q_GADGET
|
||||||
Q_PROPERTY(QString username READ username)
|
Q_PROPERTY(QString username READ username)
|
||||||
|
Q_PROPERTY(QString email READ email)
|
||||||
|
Q_PROPERTY(QString displayName READ displayName)
|
||||||
|
Q_PROPERTY(Types::PermissionScopes scopes READ scopes)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UserInfo();
|
UserInfo();
|
||||||
@ -46,8 +53,29 @@ public:
|
|||||||
QString username() const;
|
QString username() const;
|
||||||
void setUsername(const QString &username);
|
void setUsername(const QString &username);
|
||||||
|
|
||||||
|
QString email();
|
||||||
|
void setEmail(const QString &email);
|
||||||
|
|
||||||
|
QString displayName() const;
|
||||||
|
void setDisplayName(const QString &displayName);
|
||||||
|
|
||||||
|
Types::PermissionScopes scopes() const;
|
||||||
|
void setScopes(Types::PermissionScopes scopes);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_username;
|
QString m_username;
|
||||||
|
QString m_email;
|
||||||
|
QString m_displayName;
|
||||||
|
Types::PermissionScopes m_scopes = Types::PermissionScopeNone;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class UserInfoList: public QList<UserInfo>
|
||||||
|
{
|
||||||
|
Q_GADGET
|
||||||
|
Q_PROPERTY(int count READ count)
|
||||||
|
public:
|
||||||
|
Q_INVOKABLE QVariant get(int index) const;
|
||||||
|
Q_INVOKABLE void put(const QVariant &variant);
|
||||||
|
};
|
||||||
|
}
|
||||||
#endif // USERINFO_H
|
#endif // USERINFO_H
|
||||||
|
|||||||
@ -77,6 +77,7 @@
|
|||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
#include <QCryptographicHash>
|
#include <QCryptographicHash>
|
||||||
#include <QSqlQuery>
|
#include <QSqlQuery>
|
||||||
|
#include <QSqlResult>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QSqlError>
|
#include <QSqlError>
|
||||||
#include <QRegExpValidator>
|
#include <QRegExpValidator>
|
||||||
@ -105,7 +106,7 @@ UserManager::UserManager(const QString &dbName, QObject *parent):
|
|||||||
if (QFileInfo(m_db.databaseName()).exists()) {
|
if (QFileInfo(m_db.databaseName()).exists()) {
|
||||||
rotate(m_db.databaseName());
|
rotate(m_db.databaseName());
|
||||||
if (!initDB()) {
|
if (!initDB()) {
|
||||||
qCWarning(dcLogEngine()) << "Error fixing user database. Giving up. Users can't be stored.";
|
qCWarning(dcUserManager()) << "Error fixing user database. Giving up. Users can't be stored.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -133,19 +134,23 @@ bool UserManager::initRequired() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*! Returns the list of user names for this UserManager. */
|
/*! Returns the list of user names for this UserManager. */
|
||||||
QStringList UserManager::users() const
|
UserInfoList UserManager::users() const
|
||||||
{
|
{
|
||||||
QString userQuery("SELECT username FROM users;");
|
QString userQuery("SELECT * FROM users;");
|
||||||
QSqlQuery result = m_db.exec(userQuery);
|
QSqlQuery result = m_db.exec(userQuery);
|
||||||
QStringList ret;
|
UserInfoList users;
|
||||||
while (result.next()) {
|
while (result.next()) {
|
||||||
ret << result.value("username").toString();
|
UserInfo info = UserInfo(result.value("username").toString());
|
||||||
|
info.setEmail(result.value("email").toString());
|
||||||
|
info.setDisplayName(result.value("displayName").toString());
|
||||||
|
info.setScopes(Types::scopesFromStringList(result.value("scopes").toString().split(',')));
|
||||||
|
users.append(info);
|
||||||
}
|
}
|
||||||
return ret;
|
return users;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Creates a new user with the given \a username and \a password. Returns the \l UserError to inform about the result. */
|
/*! Creates a new user with the given \a username and \a password. Returns the \l UserError to inform about the result. */
|
||||||
UserManager::UserError UserManager::createUser(const QString &username, const QString &password)
|
UserManager::UserError UserManager::createUser(const QString &username, const QString &password, const QString &email, const QString &displayName, Types::PermissionScopes scopes)
|
||||||
{
|
{
|
||||||
if (!validateUsername(username)) {
|
if (!validateUsername(username)) {
|
||||||
qCWarning(dcUserManager) << "Error creating user. Invalid username:" << username;
|
qCWarning(dcUserManager) << "Error creating user. Invalid username:" << username;
|
||||||
@ -157,24 +162,33 @@ UserManager::UserError UserManager::createUser(const QString &username, const QS
|
|||||||
return UserErrorBadPassword;
|
return UserErrorBadPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString checkForDuplicateUserQuery = QString("SELECT * FROM users WHERE lower(username) = \"%1\";").arg(username.toLower());
|
QSqlQuery checkForDuplicateUserQuery(m_db);
|
||||||
QSqlQuery result = m_db.exec(checkForDuplicateUserQuery);
|
checkForDuplicateUserQuery.prepare("SELECT * FROM users WHERE lower(username) = ?;");
|
||||||
if (result.first()) {
|
checkForDuplicateUserQuery.addBindValue(username.toLower());
|
||||||
|
checkForDuplicateUserQuery.exec();
|
||||||
|
if (checkForDuplicateUserQuery.first()) {
|
||||||
qCWarning(dcUserManager) << "Username already in use";
|
qCWarning(dcUserManager) << "Username already in use";
|
||||||
return UserErrorDuplicateUserId;
|
return UserErrorDuplicateUserId;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray salt = QUuid::createUuid().toString().remove(QRegExp("[{}]")).toUtf8();
|
QByteArray salt = QUuid::createUuid().toString().remove(QRegExp("[{}]")).toUtf8();
|
||||||
QByteArray hashedPassword = QCryptographicHash::hash(QString(password + salt).toUtf8(), QCryptographicHash::Sha512).toBase64();
|
QByteArray hashedPassword = QCryptographicHash::hash(QString(password + salt).toUtf8(), QCryptographicHash::Sha512).toBase64();
|
||||||
QString queryString = QString("INSERT INTO users(username, password, salt) values(\"%1\", \"%2\", \"%3\");")
|
QSqlQuery query(m_db);
|
||||||
.arg(username.toLower())
|
query.prepare("INSERT INTO users(username, email, displayName, password, salt, scopes) VALUES(?, ?, ?, ?, ?, ?);");
|
||||||
.arg(QString::fromUtf8(hashedPassword))
|
query.addBindValue(username.toLower());
|
||||||
.arg(QString::fromUtf8(salt));
|
query.addBindValue(email);
|
||||||
m_db.exec(queryString);
|
query.addBindValue(displayName);
|
||||||
if (m_db.lastError().type() != QSqlError::NoError) {
|
query.addBindValue(QString::fromUtf8(hashedPassword));
|
||||||
qCWarning(dcUserManager) << "Error creating user:" << m_db.lastError().databaseText() << m_db.lastError().driverText();
|
query.addBindValue(QString::fromUtf8(salt));
|
||||||
|
query.addBindValue(Types::scopesToStringList(scopes).join(','));
|
||||||
|
query.exec();
|
||||||
|
if (query.lastError().type() != QSqlError::NoError) {
|
||||||
|
qCWarning(dcUserManager) << "Error creating user:" << query.lastError().databaseText() << query.lastError().driverText();
|
||||||
return UserErrorBackendError;
|
return UserErrorBackendError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qCInfo(dcUserManager()) << "New user" << username << "added to the system with permissions:" << Types::scopesToStringList(scopes);
|
||||||
|
emit userAdded(username);
|
||||||
return UserErrorNoError;
|
return UserErrorNoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,17 +229,49 @@ UserManager::UserError UserManager::changePassword(const QString &username, cons
|
|||||||
|
|
||||||
UserManager::UserError UserManager::removeUser(const QString &username)
|
UserManager::UserError UserManager::removeUser(const QString &username)
|
||||||
{
|
{
|
||||||
if (!username.isEmpty()) {
|
QString dropUserQuery = QString("DELETE FROM users WHERE lower(username) =\"%1\";").arg(username.toLower());
|
||||||
QString dropUserQuery = QString("DELETE FROM users WHERE lower(username) =\"%1\";").arg(username.toLower());
|
QSqlQuery result = m_db.exec(dropUserQuery);
|
||||||
QSqlQuery result = m_db.exec(dropUserQuery);
|
if (result.numRowsAffected() == 0) {
|
||||||
if (result.numRowsAffected() == 0) {
|
return UserErrorInvalidUserId;
|
||||||
return UserErrorInvalidUserId;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString dropTokensQuery = QString("DELETE FROM tokens WHERE lower(username) = \"%1\";").arg(username.toLower());
|
QString dropTokensQuery = QString("DELETE FROM tokens WHERE lower(username) = \"%1\";").arg(username.toLower());
|
||||||
m_db.exec(dropTokensQuery);
|
m_db.exec(dropTokensQuery);
|
||||||
|
|
||||||
|
emit userRemoved(username);
|
||||||
|
return UserErrorNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
UserManager::UserError UserManager::setUserScopes(const QString &username, Types::PermissionScopes scopes)
|
||||||
|
{
|
||||||
|
QString scopesString = Types::scopesToStringList(scopes).join(',');
|
||||||
|
QSqlQuery setScopesQuery(m_db);
|
||||||
|
setScopesQuery.prepare("UPDATE users SET scopes = '%1' WHERE username = '%2'");
|
||||||
|
setScopesQuery.addBindValue(scopesString);
|
||||||
|
setScopesQuery.addBindValue(username);
|
||||||
|
setScopesQuery.exec();
|
||||||
|
if (setScopesQuery.lastError().type() != QSqlError::NoError) {
|
||||||
|
qCWarning(dcUserManager()) << "Error updating scopes for user" << username << setScopesQuery.lastError().databaseText() << setScopesQuery.lastError().driverText();
|
||||||
|
return UserErrorBackendError;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit userChanged(username);
|
||||||
|
return UserErrorNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
UserManager::UserError UserManager::setUserInfo(const QString &username, const QString &email, const QString &displayName)
|
||||||
|
{
|
||||||
|
QSqlQuery query(m_db);
|
||||||
|
query.prepare("UPDATE users SET email = ?, displayName = ? WHERE username = ?;");
|
||||||
|
query.addBindValue(email);
|
||||||
|
query.addBindValue(displayName);
|
||||||
|
query.addBindValue(username);
|
||||||
|
query.exec();
|
||||||
|
if (query.lastError().type() != QSqlError::NoError) {
|
||||||
|
qCWarning(dcUserManager()) << "Error updating user info for user" << username << query.lastError().databaseText() << query.lastError().driverText() << query.executedQuery();
|
||||||
|
return UserErrorBackendError;
|
||||||
|
}
|
||||||
|
emit userChanged(username);
|
||||||
return UserErrorNoError;
|
return UserErrorNoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,18 +287,20 @@ bool UserManager::pushButtonAuthAvailable() const
|
|||||||
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)) {
|
||||||
qCWarning(dcUserManager) << "Username did not pass validation:" << username;
|
qCWarning(dcUserManager) << "Authenticate: Username did not pass validation:" << username;
|
||||||
return QByteArray();
|
return QByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString passwordQuery = QString("SELECT password, salt FROM users WHERE lower(username) = \"%1\";").arg(username.toLower());
|
QSqlQuery passwordQuery(m_db);
|
||||||
QSqlQuery result = m_db.exec(passwordQuery);
|
passwordQuery.prepare("SELECT password, salt FROM users WHERE lower(username) = ?;");
|
||||||
if (!result.first()) {
|
passwordQuery.addBindValue(username.toLower());
|
||||||
|
passwordQuery.exec();
|
||||||
|
if (!passwordQuery.first()) {
|
||||||
qCWarning(dcUserManager) << "No such username" << username;
|
qCWarning(dcUserManager) << "No such username" << username;
|
||||||
return QByteArray();
|
return QByteArray();
|
||||||
}
|
}
|
||||||
QByteArray salt = result.value("salt").toByteArray();
|
QByteArray salt = passwordQuery.value("salt").toByteArray();
|
||||||
QByteArray hashedPassword = result.value("password").toByteArray();
|
QByteArray hashedPassword = passwordQuery.value("password").toByteArray();
|
||||||
|
|
||||||
if (hashedPassword != QCryptographicHash::hash(QString(password + salt).toUtf8(), QCryptographicHash::Sha512).toBase64()) {
|
if (hashedPassword != QCryptographicHash::hash(QString(password + salt).toUtf8(), QCryptographicHash::Sha512).toBase64()) {
|
||||||
qCWarning(dcUserManager) << "Authentication error for user:" << username;
|
qCWarning(dcUserManager) << "Authentication error for user:" << username;
|
||||||
@ -309,50 +357,47 @@ void UserManager::cancelPushButtonAuth(int transactionId)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UserInfo UserManager::userInfo(const QByteArray &token) const
|
/*! Request UserInfo.
|
||||||
|
The UserInfo for the given username is returned.
|
||||||
|
*/
|
||||||
|
UserInfo UserManager::userInfo(const QString &username) const
|
||||||
{
|
{
|
||||||
TokenInfo tokenInfo = this->tokenInfo(token);
|
|
||||||
|
|
||||||
if (tokenInfo.id().isNull()) {
|
QString getUserQuery = QString("SELECT * FROM users WHERE lower(username) = \"%1\";")
|
||||||
qCWarning(dcUserManager) << "Cannot fetch user info for invalid token:" << token;
|
.arg(username);
|
||||||
return UserInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
// OK, this seems pointless, but data structures are prepared to have more details about users than just the username
|
|
||||||
// i.e. permissions etc will be in here at some point
|
|
||||||
QString getUserQuery = QString("SELECT username FROM users WHERE lower(username) = \"%1\";")
|
|
||||||
.arg(tokenInfo.username().toLower());
|
|
||||||
QSqlQuery result = m_db.exec(getUserQuery);
|
QSqlQuery result = m_db.exec(getUserQuery);
|
||||||
if (m_db.lastError().type() != QSqlError::NoError) {
|
if (m_db.lastError().type() != QSqlError::NoError) {
|
||||||
qCWarning(dcUserManager) << "Query for token failed:" << m_db.lastError().databaseText() << m_db.lastError().driverText() << getUserQuery;
|
qCWarning(dcUserManager) << "Query for user" << username << "failed:" << m_db.lastError().databaseText() << m_db.lastError().driverText() << getUserQuery;
|
||||||
return UserInfo();
|
return UserInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result.first()) {
|
if (!result.first()) {
|
||||||
return UserInfo();
|
return UserInfo();
|
||||||
}
|
}
|
||||||
return UserInfo(result.value("username").toString());
|
|
||||||
|
|
||||||
|
UserInfo userInfo = UserInfo(result.value("username").toString());
|
||||||
|
userInfo.setEmail(result.value("email").toString());
|
||||||
|
userInfo.setDisplayName(result.value("displayName").toString());
|
||||||
|
userInfo.setScopes(Types::scopesFromStringList(result.value("scopes").toString().split(',')));
|
||||||
|
|
||||||
|
return userInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<TokenInfo> UserManager::tokens(const QString &username) const
|
QList<TokenInfo> UserManager::tokens(const QString &username) const
|
||||||
{
|
{
|
||||||
QList<TokenInfo> ret;
|
QList<TokenInfo> ret;
|
||||||
|
|
||||||
if (!validateUsername(username)) {
|
QSqlQuery query(m_db);
|
||||||
qCWarning(dcUserManager) << "Username did not pass validation:" << username;
|
query.prepare("SELECT id, username, creationdate, deviceName FROM tokens WHERE lower(username) = ?;");
|
||||||
return ret;
|
query.addBindValue(username.toLower());
|
||||||
}
|
query.exec();
|
||||||
QString getTokensQuery = QString("SELECT id, username, creationdate, deviceName FROM tokens WHERE lower(username) = \"%1\";")
|
|
||||||
.arg(username.toLower());
|
|
||||||
QSqlQuery result = m_db.exec(getTokensQuery);
|
|
||||||
if (m_db.lastError().type() != QSqlError::NoError) {
|
if (m_db.lastError().type() != QSqlError::NoError) {
|
||||||
qCWarning(dcUserManager) << "Query for tokens failed:" << m_db.lastError().databaseText() << m_db.lastError().driverText() << getTokensQuery;
|
qCWarning(dcUserManager) << "Query for tokens failed:" << query.lastError().databaseText() << query.lastError().driverText() << query.executedQuery();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (result.next()) {
|
while (query.next()) {
|
||||||
ret << TokenInfo(result.value("id").toUuid(), result.value("username").toString(), result.value("creationdate").toDateTime(), result.value("devicename").toString());
|
ret << TokenInfo(query.value("id").toUuid(), query.value("username").toString(), query.value("creationdate").toDateTime(), query.value("devicename").toString());
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -441,30 +486,137 @@ bool UserManager::initDB()
|
|||||||
m_db.close();
|
m_db.close();
|
||||||
|
|
||||||
if (!m_db.open()) {
|
if (!m_db.open()) {
|
||||||
qCWarning(dcUserManager()) << "Can't open user database. Init failed.";
|
dumpDBError("Can't open user database. Init failed.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int currentVersion = -1;
|
||||||
|
int newVersion = 1;
|
||||||
|
if (m_db.tables().contains("metadata")) {
|
||||||
|
QSqlQuery query = m_db.exec("SELECT data FROM metadata WHERE `key` = 'version';");
|
||||||
|
if (query.next()) {
|
||||||
|
currentVersion = query.value("data").toInt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_db.tables().contains("users")) {
|
if (!m_db.tables().contains("users")) {
|
||||||
qCDebug(dcUserManager()) << "Empty user database. Setting up metadata...";
|
qCDebug(dcUserManager()) << "Empty user database. Setting up metadata...";
|
||||||
m_db.exec("CREATE TABLE users (username VARCHAR(40) UNIQUE, password VARCHAR(100), salt VARCHAR(100));");
|
m_db.exec("CREATE TABLE users (username VARCHAR(40) UNIQUE PRIMARY KEY, email VARCHAR(40), displayName VARCHAR(40), password VARCHAR(100), salt VARCHAR(100), scopes TEXT);");
|
||||||
if (m_db.lastError().isValid()) {
|
if (m_db.lastError().isValid()) {
|
||||||
qCWarning(dcUserManager) << "Error initualizing user database. Driver error:" << m_db.lastError().driverText() << "Database error:" << m_db.lastError().databaseText();
|
dumpDBError("Error initializing user database (table users).");
|
||||||
m_db.close();
|
m_db.close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (currentVersion < 1) {
|
||||||
|
m_db.exec("ALTER TABLE users ADD COLUMN scopes TEXT;");
|
||||||
|
if (m_db.lastError().isValid()) {
|
||||||
|
dumpDBError("Error migrating user database (table users).");
|
||||||
|
m_db.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Migrated existing users from before multiuser support are admins by default
|
||||||
|
QSqlQuery query(m_db);
|
||||||
|
query.prepare("UPDATE users SET scopes = ?;");
|
||||||
|
query.addBindValue(Types::scopesToStringList(Types::PermissionScopeAdmin).join(','));
|
||||||
|
query.exec();
|
||||||
|
|
||||||
|
if (query.lastError().isValid()) {
|
||||||
|
dumpDBError("Error migrating user database (updating existing users).");
|
||||||
|
m_db.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_db.exec("ALTER TABLE users ADD COLUMN email VARCHAR(40);");
|
||||||
|
if (m_db.lastError().isValid()) {
|
||||||
|
dumpDBError("Error migrating user database (table users).");
|
||||||
|
m_db.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_db.exec("ALTER TABLE users ADD COLUMN displayName VARCHAR(40);");
|
||||||
|
if (m_db.lastError().isValid()) {
|
||||||
|
dumpDBError("Error migrating user database (table users).");
|
||||||
|
m_db.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Up until schema 1, username was an email. Copy it to initialize the email field.
|
||||||
|
m_db.exec("UPDATE users SET email = username;");
|
||||||
|
if (m_db.lastError().isValid()) {
|
||||||
|
dumpDBError("Error migrating user database (table users).");
|
||||||
|
m_db.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
currentVersion = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_db.tables().contains("tokens")) {
|
if (!m_db.tables().contains("tokens")) {
|
||||||
qCDebug(dcUserManager()) << "Empty user database. Setting up metadata...";
|
qCDebug(dcUserManager()) << "Empty user database. Setting up metadata...";
|
||||||
m_db.exec("CREATE TABLE tokens (id VARCHAR(40) UNIQUE, username VARCHAR(40), token VARCHAR(100) UNIQUE, creationdate DATETIME, devicename VARCHAR(40));");
|
m_db.exec("CREATE TABLE tokens (id VARCHAR(40) UNIQUE, username VARCHAR(40), token VARCHAR(100) UNIQUE, creationdate DATETIME, devicename VARCHAR(40));");
|
||||||
if (m_db.lastError().isValid()) {
|
if (m_db.lastError().isValid()) {
|
||||||
qCWarning(dcUserManager()) << "Error initializing user database. Driver error:" << m_db.lastError().driverText() << "Database error:" << m_db.lastError().databaseText();
|
dumpDBError("Error initializing user database (table tokens).");
|
||||||
m_db.close();
|
m_db.close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_db.tables().contains("metadata")) {
|
||||||
|
if (currentVersion < newVersion) {
|
||||||
|
m_db.exec(QString("UPDATE metadata SET data = %1 WHERE `key` = 'version')").arg(newVersion));
|
||||||
|
if (m_db.lastError().isValid()) {
|
||||||
|
dumpDBError("Error updating up user database schema version!");
|
||||||
|
m_db.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
qCInfo(dcUserManager()) << "Successfully migrated user database.";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_db.exec("CREATE TABLE metadata (`key` VARCHAR(10), data VARCHAR(40));");
|
||||||
|
if (m_db.lastError().isValid()) {
|
||||||
|
dumpDBError("Error setting up user database (table metadata)!");
|
||||||
|
m_db.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_db.exec(QString("INSERT INTO metadata (`key`, `data`) VALUES ('version', %1);").arg(newVersion));
|
||||||
|
if (m_db.lastError().isValid()) {
|
||||||
|
dumpDBError("Error setting up user database (setting version metadata)!");
|
||||||
|
m_db.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
qCInfo(dcUserManager()) << "Successfully initialized user database.";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Migration from before 1.0:
|
||||||
|
// Push button tokens were given out without an explicit user name
|
||||||
|
// If we have push button tokens (userId "") but no explicit user, let's create it as admin
|
||||||
|
// Users without valid username will have password login disabled.
|
||||||
|
QSqlQuery query(m_db);
|
||||||
|
query.prepare("SELECT * FROM tokens WHERE username = \"\";");
|
||||||
|
query.exec();
|
||||||
|
if (query.lastError().type() == QSqlError::NoError && query.next()) {
|
||||||
|
QSqlQuery query(m_db);
|
||||||
|
query.prepare("SELECT * FROM users WHERE username = \"\";");
|
||||||
|
query.exec();
|
||||||
|
if (!query.next()) {
|
||||||
|
qCDebug(dcUserManager()) << "Tokens existing but no user. Creating token admin user";
|
||||||
|
QSqlQuery query(m_db);
|
||||||
|
query.prepare("INSERT INTO users(username, email, displayName, password, salt, scopes) values(?, ?, ?, ?, ?, ?);");
|
||||||
|
query.addBindValue("");
|
||||||
|
query.addBindValue("");
|
||||||
|
query.addBindValue("Admin");
|
||||||
|
query.addBindValue("");
|
||||||
|
query.addBindValue("");
|
||||||
|
query.addBindValue(Types::scopeToString(Types::PermissionScopeAdmin));
|
||||||
|
query.exec();
|
||||||
|
if (query.lastError().type() != QSqlError::NoError) {
|
||||||
|
qCWarning(dcUserManager) << "Error creating push button user:" << query.lastError().databaseText() << query.lastError().driverText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
qCDebug(dcUserManager()) << "User database initialized successfully.";
|
qCDebug(dcUserManager()) << "User database initialized successfully.";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -486,7 +638,7 @@ void UserManager::rotate(const QString &dbName)
|
|||||||
|
|
||||||
bool UserManager::validateUsername(const QString &username) const
|
bool UserManager::validateUsername(const QString &username) const
|
||||||
{
|
{
|
||||||
QRegExp validator("(^[a-zA-Z0-9_\\.+-]+@[a-zA-Z0-9-_]+(\\.[a-zA-Z]+){1,2}$)");
|
QRegExp validator("[a-zA-Z0-9_\\.+-@]{3,}");
|
||||||
return validator.exactMatch(username);
|
return validator.exactMatch(username);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -513,6 +665,11 @@ bool UserManager::validateToken(const QByteArray &token) const
|
|||||||
return validator.exactMatch(token);
|
return validator.exactMatch(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UserManager::dumpDBError(const QString &message)
|
||||||
|
{
|
||||||
|
qCCritical(dcUserManager) << message << "Driver error:" << m_db.lastError().driverText() << "Database error:" << m_db.lastError().databaseText();
|
||||||
|
}
|
||||||
|
|
||||||
void UserManager::onPushButtonPressed()
|
void UserManager::onPushButtonPressed()
|
||||||
{
|
{
|
||||||
if (m_pushButtonTransaction.first == -1) {
|
if (m_pushButtonTransaction.first == -1) {
|
||||||
@ -520,6 +677,24 @@ void UserManager::onPushButtonPressed()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Creating a user without username and password. It won't be able to log in via user/password
|
||||||
|
QSqlQuery query(m_db);
|
||||||
|
query.prepare("SELECT * FROM users WHERE username = \"\";");
|
||||||
|
query.exec();
|
||||||
|
if (!query.next()) {
|
||||||
|
qCDebug(dcUserManager()) << "Creating token admin user";
|
||||||
|
QSqlQuery query(m_db);
|
||||||
|
query.prepare("INSERT INTO users(username, password, salt, scopes) values(?, ?, ?, ?);");
|
||||||
|
query.addBindValue("");
|
||||||
|
query.addBindValue("");
|
||||||
|
query.addBindValue("");
|
||||||
|
query.addBindValue(Types::scopeToString(Types::PermissionScopeAdmin));
|
||||||
|
query.exec();
|
||||||
|
if (query.lastError().type() != QSqlError::NoError) {
|
||||||
|
qCWarning(dcUserManager) << "Error creating push button user:" << query.lastError().databaseText() << query.lastError().driverText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QByteArray token = QCryptographicHash::hash(QUuid::createUuid().toByteArray(), QCryptographicHash::Sha256).toBase64();
|
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\");")
|
QString storeTokenQuery = QString("INSERT INTO tokens(id, username, token, creationdate, devicename) VALUES(\"%1\", \"%2\", \"%3\", \"%4\", \"%5\");")
|
||||||
.arg(QUuid::createUuid().toString())
|
.arg(QUuid::createUuid().toString())
|
||||||
|
|||||||
@ -59,11 +59,13 @@ public:
|
|||||||
explicit UserManager(const QString &dbName, QObject *parent = nullptr);
|
explicit UserManager(const QString &dbName, QObject *parent = nullptr);
|
||||||
|
|
||||||
bool initRequired() const;
|
bool initRequired() const;
|
||||||
QStringList users() const;
|
UserInfoList users() const;
|
||||||
|
|
||||||
UserError createUser(const QString &username, const QString &password);
|
UserError createUser(const QString &username, const QString &password, const QString &email, const QString &displayName, Types::PermissionScopes scopes);
|
||||||
UserError changePassword(const QString &username, const QString &newPassword);
|
UserError changePassword(const QString &username, const QString &newPassword);
|
||||||
UserError removeUser(const QString &username);
|
UserError removeUser(const QString &username);
|
||||||
|
UserError setUserScopes(const QString &username, Types::PermissionScopes scopes);
|
||||||
|
UserError setUserInfo(const QString &username, const QString &email, const QString &displayName);
|
||||||
|
|
||||||
bool pushButtonAuthAvailable() const;
|
bool pushButtonAuthAvailable() const;
|
||||||
|
|
||||||
@ -71,7 +73,7 @@ public:
|
|||||||
int requestPushButtonAuth(const QString &deviceName);
|
int requestPushButtonAuth(const QString &deviceName);
|
||||||
void cancelPushButtonAuth(int transactionId);
|
void cancelPushButtonAuth(int transactionId);
|
||||||
|
|
||||||
UserInfo userInfo(const QByteArray &token) const;
|
UserInfo userInfo(const QString &username = QString()) const;
|
||||||
TokenInfo tokenInfo(const QByteArray &token) const;
|
TokenInfo tokenInfo(const QByteArray &token) const;
|
||||||
TokenInfo tokenInfo(const QUuid &tokenId) const;
|
TokenInfo tokenInfo(const QUuid &tokenId) const;
|
||||||
QList<TokenInfo> tokens(const QString &username) const;
|
QList<TokenInfo> tokens(const QString &username) const;
|
||||||
@ -82,6 +84,9 @@ public:
|
|||||||
bool verifyToken(const QByteArray &token);
|
bool verifyToken(const QByteArray &token);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
void userAdded(const QString &username);
|
||||||
|
void userRemoved(const QString &username);
|
||||||
|
void userChanged(const QString &username);
|
||||||
void pushButtonAuthFinished(int transactionId, bool success, const QByteArray &token);
|
void pushButtonAuthFinished(int transactionId, bool success, const QByteArray &token);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -91,6 +96,8 @@ private:
|
|||||||
bool validatePassword(const QString &password) const;
|
bool validatePassword(const QString &password) const;
|
||||||
bool validateToken(const QByteArray &token) const;
|
bool validateToken(const QByteArray &token) const;
|
||||||
|
|
||||||
|
void dumpDBError(const QString &message);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onPushButtonPressed();
|
void onPushButtonPressed();
|
||||||
|
|
||||||
|
|||||||
@ -45,7 +45,6 @@ class LIBNYMEA_EXPORT ThingDescriptor
|
|||||||
Q_PROPERTY(QUuid thingId READ thingId USER true)
|
Q_PROPERTY(QUuid thingId READ thingId USER true)
|
||||||
Q_PROPERTY(QString title READ title)
|
Q_PROPERTY(QString title READ title)
|
||||||
Q_PROPERTY(QString description READ description)
|
Q_PROPERTY(QString description READ description)
|
||||||
Q_PROPERTY(ParamList deviceParams READ params REVISION 1) // Had been forgotten in the device->thing transition.
|
|
||||||
Q_PROPERTY(ParamList params READ params) // added in 0.27
|
Q_PROPERTY(ParamList params READ params) // added in 0.27
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@ -147,12 +147,13 @@ void JsonHandler::registerObject(const QString &name, const QVariantMap &object)
|
|||||||
m_objects.insert(name, object);
|
m_objects.insert(name, object);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JsonHandler::registerMethod(const QString &name, const QString &description, const QVariantMap ¶ms, const QVariantMap &returns, const QString &deprecationInfo)
|
void JsonHandler::registerMethod(const QString &name, const QString &description, const QVariantMap ¶ms, const QVariantMap &returns, Types::PermissionScope permissionScope, const QString &deprecationInfo)
|
||||||
{
|
{
|
||||||
QVariantMap methodData;
|
QVariantMap methodData;
|
||||||
methodData.insert("description", description);
|
methodData.insert("description", description);
|
||||||
methodData.insert("params", params);
|
methodData.insert("params", params);
|
||||||
methodData.insert("returns", returns);
|
methodData.insert("returns", returns);
|
||||||
|
methodData.insert("permissionScope", enumValueName(permissionScope));
|
||||||
if (!deprecationInfo.isEmpty()) {
|
if (!deprecationInfo.isEmpty()) {
|
||||||
methodData.insert("deprecated", deprecationInfo);
|
methodData.insert("deprecated", deprecationInfo);
|
||||||
}
|
}
|
||||||
@ -285,7 +286,8 @@ QVariant JsonHandler::pack(const QMetaObject &metaObject, const void *value) con
|
|||||||
int flagValue = propertyValue.toInt();
|
int flagValue = propertyValue.toInt();
|
||||||
QStringList flags;
|
QStringList flags;
|
||||||
for (int i = 0; i < metaFlag.keyCount(); i++) {
|
for (int i = 0; i < metaFlag.keyCount(); i++) {
|
||||||
if ((metaFlag.value(i) & flagValue) > 0) {
|
int flag = metaFlag.value(i) & flagValue;
|
||||||
|
if (flag == metaFlag.value(i) && flag > 0) {
|
||||||
flags.append(metaFlag.key(i));
|
flags.append(metaFlag.key(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,6 +40,7 @@
|
|||||||
|
|
||||||
#include "jsonreply.h"
|
#include "jsonreply.h"
|
||||||
#include "jsoncontext.h"
|
#include "jsoncontext.h"
|
||||||
|
#include "typeutils.h"
|
||||||
|
|
||||||
class JsonHandler : public QObject
|
class JsonHandler : public QObject
|
||||||
{
|
{
|
||||||
@ -108,7 +109,7 @@ protected:
|
|||||||
// Deprecated QString based registerObject
|
// Deprecated QString based registerObject
|
||||||
void registerObject(const QString &name, const QVariantMap &object);
|
void registerObject(const QString &name, const QVariantMap &object);
|
||||||
|
|
||||||
void registerMethod(const QString &name, const QString &description, const QVariantMap ¶ms, const QVariantMap &returns, const QString &deprecationInfo = QString());
|
void registerMethod(const QString &name, const QString &description, const QVariantMap ¶ms, const QVariantMap &returns, Types::PermissionScope permissionScope = Types::PermissionScopeAdmin, const QString &deprecationInfo = QString());
|
||||||
void registerNotification(const QString &name, const QString &description, const QVariantMap ¶ms, const QString &deprecationInfo = QString());
|
void registerNotification(const QString &name, const QString &description, const QVariantMap ¶ms, const QString &deprecationInfo = QString());
|
||||||
|
|
||||||
JsonReply *createReply(const QVariantMap &data) const;
|
JsonReply *createReply(const QVariantMap &data) const;
|
||||||
|
|||||||
@ -200,6 +200,7 @@ SOURCES += \
|
|||||||
types/event.cpp \
|
types/event.cpp \
|
||||||
types/eventdescriptor.cpp \
|
types/eventdescriptor.cpp \
|
||||||
types/thingclass.cpp \
|
types/thingclass.cpp \
|
||||||
|
types/typeutils.cpp \
|
||||||
types/vendor.cpp \
|
types/vendor.cpp \
|
||||||
types/paramtype.cpp \
|
types/paramtype.cpp \
|
||||||
types/param.cpp \
|
types/param.cpp \
|
||||||
|
|||||||
@ -42,7 +42,6 @@ class LIBNYMEA_EXPORT Action
|
|||||||
Q_GADGET
|
Q_GADGET
|
||||||
Q_PROPERTY(QUuid actionTypeId READ actionTypeId WRITE setActionTypeId)
|
Q_PROPERTY(QUuid actionTypeId READ actionTypeId WRITE setActionTypeId)
|
||||||
Q_PROPERTY(QUuid thingId READ thingId WRITE setThingId)
|
Q_PROPERTY(QUuid thingId READ thingId WRITE setThingId)
|
||||||
Q_PROPERTY(QUuid deviceId READ thingId WRITE setThingId REVISION 1)
|
|
||||||
Q_PROPERTY(ParamList params READ params WRITE setParams USER true)
|
Q_PROPERTY(ParamList params READ params WRITE setParams USER true)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@ -44,7 +44,6 @@ class LIBNYMEA_EXPORT Event
|
|||||||
Q_GADGET
|
Q_GADGET
|
||||||
Q_PROPERTY(QUuid eventTypeId READ eventTypeId)
|
Q_PROPERTY(QUuid eventTypeId READ eventTypeId)
|
||||||
Q_PROPERTY(QUuid thingId READ thingId)
|
Q_PROPERTY(QUuid thingId READ thingId)
|
||||||
Q_PROPERTY(QUuid deviceId READ thingId REVISION 1)
|
|
||||||
Q_PROPERTY(ParamList params READ params)
|
Q_PROPERTY(ParamList params READ params)
|
||||||
public:
|
public:
|
||||||
Event();
|
Event();
|
||||||
|
|||||||
@ -44,7 +44,6 @@ class LIBNYMEA_EXPORT EventDescriptor
|
|||||||
{
|
{
|
||||||
Q_GADGET
|
Q_GADGET
|
||||||
Q_PROPERTY(QUuid thingId READ thingId WRITE setThingId USER true)
|
Q_PROPERTY(QUuid thingId READ thingId WRITE setThingId USER true)
|
||||||
Q_PROPERTY(QUuid deviceId READ thingId WRITE setThingId USER true REVISION 1)
|
|
||||||
Q_PROPERTY(QUuid eventTypeId READ eventTypeId WRITE setEventTypeId USER true)
|
Q_PROPERTY(QUuid eventTypeId READ eventTypeId WRITE setEventTypeId USER true)
|
||||||
Q_PROPERTY(QString interface READ interface WRITE setInterface USER true)
|
Q_PROPERTY(QString interface READ interface WRITE setInterface USER true)
|
||||||
Q_PROPERTY(QString interfaceEvent READ interfaceEvent WRITE setInterfaceEvent USER true)
|
Q_PROPERTY(QString interfaceEvent READ interfaceEvent WRITE setInterfaceEvent USER true)
|
||||||
|
|||||||
@ -46,7 +46,6 @@ class LIBNYMEA_EXPORT StateDescriptor
|
|||||||
Q_GADGET
|
Q_GADGET
|
||||||
Q_PROPERTY(QUuid stateTypeId READ stateTypeId WRITE setStateTypeId USER true)
|
Q_PROPERTY(QUuid stateTypeId READ stateTypeId WRITE setStateTypeId USER true)
|
||||||
Q_PROPERTY(QUuid thingId READ thingId WRITE setThingId USER true)
|
Q_PROPERTY(QUuid thingId READ thingId WRITE setThingId USER true)
|
||||||
Q_PROPERTY(QUuid deviceId READ thingId WRITE setThingId USER true REVISION 1)
|
|
||||||
Q_PROPERTY(QString interface READ interface WRITE setInterface USER true)
|
Q_PROPERTY(QString interface READ interface WRITE setInterface USER true)
|
||||||
Q_PROPERTY(QString interfaceState READ interfaceState WRITE setInterfaceState USER true)
|
Q_PROPERTY(QString interfaceState READ interfaceState WRITE setInterfaceState USER true)
|
||||||
Q_PROPERTY(QVariant value READ stateValue WRITE setStateValue USER true)
|
Q_PROPERTY(QVariant value READ stateValue WRITE setStateValue USER true)
|
||||||
|
|||||||
70
libnymea/types/typeutils.cpp
Normal file
70
libnymea/types/typeutils.cpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2021, nymea GmbH
|
||||||
|
* Contact: contact@nymea.io
|
||||||
|
*
|
||||||
|
* This file is part of nymea.
|
||||||
|
* This project including source code and documentation is protected by
|
||||||
|
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||||
|
* reproduction, publication, editing and translation, are reserved. The use of
|
||||||
|
* this project is subject to the terms of a license agreement to be concluded
|
||||||
|
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||||
|
* under https://nymea.io/license
|
||||||
|
*
|
||||||
|
* GNU Lesser General Public License Usage
|
||||||
|
* Alternatively, this project may be redistributed and/or modified under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free
|
||||||
|
* Software Foundation; version 3. This project 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
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* For any further details and any questions please contact us under
|
||||||
|
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||||
|
* https://nymea.io/license/faq
|
||||||
|
*
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
|
#include "typeutils.h"
|
||||||
|
|
||||||
|
#include <QMetaEnum>
|
||||||
|
|
||||||
|
QStringList Types::scopesToStringList(Types::PermissionScopes scopes)
|
||||||
|
{
|
||||||
|
QStringList ret;
|
||||||
|
QMetaEnum metaEnum = QMetaEnum::fromType<PermissionScope>();
|
||||||
|
for (int i = 0; i < metaEnum.keyCount(); i++) {
|
||||||
|
if (scopes.testFlag(static_cast<PermissionScope>(metaEnum.value(i)))) {
|
||||||
|
ret << metaEnum.key(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Types::scopeToString(Types::PermissionScope scope)
|
||||||
|
{
|
||||||
|
QMetaEnum metaEnum = QMetaEnum::fromType<PermissionScope>();
|
||||||
|
return metaEnum.valueToKey(scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
Types::PermissionScope Types::scopeFromString(const QString &scopeString)
|
||||||
|
{
|
||||||
|
QMetaEnum metaEnum = QMetaEnum::fromType<PermissionScope>();
|
||||||
|
return static_cast<PermissionScope>(metaEnum.keyToValue(scopeString.toUtf8()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Types::PermissionScopes Types::scopesFromStringList(const QStringList &scopeList)
|
||||||
|
{
|
||||||
|
PermissionScopes ret;
|
||||||
|
QMetaEnum metaEnum = QMetaEnum::fromType<PermissionScopes>();
|
||||||
|
for (int i = 0; i < metaEnum.keyCount(); i++) {
|
||||||
|
if (scopeList.contains(metaEnum.key(i))) {
|
||||||
|
ret |= static_cast<PermissionScope>(metaEnum.value(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
*
|
*
|
||||||
* Copyright 2013 - 2020, nymea GmbH
|
* Copyright 2013 - 2021, nymea GmbH
|
||||||
* Contact: contact@nymea.io
|
* Contact: contact@nymea.io
|
||||||
*
|
*
|
||||||
* This file is part of nymea.
|
* This file is part of nymea.
|
||||||
@ -182,6 +182,23 @@ public:
|
|||||||
StateValueFilterAdaptive
|
StateValueFilterAdaptive
|
||||||
};
|
};
|
||||||
Q_ENUM(StateValueFilter)
|
Q_ENUM(StateValueFilter)
|
||||||
|
|
||||||
|
enum PermissionScope {
|
||||||
|
PermissionScopeNone = 0x0000,
|
||||||
|
PermissionScopeControlThings = 0x0001,
|
||||||
|
PermissionScopeConfigureThings = 0x0003,
|
||||||
|
PermissionScopeExecuteRules = 0x0010,
|
||||||
|
PermissionScopeConfigureRules = 0x0030,
|
||||||
|
PermissionScopeAdmin = 0xFFFF,
|
||||||
|
};
|
||||||
|
Q_ENUM(PermissionScope)
|
||||||
|
Q_DECLARE_FLAGS(PermissionScopes, PermissionScope)
|
||||||
|
Q_FLAG(PermissionScopes)
|
||||||
|
|
||||||
|
static PermissionScopes scopesFromStringList(const QStringList &scopeList);
|
||||||
|
static PermissionScope scopeFromString(const QString &scopeString);
|
||||||
|
static QStringList scopesToStringList(PermissionScopes scopes);
|
||||||
|
static QString scopeToString(PermissionScope scope);
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(Types::InputType)
|
Q_DECLARE_METATYPE(Types::InputType)
|
||||||
|
|||||||
@ -4,8 +4,8 @@ include(nymea.pri)
|
|||||||
NYMEA_VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p"')
|
NYMEA_VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p"')
|
||||||
|
|
||||||
# define protocol versions
|
# define protocol versions
|
||||||
JSON_PROTOCOL_VERSION_MAJOR=5
|
JSON_PROTOCOL_VERSION_MAJOR=6
|
||||||
JSON_PROTOCOL_VERSION_MINOR=8
|
JSON_PROTOCOL_VERSION_MINOR=0
|
||||||
JSON_PROTOCOL_VERSION="$${JSON_PROTOCOL_VERSION_MAJOR}.$${JSON_PROTOCOL_VERSION_MINOR}"
|
JSON_PROTOCOL_VERSION="$${JSON_PROTOCOL_VERSION_MAJOR}.$${JSON_PROTOCOL_VERSION_MINOR}"
|
||||||
LIBNYMEA_API_VERSION_MAJOR=7
|
LIBNYMEA_API_VERSION_MAJOR=7
|
||||||
LIBNYMEA_API_VERSION_MINOR=3
|
LIBNYMEA_API_VERSION_MINOR=3
|
||||||
|
|||||||
@ -1,6 +0,0 @@
|
|||||||
TARGET = testactions
|
|
||||||
|
|
||||||
include(../../../nymea.pri)
|
|
||||||
include(../autotests.pri)
|
|
||||||
|
|
||||||
SOURCES += testactions.cpp
|
|
||||||
@ -1,153 +0,0 @@
|
|||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
||||||
*
|
|
||||||
* Copyright 2013 - 2020, nymea GmbH
|
|
||||||
* Contact: contact@nymea.io
|
|
||||||
*
|
|
||||||
* This file is part of nymea.
|
|
||||||
* This project including source code and documentation is protected by
|
|
||||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
|
||||||
* reproduction, publication, editing and translation, are reserved. The use of
|
|
||||||
* this project is subject to the terms of a license agreement to be concluded
|
|
||||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
|
||||||
* under https://nymea.io/license
|
|
||||||
*
|
|
||||||
* GNU General Public License Usage
|
|
||||||
* Alternatively, this project may be redistributed and/or modified under the
|
|
||||||
* terms of the GNU General Public License as published by the Free Software
|
|
||||||
* Foundation, GNU version 3. This project 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
|
|
||||||
* this project. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
* For any further details and any questions please contact us under
|
|
||||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
|
||||||
* https://nymea.io/license/faq
|
|
||||||
*
|
|
||||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
||||||
|
|
||||||
#include "nymeatestbase.h"
|
|
||||||
#include "integrations/thing.h"
|
|
||||||
#include "jsonrpc/devicehandler.h"
|
|
||||||
|
|
||||||
using namespace nymeaserver;
|
|
||||||
|
|
||||||
class TestActions: public NymeaTestBase
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void executeAction_data();
|
|
||||||
void executeAction();
|
|
||||||
|
|
||||||
void getActionType_data();
|
|
||||||
void getActionType();
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
void TestActions::executeAction_data()
|
|
||||||
{
|
|
||||||
QTest::addColumn<ThingId>("deviceId");
|
|
||||||
QTest::addColumn<ActionTypeId>("actionTypeId");
|
|
||||||
QTest::addColumn<QVariantList>("actionParams");
|
|
||||||
QTest::addColumn<Device::DeviceError>("error");
|
|
||||||
|
|
||||||
QVariantList params;
|
|
||||||
QVariantMap param1;
|
|
||||||
param1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId);
|
|
||||||
param1.insert("value", 5);
|
|
||||||
params.append(param1);
|
|
||||||
QVariantMap param2;
|
|
||||||
param2.insert("paramTypeId", mockWithParamsActionParam2ParamTypeId);
|
|
||||||
param2.insert("value", true);
|
|
||||||
params.append(param2);
|
|
||||||
|
|
||||||
QTest::newRow("valid action") << m_mockThingId << mockWithParamsActionTypeId << params << Device::DeviceErrorNoError;
|
|
||||||
QTest::newRow("invalid deviceId") << ThingId::createThingId() << mockWithParamsActionTypeId << params << Device::DeviceErrorDeviceNotFound;
|
|
||||||
QTest::newRow("invalid actionTypeId") << m_mockThingId << ActionTypeId::createActionTypeId() << params << Device::DeviceErrorActionTypeNotFound;
|
|
||||||
QTest::newRow("missing params") << m_mockThingId << mockWithParamsActionTypeId << QVariantList() << Device::DeviceErrorMissingParameter;
|
|
||||||
QTest::newRow("async action") << m_mockThingId << mockAsyncActionTypeId << QVariantList() << Device::DeviceErrorNoError;
|
|
||||||
QTest::newRow("broken action") << m_mockThingId << mockFailingActionTypeId << QVariantList() << Device::DeviceErrorSetupFailed;
|
|
||||||
QTest::newRow("async broken action") << m_mockThingId << mockAsyncFailingActionTypeId << QVariantList() << Device::DeviceErrorSetupFailed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestActions::executeAction()
|
|
||||||
{
|
|
||||||
QFETCH(ThingId, deviceId);
|
|
||||||
QFETCH(ActionTypeId, actionTypeId);
|
|
||||||
QFETCH(QVariantList, actionParams);
|
|
||||||
QFETCH(Device::DeviceError, error);
|
|
||||||
|
|
||||||
QVariantMap params;
|
|
||||||
params.insert("actionTypeId", actionTypeId);
|
|
||||||
params.insert("deviceId", deviceId);
|
|
||||||
params.insert("params", actionParams);
|
|
||||||
QVariant response = injectAndWait("Actions.ExecuteAction", params);
|
|
||||||
verifyError(response, "deviceError", enumValueName(error));
|
|
||||||
|
|
||||||
// Fetch action execution history from mock device
|
|
||||||
QNetworkAccessManager nam;
|
|
||||||
QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*)));
|
|
||||||
|
|
||||||
QNetworkRequest request(QUrl(QString("http://localhost:%1/actionhistory").arg(m_mockThing1Port)));
|
|
||||||
QNetworkReply *reply = nam.get(request);
|
|
||||||
spy.wait();
|
|
||||||
QCOMPARE(spy.count(), 1);
|
|
||||||
reply->deleteLater();
|
|
||||||
QByteArray data = reply->readAll();
|
|
||||||
|
|
||||||
if (error == Device::DeviceErrorNoError) {
|
|
||||||
QVERIFY2(actionTypeId == ActionTypeId(data), QString("ActionTypeId mismatch. Got %1, Expected: %2")
|
|
||||||
.arg(ActionTypeId(data).toString()).arg(actionTypeId.toString()).toLatin1().data());
|
|
||||||
} else {
|
|
||||||
QVERIFY2(data.length() == 0, QString("Data is %1, should be empty.").arg(QString(data)).toLatin1().data());
|
|
||||||
}
|
|
||||||
|
|
||||||
// cleanup for the next run
|
|
||||||
spy.clear();
|
|
||||||
request.setUrl(QUrl(QString("http://localhost:%1/clearactionhistory").arg(m_mockThing1Port)));
|
|
||||||
reply = nam.get(request);
|
|
||||||
spy.wait();
|
|
||||||
QCOMPARE(spy.count(), 1);
|
|
||||||
reply->deleteLater();
|
|
||||||
|
|
||||||
spy.clear();
|
|
||||||
request.setUrl(QUrl(QString("http://localhost:%1/actionhistory").arg(m_mockThing1Port)));
|
|
||||||
reply = nam.get(request);
|
|
||||||
spy.wait();
|
|
||||||
QCOMPARE(spy.count(), 1);
|
|
||||||
reply->deleteLater();
|
|
||||||
data = reply->readAll();
|
|
||||||
qDebug() << "cleared data:" << data;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestActions::getActionType_data()
|
|
||||||
{
|
|
||||||
QTest::addColumn<ActionTypeId>("actionTypeId");
|
|
||||||
QTest::addColumn<Device::DeviceError>("error");
|
|
||||||
|
|
||||||
QTest::newRow("valid actiontypeid") << mockWithParamsActionTypeId << Device::DeviceErrorNoError;
|
|
||||||
QTest::newRow("invalid actiontypeid") << ActionTypeId::createActionTypeId() << Device::DeviceErrorActionTypeNotFound;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestActions::getActionType()
|
|
||||||
{
|
|
||||||
QFETCH(ActionTypeId, actionTypeId);
|
|
||||||
QFETCH(Device::DeviceError, error);
|
|
||||||
|
|
||||||
QVariantMap params;
|
|
||||||
params.insert("actionTypeId", actionTypeId.toString());
|
|
||||||
QVariant response = injectAndWait("Actions.GetActionType", params);
|
|
||||||
|
|
||||||
verifyError(response, "deviceError", enumValueName(error));
|
|
||||||
|
|
||||||
if (error == Device::DeviceErrorNoError) {
|
|
||||||
QVERIFY2(ActionTypeId(response.toMap().value("params").toMap().value("actionType").toMap().value("id").toString()) == actionTypeId, "Didn't get a reply for the same actionTypeId as requested.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "testactions.moc"
|
|
||||||
QTEST_MAIN(TestActions)
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,10 +1,7 @@
|
|||||||
TEMPLATE = subdirs
|
TEMPLATE = subdirs
|
||||||
|
|
||||||
SUBDIRS = \
|
SUBDIRS = \
|
||||||
actions \
|
|
||||||
configurations \
|
configurations \
|
||||||
devices \
|
|
||||||
events \
|
|
||||||
integrations \
|
integrations \
|
||||||
ioconnections \
|
ioconnections \
|
||||||
jsonrpc \
|
jsonrpc \
|
||||||
@ -16,7 +13,6 @@ SUBDIRS = \
|
|||||||
pythonplugins \
|
pythonplugins \
|
||||||
rules \
|
rules \
|
||||||
scripts \
|
scripts \
|
||||||
states \
|
|
||||||
tags \
|
tags \
|
||||||
timemanager \
|
timemanager \
|
||||||
userloading \
|
userloading \
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
include(../../../nymea.pri)
|
|
||||||
include(../autotests.pri)
|
|
||||||
|
|
||||||
TARGET = testdevices
|
|
||||||
SOURCES += testdevices.cpp
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,5 +0,0 @@
|
|||||||
include(../../../nymea.pri)
|
|
||||||
include(../autotests.pri)
|
|
||||||
|
|
||||||
TARGET = testevents
|
|
||||||
SOURCES += testevents.cpp
|
|
||||||
@ -1,181 +0,0 @@
|
|||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
||||||
*
|
|
||||||
* Copyright 2013 - 2020, nymea GmbH
|
|
||||||
* Contact: contact@nymea.io
|
|
||||||
*
|
|
||||||
* This file is part of nymea.
|
|
||||||
* This project including source code and documentation is protected by
|
|
||||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
|
||||||
* reproduction, publication, editing and translation, are reserved. The use of
|
|
||||||
* this project is subject to the terms of a license agreement to be concluded
|
|
||||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
|
||||||
* under https://nymea.io/license
|
|
||||||
*
|
|
||||||
* GNU General Public License Usage
|
|
||||||
* Alternatively, this project may be redistributed and/or modified under the
|
|
||||||
* terms of the GNU General Public License as published by the Free Software
|
|
||||||
* Foundation, GNU version 3. This project 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
|
|
||||||
* this project. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
* For any further details and any questions please contact us under
|
|
||||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
|
||||||
* https://nymea.io/license/faq
|
|
||||||
*
|
|
||||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
||||||
|
|
||||||
#include "nymeatestbase.h"
|
|
||||||
#include "nymeacore.h"
|
|
||||||
|
|
||||||
#include "jsonrpc/devicehandler.h"
|
|
||||||
#include "servers/mocktcpserver.h"
|
|
||||||
|
|
||||||
using namespace nymeaserver;
|
|
||||||
|
|
||||||
class TestEvents: public NymeaTestBase
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void triggerEvent();
|
|
||||||
void triggerStateChangeEvent();
|
|
||||||
|
|
||||||
void params();
|
|
||||||
|
|
||||||
void getEventType_data();
|
|
||||||
void getEventType();
|
|
||||||
};
|
|
||||||
|
|
||||||
void TestEvents::triggerEvent()
|
|
||||||
{
|
|
||||||
enableNotifications({"Events"});
|
|
||||||
|
|
||||||
QList<Thing*> devices = NymeaCore::instance()->thingManager()->findConfiguredThings(mockThingClassId);
|
|
||||||
QVERIFY2(devices.count() > 0, "There needs to be at least one configured Mock Device for this test");
|
|
||||||
Thing *device = devices.first();
|
|
||||||
|
|
||||||
QSignalSpy spy(NymeaCore::instance(), SIGNAL(eventTriggered(const Event&)));
|
|
||||||
QSignalSpy notificationSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
|
|
||||||
|
|
||||||
// Setup connection to mock client
|
|
||||||
QNetworkAccessManager nam;
|
|
||||||
|
|
||||||
// trigger event in mock device
|
|
||||||
int port = device->paramValue(mockThingHttpportParamTypeId).toInt();
|
|
||||||
QNetworkRequest request(QUrl(QString("http://localhost:%1/generateevent?eventtypeid=%2").arg(port).arg(mockEvent1EventTypeId.toString())));
|
|
||||||
QNetworkReply *reply = nam.get(request);
|
|
||||||
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
|
||||||
|
|
||||||
// Lets wait for the notification
|
|
||||||
spy.wait();
|
|
||||||
QVERIFY(spy.count() > 0);
|
|
||||||
for (int i = 0; i < spy.count(); i++ ){
|
|
||||||
Event event = spy.at(i).at(0).value<Event>();
|
|
||||||
if (event.thingId() == device->id()) {
|
|
||||||
// Make sure the event contains all the stuff we expect
|
|
||||||
QCOMPARE(event.eventTypeId(), mockEvent1EventTypeId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for the notification on JSON API
|
|
||||||
QVariantList notifications;
|
|
||||||
notifications = checkNotifications(notificationSpy, "Events.EventTriggered");
|
|
||||||
QVERIFY2(notifications.count() == 1, "Should get Events.EventTriggered notification");
|
|
||||||
QVERIFY2(notifications.first().toMap().contains("deprecationWarning"), "Deprecation warning not included in notification");
|
|
||||||
|
|
||||||
QVariantMap notificationContent = notifications.first().toMap().value("params").toMap();
|
|
||||||
QCOMPARE(notificationContent.value("event").toMap().value("deviceId").toUuid().toString(), device->id().toString());
|
|
||||||
QCOMPARE(notificationContent.value("event").toMap().value("eventTypeId").toUuid().toString(), mockEvent1EventTypeId.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestEvents::triggerStateChangeEvent()
|
|
||||||
{
|
|
||||||
enableNotifications({"Events"});
|
|
||||||
|
|
||||||
QList<Thing*> devices = NymeaCore::instance()->thingManager()->findConfiguredThings(mockThingClassId);
|
|
||||||
QVERIFY2(devices.count() > 0, "There needs to be at least one configured Mock Device for this test");
|
|
||||||
Thing *device = devices.first();
|
|
||||||
|
|
||||||
QSignalSpy spy(NymeaCore::instance(), SIGNAL(eventTriggered(const Event&)));
|
|
||||||
QSignalSpy notificationSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
|
|
||||||
|
|
||||||
// Setup connection to mock client
|
|
||||||
QNetworkAccessManager nam;
|
|
||||||
|
|
||||||
// trigger state changed event in mock device
|
|
||||||
int port = device->paramValue(mockThingHttpportParamTypeId).toInt();
|
|
||||||
QNetworkRequest request(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(port).arg(mockIntStateTypeId.toString()).arg(11)));
|
|
||||||
QNetworkReply *reply = nam.get(request);
|
|
||||||
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
|
||||||
|
|
||||||
// Lets wait for the notification
|
|
||||||
spy.wait();
|
|
||||||
QVERIFY(spy.count() > 0);
|
|
||||||
for (int i = 0; i < spy.count(); i++ ){
|
|
||||||
Event event = spy.at(i).at(0).value<Event>();
|
|
||||||
if (event.thingId() == device->id()) {
|
|
||||||
// Make sure the event contains all the stuff we expect
|
|
||||||
QCOMPARE(event.eventTypeId().toString(), mockIntStateTypeId.toString());
|
|
||||||
QCOMPARE(event.param(ParamTypeId(mockIntStateTypeId.toString())).value().toInt(), 11);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for the notification on JSON API
|
|
||||||
QVariantList notifications;
|
|
||||||
notifications = checkNotifications(notificationSpy, "Events.EventTriggered");
|
|
||||||
QVERIFY2(notifications.count() == 1, "Should get Devices.EventTriggered notification");
|
|
||||||
QVERIFY2(notifications.first().toMap().contains("deprecationWarning"), "Deprecation warning not included in notification!");
|
|
||||||
|
|
||||||
QVariantMap notificationContent = notifications.first().toMap().value("params").toMap();
|
|
||||||
|
|
||||||
QCOMPARE(notificationContent.value("event").toMap().value("deviceId").toUuid().toString(), device->id().toString());
|
|
||||||
QCOMPARE(notificationContent.value("event").toMap().value("eventTypeId").toUuid().toString(), mockIntEventTypeId.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestEvents::params()
|
|
||||||
{
|
|
||||||
Event event;
|
|
||||||
ParamList params;
|
|
||||||
ParamTypeId id = ParamTypeId::createParamTypeId();
|
|
||||||
Param p(id, "foo bar");
|
|
||||||
params.append(p);
|
|
||||||
event.setParams(params);
|
|
||||||
|
|
||||||
QVERIFY(event.param(id).value().toString() == "foo bar");
|
|
||||||
QVERIFY(!event.param(ParamTypeId::createParamTypeId()).value().isValid());
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestEvents::getEventType_data()
|
|
||||||
{
|
|
||||||
QTest::addColumn<EventTypeId>("eventTypeId");
|
|
||||||
QTest::addColumn<Device::DeviceError>("error");
|
|
||||||
|
|
||||||
QTest::newRow("valid eventypeid") << mockEvent1EventTypeId << Device::DeviceErrorNoError;
|
|
||||||
QTest::newRow("invalid eventypeid") << EventTypeId::createEventTypeId() << Device::DeviceErrorEventTypeNotFound;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestEvents::getEventType()
|
|
||||||
{
|
|
||||||
QFETCH(EventTypeId, eventTypeId);
|
|
||||||
QFETCH(Device::DeviceError, error);
|
|
||||||
|
|
||||||
QVariantMap params;
|
|
||||||
params.insert("eventTypeId", eventTypeId.toString());
|
|
||||||
QVariant response = injectAndWait("Events.GetEventType", params);
|
|
||||||
|
|
||||||
verifyError(response, "deviceError", enumValueName(error));
|
|
||||||
|
|
||||||
qCDebug(dcTests()) << "*content" << response;
|
|
||||||
QVERIFY2(response.toMap().contains("deprecationWarning"), "Deprecation warning not shown in reply");
|
|
||||||
|
|
||||||
if (error == Device::DeviceErrorNoError) {
|
|
||||||
QVERIFY2(EventTypeId(response.toMap().value("params").toMap().value("eventType").toMap().value("id").toString()) == eventTypeId, "Didn't get a reply for the same actionTypeId as requested.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "testevents.moc"
|
|
||||||
QTEST_MAIN(TestEvents)
|
|
||||||
@ -84,6 +84,8 @@ private slots:
|
|||||||
|
|
||||||
void storedThings();
|
void storedThings();
|
||||||
|
|
||||||
|
void stateCache();
|
||||||
|
|
||||||
void discoverThings_data();
|
void discoverThings_data();
|
||||||
void discoverThings();
|
void discoverThings();
|
||||||
|
|
||||||
@ -650,6 +652,74 @@ void TestIntegrations::storedThings()
|
|||||||
verifyThingError(response);
|
verifyThingError(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestIntegrations::stateCache()
|
||||||
|
{
|
||||||
|
ThingClass mockThingClass = NymeaCore::instance()->thingManager()->findThingClass(mockThingClassId);
|
||||||
|
|
||||||
|
QVERIFY2(mockThingClass.getStateType(mockIntStateTypeId).cached(), "Mock int state is not cached (required to be true for this test)");
|
||||||
|
QVERIFY2(!mockThingClass.getStateType(mockBoolStateTypeId).cached(), "Mock bool state is cached (required to be false for this test)");
|
||||||
|
|
||||||
|
Thing* thing = NymeaCore::instance()->thingManager()->findConfiguredThings(mockThingClassId).first();
|
||||||
|
int port = thing->paramValue(mockThingHttpportParamTypeId).toInt();
|
||||||
|
QNetworkAccessManager nam;
|
||||||
|
QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*)));
|
||||||
|
|
||||||
|
|
||||||
|
// First set the state values to something that is *not* the default
|
||||||
|
int oldIntValue = mockThingClass.getStateType(mockIntStateTypeId).defaultValue().toInt();
|
||||||
|
int newIntValue = oldIntValue + 1;
|
||||||
|
bool oldBoolValue = mockThingClass.getStateType(mockBoolStateTypeId).defaultValue().toBool();
|
||||||
|
bool newBoolValue = !oldBoolValue;
|
||||||
|
|
||||||
|
QNetworkRequest request(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(port).arg(mockIntStateTypeId.toString()).arg(newIntValue)));
|
||||||
|
QNetworkReply *reply = nam.get(request);
|
||||||
|
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
||||||
|
spy.wait();
|
||||||
|
|
||||||
|
spy.clear();
|
||||||
|
request = QNetworkRequest(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(port).arg(mockBoolStateTypeId.toString()).arg(newBoolValue)));
|
||||||
|
reply = nam.get(request);
|
||||||
|
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
||||||
|
spy.wait();
|
||||||
|
|
||||||
|
// For completeness, verify through JSONRPC that they were actually yet.
|
||||||
|
QVariantMap params;
|
||||||
|
params.insert("thingId", thing->id());
|
||||||
|
|
||||||
|
params["stateTypeId"] = mockIntStateTypeId;
|
||||||
|
QVariant response = injectAndWait("Integrations.GetStateValue", params);
|
||||||
|
QCOMPARE(response.toMap().value("params").toMap().value("value").toInt(), newIntValue);
|
||||||
|
|
||||||
|
params["stateTypeId"] = mockBoolStateTypeId;
|
||||||
|
response = injectAndWait("Integrations.GetStateValue", params);
|
||||||
|
QCOMPARE(response.toMap().value("params").toMap().value("value").toBool(), newBoolValue);
|
||||||
|
|
||||||
|
// Restart the server
|
||||||
|
restartServer();
|
||||||
|
|
||||||
|
// And check if the cached int state has successfully been restored
|
||||||
|
params["stateTypeId"] = mockIntStateTypeId;
|
||||||
|
response = injectAndWait("Integrations.GetStateValue", params);
|
||||||
|
QCOMPARE(response.toMap().value("params").toMap().value("value").toInt(), newIntValue);
|
||||||
|
|
||||||
|
// and that the non-cached bool state is back to its default
|
||||||
|
params["stateTypeId"] = mockBoolStateTypeId;
|
||||||
|
response = injectAndWait("Integrations.GetStateValue", params);
|
||||||
|
QCOMPARE(response.toMap().value("params").toMap().value("value").toBool(), mockThingClass.getStateType(mockBoolStateTypeId).defaultValue().toBool());
|
||||||
|
|
||||||
|
// Reset back to default values
|
||||||
|
spy.clear();
|
||||||
|
request = QNetworkRequest(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(port).arg(mockBoolStateTypeId.toString()).arg(oldIntValue)));
|
||||||
|
reply = nam.get(request);
|
||||||
|
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
||||||
|
spy.wait();
|
||||||
|
spy.clear();
|
||||||
|
request = QNetworkRequest(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(port).arg(mockBoolStateTypeId.toString()).arg(oldBoolValue)));
|
||||||
|
reply = nam.get(request);
|
||||||
|
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
||||||
|
spy.wait();
|
||||||
|
}
|
||||||
|
|
||||||
void TestIntegrations::discoverThings_data()
|
void TestIntegrations::discoverThings_data()
|
||||||
{
|
{
|
||||||
QTest::addColumn<ThingClassId>("thingClassId");
|
QTest::addColumn<ThingClassId>("thingClassId");
|
||||||
@ -1011,6 +1081,7 @@ void TestIntegrations::getStateValue_data()
|
|||||||
|
|
||||||
void TestIntegrations::getStateValue()
|
void TestIntegrations::getStateValue()
|
||||||
{
|
{
|
||||||
|
NymeaCore::instance()->thingManager()->findConfiguredThing(m_mockThingId)->setStateValue(mockIntStateTypeId, 10);
|
||||||
QFETCH(ThingId, thingId);
|
QFETCH(ThingId, thingId);
|
||||||
QFETCH(StateTypeId, stateTypeId);
|
QFETCH(StateTypeId, stateTypeId);
|
||||||
QFETCH(Thing::ThingError, statusCode);
|
QFETCH(Thing::ThingError, statusCode);
|
||||||
@ -2085,7 +2156,7 @@ void TestIntegrations::triggerStateChangeEvent()
|
|||||||
|
|
||||||
// trigger state changed event in mock device
|
// trigger state changed event in mock device
|
||||||
int port = thing->paramValue(mockThingHttpportParamTypeId).toInt();
|
int port = thing->paramValue(mockThingHttpportParamTypeId).toInt();
|
||||||
QNetworkRequest request(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(port).arg(mockIntStateTypeId.toString()).arg(11)));
|
QNetworkRequest request(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(port).arg(mockIntStateTypeId.toString()).arg(37)));
|
||||||
QNetworkReply *reply = nam.get(request);
|
QNetworkReply *reply = nam.get(request);
|
||||||
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
||||||
|
|
||||||
@ -2097,7 +2168,7 @@ void TestIntegrations::triggerStateChangeEvent()
|
|||||||
if (event.thingId() == thing->id()) {
|
if (event.thingId() == thing->id()) {
|
||||||
// Make sure the event contains all the stuff we expect
|
// Make sure the event contains all the stuff we expect
|
||||||
QCOMPARE(event.eventTypeId().toString(), mockIntStateTypeId.toString());
|
QCOMPARE(event.eventTypeId().toString(), mockIntStateTypeId.toString());
|
||||||
QCOMPARE(event.param(ParamTypeId(mockIntStateTypeId.toString())).value().toInt(), 11);
|
QCOMPARE(event.param(ParamTypeId(mockIntStateTypeId.toString())).value().toInt(), 37);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2107,7 +2178,7 @@ void TestIntegrations::triggerStateChangeEvent()
|
|||||||
QVERIFY2(notifications.count() == 1, "Should get Integrations.EventTriggered notification");
|
QVERIFY2(notifications.count() == 1, "Should get Integrations.EventTriggered notification");
|
||||||
QVariantMap notificationContent = notifications.first().toMap().value("params").toMap();
|
QVariantMap notificationContent = notifications.first().toMap().value("params").toMap();
|
||||||
|
|
||||||
QCOMPARE(notificationContent.value("event").toMap().value("deviceId").toUuid().toString(), thing->id().toString());
|
QCOMPARE(notificationContent.value("event").toMap().value("thingId").toUuid().toString(), thing->id().toString());
|
||||||
QCOMPARE(notificationContent.value("event").toMap().value("eventTypeId").toUuid().toString(), mockIntEventTypeId.toString());
|
QCOMPARE(notificationContent.value("event").toMap().value("eventTypeId").toUuid().toString(), mockIntEventTypeId.toString());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -150,8 +150,7 @@ QStringList TestJSONRPC::extractRefs(const QVariant &variant)
|
|||||||
void TestJSONRPC::initTestCase()
|
void TestJSONRPC::initTestCase()
|
||||||
{
|
{
|
||||||
NymeaDBusService::setBusType(QDBusConnection::SessionBus);
|
NymeaDBusService::setBusType(QDBusConnection::SessionBus);
|
||||||
NymeaTestBase::initTestCase();
|
NymeaTestBase::initTestCase("*.debug=false\n"
|
||||||
QLoggingCategory::setFilterRules("*.debug=false\n"
|
|
||||||
// "JsonRpcTraffic.debug=true\n"
|
// "JsonRpcTraffic.debug=true\n"
|
||||||
"JsonRpc.debug=true\n"
|
"JsonRpc.debug=true\n"
|
||||||
"Translations.debug=true\n"
|
"Translations.debug=true\n"
|
||||||
@ -236,8 +235,8 @@ void TestJSONRPC::testHandshakeLocale()
|
|||||||
|
|
||||||
void TestJSONRPC::testInitialSetup()
|
void TestJSONRPC::testInitialSetup()
|
||||||
{
|
{
|
||||||
foreach (const QString &user, NymeaCore::instance()->userManager()->users()) {
|
foreach (const UserInfo &userInfo, NymeaCore::instance()->userManager()->users()) {
|
||||||
NymeaCore::instance()->userManager()->removeUser(user);
|
NymeaCore::instance()->userManager()->removeUser(userInfo.username());
|
||||||
}
|
}
|
||||||
NymeaCore::instance()->userManager()->removeUser("");
|
NymeaCore::instance()->userManager()->removeUser("");
|
||||||
|
|
||||||
@ -246,7 +245,6 @@ void TestJSONRPC::testInitialSetup()
|
|||||||
|
|
||||||
QSignalSpy spy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
|
QSignalSpy spy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
|
||||||
QVERIFY(spy.isValid());
|
QVERIFY(spy.isValid());
|
||||||
QSignalSpy connectedSpy(m_mockTcpServer, &MockTcpServer::clientConnected);
|
|
||||||
QSignalSpy disconnectedSpy(m_mockTcpServer, &MockTcpServer::clientDisconnected);
|
QSignalSpy disconnectedSpy(m_mockTcpServer, &MockTcpServer::clientDisconnected);
|
||||||
|
|
||||||
// Introspect call should work in any case
|
// Introspect call should work in any case
|
||||||
@ -294,11 +292,10 @@ void TestJSONRPC::testInitialSetup()
|
|||||||
if (disconnectedSpy.count() == 0) disconnectedSpy.wait();
|
if (disconnectedSpy.count() == 0) disconnectedSpy.wait();
|
||||||
QCOMPARE(disconnectedSpy.count(), 1);
|
QCOMPARE(disconnectedSpy.count(), 1);
|
||||||
qCDebug(dcTests()) << "Mock client disconnected";
|
qCDebug(dcTests()) << "Mock client disconnected";
|
||||||
connectedSpy.clear();
|
|
||||||
|
// The connection will be locked down for 3 seconds
|
||||||
|
QTest::qWait(3200);
|
||||||
emit m_mockTcpServer->clientConnected(m_clientId);
|
emit m_mockTcpServer->clientConnected(m_clientId);
|
||||||
if (connectedSpy.count() == 0) connectedSpy.wait();
|
|
||||||
QCOMPARE(connectedSpy.count(), 1);
|
|
||||||
qCDebug(dcTests()) << "Mock client connected";
|
|
||||||
|
|
||||||
spy.clear();
|
spy.clear();
|
||||||
m_mockTcpServer->injectData(m_clientId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}");
|
m_mockTcpServer->injectData(m_clientId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}");
|
||||||
@ -312,7 +309,7 @@ void TestJSONRPC::testInitialSetup()
|
|||||||
// But it should still fail when giving a an invalid username
|
// But it should still fail when giving a an invalid username
|
||||||
spy.clear();
|
spy.clear();
|
||||||
qCDebug(dcTests()) << "Calling CreateUser, expecting failure (bad username)";
|
qCDebug(dcTests()) << "Calling CreateUser, expecting failure (bad username)";
|
||||||
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.CreateUser\", \"params\": {\"username\": \"dummy\", \"password\": \"DummyPW1!\"}}\n");
|
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.CreateUser\", \"params\": {\"username\": \"a\", \"password\": \"DummyPW1!\"}}\n");
|
||||||
if (spy.count() == 0) {
|
if (spy.count() == 0) {
|
||||||
spy.wait();
|
spy.wait();
|
||||||
}
|
}
|
||||||
@ -326,7 +323,7 @@ void TestJSONRPC::testInitialSetup()
|
|||||||
// or when giving a bad password
|
// or when giving a bad password
|
||||||
spy.clear();
|
spy.clear();
|
||||||
qCDebug(dcTests()) << "Calling CreateUser, expecting failure (bad password)";
|
qCDebug(dcTests()) << "Calling CreateUser, expecting failure (bad password)";
|
||||||
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.CreateUser\", \"params\": {\"username\": \"dummy@guh.io\", \"password\": \"weak\"}}\n");
|
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.CreateUser\", \"params\": {\"username\": \"nymea\", \"password\": \"weak\"}}\n");
|
||||||
if (spy.count() == 0) {
|
if (spy.count() == 0) {
|
||||||
spy.wait();
|
spy.wait();
|
||||||
}
|
}
|
||||||
@ -382,12 +379,10 @@ void TestJSONRPC::testInitialSetup()
|
|||||||
// Connection should terminate
|
// Connection should terminate
|
||||||
if (disconnectedSpy.count() == 0) disconnectedSpy.wait();
|
if (disconnectedSpy.count() == 0) disconnectedSpy.wait();
|
||||||
QCOMPARE(disconnectedSpy.count(), 1);
|
QCOMPARE(disconnectedSpy.count(), 1);
|
||||||
qCDebug(dcTests()) << "Mock client disconnected";
|
|
||||||
connectedSpy.clear();
|
// The connection will be locked down for 3 secs
|
||||||
|
QTest::qWait(3200);
|
||||||
emit m_mockTcpServer->clientConnected(m_clientId);
|
emit m_mockTcpServer->clientConnected(m_clientId);
|
||||||
if (connectedSpy.count() == 0) connectedSpy.wait();
|
|
||||||
QCOMPARE(connectedSpy.count(), 1);
|
|
||||||
qCDebug(dcTests()) << "Mock client connected";
|
|
||||||
|
|
||||||
spy.clear();
|
spy.clear();
|
||||||
m_mockTcpServer->injectData(m_clientId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}");
|
m_mockTcpServer->injectData(m_clientId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}");
|
||||||
@ -415,6 +410,7 @@ void TestJSONRPC::testInitialSetup()
|
|||||||
|
|
||||||
// Now lets authenticate with a wrong password
|
// Now lets authenticate with a wrong password
|
||||||
spy.clear();
|
spy.clear();
|
||||||
|
disconnectedSpy.clear();
|
||||||
qCDebug(dcTests()) << "Calling Authenticate, expecting failure (bad password)";
|
qCDebug(dcTests()) << "Calling Authenticate, expecting failure (bad password)";
|
||||||
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Authenticate\", \"params\": {\"username\": \"Dummy@guh.io\", \"password\": \"wrongpw\", \"deviceName\": \"testcase\"}}\n");
|
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Authenticate\", \"params\": {\"username\": \"Dummy@guh.io\", \"password\": \"wrongpw\", \"deviceName\": \"testcase\"}}\n");
|
||||||
if (spy.count() == 0) {
|
if (spy.count() == 0) {
|
||||||
@ -423,11 +419,24 @@ void TestJSONRPC::testInitialSetup()
|
|||||||
QVERIFY(spy.count() == 1);
|
QVERIFY(spy.count() == 1);
|
||||||
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
|
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
|
||||||
response = jsonDoc.toVariant().toMap();
|
response = jsonDoc.toVariant().toMap();
|
||||||
qCDebug(dcTests()) << "Calling Authenticate with wrong password:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString();
|
|
||||||
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
|
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
|
||||||
QCOMPARE(response.value("params").toMap().value("success").toBool(), false);
|
QCOMPARE(response.value("params").toMap().value("success").toBool(), false);
|
||||||
QVERIFY(response.value("params").toMap().value("token").toByteArray().isEmpty());
|
QVERIFY(response.value("params").toMap().value("token").toByteArray().isEmpty());
|
||||||
|
|
||||||
|
// Connection should terminate
|
||||||
|
if (disconnectedSpy.count() == 0) disconnectedSpy.wait();
|
||||||
|
QCOMPARE(disconnectedSpy.count(), 1);
|
||||||
|
|
||||||
|
// The connection will be locked down for 3 secs
|
||||||
|
QTest::qWait(3200);
|
||||||
|
emit m_mockTcpServer->clientConnected(m_clientId);
|
||||||
|
|
||||||
|
spy.clear();
|
||||||
|
m_mockTcpServer->injectData(m_clientId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}");
|
||||||
|
if (spy.count() == 0) {
|
||||||
|
spy.wait();
|
||||||
|
}
|
||||||
|
QVERIFY(spy.count() == 1);
|
||||||
|
|
||||||
// Now lets authenticate for real (but intentionally use a lowercase email here, should still work)
|
// Now lets authenticate for real (but intentionally use a lowercase email here, should still work)
|
||||||
spy.clear();
|
spy.clear();
|
||||||
@ -466,19 +475,17 @@ void TestJSONRPC::testRevokeToken()
|
|||||||
QVERIFY(spy.isValid());
|
QVERIFY(spy.isValid());
|
||||||
QSignalSpy disconnectedSpy(m_mockTcpServer, &MockTcpServer::clientDisconnected);
|
QSignalSpy disconnectedSpy(m_mockTcpServer, &MockTcpServer::clientDisconnected);
|
||||||
QVERIFY(disconnectedSpy.isValid());
|
QVERIFY(disconnectedSpy.isValid());
|
||||||
QSignalSpy connectedSpy(m_mockTcpServer, &MockTcpServer::clientConnected);
|
|
||||||
QVERIFY(connectedSpy.isValid());
|
|
||||||
|
|
||||||
// Now get all the tokens
|
// Now get all the tokens
|
||||||
spy.clear();
|
spy.clear();
|
||||||
m_mockTcpServer->injectData(m_clientId, "{\"id\": 123, \"token\": \"" + m_apiToken + "\", \"method\": \"JSONRPC.Tokens\"}\n");
|
qCDebug(dcTests()) << "Getting existing Tokens";
|
||||||
|
m_mockTcpServer->injectData(m_clientId, "{\"id\": 123, \"token\": \"" + m_apiToken + "\", \"method\": \"Users.GetTokens\"}\n");
|
||||||
if (spy.count() == 0) {
|
if (spy.count() == 0) {
|
||||||
spy.wait();
|
spy.wait();
|
||||||
}
|
}
|
||||||
QVERIFY(spy.count() == 1);
|
QVERIFY(spy.count() == 1);
|
||||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
|
||||||
QVariantMap response = jsonDoc.toVariant().toMap();
|
QVariantMap response = jsonDoc.toVariant().toMap();
|
||||||
qCDebug(dcTests()) << "Getting existing Tokens" << response.value("status").toString() << response;
|
|
||||||
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
|
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
|
||||||
QVariantList tokenList = response.value("params").toMap().value("tokenInfoList").toList();
|
QVariantList tokenList = response.value("params").toMap().value("tokenInfoList").toList();
|
||||||
QCOMPARE(tokenList.count(), 1);
|
QCOMPARE(tokenList.count(), 1);
|
||||||
@ -486,6 +493,7 @@ void TestJSONRPC::testRevokeToken()
|
|||||||
|
|
||||||
// Authenticate and create a new token
|
// Authenticate and create a new token
|
||||||
spy.clear();
|
spy.clear();
|
||||||
|
qCDebug(dcTests()) << "Calling Authenticate with valid credentials" ;
|
||||||
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Authenticate\", \"params\": {\"username\": \"dummy@guh.io\", \"password\": \"DummyPW1!\", \"deviceName\": \"testcase\"}}\n");
|
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Authenticate\", \"params\": {\"username\": \"dummy@guh.io\", \"password\": \"DummyPW1!\", \"deviceName\": \"testcase\"}}\n");
|
||||||
if (spy.count() == 0) {
|
if (spy.count() == 0) {
|
||||||
spy.wait();
|
spy.wait();
|
||||||
@ -493,7 +501,6 @@ void TestJSONRPC::testRevokeToken()
|
|||||||
QVERIFY(spy.count() == 1);
|
QVERIFY(spy.count() == 1);
|
||||||
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
|
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
|
||||||
response = jsonDoc.toVariant().toMap();
|
response = jsonDoc.toVariant().toMap();
|
||||||
qCDebug(dcTests()) << "Calling Authenticate with valid credentials:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString();
|
|
||||||
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
|
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
|
||||||
QCOMPARE(response.value("params").toMap().value("success").toBool(), true);
|
QCOMPARE(response.value("params").toMap().value("success").toBool(), true);
|
||||||
QByteArray newToken = response.value("params").toMap().value("token").toByteArray();
|
QByteArray newToken = response.value("params").toMap().value("token").toByteArray();
|
||||||
@ -513,14 +520,14 @@ void TestJSONRPC::testRevokeToken()
|
|||||||
|
|
||||||
// Now get all the tokens using the old token
|
// Now get all the tokens using the old token
|
||||||
spy.clear();
|
spy.clear();
|
||||||
m_mockTcpServer->injectData(m_clientId, "{\"id\": 123, \"token\": \"" + m_apiToken + "\", \"method\": \"JSONRPC.Tokens\"}\n");
|
qCDebug(dcTests()) << "Calling Tokens";
|
||||||
|
m_mockTcpServer->injectData(m_clientId, "{\"id\": 123, \"token\": \"" + m_apiToken + "\", \"method\": \"Users.GetTokens\"}\n");
|
||||||
if (spy.count() == 0) {
|
if (spy.count() == 0) {
|
||||||
spy.wait();
|
spy.wait();
|
||||||
}
|
}
|
||||||
QVERIFY(spy.count() == 1);
|
QVERIFY(spy.count() == 1);
|
||||||
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
|
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
|
||||||
response = jsonDoc.toVariant().toMap();
|
response = jsonDoc.toVariant().toMap();
|
||||||
qCDebug(dcTests()) << "Calling Tokens" << response.value("status").toString();
|
|
||||||
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
|
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
|
||||||
tokenList = response.value("params").toMap().value("tokenInfoList").toList();
|
tokenList = response.value("params").toMap().value("tokenInfoList").toList();
|
||||||
QCOMPARE(tokenList.count(), 2);
|
QCOMPARE(tokenList.count(), 2);
|
||||||
@ -536,19 +543,20 @@ void TestJSONRPC::testRevokeToken()
|
|||||||
|
|
||||||
// Revoke the new token
|
// Revoke the new token
|
||||||
spy.clear();
|
spy.clear();
|
||||||
m_mockTcpServer->injectData(m_clientId, "{\"id\": 123, \"token\": \"" + m_apiToken + "\", \"method\": \"JSONRPC.RemoveToken\", \"params\": {\"tokenId\": \"" + newTokenId.toByteArray() + "\"}}\n");
|
qCDebug(dcTests()) << "Calling RemoveToken";
|
||||||
|
m_mockTcpServer->injectData(m_clientId, "{\"id\": 123, \"token\": \"" + m_apiToken + "\", \"method\": \"Users.RemoveToken\", \"params\": {\"tokenId\": \"" + newTokenId.toByteArray() + "\"}}\n");
|
||||||
if (spy.count() == 0) {
|
if (spy.count() == 0) {
|
||||||
spy.wait();
|
spy.wait();
|
||||||
}
|
}
|
||||||
QVERIFY(spy.count() == 1);
|
QVERIFY(spy.count() == 1);
|
||||||
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
|
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
|
||||||
response = jsonDoc.toVariant().toMap();
|
response = jsonDoc.toVariant().toMap();
|
||||||
qCDebug(dcTests()) << "Calling RemoveToken" << response.value("status").toString() << response;
|
|
||||||
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
|
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
|
||||||
|
|
||||||
// Do a call with the now removed token, it should be forbidden
|
// Do a call with the now removed token, it should be forbidden
|
||||||
spy.clear();
|
spy.clear();
|
||||||
disconnectedSpy.clear();
|
disconnectedSpy.clear();
|
||||||
|
qCDebug(dcTests()) << "Calling Version with now removed token";
|
||||||
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"token\": \"" + newToken + "\", \"method\": \"JSONRPC.Version\"}\n");
|
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"token\": \"" + newToken + "\", \"method\": \"JSONRPC.Version\"}\n");
|
||||||
if (spy.count() == 0) {
|
if (spy.count() == 0) {
|
||||||
spy.wait();
|
spy.wait();
|
||||||
@ -556,18 +564,16 @@ void TestJSONRPC::testRevokeToken()
|
|||||||
QVERIFY(spy.count() == 1);
|
QVERIFY(spy.count() == 1);
|
||||||
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
|
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
|
||||||
response = jsonDoc.toVariant().toMap();
|
response = jsonDoc.toVariant().toMap();
|
||||||
qCDebug(dcTests()) << "Calling Version with valid token:" << response.value("status").toString() << response.value("error").toString();
|
|
||||||
QCOMPARE(response.value("status").toString(), QStringLiteral("unauthorized"));
|
QCOMPARE(response.value("status").toString(), QStringLiteral("unauthorized"));
|
||||||
|
|
||||||
// And connection should drop
|
// And connection should drop
|
||||||
if (disconnectedSpy.count() == 0) disconnectedSpy.wait();
|
if (disconnectedSpy.count() == 0) disconnectedSpy.wait();
|
||||||
QCOMPARE(disconnectedSpy.count(), 1);
|
QCOMPARE(disconnectedSpy.count(), 1);
|
||||||
|
|
||||||
|
QTest::qWait(3200);
|
||||||
|
|
||||||
// Connect again to not impact subsequent tests...
|
// Connect again to not impact subsequent tests...
|
||||||
connectedSpy.clear();
|
|
||||||
emit m_mockTcpServer->clientConnected(m_clientId);
|
emit m_mockTcpServer->clientConnected(m_clientId);
|
||||||
if (connectedSpy.count() == 0) connectedSpy.wait();
|
|
||||||
QCOMPARE(connectedSpy.count(), 1);
|
|
||||||
injectAndWait("JSONRPC.Hello");
|
injectAndWait("JSONRPC.Hello");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -674,7 +680,7 @@ void TestJSONRPC::enableDisableNotifications_legacy()
|
|||||||
|
|
||||||
QStringList expectedNamespaces;
|
QStringList expectedNamespaces;
|
||||||
if (enabled == "true") {
|
if (enabled == "true") {
|
||||||
expectedNamespaces << "Actions" << "NetworkManager" << "Devices" << "Integrations" << "System" << "Rules" << "States" << "Logging" << "Tags" << "AppData" << "JSONRPC" << "Configuration" << "Events" << "Scripts" << "Users" << "Zigbee" << "ModbusRtu";
|
expectedNamespaces << "NetworkManager" << "Integrations" << "System" << "Rules"<< "Logging" << "Tags" << "AppData" << "JSONRPC" << "Configuration" << "Scripts" << "Users" << "Zigbee" << "ModbusRtu";
|
||||||
}
|
}
|
||||||
std::sort(expectedNamespaces.begin(), expectedNamespaces.end());
|
std::sort(expectedNamespaces.begin(), expectedNamespaces.end());
|
||||||
|
|
||||||
@ -700,7 +706,6 @@ void TestJSONRPC::ruleAddedRemovedNotifications()
|
|||||||
QVariantMap stateDescriptor;
|
QVariantMap stateDescriptor;
|
||||||
stateDescriptor.insert("stateTypeId", mockIntStateTypeId);
|
stateDescriptor.insert("stateTypeId", mockIntStateTypeId);
|
||||||
stateDescriptor.insert("thingId", m_mockThingId);
|
stateDescriptor.insert("thingId", m_mockThingId);
|
||||||
stateDescriptor.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
stateDescriptor.insert("operator", enumValueName(Types::ValueOperatorLess));
|
stateDescriptor.insert("operator", enumValueName(Types::ValueOperatorLess));
|
||||||
stateDescriptor.insert("value", "20");
|
stateDescriptor.insert("value", "20");
|
||||||
// This is a bit odd: QUuid.toString() wraps the uuids in {}, however, the implicit cast doesn't
|
// This is a bit odd: QUuid.toString() wraps the uuids in {}, however, the implicit cast doesn't
|
||||||
@ -715,7 +720,6 @@ void TestJSONRPC::ruleAddedRemovedNotifications()
|
|||||||
QVariantMap actionNoParams;
|
QVariantMap actionNoParams;
|
||||||
actionNoParams.insert("actionTypeId", mockWithoutParamsActionTypeId);
|
actionNoParams.insert("actionTypeId", mockWithoutParamsActionTypeId);
|
||||||
actionNoParams.insert("thingId", m_mockThingId);
|
actionNoParams.insert("thingId", m_mockThingId);
|
||||||
actionNoParams.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
// This is a bit odd: QUuid.toString() wraps the uuids in {}, however, the implicit cast doesn't
|
// This is a bit odd: QUuid.toString() wraps the uuids in {}, however, the implicit cast doesn't
|
||||||
// .toString(QUuid::WithoutBraces) has only been added in 5.11 so we can't use that either...
|
// .toString(QUuid::WithoutBraces) has only been added in 5.11 so we can't use that either...
|
||||||
// Only hack I can come up with right now is to convert it to a Json and back to use the implicit cast
|
// Only hack I can come up with right now is to convert it to a Json and back to use the implicit cast
|
||||||
@ -725,7 +729,6 @@ void TestJSONRPC::ruleAddedRemovedNotifications()
|
|||||||
QVariantMap eventDescriptor;
|
QVariantMap eventDescriptor;
|
||||||
eventDescriptor.insert("eventTypeId", mockEvent1EventTypeId);
|
eventDescriptor.insert("eventTypeId", mockEvent1EventTypeId);
|
||||||
eventDescriptor.insert("thingId", m_mockThingId);
|
eventDescriptor.insert("thingId", m_mockThingId);
|
||||||
eventDescriptor.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
// This is a bit odd: QUuid.toString() wraps the uuids in {}, however, the implicit cast doesn't
|
// This is a bit odd: QUuid.toString() wraps the uuids in {}, however, the implicit cast doesn't
|
||||||
// .toString(QUuid::WithoutBraces) has only been added in 5.11 so we can't use that either...
|
// .toString(QUuid::WithoutBraces) has only been added in 5.11 so we can't use that either...
|
||||||
// Only hack I can come up with right now is to convert it to a Json and back to use the implicit cast
|
// Only hack I can come up with right now is to convert it to a Json and back to use the implicit cast
|
||||||
@ -789,7 +792,6 @@ void TestJSONRPC::ruleActiveChangedNotifications()
|
|||||||
QVariantMap stateDescriptor;
|
QVariantMap stateDescriptor;
|
||||||
stateDescriptor.insert("stateTypeId", mockIntStateTypeId);
|
stateDescriptor.insert("stateTypeId", mockIntStateTypeId);
|
||||||
stateDescriptor.insert("thingId", m_mockThingId);
|
stateDescriptor.insert("thingId", m_mockThingId);
|
||||||
stateDescriptor.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
stateDescriptor.insert("operator", enumValueName(Types::ValueOperatorEquals));
|
stateDescriptor.insert("operator", enumValueName(Types::ValueOperatorEquals));
|
||||||
stateDescriptor.insert("value", "20");
|
stateDescriptor.insert("value", "20");
|
||||||
// This is a bit odd: QUuid.toString() wraps the uuids in {}, however, the implicit cast doesn't
|
// This is a bit odd: QUuid.toString() wraps the uuids in {}, however, the implicit cast doesn't
|
||||||
@ -804,7 +806,6 @@ void TestJSONRPC::ruleActiveChangedNotifications()
|
|||||||
QVariantMap actionNoParams;
|
QVariantMap actionNoParams;
|
||||||
actionNoParams.insert("actionTypeId", mockWithoutParamsActionTypeId);
|
actionNoParams.insert("actionTypeId", mockWithoutParamsActionTypeId);
|
||||||
actionNoParams.insert("thingId", m_mockThingId);
|
actionNoParams.insert("thingId", m_mockThingId);
|
||||||
actionNoParams.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
// This is a bit odd: QUuid.toString() wraps the uuids in {}, however, the implicit cast doesn't
|
// This is a bit odd: QUuid.toString() wraps the uuids in {}, however, the implicit cast doesn't
|
||||||
// .toString(QUuid::WithoutBraces) has only been added in 5.11 so we can't use that either...
|
// .toString(QUuid::WithoutBraces) has only been added in 5.11 so we can't use that either...
|
||||||
// Only hack I can come up with right now is to convert it to a Json and back to use the implicit cast
|
// Only hack I can come up with right now is to convert it to a Json and back to use the implicit cast
|
||||||
@ -855,21 +856,18 @@ void TestJSONRPC::ruleActiveChangedNotifications()
|
|||||||
spy.clear(); clientSpy.clear();
|
spy.clear(); clientSpy.clear();
|
||||||
|
|
||||||
// set the rule inactive
|
// set the rule inactive
|
||||||
qDebug() << "setting mock int state to 42";
|
qCDebug(dcTests) << "setting mock int state to 42";
|
||||||
QNetworkRequest request2(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockThing1Port).arg(mockIntStateTypeId.toString()).arg(42)));
|
QNetworkRequest request2(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockThing1Port).arg(mockIntStateTypeId.toString()).arg(42)));
|
||||||
QNetworkReply *reply2 = nam.get(request2);
|
QNetworkReply *reply2 = nam.get(request2);
|
||||||
connect(reply2, SIGNAL(finished()), reply2, SLOT(deleteLater()));
|
connect(reply2, SIGNAL(finished()), reply2, SLOT(deleteLater()));
|
||||||
|
|
||||||
// Waiting for notifications:
|
// Waiting for notifications:
|
||||||
// Devices.StateChanged for the change we did
|
// Integrations.StateChanged for the change we did
|
||||||
// Devices.EventTriggered
|
// Integrations.EventTriggered
|
||||||
// Events.EventTriggered <-- deprecated
|
|
||||||
// Rules.RuleActiveChanged
|
// Rules.RuleActiveChanged
|
||||||
// Logging.LogEntryAdded
|
// Logging.LogEntryAdded // One for the state change we did
|
||||||
// Devices.StateChanged for the change done by the rule
|
// Logging.LogEntryAdded // One for the rule state change
|
||||||
// Devices.EventTriggered
|
while (clientSpy.count() < 5) {
|
||||||
// Events.EventTriggered <-- deprecated
|
|
||||||
while (clientSpy.count() < 8) {
|
|
||||||
clientSpy.wait();
|
clientSpy.wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -894,7 +892,7 @@ void TestJSONRPC::ruleActiveChangedNotifications()
|
|||||||
|
|
||||||
void TestJSONRPC::stateChangeEmitsNotifications()
|
void TestJSONRPC::stateChangeEmitsNotifications()
|
||||||
{
|
{
|
||||||
enableNotifications({"Devices", "States", "Logging", "Events"});
|
enableNotifications({"Integrations", "Logging"});
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
||||||
// Setup connection to mock client
|
// Setup connection to mock client
|
||||||
@ -911,9 +909,9 @@ void TestJSONRPC::stateChangeEmitsNotifications()
|
|||||||
if (replySpy.count() == 0) replySpy.wait();
|
if (replySpy.count() == 0) replySpy.wait();
|
||||||
|
|
||||||
// Make sure the notification contains all the stuff we expect
|
// Make sure the notification contains all the stuff we expect
|
||||||
QVariantList stateChangedVariants = checkNotifications(clientSpy, "Devices.StateChanged");
|
QVariantList stateChangedVariants = checkNotifications(clientSpy, "Integrations.StateChanged");
|
||||||
QVERIFY2(!stateChangedVariants.isEmpty(), "Did not get Devices.StateChanged notification.");
|
QVERIFY2(!stateChangedVariants.isEmpty(), "Did not get Integrations.StateChanged notification.");
|
||||||
qDebug() << "got" << stateChangedVariants.count() << "Devices.StateChanged notifications";
|
qDebug() << "got" << stateChangedVariants.count() << "Integrations.StateChanged notifications";
|
||||||
foreach (const QVariant &stateChangedVariant, stateChangedVariants) {
|
foreach (const QVariant &stateChangedVariant, stateChangedVariants) {
|
||||||
if (stateChangedVariant.toMap().value("params").toMap().value("stateTypeId").toUuid() == stateTypeId) {
|
if (stateChangedVariant.toMap().value("params").toMap().value("stateTypeId").toUuid() == stateTypeId) {
|
||||||
found = true;
|
found = true;
|
||||||
@ -933,8 +931,8 @@ void TestJSONRPC::stateChangeEmitsNotifications()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the notification contains all the stuff we expect
|
// Make sure the notification contains all the stuff we expect
|
||||||
QVariantList eventTriggeredVariants = checkNotifications(clientSpy, "Events.EventTriggered");
|
QVariantList eventTriggeredVariants = checkNotifications(clientSpy, "Integrations.EventTriggered");
|
||||||
QVERIFY2(!eventTriggeredVariants.isEmpty(), "Did not get Events.EventTriggered notification.");
|
QVERIFY2(!eventTriggeredVariants.isEmpty(), "Did not get Integrations.EventTriggered notification.");
|
||||||
found = false;
|
found = false;
|
||||||
foreach (const QVariant &eventTriggeredVariant, eventTriggeredVariants) {
|
foreach (const QVariant &eventTriggeredVariant, eventTriggeredVariants) {
|
||||||
if (eventTriggeredVariant.toMap().value("params").toMap().value("event").toMap().value("eventTypeId").toUuid() == stateTypeId) {
|
if (eventTriggeredVariant.toMap().value("params").toMap().value("event").toMap().value("eventTypeId").toUuid() == stateTypeId) {
|
||||||
@ -944,7 +942,7 @@ void TestJSONRPC::stateChangeEmitsNotifications()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QVERIFY2(found, "Could not find the corresponding Events.EventTriggered notification");
|
QVERIFY2(found, "Could not find the corresponding Integrations.EventTriggered notification");
|
||||||
|
|
||||||
// Now turn off notifications
|
// Now turn off notifications
|
||||||
QCOMPARE(disableNotifications(), true);
|
QCOMPARE(disableNotifications(), true);
|
||||||
@ -1185,8 +1183,8 @@ void TestJSONRPC::testPushButtonAuthConnectionDrop()
|
|||||||
|
|
||||||
void TestJSONRPC::testInitialSetupWithPushButtonAuth()
|
void TestJSONRPC::testInitialSetupWithPushButtonAuth()
|
||||||
{
|
{
|
||||||
foreach (const QString &user, NymeaCore::instance()->userManager()->users()) {
|
foreach (const UserInfo &userInfo, NymeaCore::instance()->userManager()->users()) {
|
||||||
NymeaCore::instance()->userManager()->removeUser(user);
|
NymeaCore::instance()->userManager()->removeUser(userInfo.username());
|
||||||
}
|
}
|
||||||
NymeaCore::instance()->userManager()->removeUser("");
|
NymeaCore::instance()->userManager()->removeUser("");
|
||||||
QVERIFY(NymeaCore::instance()->userManager()->initRequired());
|
QVERIFY(NymeaCore::instance()->userManager()->initRequired());
|
||||||
@ -1256,11 +1254,11 @@ void TestJSONRPC::testInitialSetupWithPushButtonAuth()
|
|||||||
QCOMPARE(response.toMap().value("params").toMap().value("initialSetupRequired").toBool(), false);
|
QCOMPARE(response.toMap().value("params").toMap().value("initialSetupRequired").toBool(), false);
|
||||||
|
|
||||||
|
|
||||||
// CreateUser without a token should fail now even though there are 0 users in the DB
|
// CreateUser without a token should fail now that there is the push button generated user
|
||||||
spy.clear();
|
spy.clear();
|
||||||
QSignalSpy disconnectedSpy(m_mockTcpServer, &MockTcpServer::clientDisconnected);
|
QSignalSpy disconnectedSpy(m_mockTcpServer, &MockTcpServer::clientDisconnected);
|
||||||
qCDebug(dcTests()) << "Calling CreateUser on uninitialized instance";
|
qCDebug(dcTests()) << "Calling CreateUser on pushbutton initialized instance";
|
||||||
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.CreateUser\", \"params\": {\"username\": \"Dummy@guh.io\", \"password\": \"DummyPW1!\"}}\n");
|
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.CreateUser\", \"params\": {\"username\": \"nymea\", \"password\": \"DummyPW1!\"}}\n");
|
||||||
if (spy.count() == 0) {
|
if (spy.count() == 0) {
|
||||||
spy.wait();
|
spy.wait();
|
||||||
}
|
}
|
||||||
@ -1269,12 +1267,14 @@ void TestJSONRPC::testInitialSetupWithPushButtonAuth()
|
|||||||
response = jsonDoc.toVariant();
|
response = jsonDoc.toVariant();
|
||||||
qCDebug(dcTests()) << "Result:" << response.toMap().value("status").toString() << response.toMap().value("error").toString();
|
qCDebug(dcTests()) << "Result:" << response.toMap().value("status").toString() << response.toMap().value("error").toString();
|
||||||
QCOMPARE(response.toMap().value("status").toString(), QStringLiteral("unauthorized"));
|
QCOMPARE(response.toMap().value("status").toString(), QStringLiteral("unauthorized"));
|
||||||
QCOMPARE(NymeaCore::instance()->userManager()->users().count(), 0);
|
QCOMPARE(NymeaCore::instance()->userManager()->users().count(), 1);
|
||||||
|
|
||||||
// Connection should drop
|
// Connection should drop
|
||||||
if (disconnectedSpy.isEmpty()) disconnectedSpy.wait();
|
if (disconnectedSpy.isEmpty()) disconnectedSpy.wait();
|
||||||
QCOMPARE(disconnectedSpy.count(), 1);
|
QCOMPARE(disconnectedSpy.count(), 1);
|
||||||
|
|
||||||
|
QTest::qWait(3200);
|
||||||
|
|
||||||
// Reconnect to not impact subsequent tests
|
// Reconnect to not impact subsequent tests
|
||||||
m_mockTcpServer->clientConnected(m_clientId);
|
m_mockTcpServer->clientConnected(m_clientId);
|
||||||
spy.clear();
|
spy.clear();
|
||||||
|
|||||||
@ -300,7 +300,7 @@ void TestLogging::eventLogs()
|
|||||||
|
|
||||||
// Now snoop in for the events
|
// Now snoop in for the events
|
||||||
clearLoggingDatabase();
|
clearLoggingDatabase();
|
||||||
enableNotifications({"Events", "Logging"});
|
enableNotifications({"Integrations", "Logging"});
|
||||||
QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
|
QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
|
||||||
|
|
||||||
// trigger state change in mock device
|
// trigger state change in mock device
|
||||||
|
|||||||
@ -422,7 +422,6 @@ void TestRules::addRemoveRules_data()
|
|||||||
QVariantMap validActionNoParams;
|
QVariantMap validActionNoParams;
|
||||||
validActionNoParams.insert("actionTypeId", mockWithoutParamsActionTypeId);
|
validActionNoParams.insert("actionTypeId", mockWithoutParamsActionTypeId);
|
||||||
validActionNoParams.insert("thingId", m_mockThingId);
|
validActionNoParams.insert("thingId", m_mockThingId);
|
||||||
validActionNoParams.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
|
|
||||||
QVariantMap invalidAction;
|
QVariantMap invalidAction;
|
||||||
invalidAction.insert("actionTypeId", ActionTypeId("f32c7efb-38b6-4576-a496-c75bbb23132f"));
|
invalidAction.insert("actionTypeId", ActionTypeId("f32c7efb-38b6-4576-a496-c75bbb23132f"));
|
||||||
@ -432,7 +431,6 @@ void TestRules::addRemoveRules_data()
|
|||||||
QVariantMap validExitActionNoParams;
|
QVariantMap validExitActionNoParams;
|
||||||
validExitActionNoParams.insert("actionTypeId", mockWithoutParamsActionTypeId);
|
validExitActionNoParams.insert("actionTypeId", mockWithoutParamsActionTypeId);
|
||||||
validExitActionNoParams.insert("thingId", m_mockThingId);
|
validExitActionNoParams.insert("thingId", m_mockThingId);
|
||||||
validExitActionNoParams.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
|
|
||||||
QVariantMap invalidExitAction;
|
QVariantMap invalidExitAction;
|
||||||
invalidExitAction.insert("actionTypeId", ActionTypeId("f32c7efb-38b6-4576-a496-c75bbb23132f"));
|
invalidExitAction.insert("actionTypeId", ActionTypeId("f32c7efb-38b6-4576-a496-c75bbb23132f"));
|
||||||
@ -458,12 +456,10 @@ void TestRules::addRemoveRules_data()
|
|||||||
QVariantMap validEventDescriptor1;
|
QVariantMap validEventDescriptor1;
|
||||||
validEventDescriptor1.insert("eventTypeId", mockEvent1EventTypeId);
|
validEventDescriptor1.insert("eventTypeId", mockEvent1EventTypeId);
|
||||||
validEventDescriptor1.insert("thingId", m_mockThingId);
|
validEventDescriptor1.insert("thingId", m_mockThingId);
|
||||||
validEventDescriptor1.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
|
|
||||||
QVariantMap validEventDescriptor2;
|
QVariantMap validEventDescriptor2;
|
||||||
validEventDescriptor2.insert("eventTypeId", mockEvent2EventTypeId);
|
validEventDescriptor2.insert("eventTypeId", mockEvent2EventTypeId);
|
||||||
validEventDescriptor2.insert("thingId", m_mockThingId);
|
validEventDescriptor2.insert("thingId", m_mockThingId);
|
||||||
validEventDescriptor2.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
|
|
||||||
QVariantList params;
|
QVariantList params;
|
||||||
QVariantMap param1;
|
QVariantMap param1;
|
||||||
@ -476,7 +472,6 @@ void TestRules::addRemoveRules_data()
|
|||||||
QVariantMap validEventDescriptor3;
|
QVariantMap validEventDescriptor3;
|
||||||
validEventDescriptor3.insert("eventTypeId", mockEvent2EventTypeId);
|
validEventDescriptor3.insert("eventTypeId", mockEvent2EventTypeId);
|
||||||
validEventDescriptor3.insert("thingId", m_mockThingId);
|
validEventDescriptor3.insert("thingId", m_mockThingId);
|
||||||
validEventDescriptor3.insert("deviceId", m_mockThingId); // DREPECATED
|
|
||||||
|
|
||||||
// EventDescriptorList
|
// EventDescriptorList
|
||||||
QVariantList eventDescriptorList;
|
QVariantList eventDescriptorList;
|
||||||
@ -491,7 +486,6 @@ void TestRules::addRemoveRules_data()
|
|||||||
QVariantMap validActionEventBased;
|
QVariantMap validActionEventBased;
|
||||||
validActionEventBased.insert("actionTypeId", mockWithParamsActionTypeId);
|
validActionEventBased.insert("actionTypeId", mockWithParamsActionTypeId);
|
||||||
validActionEventBased.insert("thingId", m_mockThingId);
|
validActionEventBased.insert("thingId", m_mockThingId);
|
||||||
validActionEventBased.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
QVariantMap validActionEventBasedParam1;
|
QVariantMap validActionEventBasedParam1;
|
||||||
validActionEventBasedParam1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId);
|
validActionEventBasedParam1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId);
|
||||||
validActionEventBasedParam1.insert("eventTypeId", mockEvent2EventTypeId);
|
validActionEventBasedParam1.insert("eventTypeId", mockEvent2EventTypeId);
|
||||||
@ -675,7 +669,6 @@ void TestRules::editRules_data()
|
|||||||
QVariantMap validActionNoParams;
|
QVariantMap validActionNoParams;
|
||||||
validActionNoParams.insert("actionTypeId", mockWithoutParamsActionTypeId);
|
validActionNoParams.insert("actionTypeId", mockWithoutParamsActionTypeId);
|
||||||
validActionNoParams.insert("thingId", m_mockThingId);
|
validActionNoParams.insert("thingId", m_mockThingId);
|
||||||
validActionNoParams.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
|
|
||||||
QVariantMap invalidAction;
|
QVariantMap invalidAction;
|
||||||
invalidAction.insert("actionTypeId", ActionTypeId());
|
invalidAction.insert("actionTypeId", ActionTypeId());
|
||||||
@ -685,7 +678,6 @@ void TestRules::editRules_data()
|
|||||||
QVariantMap validExitActionNoParams;
|
QVariantMap validExitActionNoParams;
|
||||||
validExitActionNoParams.insert("actionTypeId", mockWithoutParamsActionTypeId);
|
validExitActionNoParams.insert("actionTypeId", mockWithoutParamsActionTypeId);
|
||||||
validExitActionNoParams.insert("thingId", m_mockThingId);
|
validExitActionNoParams.insert("thingId", m_mockThingId);
|
||||||
validExitActionNoParams.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
|
|
||||||
QVariantMap invalidExitAction;
|
QVariantMap invalidExitAction;
|
||||||
invalidExitAction.insert("actionTypeId", ActionTypeId());
|
invalidExitAction.insert("actionTypeId", ActionTypeId());
|
||||||
@ -711,12 +703,10 @@ void TestRules::editRules_data()
|
|||||||
QVariantMap validEventDescriptor1;
|
QVariantMap validEventDescriptor1;
|
||||||
validEventDescriptor1.insert("eventTypeId", mockEvent1EventTypeId);
|
validEventDescriptor1.insert("eventTypeId", mockEvent1EventTypeId);
|
||||||
validEventDescriptor1.insert("thingId", m_mockThingId);
|
validEventDescriptor1.insert("thingId", m_mockThingId);
|
||||||
validEventDescriptor1.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
|
|
||||||
QVariantMap validEventDescriptor2;
|
QVariantMap validEventDescriptor2;
|
||||||
validEventDescriptor2.insert("eventTypeId", mockEvent2EventTypeId);
|
validEventDescriptor2.insert("eventTypeId", mockEvent2EventTypeId);
|
||||||
validEventDescriptor2.insert("thingId", m_mockThingId);
|
validEventDescriptor2.insert("thingId", m_mockThingId);
|
||||||
validEventDescriptor2.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
QVariantList params;
|
QVariantList params;
|
||||||
QVariantMap param1;
|
QVariantMap param1;
|
||||||
param1.insert("paramTypeId", mockEvent2EventIntParamParamTypeId);
|
param1.insert("paramTypeId", mockEvent2EventIntParamParamTypeId);
|
||||||
@ -728,7 +718,6 @@ void TestRules::editRules_data()
|
|||||||
QVariantMap validEventDescriptor3;
|
QVariantMap validEventDescriptor3;
|
||||||
validEventDescriptor3.insert("eventTypeId", mockEvent2EventTypeId);
|
validEventDescriptor3.insert("eventTypeId", mockEvent2EventTypeId);
|
||||||
validEventDescriptor3.insert("thingId", m_mockThingId);
|
validEventDescriptor3.insert("thingId", m_mockThingId);
|
||||||
validEventDescriptor3.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
|
|
||||||
// EventDescriptorList
|
// EventDescriptorList
|
||||||
QVariantList eventDescriptorList;
|
QVariantList eventDescriptorList;
|
||||||
@ -743,7 +732,6 @@ void TestRules::editRules_data()
|
|||||||
QVariantMap validActionEventBased;
|
QVariantMap validActionEventBased;
|
||||||
validActionEventBased.insert("actionTypeId", mockWithParamsActionTypeId);
|
validActionEventBased.insert("actionTypeId", mockWithParamsActionTypeId);
|
||||||
validActionEventBased.insert("thingId", m_mockThingId);
|
validActionEventBased.insert("thingId", m_mockThingId);
|
||||||
validActionEventBased.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
QVariantMap validActionEventBasedParam1;
|
QVariantMap validActionEventBasedParam1;
|
||||||
validActionEventBasedParam1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId);
|
validActionEventBasedParam1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId);
|
||||||
validActionEventBasedParam1.insert("eventTypeId", mockEvent2EventTypeId);
|
validActionEventBasedParam1.insert("eventTypeId", mockEvent2EventTypeId);
|
||||||
@ -828,11 +816,9 @@ void TestRules::editRules()
|
|||||||
QVariantMap eventDescriptor1;
|
QVariantMap eventDescriptor1;
|
||||||
eventDescriptor1.insert("eventTypeId", mockEvent1EventTypeId);
|
eventDescriptor1.insert("eventTypeId", mockEvent1EventTypeId);
|
||||||
eventDescriptor1.insert("thingId", m_mockThingId);
|
eventDescriptor1.insert("thingId", m_mockThingId);
|
||||||
eventDescriptor1.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
QVariantMap eventDescriptor2;
|
QVariantMap eventDescriptor2;
|
||||||
eventDescriptor2.insert("eventTypeId", mockEvent2EventTypeId);
|
eventDescriptor2.insert("eventTypeId", mockEvent2EventTypeId);
|
||||||
eventDescriptor2.insert("thingId", m_mockThingId);
|
eventDescriptor2.insert("thingId", m_mockThingId);
|
||||||
eventDescriptor2.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
QVariantMap eventParam1;
|
QVariantMap eventParam1;
|
||||||
eventParam1.insert("paramTypeId", mockEvent2EventIntParamParamTypeId);
|
eventParam1.insert("paramTypeId", mockEvent2EventIntParamParamTypeId);
|
||||||
eventParam1.insert("value", 3);
|
eventParam1.insert("value", 3);
|
||||||
@ -889,7 +875,6 @@ void TestRules::editRules()
|
|||||||
QVariantMap validActionEventBased;
|
QVariantMap validActionEventBased;
|
||||||
validActionEventBased.insert("actionTypeId", mockWithParamsActionTypeId);
|
validActionEventBased.insert("actionTypeId", mockWithParamsActionTypeId);
|
||||||
validActionEventBased.insert("thingId", m_mockThingId);
|
validActionEventBased.insert("thingId", m_mockThingId);
|
||||||
validActionEventBased.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
QVariantMap validActionEventBasedParam1;
|
QVariantMap validActionEventBasedParam1;
|
||||||
validActionEventBasedParam1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId);
|
validActionEventBasedParam1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId);
|
||||||
validActionEventBasedParam1.insert("eventTypeId", mockEvent2EventTypeId);
|
validActionEventBasedParam1.insert("eventTypeId", mockEvent2EventTypeId);
|
||||||
@ -903,7 +888,6 @@ void TestRules::editRules()
|
|||||||
QVariantMap validEventDescriptor3;
|
QVariantMap validEventDescriptor3;
|
||||||
validEventDescriptor3.insert("eventTypeId", mockEvent2EventTypeId);
|
validEventDescriptor3.insert("eventTypeId", mockEvent2EventTypeId);
|
||||||
validEventDescriptor3.insert("thingId", m_mockThingId);
|
validEventDescriptor3.insert("thingId", m_mockThingId);
|
||||||
validEventDescriptor3.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
validEventDescriptor3.insert("paramDescriptors", QVariantList());
|
validEventDescriptor3.insert("paramDescriptors", QVariantList());
|
||||||
validEventDescriptors3.append(validEventDescriptor3);
|
validEventDescriptors3.append(validEventDescriptor3);
|
||||||
|
|
||||||
@ -978,7 +962,7 @@ void TestRules::editRules()
|
|||||||
foreach (const QVariant &eventDescriptorVariant, eventDescriptorList) {
|
foreach (const QVariant &eventDescriptorVariant, eventDescriptorList) {
|
||||||
bool found = false;
|
bool found = false;
|
||||||
foreach (const QVariant &replyEventDescriptorVariant, eventDescriptors) {
|
foreach (const QVariant &replyEventDescriptorVariant, eventDescriptors) {
|
||||||
if (eventDescriptorVariant.toMap().value("deviceId") == replyEventDescriptorVariant.toMap().value("deviceId") &&
|
if (eventDescriptorVariant.toMap().value("thingId") == replyEventDescriptorVariant.toMap().value("thingId") &&
|
||||||
eventDescriptorVariant.toMap().value("eventTypeId") == replyEventDescriptorVariant.toMap().value("eventTypeId")) {
|
eventDescriptorVariant.toMap().value("eventTypeId") == replyEventDescriptorVariant.toMap().value("eventTypeId")) {
|
||||||
found = true;
|
found = true;
|
||||||
QVERIFY2(eventDescriptorVariant == replyEventDescriptorVariant, "Event descriptor doesn't match");
|
QVERIFY2(eventDescriptorVariant == replyEventDescriptorVariant, "Event descriptor doesn't match");
|
||||||
@ -1135,12 +1119,10 @@ void TestRules::loadStoreConfig()
|
|||||||
QVariantMap eventDescriptor1;
|
QVariantMap eventDescriptor1;
|
||||||
eventDescriptor1.insert("eventTypeId", mockEvent1EventTypeId);
|
eventDescriptor1.insert("eventTypeId", mockEvent1EventTypeId);
|
||||||
eventDescriptor1.insert("thingId", m_mockThingId);
|
eventDescriptor1.insert("thingId", m_mockThingId);
|
||||||
eventDescriptor1.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
|
|
||||||
QVariantMap eventDescriptor2;
|
QVariantMap eventDescriptor2;
|
||||||
eventDescriptor2.insert("eventTypeId", mockEvent2EventTypeId);
|
eventDescriptor2.insert("eventTypeId", mockEvent2EventTypeId);
|
||||||
eventDescriptor2.insert("thingId", m_mockThingId);
|
eventDescriptor2.insert("thingId", m_mockThingId);
|
||||||
eventDescriptor2.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
QVariantList eventParamDescriptors;
|
QVariantList eventParamDescriptors;
|
||||||
QVariantMap eventParam1;
|
QVariantMap eventParam1;
|
||||||
eventParam1.insert("paramTypeId", mockEvent2EventIntParamParamTypeId);
|
eventParam1.insert("paramTypeId", mockEvent2EventIntParamParamTypeId);
|
||||||
@ -1158,7 +1140,6 @@ void TestRules::loadStoreConfig()
|
|||||||
|
|
||||||
QVariantMap stateDescriptor2;
|
QVariantMap stateDescriptor2;
|
||||||
stateDescriptor2.insert("thingId", m_mockThingId);
|
stateDescriptor2.insert("thingId", m_mockThingId);
|
||||||
stateDescriptor2.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
stateDescriptor2.insert("operator", enumValueName(Types::ValueOperatorEquals));
|
stateDescriptor2.insert("operator", enumValueName(Types::ValueOperatorEquals));
|
||||||
stateDescriptor2.insert("stateTypeId", mockIntStateTypeId);
|
stateDescriptor2.insert("stateTypeId", mockIntStateTypeId);
|
||||||
stateDescriptor2.insert("value", 1);
|
stateDescriptor2.insert("value", 1);
|
||||||
@ -1168,7 +1149,6 @@ void TestRules::loadStoreConfig()
|
|||||||
|
|
||||||
QVariantMap stateDescriptor3;
|
QVariantMap stateDescriptor3;
|
||||||
stateDescriptor3.insert("thingId", m_mockThingId);
|
stateDescriptor3.insert("thingId", m_mockThingId);
|
||||||
stateDescriptor3.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
stateDescriptor3.insert("operator", enumValueName(Types::ValueOperatorEquals));
|
stateDescriptor3.insert("operator", enumValueName(Types::ValueOperatorEquals));
|
||||||
stateDescriptor3.insert("stateTypeId", mockBoolStateTypeId);
|
stateDescriptor3.insert("stateTypeId", mockBoolStateTypeId);
|
||||||
stateDescriptor3.insert("value", true);
|
stateDescriptor3.insert("value", true);
|
||||||
@ -1196,13 +1176,11 @@ void TestRules::loadStoreConfig()
|
|||||||
QVariantMap action1;
|
QVariantMap action1;
|
||||||
action1.insert("actionTypeId", mockWithoutParamsActionTypeId);
|
action1.insert("actionTypeId", mockWithoutParamsActionTypeId);
|
||||||
action1.insert("thingId", m_mockThingId);
|
action1.insert("thingId", m_mockThingId);
|
||||||
action1.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
action1.insert("ruleActionParams", QVariantList());
|
action1.insert("ruleActionParams", QVariantList());
|
||||||
|
|
||||||
QVariantMap action2;
|
QVariantMap action2;
|
||||||
action2.insert("actionTypeId", mockWithParamsActionTypeId);
|
action2.insert("actionTypeId", mockWithParamsActionTypeId);
|
||||||
action2.insert("thingId", m_mockThingId);
|
action2.insert("thingId", m_mockThingId);
|
||||||
action2.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
QVariantList action2Params;
|
QVariantList action2Params;
|
||||||
QVariantMap action2Param1;
|
QVariantMap action2Param1;
|
||||||
action2Param1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId);
|
action2Param1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId);
|
||||||
@ -1218,7 +1196,6 @@ void TestRules::loadStoreConfig()
|
|||||||
QVariantMap validActionEventBased;
|
QVariantMap validActionEventBased;
|
||||||
validActionEventBased.insert("actionTypeId", mockWithParamsActionTypeId);
|
validActionEventBased.insert("actionTypeId", mockWithParamsActionTypeId);
|
||||||
validActionEventBased.insert("thingId", m_mockThingId);
|
validActionEventBased.insert("thingId", m_mockThingId);
|
||||||
validActionEventBased.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
QVariantMap validActionEventBasedParam1;
|
QVariantMap validActionEventBasedParam1;
|
||||||
validActionEventBasedParam1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId);
|
validActionEventBasedParam1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId);
|
||||||
validActionEventBasedParam1.insert("eventTypeId", mockEvent2EventTypeId);
|
validActionEventBasedParam1.insert("eventTypeId", mockEvent2EventTypeId);
|
||||||
@ -1232,7 +1209,6 @@ void TestRules::loadStoreConfig()
|
|||||||
QVariantMap validEventDescriptor3;
|
QVariantMap validEventDescriptor3;
|
||||||
validEventDescriptor3.insert("eventTypeId", mockEvent2EventTypeId);
|
validEventDescriptor3.insert("eventTypeId", mockEvent2EventTypeId);
|
||||||
validEventDescriptor3.insert("thingId", m_mockThingId);
|
validEventDescriptor3.insert("thingId", m_mockThingId);
|
||||||
validEventDescriptor3.insert("deviceId", m_mockThingId); // DEPRECATED
|
|
||||||
validEventDescriptors3.append(validEventDescriptor3);
|
validEventDescriptors3.append(validEventDescriptor3);
|
||||||
|
|
||||||
// Interface based event descriptor
|
// Interface based event descriptor
|
||||||
@ -1446,7 +1422,7 @@ void TestRules::loadStoreConfig()
|
|||||||
QVERIFY2(stateDescriptor.value("interface") == "battery", "Interface of stateDescriptor does not match");
|
QVERIFY2(stateDescriptor.value("interface") == "battery", "Interface of stateDescriptor does not match");
|
||||||
QVERIFY2(stateDescriptor.value("interfaceState") == "batteryCritical", "InterfaceState of stateDescriptor doesn't match");
|
QVERIFY2(stateDescriptor.value("interfaceState") == "batteryCritical", "InterfaceState of stateDescriptor doesn't match");
|
||||||
} else {
|
} else {
|
||||||
QVERIFY2(false, "StateDescriptor must have either deviceId/stateTypeId or interface/interfaceState.");
|
QVERIFY2(false, "StateDescriptor must have either thingId/stateTypeId or interface/interfaceState.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -99,11 +99,11 @@ void TestScripts::testScriptEventById()
|
|||||||
QString script = QString("import QtQuick 2.0\n"
|
QString script = QString("import QtQuick 2.0\n"
|
||||||
"import nymea 1.0\n"
|
"import nymea 1.0\n"
|
||||||
"Item {\n"
|
"Item {\n"
|
||||||
" DeviceEvent {\n"
|
" ThingEvent {\n"
|
||||||
" deviceId: \"%1\"\n"
|
" thingId: \"%1\"\n"
|
||||||
" eventTypeId: \"%2\"\n"
|
" eventTypeId: \"%2\"\n"
|
||||||
" onTriggered: {\n"
|
" onTriggered: {\n"
|
||||||
" TestHelper.logEvent(deviceId, eventTypeId, params);\n"
|
" TestHelper.logEvent(thingId, eventTypeId, params);\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}\n").arg(m_mockThingId.toString()).arg(mockPowerEventTypeId.toString());
|
"}\n").arg(m_mockThingId.toString()).arg(mockPowerEventTypeId.toString());
|
||||||
@ -135,11 +135,11 @@ void TestScripts::testScriptEventByName()
|
|||||||
QString script = QString("import QtQuick 2.0\n"
|
QString script = QString("import QtQuick 2.0\n"
|
||||||
"import nymea 1.0\n"
|
"import nymea 1.0\n"
|
||||||
"Item {\n"
|
"Item {\n"
|
||||||
" DeviceEvent {\n"
|
" ThingEvent {\n"
|
||||||
" deviceId: \"%1\"\n"
|
" thingId: \"%1\"\n"
|
||||||
" eventName: \"%2\"\n"
|
" eventName: \"%2\"\n"
|
||||||
" onTriggered: {\n"
|
" onTriggered: {\n"
|
||||||
" TestHelper.logEvent(deviceId, eventName, params);\n"
|
" TestHelper.logEvent(thingId, eventName, params);\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}\n").arg(m_mockThingId.toString()).arg("power");
|
"}\n").arg(m_mockThingId.toString()).arg("power");
|
||||||
@ -171,11 +171,11 @@ void TestScripts::testReadScriptStateById()
|
|||||||
QString script = QString("import QtQuick 2.0\n"
|
QString script = QString("import QtQuick 2.0\n"
|
||||||
"import nymea 1.0\n"
|
"import nymea 1.0\n"
|
||||||
"Item {\n"
|
"Item {\n"
|
||||||
" DeviceState {\n"
|
" ThingState {\n"
|
||||||
" deviceId: \"%1\"\n"
|
" thingId: \"%1\"\n"
|
||||||
" stateTypeId: \"%2\"\n"
|
" stateTypeId: \"%2\"\n"
|
||||||
" onValueChanged: {\n"
|
" onValueChanged: {\n"
|
||||||
" TestHelper.logStateChange(deviceId, stateTypeId, value);\n"
|
" TestHelper.logStateChange(thingId, stateTypeId, value);\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}\n").arg(m_mockThingId.toString()).arg(mockPowerStateTypeId.toString());
|
"}\n").arg(m_mockThingId.toString()).arg(mockPowerStateTypeId.toString());
|
||||||
@ -204,11 +204,11 @@ void TestScripts::testReadScriptStateByNyme()
|
|||||||
QString script = QString("import QtQuick 2.0\n"
|
QString script = QString("import QtQuick 2.0\n"
|
||||||
"import nymea 1.0\n"
|
"import nymea 1.0\n"
|
||||||
"Item {\n"
|
"Item {\n"
|
||||||
" DeviceState {\n"
|
" ThingState {\n"
|
||||||
" deviceId: \"%1\"\n"
|
" thingId: \"%1\"\n"
|
||||||
" stateName: \"%2\"\n"
|
" stateName: \"%2\"\n"
|
||||||
" onValueChanged: {\n"
|
" onValueChanged: {\n"
|
||||||
" TestHelper.logStateChange(deviceId, stateName, value);\n"
|
" TestHelper.logStateChange(thingId, stateName, value);\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}\n").arg(m_mockThingId.toString()).arg("power");
|
"}\n").arg(m_mockThingId.toString()).arg("power");
|
||||||
@ -237,15 +237,15 @@ void TestScripts::testWriteScriptStateById()
|
|||||||
QString script = QString("import QtQuick 2.0\n"
|
QString script = QString("import QtQuick 2.0\n"
|
||||||
"import nymea 1.0\n"
|
"import nymea 1.0\n"
|
||||||
"Item {\n"
|
"Item {\n"
|
||||||
" DeviceState {\n"
|
" ThingState {\n"
|
||||||
" id: deviceState\n"
|
" id: thingState\n"
|
||||||
" deviceId: \"%1\"\n"
|
" thingId: \"%1\"\n"
|
||||||
" stateTypeId: \"%2\"\n"
|
" stateTypeId: \"%2\"\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" Connections {\n"
|
" Connections {\n"
|
||||||
" target: TestHelper\n"
|
" target: TestHelper\n"
|
||||||
" onSetState: {\n"
|
" onSetState: {\n"
|
||||||
" deviceState.value = value\n"
|
" thingState.value = value\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}\n").arg(m_mockThingId.toString()).arg(mockPowerStateTypeId.toString());
|
"}\n").arg(m_mockThingId.toString()).arg(mockPowerStateTypeId.toString());
|
||||||
@ -271,15 +271,15 @@ void TestScripts::testWriteScriptStateByName()
|
|||||||
QString script = QString("import QtQuick 2.0\n"
|
QString script = QString("import QtQuick 2.0\n"
|
||||||
"import nymea 1.0\n"
|
"import nymea 1.0\n"
|
||||||
"Item {\n"
|
"Item {\n"
|
||||||
" DeviceState {\n"
|
" ThingState {\n"
|
||||||
" id: deviceState\n"
|
" id: thingState\n"
|
||||||
" deviceId: \"%1\"\n"
|
" thingId: \"%1\"\n"
|
||||||
" stateName: \"%2\"\n"
|
" stateName: \"%2\"\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" Connections {\n"
|
" Connections {\n"
|
||||||
" target: TestHelper\n"
|
" target: TestHelper\n"
|
||||||
" onSetState: {\n"
|
" onSetState: {\n"
|
||||||
" deviceState.value = value\n"
|
" thingState.value = value\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}\n").arg(m_mockThingId.toString()).arg("power");
|
"}\n").arg(m_mockThingId.toString()).arg("power");
|
||||||
@ -305,15 +305,15 @@ void TestScripts::testScriptActionById()
|
|||||||
QString script = QString("import QtQuick 2.0\n"
|
QString script = QString("import QtQuick 2.0\n"
|
||||||
"import nymea 1.0\n"
|
"import nymea 1.0\n"
|
||||||
"Item {\n"
|
"Item {\n"
|
||||||
" DeviceAction {\n"
|
" ThingAction {\n"
|
||||||
" id: deviceAction\n"
|
" id: thingAction\n"
|
||||||
" deviceId: \"%1\"\n"
|
" thingId: \"%1\"\n"
|
||||||
" actionTypeId: \"%2\"\n"
|
" actionTypeId: \"%2\"\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" Connections {\n"
|
" Connections {\n"
|
||||||
" target: TestHelper\n"
|
" target: TestHelper\n"
|
||||||
" onExecuteAction: {\n"
|
" onExecuteAction: {\n"
|
||||||
" deviceAction.execute(params)\n"
|
" thingAction.execute(params)\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}\n").arg(m_mockThingId.toString()).arg(mockPowerActionTypeId.toString());
|
"}\n").arg(m_mockThingId.toString()).arg(mockPowerActionTypeId.toString());
|
||||||
@ -341,15 +341,15 @@ void TestScripts::testScriptActionByName()
|
|||||||
QString script = QString("import QtQuick 2.0\n"
|
QString script = QString("import QtQuick 2.0\n"
|
||||||
"import nymea 1.0\n"
|
"import nymea 1.0\n"
|
||||||
"Item {\n"
|
"Item {\n"
|
||||||
" DeviceAction {\n"
|
" ThingAction {\n"
|
||||||
" id: deviceAction\n"
|
" id: thingAction\n"
|
||||||
" deviceId: \"%1\"\n"
|
" thingId: \"%1\"\n"
|
||||||
" actionName: \"%2\"\n"
|
" actionName: \"%2\"\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" Connections {\n"
|
" Connections {\n"
|
||||||
" target: TestHelper\n"
|
" target: TestHelper\n"
|
||||||
" onExecuteAction: {\n"
|
" onExecuteAction: {\n"
|
||||||
" deviceAction.execute(params)\n"
|
" thingAction.execute(params)\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}\n").arg(m_mockThingId.toString()).arg("power");
|
"}\n").arg(m_mockThingId.toString()).arg("power");
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
include(../../../nymea.pri)
|
|
||||||
include(../autotests.pri)
|
|
||||||
|
|
||||||
TARGET = states
|
|
||||||
SOURCES += teststates.cpp
|
|
||||||
@ -1,144 +0,0 @@
|
|||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
||||||
*
|
|
||||||
* Copyright 2013 - 2020, nymea GmbH
|
|
||||||
* Contact: contact@nymea.io
|
|
||||||
*
|
|
||||||
* This file is part of nymea.
|
|
||||||
* This project including source code and documentation is protected by
|
|
||||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
|
||||||
* reproduction, publication, editing and translation, are reserved. The use of
|
|
||||||
* this project is subject to the terms of a license agreement to be concluded
|
|
||||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
|
||||||
* under https://nymea.io/license
|
|
||||||
*
|
|
||||||
* GNU General Public License Usage
|
|
||||||
* Alternatively, this project may be redistributed and/or modified under the
|
|
||||||
* terms of the GNU General Public License as published by the Free Software
|
|
||||||
* Foundation, GNU version 3. This project 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
|
|
||||||
* this project. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
* For any further details and any questions please contact us under
|
|
||||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
|
||||||
* https://nymea.io/license/faq
|
|
||||||
*
|
|
||||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
||||||
|
|
||||||
#include "nymeatestbase.h"
|
|
||||||
#include "nymeacore.h"
|
|
||||||
#include "jsonrpc/devicehandler.h"
|
|
||||||
|
|
||||||
using namespace nymeaserver;
|
|
||||||
|
|
||||||
class TestStates: public NymeaTestBase
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void getStateTypes();
|
|
||||||
|
|
||||||
void getStateValue_data();
|
|
||||||
void getStateValue();
|
|
||||||
|
|
||||||
void save_load_states();
|
|
||||||
};
|
|
||||||
|
|
||||||
void TestStates::getStateTypes()
|
|
||||||
{
|
|
||||||
QVariantMap params;
|
|
||||||
params.insert("deviceClassId", mockThingClassId);
|
|
||||||
QVariant response = injectAndWait("Devices.GetStateTypes", params);
|
|
||||||
QVERIFY(!response.isNull());
|
|
||||||
//verifyDeviceError(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestStates::getStateValue_data()
|
|
||||||
{
|
|
||||||
QList<Thing*> devices = NymeaCore::instance()->thingManager()->findConfiguredThings(mockThingClassId);
|
|
||||||
QVERIFY2(devices.count() > 0, "There needs to be at least one configured Mock Device for this test");
|
|
||||||
Thing *device = devices.first();
|
|
||||||
|
|
||||||
QTest::addColumn<ThingId>("deviceId");
|
|
||||||
QTest::addColumn<StateTypeId>("stateTypeId");
|
|
||||||
QTest::addColumn<Device::DeviceError>("error");
|
|
||||||
|
|
||||||
QTest::newRow("existing state") << device->id() << mockIntStateTypeId << Device::DeviceErrorNoError;
|
|
||||||
QTest::newRow("invalid device") << ThingId::createThingId() << mockIntStateTypeId << Device::DeviceErrorDeviceNotFound;
|
|
||||||
QTest::newRow("invalid statetype") << device->id() << StateTypeId::createStateTypeId() << Device::DeviceErrorStateTypeNotFound;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestStates::getStateValue()
|
|
||||||
{
|
|
||||||
QFETCH(ThingId, deviceId);
|
|
||||||
QFETCH(StateTypeId, stateTypeId);
|
|
||||||
QFETCH(Device::DeviceError, error);
|
|
||||||
|
|
||||||
QVariantMap params;
|
|
||||||
params.insert("deviceId", deviceId.toString());
|
|
||||||
params.insert("stateTypeId", stateTypeId.toString());
|
|
||||||
|
|
||||||
QVariant response = injectAndWait("Devices.GetStateValue", params);
|
|
||||||
|
|
||||||
verifyError(response, "deviceError", enumValueName(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestStates::save_load_states()
|
|
||||||
{
|
|
||||||
ThingClass mockDeviceClass = NymeaCore::instance()->thingManager()->findThingClass(mockThingClassId);
|
|
||||||
|
|
||||||
QVERIFY2(mockDeviceClass.getStateType(mockIntStateTypeId).cached(), "Mock int state is not cached (required to be true for this test)");
|
|
||||||
QVERIFY2(!mockDeviceClass.getStateType(mockBoolStateTypeId).cached(), "Mock bool state is cached (required to be false for this test)");
|
|
||||||
|
|
||||||
Thing* device = NymeaCore::instance()->thingManager()->findConfiguredThings(mockThingClassId).first();
|
|
||||||
int port = device->paramValue(mockThingHttpportParamTypeId).toInt();
|
|
||||||
QNetworkAccessManager nam;
|
|
||||||
QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*)));
|
|
||||||
|
|
||||||
|
|
||||||
// First set the state values to something that is *not* the default
|
|
||||||
int newIntValue = mockDeviceClass.getStateType(mockIntStateTypeId).defaultValue().toInt() + 1;
|
|
||||||
bool newBoolValue = !mockDeviceClass.getStateType(mockBoolStateTypeId).defaultValue().toBool();
|
|
||||||
|
|
||||||
QNetworkRequest request(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(port).arg(mockIntStateTypeId.toString()).arg(newIntValue)));
|
|
||||||
QNetworkReply *reply = nam.get(request);
|
|
||||||
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
|
||||||
spy.wait();
|
|
||||||
|
|
||||||
spy.clear();
|
|
||||||
request = QNetworkRequest(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(port).arg(mockBoolStateTypeId.toString()).arg(newBoolValue)));
|
|
||||||
reply = nam.get(request);
|
|
||||||
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
|
||||||
spy.wait();
|
|
||||||
|
|
||||||
// For completeness, verify through JSONRPC that they were actually yet.
|
|
||||||
QVariantMap params;
|
|
||||||
params.insert("deviceId", device->id());
|
|
||||||
|
|
||||||
params["stateTypeId"] = mockIntStateTypeId;
|
|
||||||
QVariant response = injectAndWait("Devices.GetStateValue", params);
|
|
||||||
QCOMPARE(response.toMap().value("params").toMap().value("value").toInt(), newIntValue);
|
|
||||||
|
|
||||||
params["stateTypeId"] = mockBoolStateTypeId;
|
|
||||||
response = injectAndWait("Devices.GetStateValue", params);
|
|
||||||
QCOMPARE(response.toMap().value("params").toMap().value("value").toBool(), newBoolValue);
|
|
||||||
|
|
||||||
// Restart the server
|
|
||||||
restartServer();
|
|
||||||
|
|
||||||
// And check if the cached int state has successfully been restored
|
|
||||||
params["stateTypeId"] = mockIntStateTypeId;
|
|
||||||
response = injectAndWait("Devices.GetStateValue", params);
|
|
||||||
QCOMPARE(response.toMap().value("params").toMap().value("value").toInt(), newIntValue);
|
|
||||||
|
|
||||||
// and that the non-cached bool state is back to its default
|
|
||||||
params["stateTypeId"] = mockBoolStateTypeId;
|
|
||||||
response = injectAndWait("Devices.GetStateValue", params);
|
|
||||||
QCOMPARE(response.toMap().value("params").toMap().value("value").toBool(), mockDeviceClass.getStateType(mockBoolStateTypeId).defaultValue().toBool());
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "teststates.moc"
|
|
||||||
QTEST_MAIN(TestStates)
|
|
||||||
@ -83,7 +83,6 @@ QVariantMap TestTags::createRuleTag(const QString &ruleId, const QString &appId,
|
|||||||
bool TestTags::compareThingTag(const QVariantMap &tag, const QUuid &thingId, const QString &appId, const QString &tagId, const QString &value)
|
bool TestTags::compareThingTag(const QVariantMap &tag, const QUuid &thingId, const QString &appId, const QString &tagId, const QString &value)
|
||||||
{
|
{
|
||||||
return tag.value("thingId").toUuid() == thingId &&
|
return tag.value("thingId").toUuid() == thingId &&
|
||||||
tag.value("deviceId").toUuid() == thingId && // backwards compatibility to < 0.19 adds deviceId along with thingId
|
|
||||||
tag.value("appId").toString() == appId &&
|
tag.value("appId").toString() == appId &&
|
||||||
tag.value("tagId").toString() == tagId &&
|
tag.value("tagId").toString() == tagId &&
|
||||||
tag.value("value").toString() == value;
|
tag.value("value").toString() == value;
|
||||||
|
|||||||
@ -141,14 +141,13 @@ private:
|
|||||||
|
|
||||||
void TestTimeManager::initTestCase()
|
void TestTimeManager::initTestCase()
|
||||||
{
|
{
|
||||||
NymeaTestBase::initTestCase();
|
NymeaTestBase::initTestCase("*.debug=false\n"
|
||||||
QLoggingCategory::setFilterRules("*.debug=false\n"
|
"Tests.debug=true\n"
|
||||||
"Tests.debug=true\n"
|
"RuleEngine.debug=true\n"
|
||||||
"RuleEngine.debug=true\n"
|
// "RuleEngineDebug.debug=true\n"
|
||||||
// "RuleEngineDebug.debug=true\n"
|
"Mock.debug=true\n"
|
||||||
"Mock.debug=true\n"
|
"JsonRpc.debug=true\n"
|
||||||
"JsonRpc.debug=true\n"
|
"TimeManager.debug=true");
|
||||||
"TimeManager.debug=true");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestTimeManager::loadSaveTimeDescriptor_data()
|
void TestTimeManager::loadSaveTimeDescriptor_data()
|
||||||
@ -2028,7 +2027,7 @@ void TestTimeManager::initTimeManager()
|
|||||||
{
|
{
|
||||||
cleanupMockHistory();
|
cleanupMockHistory();
|
||||||
removeAllRules();
|
removeAllRules();
|
||||||
enableNotifications({"Rules", "Integrations", "Events"});
|
enableNotifications({"Rules", "Integrations"});
|
||||||
NymeaCore::instance()->timeManager()->stopTimer();
|
NymeaCore::instance()->timeManager()->stopTimer();
|
||||||
qDebug() << NymeaCore::instance()->timeManager()->currentDateTime().toString();
|
qDebug() << NymeaCore::instance()->timeManager()->currentDateTime().toString();
|
||||||
}
|
}
|
||||||
@ -2183,14 +2182,14 @@ void TestTimeManager::triggerMockEvent1()
|
|||||||
if (eventSpy.isEmpty()) {
|
if (eventSpy.isEmpty()) {
|
||||||
eventSpy.wait();
|
eventSpy.wait();
|
||||||
}
|
}
|
||||||
QVariantList eventTriggerVariants = checkNotifications(eventSpy, "Events.EventTriggered");
|
QVariantList eventTriggerVariants = checkNotifications(eventSpy, "Integrations.EventTriggered");
|
||||||
QVERIFY2(eventTriggerVariants.count() == 1, "Did not get Events.EventTriggered notification.");
|
QVERIFY2(eventTriggerVariants.count() == 1, "Did not get Events.EventTriggered notification.");
|
||||||
QVERIFY2(eventTriggerVariants.first().toMap().value("params").toMap().contains("event"), "Notification Events.EventTriggered does not contain event.");
|
QVERIFY2(eventTriggerVariants.first().toMap().value("params").toMap().contains("event"), "Notification Events.EventTriggered does not contain event.");
|
||||||
QVariantMap eventMap = eventTriggerVariants.first().toMap().value("params").toMap().value("event").toMap();
|
QVariantMap eventMap = eventTriggerVariants.first().toMap().value("params").toMap().value("event").toMap();
|
||||||
|
|
||||||
QVERIFY2(eventMap.contains("thingId"), "Events.EventTriggered notification does not contain thingId");
|
QVERIFY2(eventMap.contains("thingId"), "Integrations.EventTriggered notification does not contain thingId");
|
||||||
QVERIFY2(ThingId(eventMap.value("thingId").toString()) == m_mockThingId, "Events.EventTriggered notification does not contain the correct thingId");
|
QVERIFY2(ThingId(eventMap.value("thingId").toString()) == m_mockThingId, "Integrations.EventTriggered notification does not contain the correct thingId");
|
||||||
QVERIFY2(eventMap.contains("eventTypeId"), "Events.EventTriggered notification does not contain eventTypeId");
|
QVERIFY2(eventMap.contains("eventTypeId"), "Integrations.EventTriggered notification does not contain eventTypeId");
|
||||||
QVERIFY2(EventTypeId(eventMap.value("eventTypeId").toString()) == mockEvent1EventTypeId, "Events.EventTriggered notification does not contain the correct eventTypeId");
|
QVERIFY2(EventTypeId(eventMap.value("eventTypeId").toString()) == mockEvent1EventTypeId, "Events.EventTriggered notification does not contain the correct eventTypeId");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -121,9 +121,7 @@ TestUsermanager::TestUsermanager(QObject *parent): NymeaTestBase(parent)
|
|||||||
void TestUsermanager::initTestCase()
|
void TestUsermanager::initTestCase()
|
||||||
{
|
{
|
||||||
NymeaDBusService::setBusType(QDBusConnection::SessionBus);
|
NymeaDBusService::setBusType(QDBusConnection::SessionBus);
|
||||||
NymeaTestBase::initTestCase();
|
NymeaTestBase::initTestCase("*.debug=false\n"
|
||||||
|
|
||||||
QLoggingCategory::setFilterRules("*.debug=false\n"
|
|
||||||
"Application.debug=true\n"
|
"Application.debug=true\n"
|
||||||
"Tests.debug=true\n"
|
"Tests.debug=true\n"
|
||||||
"UserManager.debug=true\n"
|
"UserManager.debug=true\n"
|
||||||
@ -134,12 +132,11 @@ void TestUsermanager::initTestCase()
|
|||||||
void TestUsermanager::init()
|
void TestUsermanager::init()
|
||||||
{
|
{
|
||||||
UserManager *userManager = NymeaCore::instance()->userManager();
|
UserManager *userManager = NymeaCore::instance()->userManager();
|
||||||
foreach (const QString &user, userManager->users()) {
|
foreach (const UserInfo &userInfo, userManager->users()) {
|
||||||
qCDebug(dcTests()) << "Removing user" << user;
|
qCDebug(dcTests()) << "Removing user" << userInfo.username();
|
||||||
userManager->removeUser(user);
|
userManager->removeUser(userInfo.username());
|
||||||
}
|
}
|
||||||
userManager->removeUser("");
|
userManager->removeUser("");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestUsermanager::loginValidation_data() {
|
void TestUsermanager::loginValidation_data() {
|
||||||
@ -151,12 +148,9 @@ void TestUsermanager::loginValidation_data() {
|
|||||||
QTest::newRow("foo@bar.co.uk, Bla1234*, NoError") << "foo@bar.co.uk" << "Bla1234*" << UserManager::UserErrorNoError;
|
QTest::newRow("foo@bar.co.uk, Bla1234*, NoError") << "foo@bar.co.uk" << "Bla1234*" << UserManager::UserErrorNoError;
|
||||||
QTest::newRow("foo@bar.com.au, Bla1234*, NoError") << "foo@bar.com.au" << "Bla1234*" << UserManager::UserErrorNoError;
|
QTest::newRow("foo@bar.com.au, Bla1234*, NoError") << "foo@bar.com.au" << "Bla1234*" << UserManager::UserErrorNoError;
|
||||||
|
|
||||||
QTest::newRow("foo, Bla1234*, InvalidUserId") << "foo" << "Bla1234*" << UserManager::UserErrorInvalidUserId;
|
QTest::newRow("n, Bla1234*, InvalidUserId") << "n" << "Bla1234*" << UserManager::UserErrorInvalidUserId;
|
||||||
QTest::newRow("@, Bla1234*, InvalidUserId") << "@" << "Bla1234*" << UserManager::UserErrorInvalidUserId;
|
QTest::newRow("@, Bla1234*, InvalidUserId") << "@" << "Bla1234*" << UserManager::UserErrorInvalidUserId;
|
||||||
QTest::newRow("foo@, Bla1234*, InvalidUserId") << "foo@" << "Bla1234*" << UserManager::UserErrorInvalidUserId;
|
QTest::newRow("nymea, Bla1234*, InvalidUserId") << "nymea" << "Bla1234*" << UserManager::UserErrorNoError;
|
||||||
QTest::newRow("foo@bar, Bla1234*, InvalidUserId") << "foo@bar" << "Bla1234*" << UserManager::UserErrorInvalidUserId;
|
|
||||||
QTest::newRow("foo@bar., Bla1234*, InvalidUserId") << "foo@bar." << "Bla1234*" << UserManager::UserErrorInvalidUserId;
|
|
||||||
QTest::newRow("foo@bar.co.uk.au, Bla1234*, InvalidUserId") << "foo@bar.co.uk.au" << "Bla1234*" << UserManager::UserErrorInvalidUserId;
|
|
||||||
|
|
||||||
QTest::newRow("foo@bar.baz, a, BadPassword") << "foo@bar.baz" << "a" << UserManager::UserErrorBadPassword;
|
QTest::newRow("foo@bar.baz, a, BadPassword") << "foo@bar.baz" << "a" << UserManager::UserErrorBadPassword;
|
||||||
QTest::newRow("foo@bar.baz, a1, BadPassword") << "foo@bar.baz" << "a1" << UserManager::UserErrorBadPassword;
|
QTest::newRow("foo@bar.baz, a1, BadPassword") << "foo@bar.baz" << "a1" << UserManager::UserErrorBadPassword;
|
||||||
@ -184,10 +178,9 @@ void TestUsermanager::loginValidation()
|
|||||||
QFETCH(UserManager::UserError, expectedError);
|
QFETCH(UserManager::UserError, expectedError);
|
||||||
|
|
||||||
UserManager *userManager = NymeaCore::instance()->userManager();
|
UserManager *userManager = NymeaCore::instance()->userManager();
|
||||||
UserManager::UserError error = userManager->createUser(username, password);
|
UserManager::UserError error = userManager->createUser(username, password, "", "", Types::PermissionScopeAdmin);
|
||||||
qDebug() << "Error:" << error << "Expected:" << expectedError;
|
qDebug() << "Error:" << error << "Expected:" << expectedError;
|
||||||
QCOMPARE(error, expectedError);
|
QCOMPARE(error, expectedError);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestUsermanager::createUser()
|
void TestUsermanager::createUser()
|
||||||
@ -195,7 +188,7 @@ void TestUsermanager::createUser()
|
|||||||
QVariantMap params;
|
QVariantMap params;
|
||||||
params.insert("username", "valid@user.test");
|
params.insert("username", "valid@user.test");
|
||||||
params.insert("password", "Bla1234*");
|
params.insert("password", "Bla1234*");
|
||||||
QVariant response = injectAndWait("Users.CreateUser", params);
|
QVariant response = injectAndWait("JSONRPC.CreateUser", params);
|
||||||
|
|
||||||
QVERIFY2(response.toMap().value("status").toString() == "success", "Error creating user");
|
QVERIFY2(response.toMap().value("status").toString() == "success", "Error creating user");
|
||||||
QVERIFY2(response.toMap().value("params").toMap().value("error").toString() == "UserErrorNoError", "Error creating user");
|
QVERIFY2(response.toMap().value("params").toMap().value("error").toString() == "UserErrorNoError", "Error creating user");
|
||||||
@ -210,7 +203,7 @@ void TestUsermanager::authenticate()
|
|||||||
params.insert("username", "valid@user.test");
|
params.insert("username", "valid@user.test");
|
||||||
params.insert("password", "Bla1234*");
|
params.insert("password", "Bla1234*");
|
||||||
params.insert("deviceName", "autotests");
|
params.insert("deviceName", "autotests");
|
||||||
QVariant response = injectAndWait("Users.Authenticate", params);
|
QVariant response = injectAndWait("JSONRPC.Authenticate", params);
|
||||||
|
|
||||||
m_apiToken = response.toMap().value("params").toMap().value("token").toByteArray();
|
m_apiToken = response.toMap().value("params").toMap().value("token").toByteArray();
|
||||||
|
|
||||||
@ -225,7 +218,7 @@ void TestUsermanager::authenticatePushButton()
|
|||||||
|
|
||||||
QVariantMap params;
|
QVariantMap params;
|
||||||
params.insert("deviceName", "pbtestdevice");
|
params.insert("deviceName", "pbtestdevice");
|
||||||
QVariant response = injectAndWait("Users.RequestPushButtonAuth", params);
|
QVariant response = injectAndWait("JSONRPC.RequestPushButtonAuth", params);
|
||||||
qCDebug(dcTests()) << "Pushbutton auth response:" << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented));
|
qCDebug(dcTests()) << "Pushbutton auth response:" << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented));
|
||||||
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true);
|
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true);
|
||||||
int transactionId = response.toMap().value("params").toMap().value("transactionId").toInt();
|
int transactionId = response.toMap().value("params").toMap().value("transactionId").toInt();
|
||||||
@ -236,7 +229,7 @@ void TestUsermanager::authenticatePushButton()
|
|||||||
pushButtonAgent.sendButtonPressed();
|
pushButtonAgent.sendButtonPressed();
|
||||||
|
|
||||||
if (clientSpy.count() == 0) clientSpy.wait();
|
if (clientSpy.count() == 0) clientSpy.wait();
|
||||||
QVariantMap rsp = checkNotification(clientSpy, "Users.PushButtonAuthFinished").toMap();
|
QVariantMap rsp = checkNotification(clientSpy, "JSONRPC.PushButtonAuthFinished").toMap();
|
||||||
|
|
||||||
for (int i = 0; i < clientSpy.count(); i++) {
|
for (int i = 0; i < clientSpy.count(); i++) {
|
||||||
qCDebug(dcTests()) << "Notification:" << clientSpy.at(i);
|
qCDebug(dcTests()) << "Notification:" << clientSpy.at(i);
|
||||||
@ -267,7 +260,7 @@ void TestUsermanager::authenticatePushButtonAuthInterrupt()
|
|||||||
// request push button auth for client 1 (alice) and check for OK reply
|
// request push button auth for client 1 (alice) and check for OK reply
|
||||||
QVariantMap params;
|
QVariantMap params;
|
||||||
params.insert("deviceName", "alice");
|
params.insert("deviceName", "alice");
|
||||||
QVariant response = injectAndWait("Users.RequestPushButtonAuth", params, aliceId);
|
QVariant response = injectAndWait("JSONRPC.RequestPushButtonAuth", params, aliceId);
|
||||||
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true);
|
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true);
|
||||||
int transactionId1 = response.toMap().value("params").toMap().value("transactionId").toInt();
|
int transactionId1 = response.toMap().value("params").toMap().value("transactionId").toInt();
|
||||||
|
|
||||||
@ -276,7 +269,7 @@ void TestUsermanager::authenticatePushButtonAuthInterrupt()
|
|||||||
clientSpy.clear();
|
clientSpy.clear();
|
||||||
params.clear();
|
params.clear();
|
||||||
params.insert("deviceName", "mallory");
|
params.insert("deviceName", "mallory");
|
||||||
response = injectAndWait("Users.RequestPushButtonAuth", params, malloryId);
|
response = injectAndWait("JSONRPC.RequestPushButtonAuth", params, malloryId);
|
||||||
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true);
|
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true);
|
||||||
int transactionId2 = response.toMap().value("params").toMap().value("transactionId").toInt();
|
int transactionId2 = response.toMap().value("params").toMap().value("transactionId").toInt();
|
||||||
|
|
||||||
@ -292,7 +285,7 @@ void TestUsermanager::authenticatePushButtonAuthInterrupt()
|
|||||||
// alice should have received a failed notification. She knows something's wrong.
|
// alice should have received a failed notification. She knows something's wrong.
|
||||||
QVariantMap notification = QJsonDocument::fromJson(clientSpy.first().at(1).toByteArray()).toVariant().toMap();
|
QVariantMap notification = QJsonDocument::fromJson(clientSpy.first().at(1).toByteArray()).toVariant().toMap();
|
||||||
QCOMPARE(clientSpy.first().first().toUuid(), aliceId);
|
QCOMPARE(clientSpy.first().first().toUuid(), aliceId);
|
||||||
QCOMPARE(notification.value("notification").toString(), QLatin1String("Users.PushButtonAuthFinished"));
|
QCOMPARE(notification.value("notification").toString(), QLatin1String("JSONRPC.PushButtonAuthFinished"));
|
||||||
QCOMPARE(notification.value("params").toMap().value("transactionId").toInt(), transactionId1);
|
QCOMPARE(notification.value("params").toMap().value("transactionId").toInt(), transactionId1);
|
||||||
QCOMPARE(notification.value("params").toMap().value("success").toBool(), false);
|
QCOMPARE(notification.value("params").toMap().value("success").toBool(), false);
|
||||||
|
|
||||||
@ -306,7 +299,7 @@ void TestUsermanager::authenticatePushButtonAuthInterrupt()
|
|||||||
clientSpy.clear();
|
clientSpy.clear();
|
||||||
params.clear();
|
params.clear();
|
||||||
params.insert("deviceName", "alice");
|
params.insert("deviceName", "alice");
|
||||||
response = injectAndWait("Users.RequestPushButtonAuth", params, aliceId);
|
response = injectAndWait("JSONRPC.RequestPushButtonAuth", params, aliceId);
|
||||||
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true);
|
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true);
|
||||||
int transactionId3 = response.toMap().value("params").toMap().value("transactionId").toInt();
|
int transactionId3 = response.toMap().value("params").toMap().value("transactionId").toInt();
|
||||||
|
|
||||||
@ -321,7 +314,7 @@ void TestUsermanager::authenticatePushButtonAuthInterrupt()
|
|||||||
// mallory should have received a failed notification. She knows something's wrong.
|
// mallory should have received a failed notification. She knows something's wrong.
|
||||||
notification = QJsonDocument::fromJson(clientSpy.first().at(1).toByteArray()).toVariant().toMap();
|
notification = QJsonDocument::fromJson(clientSpy.first().at(1).toByteArray()).toVariant().toMap();
|
||||||
QCOMPARE(clientSpy.first().first().toUuid(), malloryId);
|
QCOMPARE(clientSpy.first().first().toUuid(), malloryId);
|
||||||
QCOMPARE(notification.value("notification").toString(), QLatin1String("Users.PushButtonAuthFinished"));
|
QCOMPARE(notification.value("notification").toString(), QLatin1String("JSONRPC.PushButtonAuthFinished"));
|
||||||
QCOMPARE(notification.value("params").toMap().value("transactionId").toInt(), transactionId2);
|
QCOMPARE(notification.value("params").toMap().value("transactionId").toInt(), transactionId2);
|
||||||
QCOMPARE(notification.value("params").toMap().value("success").toBool(), false);
|
QCOMPARE(notification.value("params").toMap().value("success").toBool(), false);
|
||||||
|
|
||||||
@ -345,7 +338,7 @@ void TestUsermanager::authenticatePushButtonAuthInterrupt()
|
|||||||
QCOMPARE(clientSpy.count(), 1);
|
QCOMPARE(clientSpy.count(), 1);
|
||||||
notification = QJsonDocument::fromJson(clientSpy.first().at(1).toByteArray()).toVariant().toMap();
|
notification = QJsonDocument::fromJson(clientSpy.first().at(1).toByteArray()).toVariant().toMap();
|
||||||
QCOMPARE(clientSpy.first().first().toUuid(), aliceId);
|
QCOMPARE(clientSpy.first().first().toUuid(), aliceId);
|
||||||
QCOMPARE(notification.value("notification").toString(), QLatin1String("Users.PushButtonAuthFinished"));
|
QCOMPARE(notification.value("notification").toString(), QLatin1String("JSONRPC.PushButtonAuthFinished"));
|
||||||
QCOMPARE(notification.value("params").toMap().value("transactionId").toInt(), transactionId3);
|
QCOMPARE(notification.value("params").toMap().value("transactionId").toInt(), transactionId3);
|
||||||
QCOMPARE(notification.value("params").toMap().value("success").toBool(), true);
|
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");
|
QVERIFY2(!notification.value("params").toMap().value("token").toByteArray().isEmpty(), "Token is empty while it shouldn't be");
|
||||||
@ -368,7 +361,7 @@ void TestUsermanager::authenticatePushButtonAuthConnectionDrop()
|
|||||||
// request push button auth for client 1 (alice) and check for OK reply
|
// request push button auth for client 1 (alice) and check for OK reply
|
||||||
QVariantMap params;
|
QVariantMap params;
|
||||||
params.insert("deviceName", "alice");
|
params.insert("deviceName", "alice");
|
||||||
QVariant response = injectAndWait("Users.RequestPushButtonAuth", params, aliceId);
|
QVariant response = injectAndWait("JSONRPC.RequestPushButtonAuth", params, aliceId);
|
||||||
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true);
|
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true);
|
||||||
|
|
||||||
// Disconnect alice
|
// Disconnect alice
|
||||||
@ -385,7 +378,7 @@ void TestUsermanager::authenticatePushButtonAuthConnectionDrop()
|
|||||||
// request push button auth for client 2 (bob) and check for OK reply
|
// request push button auth for client 2 (bob) and check for OK reply
|
||||||
params.clear();
|
params.clear();
|
||||||
params.insert("deviceName", "bob");
|
params.insert("deviceName", "bob");
|
||||||
response = injectAndWait("Users.RequestPushButtonAuth", params, bobId);
|
response = injectAndWait("JSONRPC.RequestPushButtonAuth", params, bobId);
|
||||||
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true);
|
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true);
|
||||||
int transactionId = response.toMap().value("params").toMap().value("transactionId").toInt();
|
int transactionId = response.toMap().value("params").toMap().value("transactionId").toInt();
|
||||||
|
|
||||||
@ -402,7 +395,7 @@ void TestUsermanager::authenticatePushButtonAuthConnectionDrop()
|
|||||||
QCOMPARE(clientSpy.count(), 1);
|
QCOMPARE(clientSpy.count(), 1);
|
||||||
QVariantMap notification = QJsonDocument::fromJson(clientSpy.first().at(1).toByteArray()).toVariant().toMap();
|
QVariantMap notification = QJsonDocument::fromJson(clientSpy.first().at(1).toByteArray()).toVariant().toMap();
|
||||||
QCOMPARE(clientSpy.first().first().toUuid(), bobId);
|
QCOMPARE(clientSpy.first().first().toUuid(), bobId);
|
||||||
QCOMPARE(notification.value("notification").toString(), QLatin1String("Users.PushButtonAuthFinished"));
|
QCOMPARE(notification.value("notification").toString(), QLatin1String("JSONRPC.PushButtonAuthFinished"));
|
||||||
QCOMPARE(notification.value("params").toMap().value("transactionId").toInt(), transactionId);
|
QCOMPARE(notification.value("params").toMap().value("transactionId").toInt(), transactionId);
|
||||||
QCOMPARE(notification.value("params").toMap().value("success").toBool(), true);
|
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");
|
QVERIFY2(!notification.value("params").toMap().value("token").toByteArray().isEmpty(), "Token is empty while it shouldn't be");
|
||||||
@ -469,7 +462,7 @@ void TestUsermanager::authenticateAfterPasswordChangeOK()
|
|||||||
params.insert("username", "valid@user.test");
|
params.insert("username", "valid@user.test");
|
||||||
params.insert("password", "Blubb123"); // New password, should be ok
|
params.insert("password", "Blubb123"); // New password, should be ok
|
||||||
params.insert("deviceName", "autotests");
|
params.insert("deviceName", "autotests");
|
||||||
QVariant response = injectAndWait("Users.Authenticate", params);
|
QVariant response = injectAndWait("JSONRPC.Authenticate", params);
|
||||||
|
|
||||||
m_apiToken = response.toMap().value("params").toMap().value("token").toByteArray();
|
m_apiToken = response.toMap().value("params").toMap().value("token").toByteArray();
|
||||||
QVERIFY2(!m_apiToken.isEmpty(), "Token should not be empty");
|
QVERIFY2(!m_apiToken.isEmpty(), "Token should not be empty");
|
||||||
@ -481,16 +474,27 @@ void TestUsermanager::authenticateAfterPasswordChangeFail()
|
|||||||
{
|
{
|
||||||
changePassword();
|
changePassword();
|
||||||
|
|
||||||
|
QSignalSpy disconnectedSpy(m_mockTcpServer, &MockTcpServer::clientDisconnected);
|
||||||
|
|
||||||
QVariantMap params;
|
QVariantMap params;
|
||||||
params.insert("username", "valid@user.test");
|
params.insert("username", "valid@user.test");
|
||||||
params.insert("password", "Bla1234*"); // Original password, should not be ok
|
params.insert("password", "Bla1234*"); // Original password, should not be ok
|
||||||
params.insert("deviceName", "autotests");
|
params.insert("deviceName", "autotests");
|
||||||
QVariant response = injectAndWait("Users.Authenticate", params);
|
QVariant response = injectAndWait("JSONRPC.Authenticate", params);
|
||||||
|
|
||||||
m_apiToken = response.toMap().value("params").toMap().value("token").toByteArray();
|
m_apiToken = response.toMap().value("params").toMap().value("token").toByteArray();
|
||||||
QVERIFY2(m_apiToken.isEmpty(), "Token should be empty");
|
QVERIFY2(m_apiToken.isEmpty(), "Token should be empty");
|
||||||
QVERIFY2(response.toMap().value("status").toString() == "success", "Error authenticating");
|
QVERIFY2(response.toMap().value("status").toString() == "success", "Error authenticating");
|
||||||
QCOMPARE(response.toMap().value("params").toMap().value("success").toString(), QString("false"));
|
QCOMPARE(response.toMap().value("params").toMap().value("success").toString(), QString("false"));
|
||||||
|
|
||||||
|
// Connection should drop
|
||||||
|
if (disconnectedSpy.count() == 0) disconnectedSpy.wait();
|
||||||
|
QVERIFY2(disconnectedSpy.count() == 1, "Connection should have dropped");
|
||||||
|
|
||||||
|
QTest::qWait(3200);
|
||||||
|
m_mockTcpServer->clientConnected(m_clientId);
|
||||||
|
injectAndWait("JSONRPC.Hello");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestUsermanager::getUserInfo()
|
void TestUsermanager::getUserInfo()
|
||||||
@ -522,8 +526,9 @@ void TestUsermanager::unauthenticatedCallAfterTokenRemove()
|
|||||||
}
|
}
|
||||||
QVERIFY2(spy.count() == 1, "Connection should be terminated!");
|
QVERIFY2(spy.count() == 1, "Connection should be terminated!");
|
||||||
|
|
||||||
// need to restart as our connection dies
|
QTest::qWait(3200);
|
||||||
restartServer();
|
m_mockTcpServer->clientConnected(m_clientId);
|
||||||
|
injectAndWait("JSONRPC.Hello");
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "testusermanager.moc"
|
#include "testusermanager.moc"
|
||||||
|
|||||||
@ -88,9 +88,9 @@ void NymeaTestBase::initTestCase(const QString &loggingRules)
|
|||||||
qCDebug(dcTests()) << "Nymea core instance initialized. Creating dummy user.";
|
qCDebug(dcTests()) << "Nymea core instance initialized. Creating dummy user.";
|
||||||
|
|
||||||
// Yes, we're intentionally mixing upper/lower case email here... username should not be case sensitive
|
// Yes, we're intentionally mixing upper/lower case email here... username should not be case sensitive
|
||||||
NymeaCore::instance()->userManager()->removeUser("dummy@guh.io");
|
NymeaCore::instance()->userManager()->removeUser("dummy");
|
||||||
NymeaCore::instance()->userManager()->createUser("dummy@guh.io", "DummyPW1!");
|
NymeaCore::instance()->userManager()->createUser("dummy", "DummyPW1!", "dummy@guh.io", "Dummy", Types::PermissionScopeAdmin);
|
||||||
m_apiToken = NymeaCore::instance()->userManager()->authenticate("Dummy@guh.io", "DummyPW1!", "testcase");
|
m_apiToken = NymeaCore::instance()->userManager()->authenticate("Dummy", "DummyPW1!", "testcase");
|
||||||
|
|
||||||
if (MockTcpServer::servers().isEmpty()) {
|
if (MockTcpServer::servers().isEmpty()) {
|
||||||
qCWarning(dcTests) << "no mock tcp server found";
|
qCWarning(dcTests) << "no mock tcp server found";
|
||||||
|
|||||||
Reference in New Issue
Block a user