diff --git a/libnymea-core/jsonrpc/actionhandler.cpp b/libnymea-core/jsonrpc/actionhandler.cpp
deleted file mode 100644
index b9f2999f..00000000
--- a/libnymea-core/jsonrpc/actionhandler.cpp
+++ /dev/null
@@ -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 .
-*
-* 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();
- registerEnum();
-
- // Objects
- registerObject();
- registerObject();
- registerObject();
- registerObject();
-
- // 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());
- returns.insert("deviceError", enumRef());
- 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());
- returns.insert("o:actionType", objectRef());
- 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());
- 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());
- returns.insert("deviceError", enumRef());
- 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(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::ThingErrorNoError).replace("ThingError", "DeviceError"));
- data.insert("actionType", pack(translatedActionType));
- return createReply(data);
- }
- }
- }
- QVariantMap data;
- data.insert("deviceError", enumValueName(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(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(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(info->status()).replace("Thing", "Device"));
- jsonReply->setData(data);
- jsonReply->finished();
- });
-
- return jsonReply;
-}
-
-}
diff --git a/libnymea-core/jsonrpc/actionhandler.h b/libnymea-core/jsonrpc/actionhandler.h
deleted file mode 100644
index 30ab6151..00000000
--- a/libnymea-core/jsonrpc/actionhandler.h
+++ /dev/null
@@ -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 .
-*
-* 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
diff --git a/libnymea-core/jsonrpc/configurationhandler.cpp b/libnymea-core/jsonrpc/configurationhandler.cpp
index ec861dfe..eb25923f 100644
--- a/libnymea-core/jsonrpc/configurationhandler.cpp
+++ b/libnymea-core/jsonrpc/configurationhandler.cpp
@@ -92,12 +92,12 @@ ConfigurationHandler::ConfigurationHandler(QObject *parent):
QString description; QVariantMap params; QVariantMap returns;
description = "Get the list of available timezones.";
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();
description = "Returns a list of locale codes available for the server. i.e. en_US, de_AT";
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();
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\"";
params.insert("timeZone", enumValueName(String));
returns.insert("configurationError", enumRef());
- registerMethod("SetTimeZone", description, params, returns, "Use System.SetTimeZone instead.");
+ registerMethod("SetTimeZone", description, params, returns, Types::PermissionScopeAdmin, "Use System.SetTimeZone instead.");
params.clear(); returns.clear();
description = "Sets the server language to the given language. See also: \"GetAvailableLanguages\"";
params.insert("language", enumValueName(String));
returns.insert("configurationError", enumRef());
- 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();
description = "Enable or disable the debug server.";
diff --git a/libnymea-core/jsonrpc/devicehandler.cpp b/libnymea-core/jsonrpc/devicehandler.cpp
deleted file mode 100644
index 8d52bf3d..00000000
--- a/libnymea-core/jsonrpc/devicehandler.cpp
+++ /dev/null
@@ -1,1092 +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 .
-*
-* 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 "devicehandler.h"
-#include "nymeacore.h"
-#include "integrations/thingmanager.h"
-#include "integrations/thing.h"
-#include "integrations/integrationplugin.h"
-#include "loggingcategories.h"
-#include "types/thingclass.h"
-#include "types/browseritem.h"
-#include "types/mediabrowseritem.h"
-#include "integrations/translator.h"
-#include "integrations/thingdiscoveryinfo.h"
-#include "integrations/thingpairinginfo.h"
-#include "integrations/thingsetupinfo.h"
-#include "integrations/browseresult.h"
-#include "integrations/browseritemresult.h"
-
-#include
-#include
-
-namespace nymeaserver {
-
-DeviceHandler::DeviceHandler(QObject *parent) :
- JsonHandler(parent)
-{
- // Enums
- registerEnum();
- registerEnum();
- registerEnum();
- registerEnum();
- registerEnum();
- registerFlag();
- registerEnum();
- registerEnum();
- registerEnum();
- registerEnum();
- registerEnum();
- registerEnum();
- registerEnum();
-
- // Objects
- registerObject();
- registerObject();
- registerUncreatableObject();
- registerUncreatableObject();
- registerObject();
- registerObject();
- registerObject();
- registerObject();
- registerObject();
- registerObject();
- registerObject();
- registerObject();
- registerObject();
- registerObject();
- registerUncreatableObject();
- registerUncreatableObject();
-
- // Regsitering browseritem manually for now. Not sure how to deal with the
- // polymorphism in int (e.g MediaBrowserItem)
- QVariantMap browserItem;
- browserItem.insert("id", enumValueName(String));
- browserItem.insert("displayName", enumValueName(String));
- browserItem.insert("description", enumValueName(String));
- browserItem.insert("icon", enumRef());
- browserItem.insert("thumbnail", enumValueName(String));
- browserItem.insert("executable", enumValueName(Bool));
- browserItem.insert("browsable", enumValueName(Bool));
- browserItem.insert("disabled", enumValueName(Bool));
- browserItem.insert("actionTypeIds", QVariantList() << enumValueName(Uuid));
- browserItem.insert("o:mediaIcon", enumRef());
- registerObject("BrowserItem", browserItem);
-
-
- // Methods
- QString description; QVariantMap returns; QVariantMap params;
- description = "Returns a list of supported Vendors.";
- returns.insert("vendors", objectRef());
- registerMethod("GetSupportedVendors", description, params, returns);
-
- params.clear(); returns.clear();
- description = "Returns a list of supported Device classes, optionally filtered by vendorId.";
- params.insert("o:vendorId", enumValueName(Uuid));
- returns.insert("deviceClasses", objectRef());
- registerMethod("GetSupportedDevices", description, params, returns);
-
- params.clear(); returns.clear();
- description = "Returns a list of loaded plugins.";
- returns.insert("plugins", objectRef());
- registerMethod("GetPlugins", description, params, returns);
-
- params.clear(); returns.clear();
- description = "Get a plugin's params.";
- params.insert("pluginId", enumValueName(Uuid));
- returns.insert("deviceError", enumRef());
- returns.insert("o:configuration", objectRef());
- registerMethod("GetPluginConfiguration", description, params, returns);
-
- params.clear(); returns.clear();
- description = "Set a plugin's params.";
- params.insert("pluginId", enumValueName(Uuid));
- params.insert("configuration", objectRef());
- returns.insert("deviceError", enumRef());
- registerMethod("SetPluginConfiguration", description, params, returns);
-
- params.clear(); returns.clear();
- description = "Add a configured device with a setupMethod of SetupMethodJustAdd. "
- "For devices with a setupMethod different than SetupMethodJustAdd, use PairDevice. "
- "Devices with CreateMethodJustAdd require all parameters to be supplied here. "
- "Devices with CreateMethodDiscovery require the use of a deviceDescriptorId. For discovered "
- "devices params are not required and will be taken from the DeviceDescriptor, however, they "
- "may be overridden by supplying deviceParams.";
- params.insert("deviceClassId", enumValueName(Uuid));
- params.insert("name", enumValueName(String));
- params.insert("o:deviceDescriptorId", enumValueName(Uuid));
- params.insert("o:deviceParams", objectRef());
- returns.insert("deviceError", enumRef());
- returns.insert("o:deviceId", enumValueName(Uuid));
- returns.insert("o:displayMessage", enumValueName(String));
- registerMethod("AddConfiguredDevice", description, params, returns);
-
- params.clear(); returns.clear();
- description = "Pair a device. "
- "Use this to set up or reconfigure devices for DeviceClasses with a setupMethod different than SetupMethodJustAdd. "
- "Depending on the CreateMethod and whether a new devices is set up or an existing one is reconfigured, different parameters "
- "are required:\n"
- "CreateMethodJustAdd takes the deviceClassId and the parameters to be used with that device. "
- "If an existing device should be reconfigured, the deviceId of said device should be given additionally.\n"
- "CreateMethodDiscovery requires the use of a deviceDescriptorId, previously obtained with DiscoverDevices. Optionally, "
- "parameters can be overridden with the give deviceParams. DeviceDescriptors containing a deviceId will reconfigure that "
- "device, descriptors without deviceId will add a new one.\n"
- "If success is true, the return values will contain a pairingTransactionId, a displayMessage and "
- "the setupMethod. Depending on the setupMethod, the application should present the use an appropriate login mask, "
- "that is, For SetupMethodDisplayPin the user should enter a pin that is displayed on the device, for SetupMethodEnterPin the "
- "application should present the given PIN so the user can enter it on the device. For SetupMethodPushButton, the displayMessage "
- "shall be presented to the user as informational hints to press a button on the device. For SetupMethodUserAndPassword a login "
- "mask for a user and password login should be presented to the user. In case of SetupMethodOAuth, an OAuth URL will be returned "
- "which shall be opened in a web view to allow the user logging in.\n"
- "Once the login procedure has completed, the application shall proceed with ConfirmPairing, providing the results of the pairing "
- "procedure.";
- params.insert("o:deviceClassId", enumValueName(Uuid));
- params.insert("o:name", enumValueName(String));
- params.insert("o:deviceDescriptorId", enumValueName(Uuid));
- params.insert("o:deviceParams", objectRef());
- params.insert("o:deviceId", enumValueName(Uuid));
- returns.insert("deviceError", enumRef());
- returns.insert("o:setupMethod", enumRef());
- returns.insert("o:pairingTransactionId", enumValueName(Uuid));
- returns.insert("o:displayMessage", enumValueName(String));
- returns.insert("o:oAuthUrl", enumValueName(String));
- returns.insert("o:pin", enumValueName(String));
- registerMethod("PairDevice", description, params, returns);
-
- params.clear(); returns.clear();
- description = "Confirm an ongoing pairing. For SetupMethodUserAndPassword, provide the username in the \"username\" field "
- "and the password in the \"secret\" field. For SetupMethodEnterPin and provide the PIN in the \"secret\" "
- "field. In case of SetupMethodOAuth, the previously opened web view will eventually be redirected to http://128.0.0.1:8888 "
- "and the OAuth code as query parameters to this url. Provide the entire unmodified URL in the secret field.";
- params.insert("pairingTransactionId", enumValueName(Uuid));
- params.insert("o:username", enumValueName(String));
- params.insert("o:secret", enumValueName(String));
- returns.insert("deviceError", enumRef());
- returns.insert("o:displayMessage", enumValueName(String));
- returns.insert("o:deviceId", enumValueName(Uuid));
- registerMethod("ConfirmPairing", description, params, returns);
-
- params.clear(); returns.clear();
- description = "Returns a list of configured devices, optionally filtered by deviceId.";
- params.insert("o:deviceId", enumValueName(Uuid));
- returns.insert("devices", objectRef());
- registerMethod("GetConfiguredDevices", description, params, returns);
-
- params.clear(); returns.clear();
- description = "Performs a device discovery and returns the results. This function may take a while to return. "
- "Note that this method will include all the found devices, that is, including devices that "
- "already have been added. Those devices will have deviceId set to the device id of the already "
- "added device. Such results may be used to reconfigure existing devices and might be filtered "
- "in cases where only unknown devices are of interest.";
- params.insert("deviceClassId", enumValueName(Uuid));
- params.insert("o:discoveryParams", objectRef());
- returns.insert("deviceError", enumRef());
- returns.insert("o:displayMessage", enumValueName(String));
- returns.insert("o:deviceDescriptors", objectRef());
- registerMethod("GetDiscoveredDevices", description, params, returns);
-
- params.clear(); returns.clear();
- description = "Reconfigure a device. This comes down to removing and recreating a device with new parameters "
- "but keeping its device id the same (and with that keeping rules, tags etc). For devices with "
- "create method CreateMethodDiscovery, a discovery (GetDiscoveredDevices) shall be performed first "
- "and this method is to be called with a deviceDescriptorId of the re-discovered device instead of "
- "the deviceId directly. Device parameters will be taken from the discovery, but can be overridden "
- "individually here by providing them in the deviceParams parameter. Only writable parameters can "
- "be changed.";
- params.insert("o:deviceId", enumValueName(Uuid));
- params.insert("o:deviceDescriptorId", enumValueName(Uuid));
- params.insert("o:deviceParams", objectRef());
- returns.insert("deviceError", enumRef());
- returns.insert("o:displayMessage", enumValueName(String));
- registerMethod("ReconfigureDevice", description, params, returns);
-
- params.clear(); returns.clear();
- description = "Edit the name of a device. This method does not change the "
- "configuration of the device.";
- params.insert("deviceId", enumValueName(Uuid));
- params.insert("name", enumValueName(String));
- returns.insert("deviceError", enumRef());
- registerMethod("EditDevice", description, params, returns);
-
- params.clear(); returns.clear();
- description = "Change the settings of a device.";
- params.insert("deviceId", enumValueName(Uuid));
- params.insert("settings", objectRef());
- returns.insert("deviceError", enumRef());
- registerMethod("SetDeviceSettings", description, params, returns);
-
- params.clear(); returns.clear();
- description = "Remove a device from the system.";
- params.insert("deviceId", enumValueName(Uuid));
- params.insert("o:removePolicy", enumRef());
- QVariantMap policy;
- policy.insert("ruleId", enumValueName(Uuid));
- policy.insert("policy", enumRef());
- QVariantList removePolicyList;
- removePolicyList.append(policy);
- params.insert("o:removePolicyList", removePolicyList);
- returns.insert("deviceError", enumRef());
- returns.insert("o:ruleIds", QVariantList() << enumValueName(Uuid));
- registerMethod("RemoveConfiguredDevice", description, params, returns);
-
- params.clear(); returns.clear();
- description = "Get event types for a specified deviceClassId.";
- params.insert("deviceClassId", enumValueName(Uuid));
- returns.insert("eventTypes", objectRef());
- registerMethod("GetEventTypes", description, params, returns);
-
- params.clear(); returns.clear();
- description = "Get action types for a specified deviceClassId.";
- params.insert("deviceClassId", enumValueName(Uuid));
- returns.insert("actionTypes", objectRef());
- registerMethod("GetActionTypes", description, params, returns);
-
- params.clear(); returns.clear();
- description = "Get state types for a specified deviceClassId.";
- params.insert("deviceClassId", enumValueName(Uuid));
- returns.insert("stateTypes", objectRef());
- registerMethod("GetStateTypes", description, params, returns);
-
- params.clear(); returns.clear();
- description = "Get the value of the given device and the given stateType";
- params.insert("deviceId", enumValueName(Uuid));
- params.insert("stateTypeId", enumValueName(Uuid));
- returns.insert("deviceError", enumRef());
- returns.insert("o:value", enumValueName(Variant));
- registerMethod("GetStateValue", description, params, returns);
-
- params.clear(); returns.clear();
- description = "Get all the state values of the given device.";
- params.insert("deviceId", enumValueName(Uuid));
- returns.insert("deviceError", enumRef());
- returns.insert("o:values", objectRef());
- registerMethod("GetStateValues", description, params, returns);
-
- params.clear(); returns.clear();
- description = "Browse a device. If a DeviceClass indicates a device is browsable, this method will return "
- "the BrowserItems. If no parameter besides the deviceId is used, the root node of this device "
- "will be returned. Any returned item which is browsable can be passed as node. Results will be "
- "children of the given node.\n"
- "In case of an error during browsing, the error will be indicated and the displayMessage may contain "
- "additional information for the user. The displayMessage will be translated. A client UI showing this "
- "message to the user should be prepared for empty, but also longer strings.";
- params.insert("deviceId", enumValueName(Uuid));
- params.insert("o:itemId", enumValueName(String));
- returns.insert("deviceError", enumRef());
- returns.insert("o:displayMessage", enumValueName(String));
- returns.insert("items", QVariantList() << objectRef("BrowserItem"));
- registerMethod("BrowseDevice", description, params, returns);
-
- params.clear(); returns.clear();
- description = "Get a single item from the browser. This won't give any more info on an item than a regular browseDevice "
- "call, but it allows to fetch details of an item if only the ID is known.\n"
- "In case of an error during browsing, the error will be indicated and the displayMessage may contain "
- "additional information for the user. The displayMessage will be translated. A client UI showing this "
- "message to the user should be prepared for empty, but also longer strings.";
- params.insert("deviceId", enumValueName(Uuid));
- params.insert("o:itemId", enumValueName(String));
- returns.insert("deviceError", enumRef());
- returns.insert("o:displayMessage", enumValueName(String));
- returns.insert("o:item", objectRef("BrowserItem"));
- registerMethod("GetBrowserItem", description, params, returns);
-
- params.clear(); returns.clear();
- description = "Execute a single action.";
- params.insert("actionTypeId", enumValueName(Uuid));
- params.insert("deviceId", enumValueName(Uuid));
- params.insert("o:params", objectRef());
- returns.insert("deviceError", enumRef());
- returns.insert("o:displayMessage", enumValueName(String));
- registerMethod("ExecuteAction", description, params, returns);
-
- params.clear(); returns.clear();
- description = "Execute the item identified by itemId on the given device.\n"
- "In case of an error during execution, the error will be indicated and the displayMessage may contain "
- "additional information for the user. The displayMessage will be translated. A client UI showing this "
- "message to the user should be prepared for empty, but also longer strings.";
- params.insert("deviceId", enumValueName(Uuid));
- params.insert("itemId", enumValueName(String));
- returns.insert("deviceError", enumRef());
- returns.insert("o:displayMessage", enumValueName(String));
- registerMethod("ExecuteBrowserItem", description, params, returns);
-
- params.clear(); returns.clear();
- description = "Execute the action for the browser item identified by actionTypeId and the itemId on the given device.\n"
- "In case of an error during execution, the error will be indicated and the displayMessage may contain "
- "additional information for the user. The displayMessage will be translated. A client UI showing this "
- "message to the user should be prepared for empty, but also longer strings.";
- params.insert("deviceId", enumValueName(Uuid));
- params.insert("itemId", enumValueName(String));
- params.insert("actionTypeId", enumValueName(Uuid));
- params.insert("o:params", objectRef());
- returns.insert("deviceError", enumRef());
- returns.insert("o:displayMessage", enumValueName(String));
- registerMethod("ExecuteBrowserItemAction", description, params, returns);
-
- // Notifications
- params.clear(); returns.clear();
- description = "Emitted whenever a State of a device changes.";
- params.insert("deviceId", enumValueName(Uuid));
- params.insert("stateTypeId", enumValueName(Uuid));
- params.insert("value", enumValueName(Variant));
- registerNotification("StateChanged", description, params);
-
- params.clear(); returns.clear();
- description = "Emitted whenever a Device was removed.";
- params.insert("deviceId", enumValueName(Uuid));
- registerNotification("DeviceRemoved", description, params);
-
- params.clear(); returns.clear();
- description = "Emitted whenever a Device was added.";
- params.insert("device", objectRef());
- registerNotification("DeviceAdded", description, params);
-
- params.clear(); returns.clear();
- description = "Emitted whenever the params, name or setupStatus of a Device changes.";
- params.insert("device", objectRef());
- registerNotification("DeviceChanged", description, params);
-
- params.clear(); returns.clear();
- description = "Emitted whenever the setting of a Device is changed.";
- params.insert("deviceId", enumValueName(Uuid));
- params.insert("paramTypeId", enumValueName(Uuid));
- params.insert("value", enumValueName(Variant));
- registerNotification("DeviceSettingChanged", description, params);
-
- params.clear(); returns.clear();
- description = "Emitted whenever a plugin's configuration is changed.";
- params.insert("pluginId", enumValueName(Uuid));
- params.insert("configuration", objectRef());
- registerNotification("PluginConfigurationChanged", description, params);
-
- params.clear(); returns.clear();
- description = "Emitted whenever an Event is triggered.";
- params.insert("event", objectRef());
- registerNotification("EventTriggered", description, params);
- connect(NymeaCore::instance(), &NymeaCore::eventTriggered, this, [this](const Event &event){
- QVariantMap params;
- params.insert("event", pack(event));
- emit EventTriggered(params);
- });
-
- connect(NymeaCore::instance(), &NymeaCore::pluginConfigChanged, this, &DeviceHandler::pluginConfigChanged);
- connect(NymeaCore::instance(), &NymeaCore::thingStateChanged, this, &DeviceHandler::deviceStateChanged);
- connect(NymeaCore::instance(), &NymeaCore::thingRemoved, this, &DeviceHandler::deviceRemovedNotification);
- connect(NymeaCore::instance(), &NymeaCore::thingAdded, this, &DeviceHandler::deviceAddedNotification);
- connect(NymeaCore::instance(), &NymeaCore::thingChanged, this, &DeviceHandler::deviceChangedNotification);
- connect(NymeaCore::instance(), &NymeaCore::thingSettingChanged, this, &DeviceHandler::deviceSettingChangedNotification);
-
- connect(NymeaCore::instance(), &NymeaCore::initialized, this, [=](){
- // Generating cache hashes.
- // NOTE: We need to sort the lists to get a stable result
- QHash thingClassesMap;
- foreach (const ThingClass &tc, NymeaCore::instance()->thingManager()->supportedThings()) {
- thingClassesMap.insert(tc.id(), tc);
- }
- QList thingClassIds = thingClassesMap.keys();
- std::sort(thingClassIds.begin(), thingClassIds.end());
- DeviceClasses thingClasses;
- foreach (const ThingClassId &id, thingClassIds) {
- thingClasses.append(thingClassesMap.value(id));
- }
- QByteArray hash = QCryptographicHash::hash(QJsonDocument::fromVariant(pack(thingClasses)).toJson(), QCryptographicHash::Md5).toHex();
- m_cacheHashes.insert("GetSupportedDevices", hash);
-
- QHash vendorsMap;
- foreach (const Vendor &v, NymeaCore::instance()->thingManager()->supportedVendors()) {
- vendorsMap.insert(v.id(), v);
- }
- QList vendorIds = vendorsMap.keys();
- std::sort(vendorIds.begin(), vendorIds.end());
- Vendors vendors;
- foreach (const VendorId &id, vendorIds) {
- vendors.append(vendorsMap.value(id));
- }
- hash = QCryptographicHash::hash(QJsonDocument::fromVariant(pack(vendors)).toJson(), QCryptographicHash::Md5).toHex();
- m_cacheHashes.insert("GetSupportedVendors", hash);
- });
-
-}
-
-/*! Returns the name of the \l{DeviceHandler}. In this case \b Devices.*/
-QString DeviceHandler::name() const
-{
- return "Devices";
-}
-
-QHash DeviceHandler::cacheHashes() const
-{
- return m_cacheHashes;
-}
-
-JsonReply* DeviceHandler::GetSupportedVendors(const QVariantMap ¶ms, const JsonContext &context) const
-{
- Q_UNUSED(params)
- QVariantList vendors;
- foreach (const Vendor &vendor, NymeaCore::instance()->thingManager()->supportedVendors()) {
- Vendor translatedVendor = NymeaCore::instance()->thingManager()->translateVendor(vendor, context.locale());
- vendors.append(pack(translatedVendor));
- }
-
- QVariantMap returns;
- returns.insert("vendors", vendors);
- return createReply(returns);
-}
-
-JsonReply* DeviceHandler::GetSupportedDevices(const QVariantMap ¶ms, const JsonContext &context) const
-{
- VendorId vendorId = VendorId(params.value("vendorId").toString());
- QVariantMap returns;
- QVariantList deviceClasses;
- foreach (const DeviceClass &deviceClass, NymeaCore::instance()->thingManager()->supportedThings(vendorId)) {
- DeviceClass translatedDeviceClass = NymeaCore::instance()->thingManager()->translateThingClass(deviceClass, context.locale());
- deviceClasses.append(pack(translatedDeviceClass));
- }
-
- returns.insert("deviceClasses", deviceClasses);
- return createReply(returns);
-}
-
-JsonReply *DeviceHandler::GetDiscoveredDevices(const QVariantMap ¶ms, const JsonContext &context) const
-{
- QLocale locale = context.locale();
-
- QVariantMap returns;
-
- ThingClassId thingClassId = ThingClassId(params.value("deviceClassId").toString());
- ParamList discoveryParams = unpack(params.value("discoveryParams"));
-
- JsonReply *reply = createAsyncReply("GetDiscoveredDevices");
- ThingDiscoveryInfo *info = NymeaCore::instance()->thingManager()->discoverThings(thingClassId, discoveryParams);
- connect(info, &ThingDiscoveryInfo::finished, reply, [this, reply, info, locale](){
- QVariantMap returns;
- returns.insert("deviceError", enumValueName(info->status()).replace("Thing", "Device"));
-
- if (info->status() == Device::ThingErrorNoError) {
- QVariantList deviceDescriptorList;
- foreach (const ThingDescriptor &thingDescriptor, info->thingDescriptors()) {
- QVariantMap packedDescriptor = pack(thingDescriptor).toMap();
- if (packedDescriptor.contains("thingId")) {
- packedDescriptor.insert("deviceId", packedDescriptor.value("thingId"));
- }
- deviceDescriptorList.append(packedDescriptor);
- }
- returns.insert("deviceDescriptors", deviceDescriptorList);
- }
-
- if (!info->displayMessage().isEmpty()) {
- returns.insert("displayMessage", info->translatedDisplayMessage(locale));
- }
-
- reply->setData(returns);
- reply->finished();
-
- });
- return reply;
-}
-
-JsonReply* DeviceHandler::GetPlugins(const QVariantMap ¶ms, const JsonContext &context) const
-{
- Q_UNUSED(params)
- QVariantList plugins;
- foreach (IntegrationPlugin* plugin, NymeaCore::instance()->thingManager()->plugins()) {
- QVariantMap packedPlugin = pack(*plugin).toMap();
- packedPlugin["displayName"] = NymeaCore::instance()->thingManager()->translate(plugin->pluginId(), plugin->pluginDisplayName(), context.locale());
- plugins.append(packedPlugin);
- }
-
- QVariantMap returns;
- returns.insert("plugins", plugins);
- return createReply(returns);
-}
-
-JsonReply *DeviceHandler::GetPluginConfiguration(const QVariantMap ¶ms) const
-{
- QVariantMap returns;
-
- IntegrationPlugin *plugin = NymeaCore::instance()->thingManager()->plugins().findById(PluginId(params.value("pluginId").toString()));
- if (!plugin) {
- returns.insert("deviceError", enumValueName(Device::ThingErrorPluginNotFound).replace("Thing", "Device"));
- return createReply(returns);
- }
-
- QVariantList paramVariantList;
- foreach (const Param ¶m, plugin->configuration()) {
- paramVariantList.append(pack(param));
- }
- returns.insert("configuration", paramVariantList);
- returns.insert("deviceError", enumValueName(Device::ThingErrorNoError).replace("Thing", "Device"));
- return createReply(returns);
-}
-
-JsonReply* DeviceHandler::SetPluginConfiguration(const QVariantMap ¶ms)
-{
- QVariantMap returns;
- PluginId pluginId = PluginId(params.value("pluginId").toString());
- ParamList pluginParams = unpack(params.value("configuration"));
- Device::ThingError result = NymeaCore::instance()->thingManager()->setPluginConfig(pluginId, pluginParams);
- returns.insert("deviceError",enumValueName(result).replace("Thing", "Device"));
- return createReply(returns);
-}
-
-JsonReply* DeviceHandler::AddConfiguredDevice(const QVariantMap ¶ms, const JsonContext &context)
-{
- ThingClassId thingClassId(params.value("deviceClassId").toString());
- QString deviceName = params.value("name").toString();
- ParamList deviceParams = unpack(params.value("deviceParams"));
- ThingDescriptorId deviceDescriptorId(params.value("deviceDescriptorId").toString());
- QLocale locale = context.locale();
-
- JsonReply *jsonReply = createAsyncReply("AddConfiguredDevice");
-
- ThingSetupInfo *info;
- if (deviceDescriptorId.isNull()) {
- info = NymeaCore::instance()->thingManager()->addConfiguredThing(thingClassId, deviceParams, deviceName);
- } else {
- info = NymeaCore::instance()->thingManager()->addConfiguredThing(deviceDescriptorId, deviceParams, deviceName);
- }
- connect(info, &ThingSetupInfo::finished, jsonReply, [info, jsonReply, locale](){
- QVariantMap returns;
- returns.insert("deviceError", enumValueName(info->status()).replace("Thing", "Device"));
-
- if (!info->displayMessage().isEmpty()) {
- returns.insert("displayMessage", info->translatedDisplayMessage(locale));
- }
-
- if(info->status() == Device::ThingErrorNoError) {
- returns.insert("deviceId", info->thing()->id());
- }
- jsonReply->setData(returns);
- jsonReply->finished();
-
- });
- return jsonReply;
-}
-
-JsonReply *DeviceHandler::PairDevice(const QVariantMap ¶ms, const JsonContext &context)
-{
- QString deviceName = params.value("name").toString();
- ParamList deviceParams = unpack(params.value("deviceParams"));
- QLocale locale = context.locale();
-
- ThingPairingInfo *info;
- if (params.contains("deviceDescriptorId")) {
- ThingDescriptorId deviceDescriptorId = ThingDescriptorId(params.value("deviceDescriptorId").toString());
- info = NymeaCore::instance()->thingManager()->pairThing(deviceDescriptorId, deviceParams, deviceName);
- } else if (params.contains("deviceId")) {
- ThingId thingId = ThingId(params.value("deviceId").toString());
- info = NymeaCore::instance()->thingManager()->pairThing(thingId, deviceParams, deviceName);
- } else {
- ThingClassId thingClassId(params.value("deviceClassId").toString());
- info = NymeaCore::instance()->thingManager()->pairThing(thingClassId, deviceParams, deviceName);
- }
-
- JsonReply *jsonReply = createAsyncReply("PairDevice");
-
- connect(info, &ThingPairingInfo::finished, jsonReply, [jsonReply, info, locale](){
- QVariantMap returns;
- returns.insert("deviceError", enumValueName(info->status()).replace("Thing", "Device"));
- returns.insert("pairingTransactionId", info->transactionId().toString());
-
- if (info->status() == Device::ThingErrorNoError) {
- ThingClass thingClass = NymeaCore::instance()->thingManager()->findThingClass(info->thingClassId());
- returns.insert("setupMethod", enumValueName(thingClass.setupMethod()));
- }
-
- if (!info->displayMessage().isEmpty()) {
- returns.insert("displayMessage", info->translatedDisplayMessage(locale));
- }
-
- if (!info->oAuthUrl().isEmpty()) {
- returns.insert("oAuthUrl", info->oAuthUrl());
- }
-
- jsonReply->setData(returns);
- jsonReply->finished();
- });
-
- return jsonReply;
-}
-
-JsonReply *DeviceHandler::ConfirmPairing(const QVariantMap ¶ms, const JsonContext &context)
-{
- PairingTransactionId pairingTransactionId = PairingTransactionId(params.value("pairingTransactionId").toString());
- QString secret = params.value("secret").toString();
- QString username = params.value("username").toString();
- QLocale locale = context.locale();
-
- JsonReply *jsonReply = createAsyncReply("ConfirmPairing");
-
- ThingPairingInfo *info = NymeaCore::instance()->thingManager()->confirmPairing(pairingTransactionId, username, secret);
- connect(info, &ThingPairingInfo::finished, jsonReply, [info, jsonReply, locale](){
-
- QVariantMap returns;
- returns.insert("deviceError", enumValueName(info->status()).replace("Thing", "Device"));
- if (!info->displayMessage().isEmpty()) {
- returns.insert("displayMessage", info->translatedDisplayMessage(locale));
- }
- if (info->status() == Device::ThingErrorNoError) {
- returns.insert("deviceId", info->thingId().toString());
- }
- jsonReply->setData(returns);
- jsonReply->finished();
- });
-
- return jsonReply;
-}
-
-JsonReply* DeviceHandler::GetConfiguredDevices(const QVariantMap ¶ms, const JsonContext &context) const
-{
- QVariantMap returns;
- QVariantList configuredDeviceList;
- if (params.contains("deviceId")) {
- Thing *thing = NymeaCore::instance()->thingManager()->findConfiguredThing(ThingId(params.value("deviceId").toString()));
- if (!thing) {
- returns.insert("devices", QVariantList());
- return createReply(returns);
- } else {
- QVariantMap deviceMap = pack(thing).toMap();
- // Patch in deviceClassId
- deviceMap.insert("deviceClassId", deviceMap.value("thingClassId"));
- configuredDeviceList.append(deviceMap);
- }
- } else {
- foreach (Thing *thing, NymeaCore::instance()->thingManager()->configuredThings()) {
- QVariantMap packedThing = pack(thing).toMap();
- QString translatedSetupStatus = NymeaCore::instance()->thingManager()->translate(thing->pluginId(), thing->setupDisplayMessage(), context.locale());
- if (!translatedSetupStatus.isEmpty()) {
- packedThing["setupDisplayMessage"] = translatedSetupStatus;
- }
- // Patch in deviceClassId
- packedThing.insert("deviceClassId", packedThing.value("thingClassId"));
- configuredDeviceList.append(packedThing);
- }
- }
- returns.insert("devices", configuredDeviceList);
- return createReply(returns);
-}
-
-JsonReply *DeviceHandler::ReconfigureDevice(const QVariantMap ¶ms, const JsonContext &context)
-{
- ThingId thingId = ThingId(params.value("deviceId").toString());
- ParamList deviceParams = unpack(params.value("deviceParams"));
- ThingDescriptorId deviceDescriptorId(params.value("deviceDescriptorId").toString());
- QLocale locale = context.locale();
-
- JsonReply *jsonReply = createAsyncReply("ReconfigureDevice");
-
- ThingSetupInfo *info;
- if (!deviceDescriptorId.isNull()) {
- info = NymeaCore::instance()->thingManager()->reconfigureThing(deviceDescriptorId, deviceParams);
- } else if (!thingId.isNull()){
- info = NymeaCore::instance()->thingManager()->reconfigureThing(thingId, deviceParams);
- } else {
- qCWarning(dcJsonRpc()) << "Either deviceId or deviceDescriptorId are required";
- QVariantMap ret;
- ret.insert("deviceError", enumValueName(Device::ThingErrorMissingParameter));
- return createReply(ret);
- }
-
- connect(info, &ThingSetupInfo::finished, jsonReply, [info, jsonReply, locale](){
-
- QVariantMap returns;
- returns.insert("deviceError", enumValueName(info->status()).replace("Thing", "Device"));
- returns.insert("displayMessage", info->translatedDisplayMessage(locale));
- jsonReply->setData(returns);
- jsonReply->finished();
-
- });
-
- return jsonReply;
-}
-
-JsonReply *DeviceHandler::EditDevice(const QVariantMap ¶ms)
-{
- ThingId thingId = ThingId(params.value("deviceId").toString());
- QString name = params.value("name").toString();
-
- qCDebug(dcJsonRpc()) << "Edit device" << thingId << name;
-
- Device::ThingError status = NymeaCore::instance()->thingManager()->editThing(thingId, name);
-
- return createReply(statusToReply(status));
-}
-
-JsonReply* DeviceHandler::RemoveConfiguredDevice(const QVariantMap ¶ms)
-{
- QVariantMap returns;
- ThingId thingId = ThingId(params.value("deviceId").toString());
-
- // global removePolicy has priority
- if (params.contains("removePolicy")) {
- RuleEngine::RemovePolicy removePolicy = params.value("removePolicy").toString() == "RemovePolicyCascade" ? RuleEngine::RemovePolicyCascade : RuleEngine::RemovePolicyUpdate;
- Device::ThingError status = NymeaCore::instance()->removeConfiguredThing(thingId, removePolicy);
- returns.insert("deviceError", enumValueName(status).replace("Thing", "Device"));
- return createReply(returns);
- }
-
- QHash removePolicyList;
- foreach (const QVariant &variant, params.value("removePolicyList").toList()) {
- RuleId ruleId = RuleId(variant.toMap().value("ruleId").toString());
- RuleEngine::RemovePolicy policy = variant.toMap().value("policy").toString() == "RemovePolicyCascade" ? RuleEngine::RemovePolicyCascade : RuleEngine::RemovePolicyUpdate;
- removePolicyList.insert(ruleId, policy);
- }
-
- QPair > status = NymeaCore::instance()->removeConfiguredThing(thingId, removePolicyList);
- returns.insert("deviceError", enumValueName(status.first).replace("Thing", "Device"));
-
- if (!status.second.isEmpty()) {
- QVariantList ruleIdList;
- foreach (const RuleId &ruleId, status.second) {
- ruleIdList.append(ruleId.toString());
- }
- returns.insert("ruleIds", ruleIdList);
- }
-
- return createReply(returns);
-}
-
-JsonReply *DeviceHandler::SetDeviceSettings(const QVariantMap ¶ms)
-{
- ThingId thingId = ThingId(params.value("deviceId").toString());
- ParamList settings = unpack(params.value("settings"));
- Device::ThingError status = NymeaCore::instance()->thingManager()->setThingSettings(thingId, settings);
- return createReply(statusToReply(status));
-}
-
-JsonReply* DeviceHandler::GetEventTypes(const QVariantMap ¶ms, const JsonContext &context) const
-{
- ThingClass deviceClass = NymeaCore::instance()->thingManager()->findThingClass(ThingClassId(params.value("deviceClassId").toString()));
- ThingClass translatedDeviceClass = NymeaCore::instance()->thingManager()->translateThingClass(deviceClass, context.locale());
-
- QVariantMap returns;
- returns.insert("eventTypes", pack(translatedDeviceClass.eventTypes()));
- return createReply(returns);
-}
-
-JsonReply* DeviceHandler::GetActionTypes(const QVariantMap ¶ms, const JsonContext &context) const
-{
- ThingClass deviceClass = NymeaCore::instance()->thingManager()->findThingClass(ThingClassId(params.value("deviceClassId").toString()));
- ThingClass translatedDeviceClass = NymeaCore::instance()->thingManager()->translateThingClass(deviceClass, context.locale());
-
- QVariantMap returns;
- returns.insert("actionTypes", pack(translatedDeviceClass.actionTypes()));
- return createReply(returns);
-}
-
-JsonReply* DeviceHandler::GetStateTypes(const QVariantMap ¶ms, const JsonContext &context) const
-{
- ThingClass deviceClass = NymeaCore::instance()->thingManager()->findThingClass(ThingClassId(params.value("deviceClassId").toString()));
- ThingClass translatedDeviceClass = NymeaCore::instance()->thingManager()->translateThingClass(deviceClass, context.locale());
-
- QVariantMap returns;
- returns.insert("stateTypes", pack(translatedDeviceClass.stateTypes()));
- return createReply(returns);
-}
-
-JsonReply* DeviceHandler::GetStateValue(const QVariantMap ¶ms) const
-{
- Thing *thing = NymeaCore::instance()->thingManager()->findConfiguredThing(ThingId(params.value("deviceId").toString()));
- if (!thing) {
- return createReply(statusToReply(Device::ThingErrorThingNotFound));
- }
- StateTypeId stateTypeId = StateTypeId(params.value("stateTypeId").toString());
- if (!thing->hasState(stateTypeId)) {
- return createReply(statusToReply(Device::ThingErrorStateTypeNotFound));
- }
-
- QVariantMap returns = statusToReply(Device::ThingErrorNoError);
- returns.insert("value", thing->state(stateTypeId).value());
- return createReply(returns);
-}
-
-JsonReply *DeviceHandler::GetStateValues(const QVariantMap ¶ms) const
-{
- Thing *thing = NymeaCore::instance()->thingManager()->findConfiguredThing(ThingId(params.value("deviceId").toString()));
- if (!thing) {
- return createReply(statusToReply(Device::ThingErrorThingNotFound));
- }
-
- QVariantMap returns = statusToReply(Device::ThingErrorNoError);
- returns.insert("values", pack(thing->states()));
- return createReply(returns);
-}
-
-JsonReply *DeviceHandler::BrowseDevice(const QVariantMap ¶ms, const JsonContext &context) const
-{
- ThingId thingId = ThingId(params.value("deviceId").toString());
- QString itemId = params.value("itemId").toString();
-
- JsonReply *jsonReply = createAsyncReply("BrowseDevice");
-
- BrowseResult *result = NymeaCore::instance()->thingManager()->browseThing(thingId, itemId, context.locale());
- connect(result, &BrowseResult::finished, jsonReply, [this, jsonReply, result, context](){
-
- QVariantMap returns = statusToReply(result->status());
- QVariantList list;
- foreach (const BrowserItem &item, result->items()) {
- list.append(packBrowserItem(item));
- }
- returns.insert("items", list);
- if (!result->displayMessage().isEmpty()) {
- returns.insert("displayMessage", result->translatedDisplayMessage(context.locale()));
- }
- jsonReply->setData(returns);
- jsonReply->finished();
- });
-
- return jsonReply;
-}
-
-JsonReply *DeviceHandler::GetBrowserItem(const QVariantMap ¶ms, const JsonContext &context) const
-{
- QVariantMap returns;
- ThingId thingId = ThingId(params.value("deviceId").toString());
- QString itemId = params.value("itemId").toString();
-
- JsonReply *jsonReply = createAsyncReply("GetBrowserItem");
-
- BrowserItemResult *result = NymeaCore::instance()->thingManager()->browserItemDetails(thingId, itemId, context.locale());
- connect(result, &BrowserItemResult::finished, jsonReply, [this, jsonReply, result, context](){
- QVariantMap params = statusToReply(result->status());
- if (result->status() == Device::ThingErrorNoError) {
- params.insert("item", packBrowserItem(result->item()));
- }
- if (!result->displayMessage().isEmpty()) {
- params.insert("displayMessage", result->translatedDisplayMessage(context.locale()));
- }
- jsonReply->setData(params);
- jsonReply->finished();
- });
-
- return jsonReply;
-}
-
-JsonReply *DeviceHandler::ExecuteAction(const QVariantMap ¶ms, const JsonContext &context)
-{
- ThingId thingId(params.value("deviceId").toString());
- ActionTypeId actionTypeId(params.value("actionTypeId").toString());
- ParamList actionParams = unpack(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 *DeviceHandler::ExecuteBrowserItem(const QVariantMap ¶ms, const JsonContext &context)
-{
- 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, context](){
- QVariantMap data;
- data.insert("deviceError", enumValueName(info->status()).replace("Thing", "Device"));
- if (!info->displayMessage().isEmpty()) {
- data.insert("displayMessage", info->translatedDisplayMessage(context.locale()));
- }
- jsonReply->setData(data);
- jsonReply->finished();
- });
-
- return jsonReply;
-}
-
-JsonReply *DeviceHandler::ExecuteBrowserItemAction(const QVariantMap ¶ms, const JsonContext &context)
-{
- ThingId thingId = ThingId(params.value("deviceId").toString());
- QString itemId = params.value("itemId").toString();
- ActionTypeId actionTypeId = ActionTypeId(params.value("actionTypeId").toString());
- ParamList paramList = unpack(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, context](){
- QVariantMap data;
- data.insert("deviceError", enumValueName(info->status()).replace("Thing", "Device"));
- if (!info->displayMessage().isEmpty()) {
- data.insert("displayMessage", info->translatedDisplayMessage(context.locale()));
- }
- jsonReply->setData(data);
- jsonReply->finished();
- });
-
- return jsonReply;
-}
-
-QVariantMap DeviceHandler::packBrowserItem(const BrowserItem &item)
-{
- QVariantMap ret;
- ret.insert("id", item.id());
- ret.insert("displayName", item.displayName());
- ret.insert("description", item.description());
- ret.insert("icon", enumValueName(item.icon()));
- if (item.extendedPropertiesFlags().testFlag(BrowserItem::ExtendedPropertiesMedia)) {
- ret.insert("mediaIcon", enumValueName(static_cast(item.extendedProperty("mediaIcon").toInt())));
- }
- ret.insert("thumbnail", item.thumbnail());
- ret.insert("executable", item.executable());
- ret.insert("browsable", item.browsable());
- ret.insert("disabled", item.disabled());
- QVariantList actionTypeIds;
- foreach (const ActionTypeId &id, item.actionTypeIds()) {
- actionTypeIds.append(id.toString());
- }
- ret.insert("actionTypeIds", actionTypeIds);
- return ret;
-}
-
-void DeviceHandler::pluginConfigChanged(const PluginId &id, const ParamList &config)
-{
- QVariantMap params;
- params.insert("pluginId", id);
- QVariantList configList;
- foreach (const Param ¶m, config) {
- configList << pack(param);
- }
- params.insert("configuration", configList);
- emit PluginConfigurationChanged(params);
-}
-
-void DeviceHandler::deviceStateChanged(Thing *device, const QUuid &stateTypeId, const QVariant &value)
-{
- QVariantMap params;
- params.insert("deviceId", device->id());
- params.insert("stateTypeId", stateTypeId);
- params.insert("value", value);
- emit StateChanged(params);
-}
-
-void DeviceHandler::deviceRemovedNotification(const QUuid &deviceId)
-{
- QVariantMap params;
- params.insert("deviceId", deviceId);
- emit DeviceRemoved(params);
-}
-
-void DeviceHandler::deviceAddedNotification(Thing *thing)
-{
- QVariantMap params;
- QVariantMap deviceMap = pack(thing).toMap();
- // Patch in deviceClassId
- deviceMap.insert("deviceClassId", deviceMap.value("thingClassId"));
- params.insert("device", deviceMap);
- emit DeviceAdded(params);
-}
-
-void DeviceHandler::deviceChangedNotification(Thing *thing)
-{
- QVariantMap params;
- QVariantMap deviceMap = pack(thing).toMap();
- // Patch in deviceClassId
- deviceMap.insert("deviceClassId", deviceMap.value("thingClassId"));
- params.insert("device", deviceMap);
- emit DeviceChanged(params);
-}
-
-void DeviceHandler::deviceSettingChangedNotification(const ThingId &thingId, const ParamTypeId ¶mTypeId, const QVariant &value)
-{
- QVariantMap params;
- params.insert("deviceId", thingId);
- params.insert("paramTypeId", paramTypeId.toString());
- params.insert("value", value);
- emit DeviceSettingChanged(params);
-}
-
-QVariantMap DeviceHandler::translateNotification(const QString ¬ification, const QVariantMap ¶ms, const QLocale &locale)
-{
- if (notification == "DeviceChanged") {
- QVariantMap deviceMap = params.value("device").toMap();
- DeviceId deviceId = params.value("device").toMap().value("id").toUuid();
- Thing *device = NymeaCore::instance()->thingManager()->findConfiguredThing(deviceId);
- QString setupDisplayMessage = params.value("device").toMap().value("setupDisplayMessage").toString();
- QString translatedSetupDisplayMessage = NymeaCore::instance()->thingManager()->translate(device->pluginId(), setupDisplayMessage, locale);
- if (!translatedSetupDisplayMessage.isEmpty()) {
- deviceMap["setupDisplayMessage"] = translatedSetupDisplayMessage;
- }
- QVariantMap translatedParams = params;
- translatedParams["device"] = deviceMap;
- return translatedParams;
- }
-
- return params;
-}
-
-QVariantMap DeviceHandler::statusToReply(Thing::ThingError status) const
-{
- QVariantMap returns;
- returns.insert("deviceError", enumValueName(status).replace("Thing", "Device"));
- return returns;
-}
-
-DeviceClass::DeviceClass(const ThingClass &other):
- ThingClass(other.pluginId(), other.vendorId(), other.id())
-{
- setName(other.name());
- setBrowsable(other.browsable());
- setEventTypes(other.eventTypes());
- setInterfaces(other.interfaces());
- setParamTypes(other.paramTypes());
- setStateTypes(other.stateTypes());
- setActionTypes(other.actionTypes());
- setDisplayName(other.displayName());
- setSetupMethod(other.setupMethod());
- setCreateMethods(other.createMethods());
- setSettingsTypes(other.settingsTypes());
- setDiscoveryParamTypes(other.discoveryParamTypes());
- setBrowserItemActionTypes(other.browserItemActionTypes());
-}
-
-DeviceClassId Device::deviceClassId() const
-{
- return thingClassId();
-}
-
-}
diff --git a/libnymea-core/jsonrpc/devicehandler.h b/libnymea-core/jsonrpc/devicehandler.h
deleted file mode 100644
index fb8c644c..00000000
--- a/libnymea-core/jsonrpc/devicehandler.h
+++ /dev/null
@@ -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 .
-*
-* 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
-
-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 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 m_cacheHashes;
-};
-
-}
-Q_DECLARE_METATYPE(nymeaserver::DeviceClass)
-Q_DECLARE_METATYPE(nymeaserver::Device::DeviceError)
-
-#endif // DEVICEHANDLER_H
diff --git a/libnymea-core/jsonrpc/eventhandler.cpp b/libnymea-core/jsonrpc/eventhandler.cpp
deleted file mode 100644
index a0cb4c1a..00000000
--- a/libnymea-core/jsonrpc/eventhandler.cpp
+++ /dev/null
@@ -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 .
-*
-* 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();
- registerEnum();
- // Objects
- registerObject();
- registerObject();
- registerObject();
- registerObject();
-
- // Methods
- QString description; QVariantMap params; QVariantMap returns;
- description = "Get the EventType for the given eventTypeId.";
- params.insert("eventTypeId", enumValueName(Uuid));
- returns.insert("deviceError", enumRef());
- returns.insert("o:eventType", objectRef());
- 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());
- 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::ThingErrorNoError).replace("ThingError", "DeviceError"));
- data.insert("eventType", pack(translatedEventType));
- return createReply(data);
- }
- }
- }
- QVariantMap data;
- data.insert("deviceError", enumValueName(Thing::ThingErrorEventTypeNotFound).replace("ThingError", "DeviceError"));
- return createReply(data);
-}
-
-}
diff --git a/libnymea-core/jsonrpc/eventhandler.h b/libnymea-core/jsonrpc/eventhandler.h
deleted file mode 100644
index 615ed2b4..00000000
--- a/libnymea-core/jsonrpc/eventhandler.h
+++ /dev/null
@@ -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 .
-*
-* 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
diff --git a/libnymea-core/jsonrpc/integrationshandler.cpp b/libnymea-core/jsonrpc/integrationshandler.cpp
index abcbe0da..9db3fd58 100644
--- a/libnymea-core/jsonrpc/integrationshandler.cpp
+++ b/libnymea-core/jsonrpc/integrationshandler.cpp
@@ -102,7 +102,7 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
QString description; QVariantMap returns; QVariantMap params;
description = "Returns a list of supported Vendors.";
returns.insert("vendors", objectRef());
- registerMethod("GetVendors", description, params, returns);
+ registerMethod("GetVendors", description, params, returns, Types::PermissionScopeNone);
params.clear(); returns.clear();
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));
returns.insert("thingError", enumRef());
returns.insert("o:thingClasses", objectRef());
- registerMethod("GetThingClasses", description, params, returns);
+ registerMethod("GetThingClasses", description, params, returns, Types::PermissionScopeNone);
params.clear(); returns.clear();
description = "Returns a list of loaded plugins.";
returns.insert("plugins", objectRef());
- registerMethod("GetPlugins", description, params, returns);
+ registerMethod("GetPlugins", description, params, returns, Types::PermissionScopeNone);
params.clear(); returns.clear();
description = "Get a plugin's params.";
params.insert("pluginId", enumValueName(Uuid));
returns.insert("thingError", enumRef());
returns.insert("o:configuration", objectRef());
- registerMethod("GetPluginConfiguration", description, params, returns);
+ registerMethod("GetPluginConfiguration", description, params, returns, Types::PermissionScopeConfigureThings);
params.clear(); returns.clear();
description = "Set a plugin's params.";
params.insert("pluginId", enumValueName(Uuid));
params.insert("configuration", objectRef());
returns.insert("thingError", enumRef());
- registerMethod("SetPluginConfiguration", description, params, returns);
+ registerMethod("SetPluginConfiguration", description, params, returns, Types::PermissionScopeConfigureThings);
params.clear(); returns.clear();
description = "Add a new thing to the system. "
@@ -146,7 +146,7 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
returns.insert("thingError", enumRef());
returns.insert("o:thingId", enumValueName(Uuid));
returns.insert("o:displayMessage", enumValueName(String));
- registerMethod("AddThing", description, params, returns);
+ registerMethod("AddThing", description, params, returns, Types::PermissionScopeConfigureThings);
params.clear(); returns.clear();
description = "Pair a new thing. "
@@ -178,7 +178,7 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
returns.insert("o:displayMessage", enumValueName(String));
returns.insert("o:oAuthUrl", enumValueName(String));
returns.insert("o:pin", enumValueName(String));
- registerMethod("PairThing", description, params, returns);
+ registerMethod("PairThing", description, params, returns, Types::PermissionScopeConfigureThings);
params.clear(); returns.clear();
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());
returns.insert("o:displayMessage", enumValueName(String));
returns.insert("o:thingId", enumValueName(Uuid));
- registerMethod("ConfirmPairing", description, params, returns);
+ registerMethod("ConfirmPairing", description, params, returns, Types::PermissionScopeConfigureThings);
params.clear(); returns.clear();
description = "Returns a list of configured things, optionally filtered by thingId.";
params.insert("o:thingId", enumValueName(Uuid));
returns.insert("o:things", objectRef());
returns.insert("thingError", enumRef());
- registerMethod("GetThings", description, params, returns);
+ registerMethod("GetThings", description, params, returns, Types::PermissionScopeNone);
params.clear(); returns.clear();
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());
returns.insert("o:displayMessage", enumValueName(String));
returns.insert("o:thingDescriptors", objectRef());
- registerMethod("DiscoverThings", description, params, returns);
+ registerMethod("DiscoverThings", description, params, returns, Types::PermissionScopeConfigureThings);
params.clear(); returns.clear();
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());
returns.insert("thingError", enumRef());
returns.insert("o:displayMessage", enumValueName(String));
- registerMethod("ReconfigureThing", description, params, returns);
+ registerMethod("ReconfigureThing", description, params, returns, Types::PermissionScopeConfigureThings);
params.clear(); returns.clear();
description = "Edit the name of a thing.";
params.insert("thingId", enumValueName(Uuid));
params.insert("name", enumValueName(String));
returns.insert("thingError", enumRef());
- registerMethod("EditThing", description, params, returns);
+ registerMethod("EditThing", description, params, returns, Types::PermissionScopeConfigureThings);
params.clear(); returns.clear();
description = "Change the settings of a thing.";
params.insert("thingId", enumValueName(Uuid));
params.insert("settings", objectRef());
returns.insert("thingError", enumRef());
- registerMethod("SetThingSettings", description, params, returns);
+ registerMethod("SetThingSettings", description, params, returns, Types::PermissionScopeConfigureThings);
params.clear(); returns.clear();
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("enabled", enumValueName(Bool));
returns.insert("thingError", enumRef());
- registerMethod("SetEventLogging", description, params, returns);
+ registerMethod("SetEventLogging", description, params, returns, Types::PermissionScopeConfigureThings);
params.clear(); returns.clear();
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("filter", enumRef());
returns.insert("thingError", enumRef());
- registerMethod("SetStateFilter", description, params, returns);
+ registerMethod("SetStateFilter", description, params, returns, Types::PermissionScopeConfigureThings);
params.clear(); returns.clear();
description = "Remove a thing from the system.";
@@ -270,25 +270,25 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
params.insert("o:removePolicyList", removePolicyList);
returns.insert("thingError", enumRef());
returns.insert("o:ruleIds", QVariantList() << enumValueName(Uuid));
- registerMethod("RemoveThing", description, params, returns);
+ registerMethod("RemoveThing", description, params, returns, Types::PermissionScopeConfigureThings);
params.clear(); returns.clear();
description = "Get event types for a specified thingClassId.";
params.insert("thingClassId", enumValueName(Uuid));
returns.insert("eventTypes", objectRef());
- registerMethod("GetEventTypes", description, params, returns);
+ registerMethod("GetEventTypes", description, params, returns, Types::PermissionScopeNone);
params.clear(); returns.clear();
description = "Get action types for a specified thingClassId.";
params.insert("thingClassId", enumValueName(Uuid));
returns.insert("actionTypes", objectRef());
- registerMethod("GetActionTypes", description, params, returns);
+ registerMethod("GetActionTypes", description, params, returns, Types::PermissionScopeNone);
params.clear(); returns.clear();
description = "Get state types for a specified thingClassId.";
params.insert("thingClassId", enumValueName(Uuid));
returns.insert("stateTypes", objectRef());
- registerMethod("GetStateTypes", description, params, returns);
+ registerMethod("GetStateTypes", description, params, returns, Types::PermissionScopeNone);
params.clear(); returns.clear();
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));
returns.insert("thingError", enumRef());
returns.insert("o:value", enumValueName(Variant));
- registerMethod("GetStateValue", description, params, returns);
+ registerMethod("GetStateValue", description, params, returns, Types::PermissionScopeNone);
params.clear(); returns.clear();
description = "Get all the state values of the given thing.";
params.insert("thingId", enumValueName(Uuid));
returns.insert("thingError", enumRef());
returns.insert("o:values", objectRef());
- registerMethod("GetStateValues", description, params, returns);
+ registerMethod("GetStateValues", description, params, returns, Types::PermissionScopeNone);
params.clear(); returns.clear();
description = "Browse a thing. "
@@ -318,7 +318,7 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
returns.insert("thingError", enumRef());
returns.insert("o:displayMessage", enumValueName(String));
returns.insert("items", QVariantList() << objectRef("BrowserItem"));
- registerMethod("BrowseThing", description, params, returns);
+ registerMethod("BrowseThing", description, params, returns, Types::PermissionScopeNone);
params.clear(); returns.clear();
description = "Get a single item from the browser. "
@@ -332,7 +332,7 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
returns.insert("thingError", enumRef());
returns.insert("o:displayMessage", enumValueName(String));
returns.insert("o:item", objectRef("BrowserItem"));
- registerMethod("GetBrowserItem", description, params, returns);
+ registerMethod("GetBrowserItem", description, params, returns, Types::PermissionScopeNone);
params.clear(); returns.clear();
description = "Execute a single action.";
@@ -341,7 +341,7 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
params.insert("o:params", objectRef());
returns.insert("thingError", enumRef());
returns.insert("o:displayMessage", enumValueName(String));
- registerMethod("ExecuteAction", description, params, returns);
+ registerMethod("ExecuteAction", description, params, returns, Types::PermissionScopeControlThings);
params.clear(); returns.clear();
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));
returns.insert("thingError", enumRef());
returns.insert("o:displayMessage", enumValueName(String));
- registerMethod("ExecuteBrowserItem", description, params, returns);
+ registerMethod("ExecuteBrowserItem", description, params, returns, Types::PermissionScopeControlThings);
params.clear(); returns.clear();
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());
returns.insert("thingError", enumRef());
returns.insert("o:displayMessage", enumValueName(String));
- registerMethod("ExecuteBrowserItemAction", description, params, returns);
+ registerMethod("ExecuteBrowserItemAction", description, params, returns, Types::PermissionScopeControlThings);
params.clear(); returns.clear();
description = "Fetch IO connections. Optionally filtered by thingId and stateTypeId.";
params.insert("o:thingId", enumValueName(Uuid));
returns.insert("ioConnections", objectRef());
- registerMethod("GetIOConnections", description, params, returns);
+ registerMethod("GetIOConnections", description, params, returns, Types::PermissionScopeNone);
params.clear(); returns.clear();
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));
returns.insert("thingError", enumRef());
returns.insert("o:ioConnectionId", enumValueName(Uuid));
- registerMethod("ConnectIO", description, params, returns);
+ registerMethod("ConnectIO", description, params, returns, Types::PermissionScopeConfigureThings);
params.clear(); returns.clear();
description = "Disconnect an existing IO connection.";
params.insert("ioConnectionId", enumValueName(Uuid));
returns.insert("thingError", enumRef());
- registerMethod("DisconnectIO", description, params, returns);
+ registerMethod("DisconnectIO", description, params, returns, Types::PermissionScopeConfigureThings);
// Notifications
diff --git a/libnymea-core/jsonrpc/jsonrpcserverimplementation.cpp b/libnymea-core/jsonrpc/jsonrpcserverimplementation.cpp
index 147c460e..6eb8baeb 100644
--- a/libnymea-core/jsonrpc/jsonrpcserverimplementation.cpp
+++ b/libnymea-core/jsonrpc/jsonrpcserverimplementation.cpp
@@ -60,14 +60,10 @@
#include "version.h"
#include "cloud/cloudmanager.h"
-#include "devicehandler.h"
#include "integrationshandler.h"
-#include "actionhandler.h"
#include "ruleshandler.h"
#include "scriptshandler.h"
-#include "eventhandler.h"
#include "logginghandler.h"
-#include "statehandler.h"
#include "configurationhandler.h"
#include "networkmanagerhandler.h"
#include "tagshandler.h"
@@ -95,6 +91,7 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration
registerEnum();
registerEnum();
registerEnum();
+ registerFlag();
// Objects
registerObject();
@@ -122,7 +119,9 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration
"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 "
"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));
returns.insert("server", enumValueName(String));
returns.insert("name", enumValueName(String));
@@ -136,14 +135,17 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration
returns.insert("pushButtonAuthAvailable", enumValueName(Bool));
returns.insert("o:experiences", QVariantList() << objectRef("Experience"));
returns.insert("o:cacheHashes", QVariantList() << objectRef("CacheHash"));
- registerMethod("Hello", description, params, returns);
+ returns.insert("o:authenticated", enumValueName(Bool));
+ returns.insert("o:permissionScopes", flagRef());
+ returns.insert("o:username", enumValueName(String));
+ registerMethod("Hello", description, params, returns, Types::PermissionScopeNone);
params.clear(); returns.clear();
description = "Introspect this API.";
returns.insert("methods", enumValueName(Object));
returns.insert("notifications", enumValueName(Object));
returns.insert("types", enumValueName(Object));
- registerMethod("Introspect", description, params, returns);
+ registerMethod("Introspect", description, params, returns, Types::PermissionScopeNone);
params.clear(); returns.clear();
description = "Version of this nymea/JSONRPC interface.";
@@ -151,7 +153,7 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration
returns.insert("protocol version", enumValueName(String));
returns.insert("qtVersion", enumValueName(String));
returns.insert("qtBuildVersion", enumValueName(String));
- registerMethod("Version", description, params, returns);
+ registerMethod("Version", description, params, returns, Types::PermissionScopeNone);
params.clear(); returns.clear();
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));
returns.insert("namespaces", enumValueName(StringList));
returns.insert("d:enabled", enumValueName(Bool));
- registerMethod("SetNotificationStatus", description, params, returns);
+ registerMethod("SetNotificationStatus", description, params, returns, Types::PermissionScopeNone);
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("password", enumValueName(String));
+ params.insert("o:displayName", enumValueName(String));
+ params.insert("o:email", enumValueName(String));
returns.insert("error", enumRef());
- registerMethod("CreateUser", description, params, returns, "Use Users.CreateUser instead.");
+ registerMethod("CreateUser", description, params, returns, Types::PermissionScopeAdmin);
params.clear(); returns.clear();
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));
returns.insert("success", enumValueName(Bool));
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());
+ registerMethod("Authenticate", description, params, returns, Types::PermissionScopeNone);
params.clear(); returns.clear();
description = "Authenticate a client to the api via Push Button method. "
@@ -204,18 +212,7 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration
params.insert("deviceName", enumValueName(String));
returns.insert("success", enumValueName(Bool));
returns.insert("transactionId", enumValueName(Int));
- registerMethod("RequestPushButtonAuth", description, params, returns, "Use Users.RequestPushButtonAuth instead.");
-
- 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());
- registerMethod("RemoveToken", description, params, returns, "Use Users.RemoveToken instead.");
+ registerMethod("RequestPushButtonAuth", description, params, returns, Types::PermissionScopeNone);
params.clear(); returns.clear();
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));
returns.insert("success", enumValueName(Bool));
returns.insert("sessionId", enumValueName(String));
- registerMethod("KeepAlive", description, params, returns);
+ registerMethod("KeepAlive", description, params, returns, Types::PermissionScopeNone);
// Notifications
params.clear(); returns.clear();
@@ -260,11 +257,14 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration
params.insert("success", enumValueName(Bool));
params.insert("transactionId", enumValueName(Int));
params.insert("o:token", enumValueName(String));
- registerNotification("PushButtonAuthFinished", description, params, "Use Users.PushButtonAuthFinished instead.");
+ registerNotification("PushButtonAuthFinished", description, params);
QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection);
connect(NymeaCore::instance()->userManager(), &UserManager::pushButtonAuthFinished, this, &JsonRPCServerImplementation::onPushButtonAuthFinished);
+
+ m_connectionLockdownTimer.setSingleShot(true);
+ m_connectionLockdownTimer.setInterval(3000);
}
/*! Returns the \e namespace of \l{JsonHandler}. */
@@ -290,7 +290,67 @@ JsonReply *JsonRPCServerImplementation::Hello(const QVariantMap ¶ms, const J
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 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
@@ -342,15 +402,17 @@ JsonReply *JsonRPCServerImplementation::CreateUser(const QVariantMap ¶ms)
{
QString username = params.value("username").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;
returns.insert("error", enumValueName(status));
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 password = params.value("password").toString();
@@ -361,7 +423,26 @@ JsonReply *JsonRPCServerImplementation::Authenticate(const QVariantMap ¶ms)
ret.insert("success", !token.isEmpty());
if (!token.isEmpty()) {
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(property("transportInterface").toLongLong());
+ interface->terminateClientConnection(context.clientId());
+ }
+
+ if (token.isEmpty()) {
+ qCWarning(dcJsonRpc()) << "Staring connection lockdown timer";
+ m_connectionLockdownTimer.start();
+ }
+
return createReply(ret);
}
@@ -380,30 +461,6 @@ JsonReply *JsonRPCServerImplementation::RequestPushButtonAuth(const QVariantMap
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 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(error));
- return createReply(ret);
-}
-
JsonReply *JsonRPCServerImplementation::SetupCloudConnection(const QVariantMap ¶ms)
{
if (NymeaCore::instance()->cloudManager()->connectionState() != CloudManager::CloudConnectionStateUnconfigured) {
@@ -543,56 +600,12 @@ void JsonRPCServerImplementation::sendUnauthorizedResponse(TransportInterface *i
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 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()
{
registerHandler(this);
registerHandler(new IntegrationsHandler(NymeaCore::instance()->thingManager(), this));
- registerHandler(new DeviceHandler(this));
- registerHandler(new ActionHandler(this));
registerHandler(new RulesHandler(this));
- registerHandler(new EventHandler(this));
registerHandler(new LoggingHandler(this));
- registerHandler(new StateHandler(this));
registerHandler(new ConfigurationHandler(this));
registerHandler(new NetworkManagerHandler(NymeaCore::instance()->networkManager(), this));
registerHandler(new TagsHandler(this));
@@ -654,7 +667,8 @@ void JsonRPCServerImplementation::processJsonPacket(TransportInterface *interfac
return;
}
- QStringList commandList = message.value("method").toString().split('.');
+ QString methodString = message.value("method").toString();
+ QStringList commandList = methodString.split('.');
if (commandList.count() != 2) {
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()));
@@ -666,23 +680,38 @@ void JsonRPCServerImplementation::processJsonPacket(TransportInterface *interfac
// check if authentication is required for this transport
if (m_interfaces.value(interface)) {
QByteArray token = message.value("token").toByteArray();
- QStringList authExemptMethodsNoUser = {"JSONRPC.Introspect", "JSONRPC.Hello", "JSONRPC.RequestPushButtonAuth", "JSONRPC.CreateUser", "Users.RequestPushButtonAuth", "Users.CreateUser"};
- QStringList authExemptMethodsWithUser = {"JSONRPC.Introspect", "JSONRPC.Hello", "JSONRPC.Authenticate", "JSONRPC.RequestPushButtonAuth", "Users.Authenticate", "Users.RequestPushButtonAuth"};
- // if there is no user in the system yet, let's fail unless this is special method for authentication itself
+ QStringList authExemptMethodsNoUser = {"JSONRPC.Introspect", "JSONRPC.Hello", "JSONRPC.RequestPushButtonAuth", "JSONRPC.CreateUser"};
+ 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 a special method for authentication itself
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.");
qCWarning(dcJsonRpc()) << "Initial setup required but client does not call the setup. Dropping connection.";
interface->terminateClientConnection(clientId);
+ qCWarning(dcJsonRpc()) << "Staring connection lockdown timer";
+ m_connectionLockdownTimer.start();
return;
}
} else {
// 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))) {
- sendUnauthorizedResponse(interface, clientId, commandId, "Forbidden: Invalid token.");
- qCWarning(dcJsonRpc()) << "Client did not not present a valid token. Dropping connection.";
- interface->terminateClientConnection(clientId);
- return;
+ if (!authExemptMethodsWithUser.contains(methodString)) {
+ if (token.isEmpty() || !NymeaCore::instance()->userManager()->verifyToken(token)) {
+ sendUnauthorizedResponse(interface, clientId, commandId, "Forbidden: Invalid token.");
+ qCWarning(dcJsonRpc()) << "Client did not not present a valid token. Dropping connection.";
+ 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();
TransportInterface *interface = qobject_cast(sender());
+ if (m_connectionLockdownTimer.isActive()) {
+ qCWarning(dcJsonRpc()) << "Connection is locked down. Rejecting new client connection.";
+ interface->terminateClientConnection(clientId);
+ return;
+ }
+
m_clientTransports.insert(clientId, interface);
// Initialize the connection locale to the settings default
diff --git a/libnymea-core/jsonrpc/jsonrpcserverimplementation.h b/libnymea-core/jsonrpc/jsonrpcserverimplementation.h
index 1430526b..b699d4e0 100644
--- a/libnymea-core/jsonrpc/jsonrpcserverimplementation.h
+++ b/libnymea-core/jsonrpc/jsonrpcserverimplementation.h
@@ -63,10 +63,8 @@ public:
Q_INVOKABLE JsonReply *SetNotificationStatus(const QVariantMap ¶ms, const JsonContext &context);
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 *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 *SetupRemoteAccess(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 sendErrorResponse(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);
@@ -129,6 +126,8 @@ private:
int m_notificationId;
+ QTimer m_connectionLockdownTimer;
+
QString formatAssertion(const QString &targetNamespace, const QString &method, QMetaMethod::MethodType methodType, JsonHandler *handler, const QVariantMap &data) const;
};
diff --git a/libnymea-core/jsonrpc/jsonvalidator.cpp b/libnymea-core/jsonrpc/jsonvalidator.cpp
index 7a2b2409..3d134042 100644
--- a/libnymea-core/jsonrpc/jsonvalidator.cpp
+++ b/libnymea-core/jsonrpc/jsonvalidator.cpp
@@ -179,7 +179,7 @@ JsonValidator::Result JsonValidator::validateEntry(const QVariant &value, const
QVariantList enumList = refDefinition.toList();
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);
}
@@ -187,7 +187,7 @@ JsonValidator::Result JsonValidator::validateEntry(const QVariant &value, const
QVariantMap flags = api.value("flags").toMap();
if (flags.contains(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());
}
QString flagEnum = refDefinition.toList().first().toString();
diff --git a/libnymea-core/jsonrpc/logginghandler.cpp b/libnymea-core/jsonrpc/logginghandler.cpp
index 88853ff0..d9b89723 100644
--- a/libnymea-core/jsonrpc/logginghandler.cpp
+++ b/libnymea-core/jsonrpc/logginghandler.cpp
@@ -76,7 +76,6 @@ LoggingHandler::LoggingHandler(QObject *parent) :
params.insert("o:eventTypes", QVariantList() << enumRef());
params.insert("o:typeIds", 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:limit", 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("thingId", logEntry.thingId());
- logEntryMap.insert("deviceId", logEntry.thingId()); // DEPRECATED
logEntryMap.insert("value", LogValueTool::convertVariantToString(logEntry.value()));
break;
case Logging::LoggingSourceSystem:
@@ -251,13 +249,6 @@ LogFilter LoggingHandler::unpackLogFilter(const QVariantMap &logFilterMap)
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")) {
QVariantList values = logFilterMap.value("values").toList();
foreach (const QVariant &value, values) {
diff --git a/libnymea-core/jsonrpc/ruleshandler.cpp b/libnymea-core/jsonrpc/ruleshandler.cpp
index e6ac1d10..bcdcdff0 100644
--- a/libnymea-core/jsonrpc/ruleshandler.cpp
+++ b/libnymea-core/jsonrpc/ruleshandler.cpp
@@ -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 "
"method Rules.GetRuleDetails.";
returns.insert("ruleDescriptions", QVariantList() << objectRef("RuleDescription"));
- registerMethod("GetRules", description, params, returns);
+ registerMethod("GetRules", description, params, returns, Types::PermissionScopeConfigureRules);
params.clear(); returns.clear();
description = "Get details for the rule identified by ruleId";
@@ -166,8 +166,7 @@ RulesHandler::RulesHandler(QObject *parent) :
params.clear(); returns.clear();
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("d:o:deviceId", enumValueName(Uuid));
+ params.insert("thingId", enumValueName(Uuid));
returns.insert("ruleIds", QVariantList() << enumValueName(Uuid));
registerMethod("FindRules", description, params, returns);
@@ -301,10 +300,7 @@ JsonReply* RulesHandler::RemoveRule(const QVariantMap ¶ms)
JsonReply *RulesHandler::FindRules(const QVariantMap ¶ms)
{
- ThingId thingId = ThingId(params.value("deviceId").toString()); // DEPRECATED
- if (params.contains("thingId")) {
- thingId = ThingId(params.value("thingId").toString());
- }
+ ThingId thingId = ThingId(params.value("thingId").toString());
QList rules = NymeaCore::instance()->ruleEngine()->findRules(thingId);
QVariantList rulesList;
diff --git a/libnymea-core/jsonrpc/statehandler.cpp b/libnymea-core/jsonrpc/statehandler.cpp
deleted file mode 100644
index b428148b..00000000
--- a/libnymea-core/jsonrpc/statehandler.cpp
+++ /dev/null
@@ -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 .
-*
-* 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();
- registerEnum();
- registerObject();
- registerObject();
-
- // Methods
- QString description; QVariantMap params; QVariantMap returns;
- description = "Get the StateType for the given stateTypeId.";
- params.insert("stateTypeId", enumValueName(Uuid));
- returns.insert("deviceError", enumRef());
- returns.insert("o:stateType", objectRef());
- 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::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::ThingErrorStateTypeNotFound).replace("ThingError", "DeviceError"));
- return createReply(data);
-}
-
-}
diff --git a/libnymea-core/jsonrpc/statehandler.h b/libnymea-core/jsonrpc/statehandler.h
deleted file mode 100644
index 85f9b1b4..00000000
--- a/libnymea-core/jsonrpc/statehandler.h
+++ /dev/null
@@ -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 .
-*
-* 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
diff --git a/libnymea-core/jsonrpc/tagshandler.cpp b/libnymea-core/jsonrpc/tagshandler.cpp
index 67b40cc1..eff2024d 100644
--- a/libnymea-core/jsonrpc/tagshandler.cpp
+++ b/libnymea-core/jsonrpc/tagshandler.cpp
@@ -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, "
"combining thingId and ruleId will return an empty result set).";
params.insert("o:thingId", enumValueName(Uuid));
- params.insert("d:o:deviceId", enumValueName(Uuid));
params.insert("o:ruleId", enumValueName(Uuid));
params.insert("o:appId", 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()) {
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()) {
continue;
}
diff --git a/libnymea-core/jsonrpc/usershandler.cpp b/libnymea-core/jsonrpc/usershandler.cpp
index 0bb8d996..563e3735 100644
--- a/libnymea-core/jsonrpc/usershandler.cpp
+++ b/libnymea-core/jsonrpc/usershandler.cpp
@@ -40,50 +40,23 @@ UsersHandler::UsersHandler(UserManager *userManager, QObject *parent):
JsonHandler(parent),
m_userManager(userManager)
{
- registerObject();
+ registerFlag();
+ registerObject();
registerObject();
QVariantMap params, returns;
QString description;
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("password", enumValueName(String));
+ params.insert("o:email", enumValueName(String));
+ params.insert("o:displayName", enumValueName(String));
+ params.insert("o:scopes", flagRef());
returns.insert("error", enumRef());
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();
description = "Change the password for the currently logged in user.";
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).";
returns.insert("o:userInfo", objectRef());
returns.insert("error", enumRef());
- registerMethod("GetUserInfo", description, params, returns);
+ registerMethod("GetUserInfo", description, params, returns, Types::PermissionScopeNone);
params.clear(); returns.clear();
description = "Get all the tokens for the current user.";
@@ -108,7 +81,48 @@ UsersHandler::UsersHandler(UserManager *userManager, QObject *parent):
returns.insert("error", enumRef());
registerMethod("RemoveToken", description, params, returns);
+ params.clear(); returns.clear();
+ description = "Return a list of all users in the system.";
+ returns.insert("users", objectRef());
+ 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());
+ 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());
+ returns.insert("error", enumRef());
+ 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());
+ registerMethod("SetUserInfo", description, params, returns);
+
// Notifications
+ params.clear();
+ description = "Emitted when a user is added to the system.";
+ params.insert("userInfo", objectRef());
+ 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());
+ registerNotification("UserChanged", description, params);
+
params.clear();
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 "
@@ -118,7 +132,21 @@ UsersHandler::UsersHandler(UserManager *userManager, QObject *parent):
params.insert("o:token", enumValueName(String));
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 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;
returns.insert("error", enumValueName(status));
@@ -157,42 +189,14 @@ JsonReply *UsersHandler::ChangePassword(const QVariantMap ¶ms, const JsonCon
}
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(status));
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)
{
Q_UNUSED(params)
@@ -200,7 +204,7 @@ JsonReply *UsersHandler::GetUserInfo(const QVariantMap ¶ms, const JsonContex
QByteArray currentToken = context.token();
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::UserErrorPermissionDenied));
return createReply(ret);
}
@@ -211,7 +215,9 @@ JsonReply *UsersHandler::GetUserInfo(const QVariantMap ¶ms, const JsonContex
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("error", enumValueName(UserManager::UserErrorNoError));
return createReply(ret);
@@ -224,7 +230,7 @@ JsonReply *UsersHandler::GetTokens(const QVariantMap ¶ms, const JsonContext
QByteArray currentToken = context.token();
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::UserErrorPermissionDenied));
return createReply(ret);
}
@@ -236,7 +242,7 @@ JsonReply *UsersHandler::GetTokens(const QVariantMap ¶ms, const JsonContext
}
TokenInfo tokenInfo = m_userManager->tokenInfo(currentToken);
- qCDebug(dcJsonRpc()) << "Fetching tokens for user" << tokenInfo.username();
+ qCDebug(dcJsonRpc()) << "Fetching tokens for user" << currentToken << tokenInfo.username();
QList tokens = m_userManager->tokens(tokenInfo.username());
QVariantList retList;
foreach (const TokenInfo &tokenInfo, tokens) {
@@ -285,24 +291,70 @@ JsonReply *UsersHandler::RemoveToken(const QVariantMap ¶ms, const JsonContex
return createReply(ret);
}
-void UsersHandler::onPushButtonAuthFinished(int transactionId, bool success, const QByteArray &token)
+JsonReply *UsersHandler::GetUsers(const QVariantMap ¶ms)
{
- Q_UNUSED(success)
- Q_UNUSED(token)
- QUuid clientId = m_pushButtonTransactions.take(transactionId);
- if (clientId.isNull()) {
- qCDebug(dcJsonRpc()) << "Received a PushButton reply but wasn't expecting it.";
- return;
+ Q_UNUSED(params)
+ QVariantMap reply;
+ reply.insert("users", pack(m_userManager->users()));
+ return createReply(reply);
+}
+
+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(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(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;
- params.insert("transactionId", transactionId);
- params.insert("success", success);
- if (success) {
- params.insert("token", token);
+ if (callingTokenInfo.username() != username && !m_userManager->userInfo(callingTokenInfo.username()).scopes().testFlag(Types::PermissionScopeAdmin)) {
+ ret.insert("error", enumValueName(UserManager::UserErrorPermissionDenied));
+ return createReply(ret);
}
- 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);
}
}
diff --git a/libnymea-core/jsonrpc/usershandler.h b/libnymea-core/jsonrpc/usershandler.h
index 10159720..2e048dea 100644
--- a/libnymea-core/jsonrpc/usershandler.h
+++ b/libnymea-core/jsonrpc/usershandler.h
@@ -49,17 +49,18 @@ public:
Q_INVOKABLE JsonReply *CreateUser(const QVariantMap ¶ms);
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 *GetTokens(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:
- void PushButtonAuthFinished(const QUuid &clientId, const QVariantMap ¶ms);
-
-private slots:
- void onPushButtonAuthFinished(int transactionId, bool success, const QByteArray &token);
+ void UserAdded(const QVariantMap ¶ms);
+ void UserRemoved(const QVariantMap ¶ms);
+ void UserChanged(const QVariantMap ¶ms);
private:
UserManager *m_userManager = nullptr;
diff --git a/libnymea-core/libnymea-core.pro b/libnymea-core/libnymea-core.pro
index c4fbbce8..e071fd8c 100644
--- a/libnymea-core/libnymea-core.pro
+++ b/libnymea-core/libnymea-core.pro
@@ -103,11 +103,7 @@ HEADERS += nymeacore.h \
jsonrpc/jsonrpcserverimplementation.h \
jsonrpc/jsonvalidator.h \
jsonrpc/integrationshandler.h \
- jsonrpc/devicehandler.h \
jsonrpc/ruleshandler.h \
- jsonrpc/actionhandler.h \
- jsonrpc/eventhandler.h \
- jsonrpc/statehandler.h \
jsonrpc/logginghandler.h \
jsonrpc/configurationhandler.h \
jsonrpc/networkmanagerhandler.h \
@@ -122,6 +118,7 @@ HEADERS += nymeacore.h \
logging/logentry.h \
logging/logvaluetool.h \
time/timemanager.h \
+ usermanager/userautorizer.h \
usermanager/userinfo.h \
usermanager/usermanager.h \
usermanager/tokeninfo.h \
@@ -197,11 +194,7 @@ SOURCES += nymeacore.cpp \
jsonrpc/jsonrpcserverimplementation.cpp \
jsonrpc/jsonvalidator.cpp \
jsonrpc/integrationshandler.cpp \
- jsonrpc/devicehandler.cpp \
jsonrpc/ruleshandler.cpp \
- jsonrpc/actionhandler.cpp \
- jsonrpc/eventhandler.cpp \
- jsonrpc/statehandler.cpp \
jsonrpc/logginghandler.cpp \
jsonrpc/configurationhandler.cpp \
jsonrpc/networkmanagerhandler.cpp \
@@ -215,6 +208,7 @@ SOURCES += nymeacore.cpp \
logging/logentry.cpp \
logging/logvaluetool.cpp \
time/timemanager.cpp \
+ usermanager/userautorizer.cpp \
usermanager/userinfo.cpp \
usermanager/usermanager.cpp \
usermanager/tokeninfo.cpp \
diff --git a/libnymea-core/logging/logentry.h b/libnymea-core/logging/logentry.h
index fcab8452..fdf49db0 100644
--- a/libnymea-core/logging/logentry.h
+++ b/libnymea-core/logging/logentry.h
@@ -48,7 +48,6 @@ class LogEntry
Q_PROPERTY(Logging::LoggingSource source READ source)
Q_PROPERTY(QUuid typeId READ typeId 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(bool active READ active USER true)
Q_PROPERTY(Logging::LoggingEventType eventType READ eventType USER true)
diff --git a/libnymea-core/ruleengine/ruleaction.h b/libnymea-core/ruleengine/ruleaction.h
index 017a05ab..d5d2ff9f 100644
--- a/libnymea-core/ruleengine/ruleaction.h
+++ b/libnymea-core/ruleengine/ruleaction.h
@@ -40,7 +40,6 @@ class LIBNYMEA_EXPORT RuleAction
{
Q_GADGET
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(QString interface READ interface WRITE setInterface USER true)
Q_PROPERTY(QString interfaceAction READ interfaceAction WRITE setInterfaceAction USER true)
diff --git a/libnymea-core/ruleengine/ruleactionparam.h b/libnymea-core/ruleengine/ruleactionparam.h
index 7925f6a3..c5ef4d4a 100644
--- a/libnymea-core/ruleengine/ruleactionparam.h
+++ b/libnymea-core/ruleengine/ruleactionparam.h
@@ -48,7 +48,6 @@ class LIBNYMEA_EXPORT RuleActionParam
Q_PROPERTY(QUuid eventTypeId READ eventTypeId WRITE setEventTypeId 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 stateDeviceId READ stateThingId WRITE setStateThingId USER true REVISION 1)
Q_PROPERTY(QUuid stateTypeId READ stateTypeId WRITE setStateTypeId USER true)
public:
diff --git a/libnymea-core/scriptengine/scriptengine.cpp b/libnymea-core/scriptengine/scriptengine.cpp
index 6cc806b5..a11645c7 100644
--- a/libnymea-core/scriptengine/scriptengine.cpp
+++ b/libnymea-core/scriptengine/scriptengine.cpp
@@ -57,12 +57,9 @@ QtMessageHandler ScriptEngine::s_upstreamMessageHandler;
QLoggingCategory::CategoryFilter ScriptEngine::s_oldCategoryFilter = nullptr;
QMutex ScriptEngine::s_loggerMutex;
-ScriptEngine::ScriptEngine(ThingManager *deviceManager, QObject *parent) : QObject(parent),
- m_deviceManager(deviceManager)
+ScriptEngine::ScriptEngine(ThingManager *thingManager, QObject *parent) : QObject(parent),
+ m_thingManager(thingManager)
{
- qmlRegisterType("nymea", 1, 0, "DeviceEvent");
- qmlRegisterType("nymea", 1, 0, "DeviceAction");
- qmlRegisterType("nymea", 1, 0, "DeviceState");
qmlRegisterType("nymea", 1, 0, "ThingEvent");
qmlRegisterType("nymea", 1, 0, "ThingAction");
qmlRegisterType("nymea", 1, 0, "ThingState");
@@ -71,7 +68,7 @@ ScriptEngine::ScriptEngine(ThingManager *deviceManager, QObject *parent) : QObje
qmlRegisterType("nymea", 1, 0, "Alarm");
m_engine = new QQmlEngine(this);
- m_engine->setProperty("thingManager", reinterpret_cast(m_deviceManager));
+ m_engine->setProperty("thingManager", reinterpret_cast(m_thingManager));
// 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.
diff --git a/libnymea-core/scriptengine/scriptengine.h b/libnymea-core/scriptengine/scriptengine.h
index e60532b9..8e3017d1 100644
--- a/libnymea-core/scriptengine/scriptengine.h
+++ b/libnymea-core/scriptengine/scriptengine.h
@@ -75,7 +75,7 @@ public:
QByteArray content;
};
- explicit ScriptEngine(ThingManager *deviceManager, QObject *parent = nullptr);
+ explicit ScriptEngine(ThingManager *thingManager, QObject *parent = nullptr);
~ScriptEngine();
Scripts scripts();
@@ -102,7 +102,7 @@ private:
void onScriptMessage(QtMsgType type, const QMessageLogContext &context, const QString &message);
private:
- ThingManager *m_deviceManager = nullptr;
+ ThingManager *m_thingManager = nullptr;
QQmlEngine *m_engine = nullptr;
QHash m_scripts;
diff --git a/libnymea-core/servers/tcpserver.cpp b/libnymea-core/servers/tcpserver.cpp
index 48b2a937..96cd478e 100644
--- a/libnymea-core/servers/tcpserver.cpp
+++ b/libnymea-core/servers/tcpserver.cpp
@@ -116,6 +116,7 @@ void TcpServer::terminateClientConnection(const QUuid &clientId)
{
QTcpSocket *client = m_clientList.value(clientId);
if (client) {
+ client->flush();
client->close();
}
}
diff --git a/libnymea-core/servers/websocketserver.cpp b/libnymea-core/servers/websocketserver.cpp
index 4fc196c4..4a1e0b93 100644
--- a/libnymea-core/servers/websocketserver.cpp
+++ b/libnymea-core/servers/websocketserver.cpp
@@ -121,6 +121,7 @@ void WebSocketServer::terminateClientConnection(const QUuid &clientId)
{
QWebSocket *client = m_clientList.value(clientId);
if (client) {
+ client->flush();
client->close();
}
}
diff --git a/libnymea-core/tagging/tag.h b/libnymea-core/tagging/tag.h
index d1bff37c..0abfb229 100644
--- a/libnymea-core/tagging/tag.h
+++ b/libnymea-core/tagging/tag.h
@@ -44,7 +44,6 @@ class Tag
Q_PROPERTY(QString appId READ appId WRITE setAppId)
Q_PROPERTY(QString tagId READ tagId WRITE setTagId)
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(QString value READ value WRITE setValue USER true)
public:
diff --git a/libnymea-core/usermanager/userautorizer.cpp b/libnymea-core/usermanager/userautorizer.cpp
new file mode 100644
index 00000000..e3fbbab7
--- /dev/null
+++ b/libnymea-core/usermanager/userautorizer.cpp
@@ -0,0 +1,6 @@
+#include "userautorizer.h"
+
+UserAutorizer::UserAutorizer(QObject *parent) : QObject(parent)
+{
+
+}
diff --git a/libnymea-core/usermanager/userautorizer.h b/libnymea-core/usermanager/userautorizer.h
new file mode 100644
index 00000000..886ef26f
--- /dev/null
+++ b/libnymea-core/usermanager/userautorizer.h
@@ -0,0 +1,16 @@
+#ifndef USERAUTORIZER_H
+#define USERAUTORIZER_H
+
+#include
+
+class UserAutorizer : public QObject
+{
+ Q_OBJECT
+public:
+ explicit UserAutorizer(QObject *parent = nullptr);
+
+signals:
+
+};
+
+#endif // USERAUTORIZER_H
diff --git a/libnymea-core/usermanager/userinfo.cpp b/libnymea-core/usermanager/userinfo.cpp
index edd07451..6e4aec8e 100644
--- a/libnymea-core/usermanager/userinfo.cpp
+++ b/libnymea-core/usermanager/userinfo.cpp
@@ -1,5 +1,9 @@
#include "userinfo.h"
+#include
+
+namespace nymeaserver {
+
UserInfo::UserInfo()
{
@@ -20,3 +24,45 @@ void UserInfo::setUsername(const QString &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());
+}
+
+}
diff --git a/libnymea-core/usermanager/userinfo.h b/libnymea-core/usermanager/userinfo.h
index 8423d470..4851a2a6 100644
--- a/libnymea-core/usermanager/userinfo.h
+++ b/libnymea-core/usermanager/userinfo.h
@@ -33,11 +33,18 @@
#include
#include
+#include
+#include "typeutils.h"
+
+namespace nymeaserver {
class UserInfo
{
Q_GADGET
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:
UserInfo();
@@ -46,8 +53,29 @@ public:
QString username() const;
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:
QString m_username;
+ QString m_email;
+ QString m_displayName;
+ Types::PermissionScopes m_scopes = Types::PermissionScopeNone;
};
+class UserInfoList: public QList
+{
+ 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
diff --git a/libnymea-core/usermanager/usermanager.cpp b/libnymea-core/usermanager/usermanager.cpp
index 23d856d2..a2817c9e 100644
--- a/libnymea-core/usermanager/usermanager.cpp
+++ b/libnymea-core/usermanager/usermanager.cpp
@@ -77,6 +77,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -105,7 +106,7 @@ UserManager::UserManager(const QString &dbName, QObject *parent):
if (QFileInfo(m_db.databaseName()).exists()) {
rotate(m_db.databaseName());
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. */
-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);
- QStringList ret;
+ UserInfoList users;
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. */
-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)) {
qCWarning(dcUserManager) << "Error creating user. Invalid username:" << username;
@@ -157,24 +162,33 @@ UserManager::UserError UserManager::createUser(const QString &username, const QS
return UserErrorBadPassword;
}
- QString checkForDuplicateUserQuery = QString("SELECT * FROM users WHERE lower(username) = \"%1\";").arg(username.toLower());
- QSqlQuery result = m_db.exec(checkForDuplicateUserQuery);
- if (result.first()) {
+ QSqlQuery checkForDuplicateUserQuery(m_db);
+ checkForDuplicateUserQuery.prepare("SELECT * FROM users WHERE lower(username) = ?;");
+ checkForDuplicateUserQuery.addBindValue(username.toLower());
+ checkForDuplicateUserQuery.exec();
+ if (checkForDuplicateUserQuery.first()) {
qCWarning(dcUserManager) << "Username already in use";
return UserErrorDuplicateUserId;
}
QByteArray salt = QUuid::createUuid().toString().remove(QRegExp("[{}]")).toUtf8();
QByteArray hashedPassword = QCryptographicHash::hash(QString(password + salt).toUtf8(), QCryptographicHash::Sha512).toBase64();
- QString queryString = QString("INSERT INTO users(username, password, salt) values(\"%1\", \"%2\", \"%3\");")
- .arg(username.toLower())
- .arg(QString::fromUtf8(hashedPassword))
- .arg(QString::fromUtf8(salt));
- m_db.exec(queryString);
- if (m_db.lastError().type() != QSqlError::NoError) {
- qCWarning(dcUserManager) << "Error creating user:" << m_db.lastError().databaseText() << m_db.lastError().driverText();
+ QSqlQuery query(m_db);
+ query.prepare("INSERT INTO users(username, email, displayName, password, salt, scopes) VALUES(?, ?, ?, ?, ?, ?);");
+ query.addBindValue(username.toLower());
+ query.addBindValue(email);
+ query.addBindValue(displayName);
+ query.addBindValue(QString::fromUtf8(hashedPassword));
+ 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;
}
+
+ qCInfo(dcUserManager()) << "New user" << username << "added to the system with permissions:" << Types::scopesToStringList(scopes);
+ emit userAdded(username);
return UserErrorNoError;
}
@@ -215,17 +229,49 @@ UserManager::UserError UserManager::changePassword(const QString &username, cons
UserManager::UserError UserManager::removeUser(const QString &username)
{
- if (!username.isEmpty()) {
- QString dropUserQuery = QString("DELETE FROM users WHERE lower(username) =\"%1\";").arg(username.toLower());
- QSqlQuery result = m_db.exec(dropUserQuery);
- if (result.numRowsAffected() == 0) {
- return UserErrorInvalidUserId;
- }
+ QString dropUserQuery = QString("DELETE FROM users WHERE lower(username) =\"%1\";").arg(username.toLower());
+ QSqlQuery result = m_db.exec(dropUserQuery);
+ if (result.numRowsAffected() == 0) {
+ return UserErrorInvalidUserId;
}
QString dropTokensQuery = QString("DELETE FROM tokens WHERE lower(username) = \"%1\";").arg(username.toLower());
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;
}
@@ -241,18 +287,20 @@ bool UserManager::pushButtonAuthAvailable() const
QByteArray UserManager::authenticate(const QString &username, const QString &password, const QString &deviceName)
{
if (!validateUsername(username)) {
- qCWarning(dcUserManager) << "Username did not pass validation:" << username;
+ qCWarning(dcUserManager) << "Authenticate: Username did not pass validation:" << username;
return QByteArray();
}
- QString passwordQuery = QString("SELECT password, salt FROM users WHERE lower(username) = \"%1\";").arg(username.toLower());
- QSqlQuery result = m_db.exec(passwordQuery);
- if (!result.first()) {
+ QSqlQuery passwordQuery(m_db);
+ passwordQuery.prepare("SELECT password, salt FROM users WHERE lower(username) = ?;");
+ passwordQuery.addBindValue(username.toLower());
+ passwordQuery.exec();
+ if (!passwordQuery.first()) {
qCWarning(dcUserManager) << "No such username" << username;
return QByteArray();
}
- QByteArray salt = result.value("salt").toByteArray();
- QByteArray hashedPassword = result.value("password").toByteArray();
+ QByteArray salt = passwordQuery.value("salt").toByteArray();
+ QByteArray hashedPassword = passwordQuery.value("password").toByteArray();
if (hashedPassword != QCryptographicHash::hash(QString(password + salt).toUtf8(), QCryptographicHash::Sha512).toBase64()) {
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()) {
- qCWarning(dcUserManager) << "Cannot fetch user info for invalid token:" << token;
- 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());
+ QString getUserQuery = QString("SELECT * FROM users WHERE lower(username) = \"%1\";")
+ .arg(username);
QSqlQuery result = m_db.exec(getUserQuery);
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();
}
if (!result.first()) {
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 UserManager::tokens(const QString &username) const
{
QList ret;
- if (!validateUsername(username)) {
- qCWarning(dcUserManager) << "Username did not pass validation:" << username;
- return ret;
- }
- QString getTokensQuery = QString("SELECT id, username, creationdate, deviceName FROM tokens WHERE lower(username) = \"%1\";")
- .arg(username.toLower());
- QSqlQuery result = m_db.exec(getTokensQuery);
+ QSqlQuery query(m_db);
+ query.prepare("SELECT id, username, creationdate, deviceName FROM tokens WHERE lower(username) = ?;");
+ query.addBindValue(username.toLower());
+ query.exec();
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;
}
- while (result.next()) {
- ret << TokenInfo(result.value("id").toUuid(), result.value("username").toString(), result.value("creationdate").toDateTime(), result.value("devicename").toString());
+ while (query.next()) {
+ ret << TokenInfo(query.value("id").toUuid(), query.value("username").toString(), query.value("creationdate").toDateTime(), query.value("devicename").toString());
}
return ret;
}
@@ -441,30 +486,137 @@ bool UserManager::initDB()
m_db.close();
if (!m_db.open()) {
- qCWarning(dcUserManager()) << "Can't open user database. Init failed.";
+ dumpDBError("Can't open user database. Init failed.");
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")) {
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()) {
- 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();
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")) {
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));");
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();
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.";
return true;
}
@@ -486,7 +638,7 @@ void UserManager::rotate(const QString &dbName)
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);
}
@@ -513,6 +665,11 @@ bool UserManager::validateToken(const QByteArray &token) const
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()
{
if (m_pushButtonTransaction.first == -1) {
@@ -520,6 +677,24 @@ void UserManager::onPushButtonPressed()
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();
QString storeTokenQuery = QString("INSERT INTO tokens(id, username, token, creationdate, devicename) VALUES(\"%1\", \"%2\", \"%3\", \"%4\", \"%5\");")
.arg(QUuid::createUuid().toString())
diff --git a/libnymea-core/usermanager/usermanager.h b/libnymea-core/usermanager/usermanager.h
index 547c7354..085e306c 100644
--- a/libnymea-core/usermanager/usermanager.h
+++ b/libnymea-core/usermanager/usermanager.h
@@ -59,11 +59,13 @@ public:
explicit UserManager(const QString &dbName, QObject *parent = nullptr);
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 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;
@@ -71,7 +73,7 @@ public:
int requestPushButtonAuth(const QString &deviceName);
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 QUuid &tokenId) const;
QList