Add thing added and removed logic depending on users thing permission
This commit is contained in:
parent
b80ad6d839
commit
360e287619
@ -446,7 +446,7 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
|
|||||||
connect(m_thingManager, &ThingManager::eventTriggered, this, [this](const Event &event){
|
connect(m_thingManager, &ThingManager::eventTriggered, this, [this](const Event &event){
|
||||||
QVariantMap params;
|
QVariantMap params;
|
||||||
params.insert("event", pack(event));
|
params.insert("event", pack(event));
|
||||||
emit EventTriggered(params);
|
emit EventTriggered(params, event.thingId());
|
||||||
});
|
});
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
@ -519,6 +519,21 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
|
|||||||
hash = QCryptographicHash::hash(QJsonDocument::fromVariant(pluginList).toJson(), QCryptographicHash::Md5).toHex();
|
hash = QCryptographicHash::hash(QJsonDocument::fromVariant(pluginList).toJson(), QCryptographicHash::Md5).toHex();
|
||||||
m_cacheHashes.insert("GetPlugins", hash);
|
m_cacheHashes.insert("GetPlugins", hash);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(NymeaCore::instance()->userManager(), &UserManager::userThingRestrictionsChanged, this, [this](const UserInfo &userInfo, const ThingId &thingId, bool accessGranted){
|
||||||
|
|
||||||
|
if (accessGranted) {
|
||||||
|
QVariantMap params;
|
||||||
|
params.insert("thing", pack(m_thingManager->findConfiguredThing(thingId)));
|
||||||
|
emit ThingAdded(params, userInfo);
|
||||||
|
qCDebug(dcJsonRpc()) << "Notify user" << userInfo.username() << "that the permission to thing with ID" << thingId.toString() << "has been granted.";
|
||||||
|
} else {
|
||||||
|
QVariantMap params;
|
||||||
|
params.insert("thingId", thingId);
|
||||||
|
emit ThingRemoved(params, userInfo);
|
||||||
|
qCDebug(dcJsonRpc()) << "Notify user" << userInfo.username() << "that the permission to thing with ID" << thingId.toString() << "has been dropped.";
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
QString IntegrationsHandler::name() const
|
QString IntegrationsHandler::name() const
|
||||||
@ -1143,8 +1158,7 @@ JsonReply *IntegrationsHandler::GetIOConnections(const QVariantMap ¶ms, cons
|
|||||||
|
|
||||||
IOConnections ioConnections = m_thingManager->ioConnections(thingId);
|
IOConnections ioConnections = m_thingManager->ioConnections(thingId);
|
||||||
QVariantMap returns;
|
QVariantMap returns;
|
||||||
QVariant bla = pack(ioConnections);
|
returns.insert("ioConnections", pack(ioConnections));
|
||||||
returns.insert("ioConnections", pack(ioConnections));
|
|
||||||
returns.insert("thingError", enumValueName<Thing::ThingError>(Thing::ThingErrorNoError));
|
returns.insert("thingError", enumValueName<Thing::ThingError>(Thing::ThingErrorNoError));
|
||||||
return createReply(returns);
|
return createReply(returns);
|
||||||
}
|
}
|
||||||
@ -1214,28 +1228,28 @@ void IntegrationsHandler::thingStateChanged(Thing *thing, const QUuid &stateType
|
|||||||
params.insert("minValue", minValue);
|
params.insert("minValue", minValue);
|
||||||
params.insert("maxValue", maxValue);
|
params.insert("maxValue", maxValue);
|
||||||
params.insert("possibleValues", possibleValues);
|
params.insert("possibleValues", possibleValues);
|
||||||
emit StateChanged(params);
|
emit StateChanged(params, thing->id());
|
||||||
}
|
}
|
||||||
|
|
||||||
void IntegrationsHandler::thingRemovedNotification(const ThingId &thingId)
|
void IntegrationsHandler::thingRemovedNotification(const ThingId &thingId)
|
||||||
{
|
{
|
||||||
QVariantMap params;
|
QVariantMap params;
|
||||||
params.insert("thingId", thingId);
|
params.insert("thingId", thingId);
|
||||||
emit ThingRemoved(params);
|
emit ThingRemoved(params, thingId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IntegrationsHandler::thingAddedNotification(Thing *thing)
|
void IntegrationsHandler::thingAddedNotification(Thing *thing)
|
||||||
{
|
{
|
||||||
QVariantMap params;
|
QVariantMap params;
|
||||||
params.insert("thing", pack(thing));
|
params.insert("thing", pack(thing));
|
||||||
emit ThingAdded(params);
|
emit ThingAdded(params, thing->id());
|
||||||
}
|
}
|
||||||
|
|
||||||
void IntegrationsHandler::thingChangedNotification(Thing *thing)
|
void IntegrationsHandler::thingChangedNotification(Thing *thing)
|
||||||
{
|
{
|
||||||
QVariantMap params;
|
QVariantMap params;
|
||||||
params.insert("thing", pack(thing));
|
params.insert("thing", pack(thing));
|
||||||
emit ThingChanged(params);
|
emit ThingChanged(params, thing->id());
|
||||||
}
|
}
|
||||||
|
|
||||||
void IntegrationsHandler::thingSettingChangedNotification(const ThingId &thingId, const ParamTypeId ¶mTypeId, const QVariant &value)
|
void IntegrationsHandler::thingSettingChangedNotification(const ThingId &thingId, const ParamTypeId ¶mTypeId, const QVariant &value)
|
||||||
@ -1244,7 +1258,7 @@ void IntegrationsHandler::thingSettingChangedNotification(const ThingId &thingId
|
|||||||
params.insert("thingId", thingId);
|
params.insert("thingId", thingId);
|
||||||
params.insert("paramTypeId", paramTypeId.toString());
|
params.insert("paramTypeId", paramTypeId.toString());
|
||||||
params.insert("value", value);
|
params.insert("value", value);
|
||||||
emit ThingSettingChanged(params);
|
emit ThingSettingChanged(params, thingId);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap IntegrationsHandler::statusToReply(Thing::ThingError status) const
|
QVariantMap IntegrationsHandler::statusToReply(Thing::ThingError status) const
|
||||||
|
|||||||
@ -26,6 +26,7 @@
|
|||||||
#define INTEGRATIONSHANDLER_H
|
#define INTEGRATIONSHANDLER_H
|
||||||
|
|
||||||
#include "jsonrpc/jsonhandler.h"
|
#include "jsonrpc/jsonhandler.h"
|
||||||
|
#include "usermanager/userinfo.h"
|
||||||
#include "integrations/thingmanager.h"
|
#include "integrations/thingmanager.h"
|
||||||
|
|
||||||
namespace nymeaserver {
|
namespace nymeaserver {
|
||||||
@ -80,15 +81,20 @@ public:
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void PluginConfigurationChanged(const QVariantMap ¶ms);
|
void PluginConfigurationChanged(const QVariantMap ¶ms);
|
||||||
void StateChanged(const QVariantMap ¶ms);
|
// Thing permission relevant notifications
|
||||||
void ThingRemoved(const QVariantMap ¶ms);
|
void StateChanged(const QVariantMap ¶ms, const ThingId &thingId);
|
||||||
void ThingAdded(const QVariantMap ¶ms);
|
void ThingRemoved(const QVariantMap ¶ms, const ThingId &thingId);
|
||||||
void ThingChanged(const QVariantMap ¶ms);
|
void ThingAdded(const QVariantMap ¶ms, const ThingId &thingId);
|
||||||
void ThingSettingChanged(const QVariantMap ¶ms);
|
void ThingChanged(const QVariantMap ¶ms, const ThingId &thingId);
|
||||||
void EventTriggered(const QVariantMap ¶ms);
|
void ThingSettingChanged(const QVariantMap ¶ms, const ThingId &thingId);
|
||||||
|
void EventTriggered(const QVariantMap ¶ms, const ThingId &thingId);
|
||||||
void IOConnectionAdded(const QVariantMap ¶ms);
|
void IOConnectionAdded(const QVariantMap ¶ms);
|
||||||
void IOConnectionRemoved(const QVariantMap ¶ms);
|
void IOConnectionRemoved(const QVariantMap ¶ms);
|
||||||
|
|
||||||
|
// User specific notifications depending on the thing based permissions
|
||||||
|
void ThingRemoved(const QVariantMap ¶ms, const nymeaserver::UserInfo &userInfo);
|
||||||
|
void ThingAdded(const QVariantMap ¶ms, const nymeaserver::UserInfo &userInfo);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void pluginConfigChanged(const PluginId &id, const ParamList &config);
|
void pluginConfigChanged(const PluginId &id, const ParamList &config);
|
||||||
|
|
||||||
|
|||||||
@ -44,19 +44,11 @@
|
|||||||
#include "jsonvalidator.h"
|
#include "jsonvalidator.h"
|
||||||
#include "nymeacore.h"
|
#include "nymeacore.h"
|
||||||
#include "usermanager/usermanager.h"
|
#include "usermanager/usermanager.h"
|
||||||
#include "integrations/thingmanager.h"
|
|
||||||
#include "integrations/integrationplugin.h"
|
|
||||||
#include "integrations/thing.h"
|
|
||||||
#include "types/thingclass.h"
|
|
||||||
#include "ruleengine/rule.h"
|
|
||||||
#include "ruleengine/ruleengine.h"
|
|
||||||
#include "loggingcategories.h"
|
#include "loggingcategories.h"
|
||||||
#include "platform/platform.h"
|
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
#include "integrationshandler.h"
|
#include "integrationshandler.h"
|
||||||
#include "ruleshandler.h"
|
#include "ruleshandler.h"
|
||||||
#include "scriptshandler.h"
|
|
||||||
#include "logginghandler.h"
|
#include "logginghandler.h"
|
||||||
#include "configurationhandler.h"
|
#include "configurationhandler.h"
|
||||||
#include "networkmanagerhandler.h"
|
#include "networkmanagerhandler.h"
|
||||||
@ -104,19 +96,19 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration
|
|||||||
// Methods
|
// Methods
|
||||||
QString description; QVariantMap returns; QVariantMap params;
|
QString description; QVariantMap returns; QVariantMap params;
|
||||||
description = "Initiates a connection. Use this method to perform an initial handshake of the "
|
description = "Initiates a connection. Use this method to perform an initial handshake of the "
|
||||||
"connection. Optionally, a parameter \"locale\" is can be passed to set up the used "
|
"connection. Optionally, a parameter \"locale\" is can be passed to set up the used "
|
||||||
"locale for this connection. Strings such as ThingClass displayNames etc will be "
|
"locale for this connection. Strings such as ThingClass displayNames etc will be "
|
||||||
"localized to this locale. If this parameter is omitted, the default system locale "
|
"localized to this locale. If this parameter is omitted, the default system locale "
|
||||||
"(depending on the configuration) is used. The reply of this method contains information "
|
"(depending on the configuration) is used. The reply of this method contains information "
|
||||||
"about this core instance such as version information, uuid and its name. The locale value"
|
"about this core instance such as version information, uuid and its name. The locale value"
|
||||||
"indicates the locale used for this connection. Note: This method can be called multiple "
|
"indicates the locale used for this connection. Note: This method can be called multiple "
|
||||||
"times. The locale used in the last call for this connection will be used. Other values, "
|
"times. The locale used in the last call for this connection will be used. Other values, "
|
||||||
"like initialSetupRequired might change if the setup has been performed in the meantime.\n "
|
"like initialSetupRequired might change if the setup has been performed in the meantime.\n "
|
||||||
"The field cacheHashes may contain a map of methods and MD5 hashes. As long as the hash for "
|
"The field cacheHashes may contain a map of methods and MD5 hashes. As long as the hash for "
|
||||||
"a method does not change, a client may use a previously cached copy of the call instead of "
|
"a method does not change, a client may use a previously cached copy of the call instead of "
|
||||||
"fetching the content again. While the Hello call doesn't necessarily require a token, this "
|
"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 "
|
"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.";
|
"information about the tokens validity and the user and permissions for the given token.";
|
||||||
params.insert("o:locale", enumValueName(String));
|
params.insert("o:locale", enumValueName(String));
|
||||||
returns.insert("server", enumValueName(String));
|
returns.insert("server", enumValueName(String));
|
||||||
returns.insert("name", enumValueName(String));
|
returns.insert("name", enumValueName(String));
|
||||||
@ -152,13 +144,13 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration
|
|||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Enable/Disable notifications for this connections. Either \"enabled\" or """
|
description = "Enable/Disable notifications for this connections. Either \"enabled\" or """
|
||||||
"\"namespaces\" needs to be given but not both of them. The boolean based "
|
"\"namespaces\" needs to be given but not both of them. The boolean based "
|
||||||
"\"enabled\" parameter will enable/disable all notifications at once. If "
|
"\"enabled\" parameter will enable/disable all notifications at once. If "
|
||||||
"instead the list-based \"namespaces\" parameter is provided, all given namespaces"
|
"instead the list-based \"namespaces\" parameter is provided, all given namespaces"
|
||||||
"will be enabled, the others will be disabled. The return value of \"success\" will "
|
"will be enabled, the others will be disabled. The return value of \"success\" will "
|
||||||
"indicate success of the operation. The \"enabled\" property in the return value is "
|
"indicate success of the operation. The \"enabled\" property in the return value is "
|
||||||
"deprecated and used for legacy compatibilty only. It will be set to true if at least "
|
"deprecated and used for legacy compatibilty only. It will be set to true if at least "
|
||||||
"one namespace has been enabled.";
|
"one namespace has been enabled.";
|
||||||
params.insert("o:namespaces", enumValueName(StringList));
|
params.insert("o:namespaces", enumValueName(StringList));
|
||||||
params.insert("d:o:enabled", enumValueName(Bool));
|
params.insert("d:o:enabled", enumValueName(Bool));
|
||||||
returns.insert("namespaces", enumValueName(StringList));
|
returns.insert("namespaces", enumValueName(StringList));
|
||||||
@ -178,9 +170,9 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration
|
|||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Authenticate a client to the api via user & password challenge. Provide "
|
description = "Authenticate a client to the api via user & password challenge. Provide "
|
||||||
"a device name which allows the user to identify the client and revoke the token in case "
|
"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 "
|
"the device is lost or stolen. This will return a new token to be used to authorize a "
|
||||||
"client at the API.";
|
"client at the API.";
|
||||||
params.insert("username", enumValueName(String));
|
params.insert("username", enumValueName(String));
|
||||||
params.insert("password", enumValueName(String));
|
params.insert("password", enumValueName(String));
|
||||||
params.insert("deviceName", enumValueName(String));
|
params.insert("deviceName", enumValueName(String));
|
||||||
@ -192,18 +184,18 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration
|
|||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
description = "Authenticate a client to the api via Push Button method. "
|
description = "Authenticate a client to the api via Push Button method. "
|
||||||
"Provide a device name which allows the user to identify the client and revoke the "
|
"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, "
|
"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 "
|
"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 "
|
"push button is pressed, the PushButtonAuthFinished notification will be sent to the "
|
||||||
"requesting client. The procedure will be cancelled when the connection is interrupted. "
|
"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 "
|
"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 "
|
"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 "
|
"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 "
|
"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 "
|
"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 "
|
"active user should press the button or b) it might indicate an attacker trying to take "
|
||||||
"over and snooping in for tokens.";
|
"over and snooping in for tokens.";
|
||||||
params.insert("deviceName", enumValueName(String));
|
params.insert("deviceName", enumValueName(String));
|
||||||
returns.insert("success", enumValueName(Bool));
|
returns.insert("success", enumValueName(Bool));
|
||||||
returns.insert("transactionId", enumValueName(Int));
|
returns.insert("transactionId", enumValueName(Int));
|
||||||
@ -228,6 +220,8 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration
|
|||||||
|
|
||||||
connect(NymeaCore::instance()->userManager(), &UserManager::pushButtonAuthFinished, this, &JsonRPCServerImplementation::onPushButtonAuthFinished);
|
connect(NymeaCore::instance()->userManager(), &UserManager::pushButtonAuthFinished, this, &JsonRPCServerImplementation::onPushButtonAuthFinished);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
m_connectionLockdownTimer.setSingleShot(true);
|
m_connectionLockdownTimer.setSingleShot(true);
|
||||||
m_connectionLockdownTimer.setInterval(3000);
|
m_connectionLockdownTimer.setInterval(3000);
|
||||||
}
|
}
|
||||||
@ -801,6 +795,69 @@ void JsonRPCServerImplementation::sendClientNotification(const QUuid &clientId,
|
|||||||
m_clientTransports.value(clientId)->sendData(clientId, data);
|
m_clientTransports.value(clientId)->sendData(clientId, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JsonRPCServerImplementation::sendClientNotification(const QVariantMap ¶ms, const ThingId &thingId)
|
||||||
|
{
|
||||||
|
JsonHandler *handler = qobject_cast<JsonHandler *>(sender());
|
||||||
|
QMetaMethod method = handler->metaObject()->method(senderSignalIndex());
|
||||||
|
|
||||||
|
QVariantMap notification;
|
||||||
|
notification.insert("id", m_notificationId++);
|
||||||
|
notification.insert("notification", handler->name() + "." + method.name());
|
||||||
|
|
||||||
|
foreach (const QUuid &clientId, m_clientNotifications.keys()) {
|
||||||
|
|
||||||
|
// Check if this client wants to be notified
|
||||||
|
if (!m_clientNotifications.value(clientId).contains(handler->name()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Make sure this client is allowed to receive this notification
|
||||||
|
if (m_clientTokens.contains(clientId)) {
|
||||||
|
const QByteArray token = m_clientTokens.value(clientId);
|
||||||
|
if (!NymeaCore::instance()->userManager()->accessToThingGranted(thingId, token)) {
|
||||||
|
qCDebug(dcJsonRpc()) << "Not sending notification to client" << "to client" << clientId.toString()
|
||||||
|
<< "due to missing thing permissions" << handler->name() + "." + method.name();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add deprecation warning if necessary
|
||||||
|
if (m_api.value("notifications").toMap().value(handler->name() + '.' + method.name()).toMap().contains("deprecated")) {
|
||||||
|
QString deprecationMessage = m_api.value("notifications").toMap().value(handler->name() + '.' + method.name()).toMap().value("deprecated").toString();
|
||||||
|
qCWarning(dcJsonRpc()) << "Client" << clientId << "uses deprecated API. Please update client implementation!";
|
||||||
|
qCWarning(dcJsonRpc()) << handler->name() + '.' + method.name() + ':' << deprecationMessage;
|
||||||
|
notification.insert("deprecationWarning", deprecationMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
QLocale locale = m_clientLocales.value(clientId);
|
||||||
|
QVariantMap translatedParams = handler->translateNotification(method.name(), params, locale);
|
||||||
|
|
||||||
|
JsonValidator validator;
|
||||||
|
Q_ASSERT_X(validator.validateNotificationParams(translatedParams, handler->name() + '.' + method.name(), m_api).success(),
|
||||||
|
validator.result().where().toUtf8(),
|
||||||
|
validator.result().errorString().toUtf8() + "\nGot:" + QJsonDocument::fromVariant(translatedParams).toJson(QJsonDocument::Indented));
|
||||||
|
|
||||||
|
notification.insert("params", translatedParams);
|
||||||
|
|
||||||
|
QByteArray data = QJsonDocument::fromVariant(notification).toJson(QJsonDocument::Compact);
|
||||||
|
|
||||||
|
qCDebug(dcJsonRpc()) << "Sending notification" << handler->name() + "." + method.name() << "to client" << clientId;
|
||||||
|
qCDebug(dcJsonRpcTraffic()) << "Notification content:" << data;
|
||||||
|
|
||||||
|
m_clientTransports.value(clientId)->sendData(clientId, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonRPCServerImplementation::sendClientNotification(const QVariantMap ¶ms, const UserInfo &userInfo)
|
||||||
|
{
|
||||||
|
// Send client specific notifications
|
||||||
|
qCDebug(dcJsonRpc()) << "Sending notification to client" << userInfo.username() << "connections...";
|
||||||
|
foreach (const QByteArray &token, m_clientTokens) {
|
||||||
|
if (NymeaCore::instance()->userManager()->tokenInfo(token).username() == userInfo.username()) {
|
||||||
|
sendClientNotification(m_clientTokens.key(token), params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void JsonRPCServerImplementation::asyncReplyFinished()
|
void JsonRPCServerImplementation::asyncReplyFinished()
|
||||||
{
|
{
|
||||||
JsonReply *reply = qobject_cast<JsonReply *>(sender());
|
JsonReply *reply = qobject_cast<JsonReply *>(sender());
|
||||||
@ -980,9 +1037,17 @@ bool JsonRPCServerImplementation::registerHandler(JsonHandler *handler)
|
|||||||
QMetaMethod method = handler->metaObject()->method(i);
|
QMetaMethod method = handler->metaObject()->method(i);
|
||||||
if (method.methodType() == QMetaMethod::Signal && QString(method.name()).contains(QRegularExpression("^[A-Z]"))) {
|
if (method.methodType() == QMetaMethod::Signal && QString(method.name()).contains(QRegularExpression("^[A-Z]"))) {
|
||||||
if (method.parameterCount() == 1 && method.parameterType(0) == QMetaType::QVariantMap) {
|
if (method.parameterCount() == 1 && method.parameterType(0) == QMetaType::QVariantMap) {
|
||||||
|
// Generic notification for all subscribed clients
|
||||||
QObject::connect(handler, method, this, metaObject()->method(metaObject()->indexOfSlot("sendNotification(QVariantMap)")));
|
QObject::connect(handler, method, this, metaObject()->method(metaObject()->indexOfSlot("sendNotification(QVariantMap)")));
|
||||||
} else if (method.parameterCount() == 2 && method.parameterType(0) == QMetaType::QUuid && method.parameterType(1) == QMetaType::QVariantMap) {
|
} else if (method.parameterCount() == 2 && method.parameterType(0) == QMetaType::QUuid && method.parameterType(1) == QMetaType::QVariantMap) {
|
||||||
|
// Notifications for a specific client with the given UUID
|
||||||
QObject::connect(handler, method, this, metaObject()->method(metaObject()->indexOfSlot("sendClientNotification(QUuid,QVariantMap)")));
|
QObject::connect(handler, method, this, metaObject()->method(metaObject()->indexOfSlot("sendClientNotification(QUuid,QVariantMap)")));
|
||||||
|
} else if (method.parameterCount() == 2 && method.parameterType(0) == QMetaType::QVariantMap && method.parameterType(1) == QMetaType::type("ThingId")) {
|
||||||
|
// Notifications which contains thing specific information which might be restricted for certain clients
|
||||||
|
QObject::connect(handler, method, this, metaObject()->method(metaObject()->indexOfSlot("sendClientNotification(QVariantMap,ThingId)")));
|
||||||
|
} else if (method.parameterCount() == 2 && method.parameterType(0) == QMetaType::QVariantMap && method.parameterType(1) == QMetaType::type("nymeaserver::UserInfo")) {
|
||||||
|
// Notifications for a specific user
|
||||||
|
QObject::connect(handler, method, this, metaObject()->method(metaObject()->indexOfSlot("sendClientNotification(QVariantMap,nymeaserver::UserInfo)")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1012,6 +1077,7 @@ void JsonRPCServerImplementation::clientConnected(const QUuid &clientId)
|
|||||||
m_newConnectionWaitTimers.remove(clientId);
|
m_newConnectionWaitTimers.remove(clientId);
|
||||||
interface->terminateClientConnection(clientId);
|
interface->terminateClientConnection(clientId);
|
||||||
});
|
});
|
||||||
|
|
||||||
m_newConnectionWaitTimers.insert(clientId, timer);
|
m_newConnectionWaitTimers.insert(clientId, timer);
|
||||||
timer->start(10000);
|
timer->start(10000);
|
||||||
}
|
}
|
||||||
@ -1024,9 +1090,11 @@ void JsonRPCServerImplementation::clientDisconnected(const QUuid &clientId)
|
|||||||
m_clientBuffers.remove(clientId);
|
m_clientBuffers.remove(clientId);
|
||||||
m_clientLocales.remove(clientId);
|
m_clientLocales.remove(clientId);
|
||||||
m_clientTokens.remove(clientId);
|
m_clientTokens.remove(clientId);
|
||||||
|
|
||||||
if (m_pushButtonTransactions.values().contains(clientId)) {
|
if (m_pushButtonTransactions.values().contains(clientId)) {
|
||||||
NymeaCore::instance()->userManager()->cancelPushButtonAuth(m_pushButtonTransactions.key(clientId));
|
NymeaCore::instance()->userManager()->cancelPushButtonAuth(m_pushButtonTransactions.key(clientId));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_newConnectionWaitTimers.contains(clientId)) {
|
if (m_newConnectionWaitTimers.contains(clientId)) {
|
||||||
delete m_newConnectionWaitTimers.take(clientId);
|
delete m_newConnectionWaitTimers.take(clientId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,12 +27,9 @@
|
|||||||
|
|
||||||
#include "jsonrpc/jsonrpcserver.h"
|
#include "jsonrpc/jsonrpcserver.h"
|
||||||
#include "jsonrpc/jsonhandler.h"
|
#include "jsonrpc/jsonhandler.h"
|
||||||
|
#include "usermanager/userinfo.h"
|
||||||
#include "transportinterface.h"
|
#include "transportinterface.h"
|
||||||
|
|
||||||
#include "types/thingclass.h"
|
|
||||||
#include "types/action.h"
|
|
||||||
#include "types/event.h"
|
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QVariantMap>
|
#include <QVariantMap>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
@ -90,6 +87,8 @@ private slots:
|
|||||||
|
|
||||||
void sendNotification(const QVariantMap ¶ms);
|
void sendNotification(const QVariantMap ¶ms);
|
||||||
void sendClientNotification(const QUuid &clientId, const QVariantMap ¶ms);
|
void sendClientNotification(const QUuid &clientId, const QVariantMap ¶ms);
|
||||||
|
void sendClientNotification(const QVariantMap ¶ms, const ThingId &thingId);
|
||||||
|
void sendClientNotification(const QVariantMap ¶ms, const nymeaserver::UserInfo &userInfo);
|
||||||
|
|
||||||
void asyncReplyFinished();
|
void asyncReplyFinished();
|
||||||
|
|
||||||
|
|||||||
@ -160,10 +160,11 @@ void NymeaCore::init(const QStringList &additionalInterfaces, bool disableLogEng
|
|||||||
m_experienceManager = new ExperienceManager(m_thingManager, m_serverManager->jsonServer(), this);
|
m_experienceManager = new ExperienceManager(m_thingManager, m_serverManager->jsonServer(), this);
|
||||||
|
|
||||||
connect(m_configuration, &NymeaConfiguration::serverNameChanged, m_serverManager, &ServerManager::setServerName);
|
connect(m_configuration, &NymeaConfiguration::serverNameChanged, m_serverManager, &ServerManager::setServerName);
|
||||||
|
|
||||||
connect(m_thingManager, &ThingManagerImplementation::loaded, this, &NymeaCore::thingManagerLoaded);
|
connect(m_thingManager, &ThingManagerImplementation::loaded, this, &NymeaCore::thingManagerLoaded);
|
||||||
|
connect(m_thingManager, &ThingManagerImplementation::thingRemoved, m_userManager, &UserManager::onThingRemoved);
|
||||||
|
|
||||||
m_logger->log({"started"}, {{"version", NYMEA_VERSION_STRING}});
|
m_logger->log({"started"}, {{"version", NYMEA_VERSION_STRING}});
|
||||||
|
|
||||||
#ifdef WITH_SYSTEMD
|
#ifdef WITH_SYSTEMD
|
||||||
sd_notify(0, "READY=1");
|
sd_notify(0, "READY=1");
|
||||||
#endif
|
#endif
|
||||||
@ -296,7 +297,7 @@ QStringList NymeaCore::loggingFiltersPlugins()
|
|||||||
QStringList loggingFiltersPlugins;
|
QStringList loggingFiltersPlugins;
|
||||||
foreach (const QJsonObject &pluginMetadata, ThingManagerImplementation::pluginsMetadata()) {
|
foreach (const QJsonObject &pluginMetadata, ThingManagerImplementation::pluginsMetadata()) {
|
||||||
QString pluginName = pluginMetadata.value("name").toString();
|
QString pluginName = pluginMetadata.value("name").toString();
|
||||||
loggingFiltersPlugins << pluginName.left(1).toUpper() + pluginName.mid(1);
|
loggingFiltersPlugins << pluginName.at(0).toUpper() + pluginName.mid(1);
|
||||||
}
|
}
|
||||||
return loggingFiltersPlugins;
|
return loggingFiltersPlugins;
|
||||||
}
|
}
|
||||||
@ -368,7 +369,6 @@ JsonRPCServerImplementation *NymeaCore::jsonRPCServer() const
|
|||||||
|
|
||||||
void NymeaCore::thingManagerLoaded()
|
void NymeaCore::thingManagerLoaded()
|
||||||
{
|
{
|
||||||
|
|
||||||
// Tell hardare resources we're done with loading stuff...
|
// Tell hardare resources we're done with loading stuff...
|
||||||
m_hardwareManager->thingsLoaded();
|
m_hardwareManager->thingsLoaded();
|
||||||
|
|
||||||
@ -396,7 +396,6 @@ void NymeaCore::thingManagerLoaded()
|
|||||||
m_tagsStorage->removeTag(tag);
|
m_tagsStorage->removeTag(tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -66,7 +66,6 @@ private:
|
|||||||
QString m_displayName;
|
QString m_displayName;
|
||||||
Types::PermissionScopes m_scopes = Types::PermissionScopeNone;
|
Types::PermissionScopes m_scopes = Types::PermissionScopeNone;
|
||||||
QList<ThingId> m_allowedThingIds;
|
QList<ThingId> m_allowedThingIds;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class UserInfoList: public QList<UserInfo>
|
class UserInfoList: public QList<UserInfo>
|
||||||
@ -78,4 +77,7 @@ public:
|
|||||||
Q_INVOKABLE void put(const QVariant &variant);
|
Q_INVOKABLE void put(const QVariant &variant);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(nymeaserver::UserInfo);
|
||||||
|
|
||||||
#endif // USERINFO_H
|
#endif // USERINFO_H
|
||||||
|
|||||||
@ -63,7 +63,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "usermanager.h"
|
#include "usermanager.h"
|
||||||
#include "nymeasettings.h"
|
|
||||||
#include "loggingcategories.h"
|
#include "loggingcategories.h"
|
||||||
#include "pushbuttondbusservice.h"
|
#include "pushbuttondbusservice.h"
|
||||||
#include "nymeacore.h"
|
#include "nymeacore.h"
|
||||||
@ -192,7 +191,8 @@ UserManager::UserError UserManager::createUser(const QString &username, const QS
|
|||||||
return UserErrorDuplicateUserId;
|
return UserErrorDuplicateUserId;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray salt = QUuid::createUuid().toString().remove(QRegularExpression("[{}]")).toUtf8();
|
static QRegularExpression bracketsRe("[{}]");
|
||||||
|
QByteArray salt = QUuid::createUuid().toString().remove(bracketsRe).toUtf8();
|
||||||
QByteArray hashedPassword = QCryptographicHash::hash(QString(password + salt).toUtf8(), QCryptographicHash::Sha512).toBase64();
|
QByteArray hashedPassword = QCryptographicHash::hash(QString(password + salt).toUtf8(), QCryptographicHash::Sha512).toBase64();
|
||||||
QSqlQuery query(m_db);
|
QSqlQuery query(m_db);
|
||||||
query.prepare("INSERT INTO users(username, email, displayName, password, salt, scopes, allowedThingIds)"
|
query.prepare("INSERT INTO users(username, email, displayName, password, salt, scopes, allowedThingIds)"
|
||||||
@ -305,6 +305,34 @@ UserManager::UserError UserManager::setUserScopes(const QString &username, Types
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<ThingId> thingsAppeared;
|
||||||
|
QList<ThingId> thingsDisappeared;
|
||||||
|
|
||||||
|
// Get the current allowed things
|
||||||
|
if (!scopes.testFlag(Types::PermissionScopeAccessAllThings)) {
|
||||||
|
|
||||||
|
// Restricted thing access, let's notify this user if any things appeared or dissapeard for the user
|
||||||
|
UserInfo currentUserInfo = userInfo(username);
|
||||||
|
|
||||||
|
// Get new appeared things for this user
|
||||||
|
foreach (const ThingId &thingId, thingIds) {
|
||||||
|
if (currentUserInfo.allowedThingIds().contains(thingId))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
qCDebug(dcUserManager()) << "Thing with ID" << thingId.toString() << "now allowed for this user any more. Notify user" << username << "that thing appeared.";
|
||||||
|
thingsAppeared.append(thingId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get disappeared things for this user
|
||||||
|
foreach (const ThingId &thingId, currentUserInfo.allowedThingIds()) {
|
||||||
|
if (thingIds.contains(thingId))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
qCDebug(dcUserManager()) << "Thing with ID" << thingId.toString() << "not allowed for this user any more. Notify user" << username << "that thing dissappeared.";
|
||||||
|
thingsDisappeared.append(thingId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QString scopesString = Types::scopesToStringList(scopes).join(',');
|
QString scopesString = Types::scopesToStringList(scopes).join(',');
|
||||||
QString allowedThingIdsString = Types::thingIdsToStringList(thingIds).join(',');
|
QString allowedThingIdsString = Types::thingIdsToStringList(thingIds).join(',');
|
||||||
|
|
||||||
@ -320,16 +348,25 @@ UserManager::UserError UserManager::setUserScopes(const QString &username, Types
|
|||||||
}
|
}
|
||||||
|
|
||||||
emit userChanged(username);
|
emit userChanged(username);
|
||||||
|
|
||||||
|
// Notify after updating the user information
|
||||||
|
UserInfo ui = userInfo(username);
|
||||||
|
foreach (const ThingId &thingId, thingsAppeared)
|
||||||
|
emit userThingRestrictionsChanged(ui, thingId, true);
|
||||||
|
|
||||||
|
foreach (const ThingId &thingId, thingsDisappeared)
|
||||||
|
emit userThingRestrictionsChanged(ui, thingId, false);
|
||||||
|
|
||||||
return UserErrorNoError;
|
return UserErrorNoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
UserManager::UserError UserManager::setUserInfo(const QString &username, const QString &email, const QString &displayName)
|
UserManager::UserError UserManager::setUserInfo(const QString &username, const QString &email, const QString &displayName)
|
||||||
{
|
{
|
||||||
QSqlQuery query(m_db);
|
QSqlQuery query(m_db);
|
||||||
query.prepare("UPDATE users SET email = ?, displayName = ? WHERE username = ?;");
|
query.prepare("UPDATE users SET email = :email, displayName = :displayName WHERE username = :username;");
|
||||||
query.addBindValue(email);
|
query.bindValue(":email", email);
|
||||||
query.addBindValue(displayName);
|
query.bindValue(":displayName", displayName);
|
||||||
query.addBindValue(username);
|
query.bindValue(":username", username);
|
||||||
query.exec();
|
query.exec();
|
||||||
if (query.lastError().type() != QSqlError::NoError) {
|
if (query.lastError().type() != QSqlError::NoError) {
|
||||||
qCWarning(dcUserManager()) << "Error updating user info for user" << username << query.lastError().databaseText() << query.lastError().driverText() << query.executedQuery();
|
qCWarning(dcUserManager()) << "Error updating user info for user" << username << query.lastError().databaseText() << query.lastError().driverText() << query.executedQuery();
|
||||||
@ -454,7 +491,6 @@ UserInfo UserManager::userInfo(const QString &username) const
|
|||||||
userInfo.setDisplayName(getUserQuery.value("displayName").toString());
|
userInfo.setDisplayName(getUserQuery.value("displayName").toString());
|
||||||
userInfo.setScopes(Types::scopesFromStringList(getUserQuery.value("scopes").toString().split(',')));
|
userInfo.setScopes(Types::scopesFromStringList(getUserQuery.value("scopes").toString().split(',')));
|
||||||
userInfo.setAllowedThingIds(Types::thingIdsFromStringList(getUserQuery.value("allowedThingIds").toString().split(',')));
|
userInfo.setAllowedThingIds(Types::thingIdsFromStringList(getUserQuery.value("allowedThingIds").toString().split(',')));
|
||||||
|
|
||||||
return userInfo;
|
return userInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -575,6 +611,7 @@ bool UserManager::verifyToken(const QByteArray &token)
|
|||||||
qCDebug(dcUserManager) << "Authorization failed for token" << token;
|
qCDebug(dcUserManager) << "Authorization failed for token" << token;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//qCDebug(dcUserManager) << "Token authorized for user" << result.value("username").toString();
|
//qCDebug(dcUserManager) << "Token authorized for user" << result.value("username").toString();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -598,6 +635,23 @@ QList<ThingId> UserManager::getAllowedThingIdsForToken(const QByteArray &token)
|
|||||||
return userInfo(tokenInfo(token).username()).allowedThingIds();
|
return userInfo(tokenInfo(token).username()).allowedThingIds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UserManager::onThingRemoved(const ThingId &thingId)
|
||||||
|
{
|
||||||
|
// If a thing has been removed from the system, clean up any thing based permissions
|
||||||
|
foreach (const UserInfo &userInfo, users()) {
|
||||||
|
if (userInfo.allowedThingIds().contains(thingId)) {
|
||||||
|
QList<ThingId> allowedThingIds = userInfo.allowedThingIds();
|
||||||
|
allowedThingIds.removeAll(thingId);
|
||||||
|
|
||||||
|
if (setUserScopes(userInfo.username(), userInfo.scopes(), allowedThingIds) != UserErrorNoError) {
|
||||||
|
qCWarning(dcUserManager()) << "Failed to remove thing with ID" << thingId.toString() << "from allowed things of user" << userInfo.username();
|
||||||
|
} else {
|
||||||
|
qCDebug(dcUserManager()) << "Removed thing with ID" << thingId.toString() << "from allowed things of user" << userInfo.username();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool UserManager::initDB()
|
bool UserManager::initDB()
|
||||||
{
|
{
|
||||||
m_db.close();
|
m_db.close();
|
||||||
@ -775,35 +829,6 @@ bool UserManager::initDB()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Migration from before 1.0:
|
|
||||||
// Push button tokens were given out without an explicit user name
|
|
||||||
// If we have push button tokens (userId "") but no explicit user, let's create it as admin
|
|
||||||
// Users without valid username will have password login disabled.
|
|
||||||
QSqlQuery query(m_db);
|
|
||||||
query.prepare("SELECT * FROM tokens WHERE username = \"\";");
|
|
||||||
query.exec();
|
|
||||||
if (query.lastError().type() == QSqlError::NoError && query.next()) {
|
|
||||||
QSqlQuery query(m_db);
|
|
||||||
query.prepare("SELECT * FROM users WHERE username = \"\";");
|
|
||||||
query.exec();
|
|
||||||
if (!query.next()) {
|
|
||||||
qCDebug(dcUserManager()) << "Tokens existing but no user. Creating token admin user";
|
|
||||||
QSqlQuery query(m_db);
|
|
||||||
query.prepare("INSERT INTO users(username, email, displayName, password, salt, scopes) values(?, ?, ?, ?, ?, ?);");
|
|
||||||
query.addBindValue("");
|
|
||||||
query.addBindValue("");
|
|
||||||
query.addBindValue("Admin");
|
|
||||||
query.addBindValue("");
|
|
||||||
query.addBindValue("");
|
|
||||||
query.addBindValue(Types::scopeToString(Types::PermissionScopeAdmin));
|
|
||||||
query.exec();
|
|
||||||
if (query.lastError().type() != QSqlError::NoError) {
|
|
||||||
qCWarning(dcUserManager) << "Error creating push button user:" << query.lastError().databaseText() << query.lastError().driverText();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
qCDebug(dcUserManager()) << "User database initialized successfully";
|
qCDebug(dcUserManager()) << "User database initialized successfully";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -811,9 +836,8 @@ bool UserManager::initDB()
|
|||||||
void UserManager::rotate(const QString &dbName)
|
void UserManager::rotate(const QString &dbName)
|
||||||
{
|
{
|
||||||
int index = 1;
|
int index = 1;
|
||||||
while (QFileInfo::exists(QString("%1.%2").arg(dbName).arg(index))) {
|
while (QFileInfo::exists(QString("%1.%2").arg(dbName).arg(index)))
|
||||||
index++;
|
index++;
|
||||||
}
|
|
||||||
|
|
||||||
qCDebug(dcUserManager()) << "Backing up old database file to" << QString("%1.%2").arg(dbName).arg(index);
|
qCDebug(dcUserManager()) << "Backing up old database file to" << QString("%1.%2").arg(dbName).arg(index);
|
||||||
QFile f(dbName);
|
QFile f(dbName);
|
||||||
@ -826,30 +850,33 @@ void UserManager::rotate(const QString &dbName)
|
|||||||
|
|
||||||
bool UserManager::validateUsername(const QString &username) const
|
bool UserManager::validateUsername(const QString &username) const
|
||||||
{
|
{
|
||||||
QRegularExpression validator("[a-zA-Z0-9_\\.+-@]{3,}");
|
static QRegularExpression validator("[a-zA-Z0-9_\\.+-@]{3,}");
|
||||||
return validator.match(username).hasMatch();
|
return validator.match(username).hasMatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UserManager::validatePassword(const QString &password) const
|
bool UserManager::validatePassword(const QString &password) const
|
||||||
{
|
{
|
||||||
if (password.length() < 8) {
|
if (password.length() < 8)
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
if (!password.contains(QRegularExpression("[a-z]"))) {
|
static QRegularExpression lowerRe("[a-z]");
|
||||||
|
if (!password.contains(lowerRe))
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
if (!password.contains(QRegularExpression("[A-Z]"))) {
|
static QRegularExpression upperRe("[A-Z]");
|
||||||
|
if (!password.contains(upperRe))
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
if (!password.contains(QRegularExpression("[0-9]"))) {
|
static QRegularExpression numbersRe("[0-9]");
|
||||||
|
if (!password.contains(numbersRe))
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UserManager::validateToken(const QByteArray &token) const
|
bool UserManager::validateToken(const QByteArray &token) const
|
||||||
{
|
{
|
||||||
QRegularExpression validator(QRegularExpression("(^[a-zA-Z0-9_\\.+-/=]+$)"));
|
static QRegularExpression validator(QRegularExpression("(^[a-zA-Z0-9_\\.+-/=]+$)"));
|
||||||
return validator.match(token).hasMatch();
|
return validator.match(token).hasMatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -899,6 +926,11 @@ void UserManager::dumpDBError(const QString &message)
|
|||||||
qCCritical(dcUserManager) << message << "Driver error:" << m_db.lastError().driverText() << "Database error:" << m_db.lastError().databaseText();
|
qCCritical(dcUserManager) << message << "Driver error:" << m_db.lastError().driverText() << "Database error:" << m_db.lastError().databaseText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UserManager::evaluateAllowedThingsForUser()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void UserManager::onPushButtonPressed()
|
void UserManager::onPushButtonPressed()
|
||||||
{
|
{
|
||||||
if (m_pushButtonTransaction.first == -1) {
|
if (m_pushButtonTransaction.first == -1) {
|
||||||
|
|||||||
@ -81,12 +81,17 @@ public:
|
|||||||
bool accessToThingGranted(const ThingId &thingId, const QByteArray &token);
|
bool accessToThingGranted(const ThingId &thingId, const QByteArray &token);
|
||||||
QList<ThingId> getAllowedThingIdsForToken(const QByteArray &token) const;
|
QList<ThingId> getAllowedThingIdsForToken(const QByteArray &token) const;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void onThingRemoved(const ThingId &thingId);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void userAdded(const QString &username);
|
void userAdded(const QString &username);
|
||||||
void userRemoved(const QString &username);
|
void userRemoved(const QString &username);
|
||||||
void userChanged(const QString &username);
|
void userChanged(const QString &username);
|
||||||
void pushButtonAuthFinished(int transactionId, bool success, const QByteArray &token);
|
void pushButtonAuthFinished(int transactionId, bool success, const QByteArray &token);
|
||||||
|
|
||||||
|
void userThingRestrictionsChanged(const nymeaserver::UserInfo &userInfo, const ThingId &thingId, bool accessGranted);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool initDB();
|
bool initDB();
|
||||||
void rotate(const QString &dbName);
|
void rotate(const QString &dbName);
|
||||||
@ -97,6 +102,8 @@ private:
|
|||||||
|
|
||||||
void dumpDBError(const QString &message);
|
void dumpDBError(const QString &message);
|
||||||
|
|
||||||
|
void evaluateAllowedThingsForUser();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onPushButtonPressed();
|
void onPushButtonPressed();
|
||||||
|
|
||||||
|
|||||||
@ -620,10 +620,10 @@ void TestUsermanager::testRestrictedThingAccess()
|
|||||||
|
|
||||||
// Add thing two
|
// Add thing two
|
||||||
QVariantMap httpportParamTwo;
|
QVariantMap httpportParamTwo;
|
||||||
httpportParamOne.insert("paramTypeId", mockThingHttpportParamTypeId.toString());
|
httpportParamTwo.insert("paramTypeId", mockThingHttpportParamTypeId.toString());
|
||||||
httpportParamOne.insert("value", m_mockThing1Port - 2);
|
httpportParamTwo.insert("value", m_mockThing1Port - 2);
|
||||||
thingParams.clear();
|
thingParams.clear();
|
||||||
thingParams << httpportParamOne;
|
thingParams << httpportParamTwo;
|
||||||
|
|
||||||
params.clear();
|
params.clear();
|
||||||
params.insert("thingClassId", mockThingClassId);
|
params.insert("thingClassId", mockThingClassId);
|
||||||
@ -689,33 +689,32 @@ void TestUsermanager::testRestrictedThingAccess()
|
|||||||
response = injectAndWait("Integrations.GetThings", params);
|
response = injectAndWait("Integrations.GetThings", params);
|
||||||
verifyError(response, "thingError", enumValueName(Thing::ThingErrorThingNotFound));
|
verifyError(response, "thingError", enumValueName(Thing::ThingErrorThingNotFound));
|
||||||
|
|
||||||
// GetStateValue
|
// GetStateValue (no access)
|
||||||
params.clear();
|
params.clear();
|
||||||
params.insert("thingId", thingIdOne);
|
params.insert("thingId", thingIdOne);
|
||||||
params.insert("stateTypeId", mockConnectedStateTypeId);
|
params.insert("stateTypeId", mockConnectedStateTypeId);
|
||||||
response = injectAndWait("Integrations.GetStateValue", params);
|
response = injectAndWait("Integrations.GetStateValue", params);
|
||||||
verifyError(response, "thingError", enumValueName(Thing::ThingErrorThingNotFound));
|
verifyError(response, "thingError", enumValueName(Thing::ThingErrorThingNotFound));
|
||||||
|
|
||||||
// BrowseThing
|
// BrowseThing (no access)
|
||||||
params.clear();
|
params.clear();
|
||||||
params.insert("thingId", thingIdOne);
|
params.insert("thingId", thingIdOne);
|
||||||
response = injectAndWait("Integrations.BrowseThing", params);
|
response = injectAndWait("Integrations.BrowseThing", params);
|
||||||
verifyError(response, "thingError", enumValueName(Thing::ThingErrorThingNotFound));
|
verifyError(response, "thingError", enumValueName(Thing::ThingErrorThingNotFound));
|
||||||
|
|
||||||
// GetBrowserItem
|
// GetBrowserItem (no access)
|
||||||
params.clear();
|
params.clear();
|
||||||
params.insert("thingId", thingIdOne);
|
params.insert("thingId", thingIdOne);
|
||||||
response = injectAndWait("Integrations.GetBrowserItem", params);
|
response = injectAndWait("Integrations.GetBrowserItem", params);
|
||||||
verifyError(response, "thingError", enumValueName(Thing::ThingErrorThingNotFound));
|
verifyError(response, "thingError", enumValueName(Thing::ThingErrorThingNotFound));
|
||||||
|
|
||||||
// Make sure notification get received from allowed thing
|
|
||||||
|
|
||||||
// Make sure no notification will be recived from restricted thing
|
|
||||||
|
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
|
UserManager *userManager = NymeaCore::instance()->userManager();
|
||||||
|
foreach (const UserInfo &userInfo, userManager->users()) {
|
||||||
|
qCDebug(dcTests()) << "Removing user" << userInfo.username();
|
||||||
|
userManager->removeUser(userInfo.username());
|
||||||
|
}
|
||||||
|
userManager->removeUser("");
|
||||||
}
|
}
|
||||||
|
|
||||||
QTEST_MAIN(TestUsermanager)
|
QTEST_MAIN(TestUsermanager)
|
||||||
|
|||||||
Reference in New Issue
Block a user