Move authentication to Users namespace
This commit is contained in:
parent
ec0aa802c5
commit
fb94178920
@ -109,12 +109,12 @@ QString ActionHandler::name() const
|
||||
return "Actions";
|
||||
}
|
||||
|
||||
JsonReply* ActionHandler::ExecuteAction(const QVariantMap ¶ms)
|
||||
JsonReply* ActionHandler::ExecuteAction(const QVariantMap ¶ms, const JsonContext &context)
|
||||
{
|
||||
DeviceId deviceId(params.value("deviceId").toString());
|
||||
ActionTypeId actionTypeId(params.value("actionTypeId").toString());
|
||||
ParamList actionParams = unpack<ParamList>(params.value("params"));
|
||||
QLocale locale = params.value("locale").toLocale();
|
||||
QLocale locale = context.locale();
|
||||
|
||||
Action action(actionTypeId, deviceId);
|
||||
action.setParams(actionParams);
|
||||
@ -135,9 +135,9 @@ JsonReply* ActionHandler::ExecuteAction(const QVariantMap ¶ms)
|
||||
return jsonReply;
|
||||
}
|
||||
|
||||
JsonReply *ActionHandler::GetActionType(const QVariantMap ¶ms) const
|
||||
JsonReply *ActionHandler::GetActionType(const QVariantMap ¶ms, const JsonContext &context) const
|
||||
{
|
||||
QLocale locale = params.value("locale").toLocale();
|
||||
QLocale locale = context.locale();
|
||||
qCDebug(dcJsonRpc) << "asked for action type" << params;
|
||||
ActionTypeId actionTypeId(params.value("actionTypeId").toString());
|
||||
foreach (const DeviceClass &deviceClass, NymeaCore::instance()->deviceManager()->supportedDevices()) {
|
||||
|
||||
@ -44,8 +44,8 @@ public:
|
||||
|
||||
QString name() const;
|
||||
|
||||
Q_INVOKABLE JsonReply *ExecuteAction(const QVariantMap ¶ms);
|
||||
Q_INVOKABLE JsonReply *GetActionType(const QVariantMap ¶ms) const;
|
||||
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);
|
||||
|
||||
@ -415,13 +415,12 @@ QString DeviceHandler::name() const
|
||||
return "Devices";
|
||||
}
|
||||
|
||||
JsonReply* DeviceHandler::GetSupportedVendors(const QVariantMap ¶ms) const
|
||||
JsonReply* DeviceHandler::GetSupportedVendors(const QVariantMap ¶ms, const JsonContext &context) const
|
||||
{
|
||||
QLocale locale = params.value("locale").toLocale();
|
||||
|
||||
Q_UNUSED(params)
|
||||
QVariantList vendors;
|
||||
foreach (const Vendor &vendor, NymeaCore::instance()->deviceManager()->supportedVendors()) {
|
||||
Vendor translatedVendor = NymeaCore::instance()->deviceManager()->translateVendor(vendor, locale);
|
||||
Vendor translatedVendor = NymeaCore::instance()->deviceManager()->translateVendor(vendor, context.locale());
|
||||
vendors.append(pack(translatedVendor));
|
||||
}
|
||||
|
||||
@ -430,14 +429,13 @@ JsonReply* DeviceHandler::GetSupportedVendors(const QVariantMap ¶ms) const
|
||||
return createReply(returns);
|
||||
}
|
||||
|
||||
JsonReply* DeviceHandler::GetSupportedDevices(const QVariantMap ¶ms) const
|
||||
JsonReply* DeviceHandler::GetSupportedDevices(const QVariantMap ¶ms, const JsonContext &context) const
|
||||
{
|
||||
QLocale locale = params.value("locale").toLocale();
|
||||
VendorId vendorId = VendorId(params.value("vendorId").toString());
|
||||
QVariantMap returns;
|
||||
QVariantList deviceClasses;
|
||||
foreach (const DeviceClass &deviceClass, NymeaCore::instance()->deviceManager()->supportedDevices(vendorId)) {
|
||||
DeviceClass translatedDeviceClass = NymeaCore::instance()->deviceManager()->translateDeviceClass(deviceClass, locale);
|
||||
DeviceClass translatedDeviceClass = NymeaCore::instance()->deviceManager()->translateDeviceClass(deviceClass, context.locale());
|
||||
deviceClasses.append(pack(translatedDeviceClass));
|
||||
}
|
||||
|
||||
@ -445,9 +443,9 @@ JsonReply* DeviceHandler::GetSupportedDevices(const QVariantMap ¶ms) const
|
||||
return createReply(returns);
|
||||
}
|
||||
|
||||
JsonReply *DeviceHandler::GetDiscoveredDevices(const QVariantMap ¶ms) const
|
||||
JsonReply *DeviceHandler::GetDiscoveredDevices(const QVariantMap ¶ms, const JsonContext &context) const
|
||||
{
|
||||
QLocale locale = params.value("locale").toLocale();
|
||||
QLocale locale = context.locale();
|
||||
|
||||
QVariantMap returns;
|
||||
|
||||
@ -479,14 +477,13 @@ JsonReply *DeviceHandler::GetDiscoveredDevices(const QVariantMap ¶ms) const
|
||||
return reply;
|
||||
}
|
||||
|
||||
JsonReply* DeviceHandler::GetPlugins(const QVariantMap ¶ms) const
|
||||
JsonReply* DeviceHandler::GetPlugins(const QVariantMap ¶ms, const JsonContext &context) const
|
||||
{
|
||||
QLocale locale = params.value("locale").toLocale();
|
||||
|
||||
Q_UNUSED(params)
|
||||
QVariantList plugins;
|
||||
foreach (DevicePlugin* plugin, NymeaCore::instance()->deviceManager()->plugins()) {
|
||||
QVariantMap packedPlugin = pack(*plugin).toMap();
|
||||
packedPlugin["displayName"] = NymeaCore::instance()->deviceManager()->translate(plugin->pluginId(), plugin->pluginDisplayName(), locale);
|
||||
packedPlugin["displayName"] = NymeaCore::instance()->deviceManager()->translate(plugin->pluginId(), plugin->pluginDisplayName(), context.locale());
|
||||
plugins.append(packedPlugin);
|
||||
}
|
||||
|
||||
@ -524,13 +521,13 @@ JsonReply* DeviceHandler::SetPluginConfiguration(const QVariantMap ¶ms)
|
||||
return createReply(returns);
|
||||
}
|
||||
|
||||
JsonReply* DeviceHandler::AddConfiguredDevice(const QVariantMap ¶ms)
|
||||
JsonReply* DeviceHandler::AddConfiguredDevice(const QVariantMap ¶ms, const JsonContext &context)
|
||||
{
|
||||
DeviceClassId deviceClassId(params.value("deviceClassId").toString());
|
||||
QString deviceName = params.value("name").toString();
|
||||
ParamList deviceParams = unpack<ParamList>(params.value("deviceParams"));
|
||||
DeviceDescriptorId deviceDescriptorId(params.value("deviceDescriptorId").toString());
|
||||
QLocale locale = params.value("locale").toLocale();
|
||||
QLocale locale = context.locale();
|
||||
|
||||
JsonReply *jsonReply = createAsyncReply("AddConfiguredDevice");
|
||||
|
||||
@ -558,11 +555,11 @@ JsonReply* DeviceHandler::AddConfiguredDevice(const QVariantMap ¶ms)
|
||||
return jsonReply;
|
||||
}
|
||||
|
||||
JsonReply *DeviceHandler::PairDevice(const QVariantMap ¶ms)
|
||||
JsonReply *DeviceHandler::PairDevice(const QVariantMap ¶ms, const JsonContext &context)
|
||||
{
|
||||
QString deviceName = params.value("name").toString();
|
||||
ParamList deviceParams = unpack<ParamList>(params.value("deviceParams"));
|
||||
QLocale locale = params.value("locale").toLocale();
|
||||
QLocale locale = context.locale();
|
||||
|
||||
DevicePairingInfo *info;
|
||||
if (params.contains("deviceDescriptorId")) {
|
||||
@ -603,12 +600,12 @@ JsonReply *DeviceHandler::PairDevice(const QVariantMap ¶ms)
|
||||
return jsonReply;
|
||||
}
|
||||
|
||||
JsonReply *DeviceHandler::ConfirmPairing(const QVariantMap ¶ms)
|
||||
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 = params.value("locale").toLocale();
|
||||
QLocale locale = context.locale();
|
||||
|
||||
JsonReply *jsonReply = createAsyncReply("ConfirmPairing");
|
||||
|
||||
@ -651,12 +648,12 @@ JsonReply* DeviceHandler::GetConfiguredDevices(const QVariantMap ¶ms) const
|
||||
return createReply(returns);
|
||||
}
|
||||
|
||||
JsonReply *DeviceHandler::ReconfigureDevice(const QVariantMap ¶ms)
|
||||
JsonReply *DeviceHandler::ReconfigureDevice(const QVariantMap ¶ms, const JsonContext &context)
|
||||
{
|
||||
DeviceId deviceId = DeviceId(params.value("deviceId").toString());
|
||||
ParamList deviceParams = unpack<ParamList>(params.value("deviceParams"));
|
||||
DeviceDescriptorId deviceDescriptorId(params.value("deviceDescriptorId").toString());
|
||||
QLocale locale = params.value("locale").toLocale();
|
||||
QLocale locale = context.locale();
|
||||
|
||||
JsonReply *jsonReply = createAsyncReply("ReconfigureDevice");
|
||||
|
||||
@ -739,36 +736,30 @@ JsonReply *DeviceHandler::SetDeviceSettings(const QVariantMap ¶ms)
|
||||
return createReply(statusToReply(status));
|
||||
}
|
||||
|
||||
JsonReply* DeviceHandler::GetEventTypes(const QVariantMap ¶ms) const
|
||||
JsonReply* DeviceHandler::GetEventTypes(const QVariantMap ¶ms, const JsonContext &context) const
|
||||
{
|
||||
QLocale locale = params.value("locale").toLocale();
|
||||
|
||||
DeviceClass deviceClass = NymeaCore::instance()->deviceManager()->findDeviceClass(DeviceClassId(params.value("deviceClassId").toString()));
|
||||
DeviceClass translatedDeviceClass = NymeaCore::instance()->deviceManager()->translateDeviceClass(deviceClass, locale);
|
||||
DeviceClass translatedDeviceClass = NymeaCore::instance()->deviceManager()->translateDeviceClass(deviceClass, context.locale());
|
||||
|
||||
QVariantMap returns;
|
||||
returns.insert("eventTypes", pack(translatedDeviceClass.eventTypes()));
|
||||
return createReply(returns);
|
||||
}
|
||||
|
||||
JsonReply* DeviceHandler::GetActionTypes(const QVariantMap ¶ms) const
|
||||
JsonReply* DeviceHandler::GetActionTypes(const QVariantMap ¶ms, const JsonContext &context) const
|
||||
{
|
||||
QLocale locale = params.value("locale").toLocale();
|
||||
|
||||
DeviceClass deviceClass = NymeaCore::instance()->deviceManager()->findDeviceClass(DeviceClassId(params.value("deviceClassId").toString()));
|
||||
DeviceClass translatedDeviceClass = NymeaCore::instance()->deviceManager()->translateDeviceClass(deviceClass, locale);
|
||||
DeviceClass translatedDeviceClass = NymeaCore::instance()->deviceManager()->translateDeviceClass(deviceClass, context.locale());
|
||||
|
||||
QVariantMap returns;
|
||||
returns.insert("actionTypes", pack(translatedDeviceClass.actionTypes()));
|
||||
return createReply(returns);
|
||||
}
|
||||
|
||||
JsonReply* DeviceHandler::GetStateTypes(const QVariantMap ¶ms) const
|
||||
JsonReply* DeviceHandler::GetStateTypes(const QVariantMap ¶ms, const JsonContext &context) const
|
||||
{
|
||||
QLocale locale = params.value("locale").toLocale();
|
||||
|
||||
DeviceClass deviceClass = NymeaCore::instance()->deviceManager()->findDeviceClass(DeviceClassId(params.value("deviceClassId").toString()));
|
||||
DeviceClass translatedDeviceClass = NymeaCore::instance()->deviceManager()->translateDeviceClass(deviceClass, locale);
|
||||
DeviceClass translatedDeviceClass = NymeaCore::instance()->deviceManager()->translateDeviceClass(deviceClass, context.locale());
|
||||
|
||||
QVariantMap returns;
|
||||
returns.insert("stateTypes", pack(translatedDeviceClass.stateTypes()));
|
||||
@ -803,14 +794,14 @@ JsonReply *DeviceHandler::GetStateValues(const QVariantMap ¶ms) const
|
||||
return createReply(returns);
|
||||
}
|
||||
|
||||
JsonReply *DeviceHandler::BrowseDevice(const QVariantMap ¶ms) const
|
||||
JsonReply *DeviceHandler::BrowseDevice(const QVariantMap ¶ms, const JsonContext &context) const
|
||||
{
|
||||
DeviceId deviceId = DeviceId(params.value("deviceId").toString());
|
||||
QString itemId = params.value("itemId").toString();
|
||||
|
||||
JsonReply *jsonReply = createAsyncReply("BrowseDevice");
|
||||
|
||||
BrowseResult *result = NymeaCore::instance()->deviceManager()->browseDevice(deviceId, itemId, params.value("locale").toLocale());
|
||||
BrowseResult *result = NymeaCore::instance()->deviceManager()->browseDevice(deviceId, itemId, context.locale());
|
||||
connect(result, &BrowseResult::finished, jsonReply, [this, jsonReply, result](){
|
||||
|
||||
QVariantMap returns = statusToReply(result->status());
|
||||
@ -826,7 +817,7 @@ JsonReply *DeviceHandler::BrowseDevice(const QVariantMap ¶ms) const
|
||||
return jsonReply;
|
||||
}
|
||||
|
||||
JsonReply *DeviceHandler::GetBrowserItem(const QVariantMap ¶ms) const
|
||||
JsonReply *DeviceHandler::GetBrowserItem(const QVariantMap ¶ms, const JsonContext &context) const
|
||||
{
|
||||
QVariantMap returns;
|
||||
DeviceId deviceId = DeviceId(params.value("deviceId").toString());
|
||||
@ -834,7 +825,7 @@ JsonReply *DeviceHandler::GetBrowserItem(const QVariantMap ¶ms) const
|
||||
|
||||
JsonReply *jsonReply = createAsyncReply("GetBrowserItem");
|
||||
|
||||
BrowserItemResult *result = NymeaCore::instance()->deviceManager()->browserItemDetails(deviceId, itemId, params.value("locale").toLocale());
|
||||
BrowserItemResult *result = NymeaCore::instance()->deviceManager()->browserItemDetails(deviceId, itemId, context.locale());
|
||||
connect(result, &BrowserItemResult::finished, jsonReply, [this, jsonReply, result](){
|
||||
QVariantMap params = statusToReply(result->status());
|
||||
if (result->status() == Device::DeviceErrorNoError) {
|
||||
@ -847,12 +838,12 @@ JsonReply *DeviceHandler::GetBrowserItem(const QVariantMap ¶ms) const
|
||||
return jsonReply;
|
||||
}
|
||||
|
||||
JsonReply *DeviceHandler::ExecuteAction(const QVariantMap ¶ms)
|
||||
JsonReply *DeviceHandler::ExecuteAction(const QVariantMap ¶ms, const JsonContext &context)
|
||||
{
|
||||
DeviceId deviceId(params.value("deviceId").toString());
|
||||
ActionTypeId actionTypeId(params.value("actionTypeId").toString());
|
||||
ParamList actionParams = unpack<ParamList>(params.value("params"));
|
||||
QLocale locale = params.value("locale").toLocale();
|
||||
QLocale locale = context.locale();
|
||||
|
||||
Action action(actionTypeId, deviceId);
|
||||
action.setParams(actionParams);
|
||||
|
||||
@ -44,32 +44,32 @@ public:
|
||||
|
||||
QString name() const override;
|
||||
|
||||
Q_INVOKABLE JsonReply *GetSupportedVendors(const QVariantMap ¶ms) const;
|
||||
Q_INVOKABLE JsonReply *GetSupportedDevices(const QVariantMap ¶ms) const;
|
||||
Q_INVOKABLE JsonReply *GetDiscoveredDevices(const QVariantMap ¶ms) const;
|
||||
Q_INVOKABLE JsonReply *GetPlugins(const QVariantMap ¶ms) const;
|
||||
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);
|
||||
Q_INVOKABLE JsonReply *PairDevice(const QVariantMap ¶ms);
|
||||
Q_INVOKABLE JsonReply *ConfirmPairing(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;
|
||||
Q_INVOKABLE JsonReply *ReconfigureDevice(const QVariantMap ¶ms);
|
||||
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;
|
||||
Q_INVOKABLE JsonReply *GetActionTypes(const QVariantMap ¶ms) const;
|
||||
Q_INVOKABLE JsonReply *GetStateTypes(const QVariantMap ¶ms) const;
|
||||
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;
|
||||
Q_INVOKABLE JsonReply *GetBrowserItem(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);
|
||||
Q_INVOKABLE JsonReply *ExecuteAction(const QVariantMap ¶ms, const JsonContext &context);
|
||||
Q_INVOKABLE JsonReply *ExecuteBrowserItem(const QVariantMap ¶ms);
|
||||
Q_INVOKABLE JsonReply *ExecuteBrowserItemAction(const QVariantMap ¶ms);
|
||||
|
||||
|
||||
@ -93,16 +93,15 @@ void EventHandler::eventTriggered(const Event &event)
|
||||
emit EventTriggered(params);
|
||||
}
|
||||
|
||||
JsonReply* EventHandler::GetEventType(const QVariantMap ¶ms) const
|
||||
JsonReply* EventHandler::GetEventType(const QVariantMap ¶ms, const JsonContext &context) const
|
||||
{
|
||||
QLocale locale = params.value("locale").toLocale();
|
||||
qCDebug(dcJsonRpc) << "asked for event type" << params;
|
||||
EventTypeId eventTypeId(params.value("eventTypeId").toString());
|
||||
foreach (const DeviceClass &deviceClass, NymeaCore::instance()->deviceManager()->supportedDevices()) {
|
||||
foreach (const EventType &eventType, deviceClass.eventTypes()) {
|
||||
if (eventType.id() == eventTypeId) {
|
||||
EventType translatedEventType = eventType;
|
||||
translatedEventType.setDisplayName(NymeaCore::instance()->deviceManager()->translate(deviceClass.pluginId(), eventType.displayName(), locale));
|
||||
translatedEventType.setDisplayName(NymeaCore::instance()->deviceManager()->translate(deviceClass.pluginId(), eventType.displayName(), context.locale()));
|
||||
QVariantMap data;
|
||||
data.insert("deviceError", enumValueName<Device::DeviceError>(Device::DeviceErrorNoError));
|
||||
data.insert("eventType", pack(translatedEventType));
|
||||
|
||||
@ -44,7 +44,7 @@ public:
|
||||
explicit EventHandler(QObject *parent = nullptr);
|
||||
QString name() const override;
|
||||
|
||||
Q_INVOKABLE JsonReply *GetEventType(const QVariantMap ¶ms) const;
|
||||
Q_INVOKABLE JsonReply *GetEventType(const QVariantMap ¶ms, const JsonContext &context) const;
|
||||
|
||||
signals:
|
||||
void EventTriggered(const QVariantMap ¶ms);
|
||||
|
||||
@ -159,7 +159,7 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration
|
||||
params.insert("username", enumValueName(String));
|
||||
params.insert("password", enumValueName(String));
|
||||
returns.insert("error", enumRef<UserManager::UserError>());
|
||||
registerMethod("CreateUser", description, params, returns);
|
||||
registerMethod("CreateUser", description, params, returns, "Use Users.CreateUser instead.");
|
||||
|
||||
params.clear(); returns.clear();
|
||||
description = "Authenticate a client to the api via user & password challenge. Provide "
|
||||
@ -171,7 +171,7 @@ 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);
|
||||
registerMethod("Authenticate", description, params, returns, "Use Users.Authenticate instead.");
|
||||
|
||||
params.clear(); returns.clear();
|
||||
description = "Authenticate a client to the api via Push Button method. "
|
||||
@ -190,7 +190,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);
|
||||
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.";
|
||||
@ -246,7 +246,7 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration
|
||||
params.insert("success", enumValueName(Bool));
|
||||
params.insert("transactionId", enumValueName(Int));
|
||||
params.insert("o:token", enumValueName(String));
|
||||
registerNotification("PushButtonAuthFinished", description, params);
|
||||
registerNotification("PushButtonAuthFinished", description, params, "Use Users.PushButtonAuthFinished instead.");
|
||||
|
||||
QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection);
|
||||
|
||||
@ -259,12 +259,12 @@ QString JsonRPCServerImplementation::name() const
|
||||
return QStringLiteral("JSONRPC");
|
||||
}
|
||||
|
||||
JsonReply *JsonRPCServerImplementation::Hello(const QVariantMap ¶ms)
|
||||
JsonReply *JsonRPCServerImplementation::Hello(const QVariantMap ¶ms, const JsonContext &context)
|
||||
{
|
||||
TransportInterface *interface = reinterpret_cast<TransportInterface*>(property("transportInterface").toLongLong());
|
||||
|
||||
qCDebug(dcJsonRpc()) << params;
|
||||
QUuid clientId = this->property("clientId").toUuid();
|
||||
QUuid clientId = context.clientId();
|
||||
if (params.contains("locale")) {
|
||||
m_clientLocales.insert(clientId, QLocale(params.value("locale").toString()));
|
||||
}
|
||||
@ -297,9 +297,9 @@ JsonReply* JsonRPCServerImplementation::Version(const QVariantMap ¶ms) const
|
||||
return createReply(data);
|
||||
}
|
||||
|
||||
JsonReply* JsonRPCServerImplementation::SetNotificationStatus(const QVariantMap ¶ms)
|
||||
JsonReply* JsonRPCServerImplementation::SetNotificationStatus(const QVariantMap ¶ms, const JsonContext &context)
|
||||
{
|
||||
QUuid clientId = this->property("clientId").toUuid();
|
||||
QUuid clientId = context.clientId();
|
||||
Q_ASSERT_X(m_clientTransports.contains(clientId), "JsonRPCServer", "Invalid client ID.");
|
||||
|
||||
QStringList enabledNamespaces;
|
||||
@ -351,10 +351,10 @@ JsonReply *JsonRPCServerImplementation::Authenticate(const QVariantMap ¶ms)
|
||||
return createReply(ret);
|
||||
}
|
||||
|
||||
JsonReply *JsonRPCServerImplementation::RequestPushButtonAuth(const QVariantMap ¶ms)
|
||||
JsonReply *JsonRPCServerImplementation::RequestPushButtonAuth(const QVariantMap ¶ms, const JsonContext &context)
|
||||
{
|
||||
QString deviceName = params.value("deviceName").toString();
|
||||
QUuid clientId = this->property("clientId").toUuid();
|
||||
QUuid clientId = context.clientId();
|
||||
|
||||
int transactionId = NymeaCore::instance()->userManager()->requestPushButtonAuth(deviceName);
|
||||
m_pushButtonTransactions.insert(transactionId, clientId);
|
||||
@ -366,12 +366,11 @@ JsonReply *JsonRPCServerImplementation::RequestPushButtonAuth(const QVariantMap
|
||||
return createReply(data);
|
||||
}
|
||||
|
||||
JsonReply *JsonRPCServerImplementation::Tokens(const QVariantMap ¶ms) const
|
||||
JsonReply *JsonRPCServerImplementation::Tokens(const QVariantMap ¶ms, const JsonContext &context) const
|
||||
{
|
||||
Q_UNUSED(params)
|
||||
QByteArray token = property("token").toByteArray();
|
||||
|
||||
TokenInfo tokenInfo = NymeaCore::instance()->userManager()->tokenInfo(token);
|
||||
TokenInfo tokenInfo = NymeaCore::instance()->userManager()->tokenInfo(context.token());
|
||||
QList<TokenInfo> tokens = NymeaCore::instance()->userManager()->tokens(tokenInfo.username());
|
||||
QVariantList retList;
|
||||
foreach (const TokenInfo &tokenInfo, tokens) {
|
||||
@ -636,8 +635,8 @@ 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.CreateUser"};
|
||||
QStringList authExemptMethodsWithUser = {"JSONRPC.Introspect", "JSONRPC.Hello", "JSONRPC.Authenticate", "JSONRPC.RequestPushButtonAuth"};
|
||||
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
|
||||
if (NymeaCore::instance()->userManager()->initRequired()) {
|
||||
if (!authExemptMethodsNoUser.contains(targetNamespace + "." + method) && (token.isEmpty() || !NymeaCore::instance()->userManager()->verifyToken(token))) {
|
||||
@ -683,13 +682,6 @@ void JsonRPCServerImplementation::processJsonPacket(TransportInterface *interfac
|
||||
return;
|
||||
}
|
||||
|
||||
// Hack: attach some properties to the handler to be able to handle the JSONRPC methods. Do not use this outside of jsonrpcserver
|
||||
handler->setProperty("clientId", clientId);
|
||||
handler->setProperty("token", message.value("token").toByteArray());
|
||||
handler->setProperty("transportInterface", reinterpret_cast<qint64>(interface));
|
||||
|
||||
qCDebug(dcJsonRpc()) << "Invoking method" << targetNamespace << method.toLatin1().data();
|
||||
|
||||
if (!(targetNamespace == "JSONRPC" && method == "Hello")) {
|
||||
// This is not the handshake message. If we've waited for it, consider this a protocol violation and drop connection
|
||||
if (m_newConnectionWaitTimers.contains(clientId)) {
|
||||
@ -698,16 +690,25 @@ void JsonRPCServerImplementation::processJsonPacket(TransportInterface *interfac
|
||||
interface->terminateClientConnection(clientId);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Unless this is the Hello message, which allows setting the locale explicitly, attach the locale
|
||||
// for this connection
|
||||
// If the client did request a locale in the Hello message, use that locale
|
||||
params.insert("locale", m_clientLocales.value(clientId));
|
||||
}
|
||||
|
||||
// Attach the transportInterface if this call is for ourselves
|
||||
if (handler == this) {
|
||||
handler->setProperty("transportInterface", reinterpret_cast<qint64>(interface));
|
||||
}
|
||||
|
||||
JsonContext callContext(clientId, m_clientLocales.value(clientId));
|
||||
callContext.setToken(message.value("token").toByteArray());
|
||||
|
||||
qCDebug(dcJsonRpc()) << "Invoking method" << targetNamespace + '.' + method << "from client" << clientId;
|
||||
|
||||
JsonReply *reply;
|
||||
QMetaObject::invokeMethod(handler, method.toLatin1().data(), Q_RETURN_ARG(JsonReply*, reply), Q_ARG(QVariantMap, params));
|
||||
if (handler->metaObject()->indexOfMethod(method.toUtf8() + "(QVariantMap,JsonContext)") >= 0) {
|
||||
QMetaObject::invokeMethod(handler, method.toUtf8().data(), Q_RETURN_ARG(JsonReply*, reply), Q_ARG(QVariantMap, params), Q_ARG(JsonContext, callContext));
|
||||
} else {
|
||||
QMetaObject::invokeMethod(handler, method.toUtf8().data(), Q_RETURN_ARG(JsonReply*, reply), Q_ARG(QVariantMap, params));
|
||||
}
|
||||
|
||||
if (reply->type() == JsonReply::TypeAsync) {
|
||||
m_asyncReplies.insert(reply, interface);
|
||||
reply->setClientId(clientId);
|
||||
@ -755,7 +756,7 @@ void JsonRPCServerImplementation::sendNotification(const QVariantMap ¶ms)
|
||||
JsonValidator validator;
|
||||
Q_ASSERT_X(validator.validateNotificationParams(params, handler->name() + '.' + method.name(), m_api).success(),
|
||||
validator.result().where().toUtf8(),
|
||||
validator.result().errorString().toUtf8());
|
||||
validator.result().errorString().toUtf8() + "\nGot:" + QJsonDocument::fromVariant(params).toJson(QJsonDocument::Indented));
|
||||
|
||||
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();
|
||||
@ -773,6 +774,39 @@ void JsonRPCServerImplementation::sendNotification(const QVariantMap ¶ms)
|
||||
}
|
||||
}
|
||||
|
||||
void JsonRPCServerImplementation::sendClientNotification(const QUuid &clientId, const QVariantMap ¶ms)
|
||||
{
|
||||
JsonHandler *handler = qobject_cast<JsonHandler *>(sender());
|
||||
QMetaMethod method = handler->metaObject()->method(senderSignalIndex());
|
||||
|
||||
if (!m_clientTransports.contains(clientId)) {
|
||||
qCWarning(dcJsonRpc()) << "No client with id" << clientId << ". Not sending client notification.";
|
||||
return;
|
||||
}
|
||||
|
||||
QVariantMap notification;
|
||||
notification.insert("id", m_notificationId++);
|
||||
notification.insert("notification", handler->name() + "." + method.name());
|
||||
notification.insert("params", params);
|
||||
|
||||
JsonValidator validator;
|
||||
Q_ASSERT_X(validator.validateNotificationParams(params, handler->name() + '.' + method.name(), m_api).success(),
|
||||
validator.result().where().toUtf8(),
|
||||
validator.result().errorString().toUtf8() + "\nGot:" + QJsonDocument::fromVariant(params).toJson(QJsonDocument::Indented));
|
||||
|
||||
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 uses deprecated API. Please update client implementation!";
|
||||
qCWarning(dcJsonRpc()) << handler->name() + '.' + method.name() + ':' << deprecationMessage;
|
||||
notification.insert("deprecationWarning", deprecationMessage);
|
||||
}
|
||||
|
||||
QByteArray data = QJsonDocument::fromVariant(notification).toJson(QJsonDocument::Compact);
|
||||
qCDebug(dcJsonRpcTraffic()) << "Notification content:" << data;
|
||||
qCDebug(dcJsonRpc()) << "Sending notification:" << handler->name() + "." + method.name();
|
||||
m_clientTransports.value(clientId)->sendData(clientId, data);
|
||||
}
|
||||
|
||||
void JsonRPCServerImplementation::asyncReplyFinished()
|
||||
{
|
||||
JsonReply *reply = qobject_cast<JsonReply *>(sender());
|
||||
@ -834,12 +868,6 @@ void JsonRPCServerImplementation::onPushButtonAuthFinished(int transactionId, bo
|
||||
return;
|
||||
}
|
||||
|
||||
TransportInterface *transport = m_clientTransports.value(clientId);
|
||||
if (!transport) {
|
||||
qCWarning(dcJsonRpc()) << "No transport for given clientId";
|
||||
return;
|
||||
}
|
||||
|
||||
QVariantMap params;
|
||||
params.insert("transactionId", transactionId);
|
||||
params.insert("success", success);
|
||||
@ -847,12 +875,7 @@ void JsonRPCServerImplementation::onPushButtonAuthFinished(int transactionId, bo
|
||||
params.insert("token", token);
|
||||
}
|
||||
|
||||
QVariantMap notification;
|
||||
notification.insert("id", transactionId);
|
||||
notification.insert("notification", "JSONRPC.PushButtonAuthFinished");
|
||||
notification.insert("params", params);
|
||||
|
||||
transport->sendData(clientId, QJsonDocument::fromVariant(notification).toJson(QJsonDocument::Compact));
|
||||
emit PushButtonAuthFinished(clientId, params);
|
||||
}
|
||||
|
||||
bool JsonRPCServerImplementation::registerHandler(JsonHandler *handler)
|
||||
@ -914,8 +937,10 @@ bool JsonRPCServerImplementation::registerHandler(JsonHandler *handler)
|
||||
QVariantMap newMethods;
|
||||
foreach (const QString &methodName, handler->jsonMethods().keys()) {
|
||||
QVariantMap method = handler->jsonMethods().value(methodName).toMap();
|
||||
if (handler->metaObject()->indexOfMethod(methodName.toUtf8() + "(QVariantMap)") < 0) {
|
||||
qCWarning(dcJsonRpc()).nospace().noquote() << "Invalid method \"" << methodName << "\". Method \"JsonReply* " + methodName + "(QVariantMap)\" does not exist. Not registering handler " << handler->name();
|
||||
|
||||
if (handler->metaObject()->indexOfMethod(methodName.toUtf8() + "(QVariantMap)") < 0
|
||||
&& handler->metaObject()->indexOfMethod(methodName.toUtf8() + "(QVariantMap,JsonContext)") < 0) {
|
||||
qCWarning(dcJsonRpc()).nospace().noquote() << "Invalid method \"" << methodName << "\". Method \"JsonReply* " + methodName + "(QVariantMap,JsonContext)\" does not exist. Not registering handler " << handler->name();
|
||||
return false;
|
||||
}
|
||||
if (!JsonValidator::checkRefs(method.value("params").toMap(), apiIncludingThis)) {
|
||||
@ -952,7 +977,11 @@ bool JsonRPCServerImplementation::registerHandler(JsonHandler *handler)
|
||||
for (int i = 0; i < handler->metaObject()->methodCount(); ++i) {
|
||||
QMetaMethod method = handler->metaObject()->method(i);
|
||||
if (method.methodType() == QMetaMethod::Signal && QString(method.name()).contains(QRegExp("^[A-Z]"))) {
|
||||
QObject::connect(handler, method, this, metaObject()->method(metaObject()->indexOfSlot("sendNotification(QVariantMap)")));
|
||||
if (method.parameterCount() == 1 && method.parameterType(0) == QVariant::Map) {
|
||||
QObject::connect(handler, method, this, metaObject()->method(metaObject()->indexOfSlot("sendNotification(QVariantMap)")));
|
||||
} else if (method.parameterCount() == 2 && method.parameterType(0) == QVariant::Uuid && method.parameterType(1) == QVariant::Map) {
|
||||
QObject::connect(handler, method, this, metaObject()->method(metaObject()->indexOfSlot("sendClientNotification(QUuid,QVariantMap)")));
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@ -57,15 +57,15 @@ public:
|
||||
|
||||
// JsonHandler API implementation
|
||||
QString name() const;
|
||||
Q_INVOKABLE JsonReply *Hello(const QVariantMap ¶ms);
|
||||
Q_INVOKABLE JsonReply *Hello(const QVariantMap ¶ms, const JsonContext &context);
|
||||
Q_INVOKABLE JsonReply *Introspect(const QVariantMap ¶ms) const;
|
||||
Q_INVOKABLE JsonReply *Version(const QVariantMap ¶ms) const;
|
||||
Q_INVOKABLE JsonReply *SetNotificationStatus(const QVariantMap ¶ms);
|
||||
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 *RequestPushButtonAuth(const QVariantMap ¶ms);
|
||||
Q_INVOKABLE JsonReply *Tokens(const QVariantMap ¶ms) const;
|
||||
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);
|
||||
@ -74,7 +74,7 @@ public:
|
||||
|
||||
signals:
|
||||
void CloudConnectedChanged(const QVariantMap &map);
|
||||
void PushButtonAuthFinished(const QVariantMap ¶ms);
|
||||
void PushButtonAuthFinished(const QUuid &clientId, const QVariantMap ¶ms);
|
||||
|
||||
// Server API
|
||||
public:
|
||||
@ -103,6 +103,7 @@ private slots:
|
||||
void processData(const QUuid &clientId, const QByteArray &data);
|
||||
|
||||
void sendNotification(const QVariantMap ¶ms);
|
||||
void sendClientNotification(const QUuid &clientId, const QVariantMap ¶ms);
|
||||
|
||||
void asyncReplyFinished();
|
||||
|
||||
|
||||
@ -70,9 +70,8 @@ QString StateHandler::name() const
|
||||
return "States";
|
||||
}
|
||||
|
||||
JsonReply* StateHandler::GetStateType(const QVariantMap ¶ms) const
|
||||
JsonReply* StateHandler::GetStateType(const QVariantMap ¶ms, const JsonContext &context) const
|
||||
{
|
||||
QLocale locale = params.value("locale").toLocale();
|
||||
qCDebug(dcJsonRpc) << "asked for state type" << params;
|
||||
StateTypeId stateTypeId(params.value("stateTypeId").toString());
|
||||
foreach (const DeviceClass &deviceClass, NymeaCore::instance()->deviceManager()->supportedDevices()) {
|
||||
@ -81,7 +80,7 @@ JsonReply* StateHandler::GetStateType(const QVariantMap ¶ms) const
|
||||
QVariantMap data;
|
||||
data.insert("deviceError", enumValueName<Device::DeviceError>(Device::DeviceErrorNoError));
|
||||
StateType translatedStateType = stateType;
|
||||
translatedStateType.setDisplayName(NymeaCore::instance()->deviceManager()->translate(deviceClass.pluginId(), stateType.displayName(), locale));
|
||||
translatedStateType.setDisplayName(NymeaCore::instance()->deviceManager()->translate(deviceClass.pluginId(), stateType.displayName(), context.locale()));
|
||||
data.insert("stateType", pack(translatedStateType));
|
||||
return createReply(data);
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ public:
|
||||
explicit StateHandler(QObject *parent = nullptr);
|
||||
QString name() const override;
|
||||
|
||||
Q_INVOKABLE JsonReply *GetStateType(const QVariantMap ¶ms) const;
|
||||
Q_INVOKABLE JsonReply *GetStateType(const QVariantMap ¶ms, const JsonContext &context) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -1,3 +1,33 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* 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 Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "usershandler.h"
|
||||
#include "usermanager/usermanager.h"
|
||||
#include "usermanager/userinfo.h"
|
||||
@ -23,6 +53,37 @@ UsersHandler::UsersHandler(UserManager *userManager, QObject *parent):
|
||||
returns.insert("error", enumRef<UserManager::UserError>());
|
||||
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));
|
||||
@ -47,6 +108,18 @@ UsersHandler::UsersHandler(UserManager *userManager, QObject *parent):
|
||||
returns.insert("error", enumRef<UserManager::UserError>());
|
||||
registerMethod("RemoveToken", description, params, returns);
|
||||
|
||||
// Notifications
|
||||
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 "
|
||||
"authentication, but also it will be emitted regardless of the notification settings.";
|
||||
params.insert("success", enumValueName(Bool));
|
||||
params.insert("transactionId", enumValueName(Int));
|
||||
params.insert("o:token", enumValueName(String));
|
||||
registerNotification("PushButtonAuthFinished", description, params);
|
||||
|
||||
connect(m_userManager, &UserManager::pushButtonAuthFinished, this, &UsersHandler::onPushButtonAuthFinished);
|
||||
|
||||
}
|
||||
|
||||
QString UsersHandler::name() const
|
||||
@ -66,11 +139,11 @@ JsonReply *UsersHandler::CreateUser(const QVariantMap ¶ms)
|
||||
return createReply(returns);
|
||||
}
|
||||
|
||||
JsonReply *UsersHandler::ChangePassword(const QVariantMap ¶ms)
|
||||
JsonReply *UsersHandler::ChangePassword(const QVariantMap ¶ms, const JsonContext &context)
|
||||
{
|
||||
QVariantMap ret;
|
||||
|
||||
QByteArray currentToken = property("token").toByteArray();
|
||||
QByteArray currentToken = context.token();
|
||||
if (currentToken.isEmpty()) {
|
||||
qCWarning(dcJsonRpc()) << "Cannot change password from an unauthenticated connection";
|
||||
ret.insert("error", enumValueName<UserManager::UserError>(UserManager::UserErrorPermissionDenied));
|
||||
@ -106,13 +179,12 @@ JsonReply *UsersHandler::Authenticate(const QVariantMap ¶ms)
|
||||
return createReply(ret);
|
||||
}
|
||||
|
||||
JsonReply *UsersHandler::RequestPushButtonAuth(const QVariantMap ¶ms)
|
||||
JsonReply *UsersHandler::RequestPushButtonAuth(const QVariantMap ¶ms, const JsonContext &context)
|
||||
{
|
||||
QString deviceName = params.value("deviceName").toString();
|
||||
QUuid clientId = this->property("clientId").toUuid();
|
||||
|
||||
int transactionId = m_userManager->requestPushButtonAuth(deviceName);
|
||||
m_pushButtonTransactions.insert(transactionId, clientId);
|
||||
m_pushButtonTransactions.insert(transactionId, context.clientId());
|
||||
|
||||
QVariantMap data;
|
||||
data.insert("transactionId", transactionId);
|
||||
@ -121,12 +193,12 @@ JsonReply *UsersHandler::RequestPushButtonAuth(const QVariantMap ¶ms)
|
||||
return createReply(data);
|
||||
}
|
||||
|
||||
JsonReply *UsersHandler::GetUserInfo(const QVariantMap ¶ms)
|
||||
JsonReply *UsersHandler::GetUserInfo(const QVariantMap ¶ms, const JsonContext &context)
|
||||
{
|
||||
Q_UNUSED(params)
|
||||
QVariantMap ret;
|
||||
|
||||
QByteArray currentToken = property("token").toByteArray();
|
||||
QByteArray currentToken = context.token();
|
||||
if (currentToken.isEmpty()) {
|
||||
qCWarning(dcJsonRpc()) << "Cannot get user info form an unauthenticated connection";
|
||||
ret.insert("error", enumValueName<UserManager::UserError>(UserManager::UserErrorPermissionDenied));
|
||||
@ -145,12 +217,12 @@ JsonReply *UsersHandler::GetUserInfo(const QVariantMap ¶ms)
|
||||
return createReply(ret);
|
||||
}
|
||||
|
||||
JsonReply *UsersHandler::GetTokens(const QVariantMap ¶ms)
|
||||
JsonReply *UsersHandler::GetTokens(const QVariantMap ¶ms, const JsonContext &context)
|
||||
{
|
||||
Q_UNUSED(params)
|
||||
QVariantMap ret;
|
||||
|
||||
QByteArray currentToken = property("token").toByteArray();
|
||||
QByteArray currentToken = context.token();
|
||||
if (currentToken.isEmpty()) {
|
||||
qCWarning(dcJsonRpc()) << "Cannot fetch tokens form an unauthenticated connection";
|
||||
ret.insert("error", enumValueName<UserManager::UserError>(UserManager::UserErrorPermissionDenied));
|
||||
@ -175,11 +247,11 @@ JsonReply *UsersHandler::GetTokens(const QVariantMap ¶ms)
|
||||
return createReply(ret);
|
||||
}
|
||||
|
||||
JsonReply *UsersHandler::RemoveToken(const QVariantMap ¶ms)
|
||||
JsonReply *UsersHandler::RemoveToken(const QVariantMap ¶ms, const JsonContext &context)
|
||||
{
|
||||
QVariantMap ret;
|
||||
|
||||
QByteArray currentToken = property("token").toByteArray();
|
||||
QByteArray currentToken = context.token();
|
||||
if (currentToken.isEmpty()) {
|
||||
qCWarning(dcJsonRpc()) << "Cannot remove a token from an unauthenticated connection.";
|
||||
ret.insert("error", enumValueName<UserManager::UserError>(UserManager::UserErrorPermissionDenied));
|
||||
@ -213,4 +285,24 @@ JsonReply *UsersHandler::RemoveToken(const QVariantMap ¶ms)
|
||||
return createReply(ret);
|
||||
}
|
||||
|
||||
void UsersHandler::onPushButtonAuthFinished(int transactionId, bool success, const QByteArray &token)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
QVariantMap params;
|
||||
params.insert("transactionId", transactionId);
|
||||
params.insert("success", success);
|
||||
if (success) {
|
||||
params.insert("token", token);
|
||||
}
|
||||
|
||||
emit PushButtonAuthFinished(clientId, params);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,3 +1,33 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* 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 Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef USERSHANDLER_H
|
||||
#define USERSHANDLER_H
|
||||
|
||||
@ -18,15 +48,18 @@ public:
|
||||
QString name() const override;
|
||||
|
||||
Q_INVOKABLE JsonReply *CreateUser(const QVariantMap ¶ms);
|
||||
Q_INVOKABLE JsonReply *ChangePassword(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);
|
||||
Q_INVOKABLE JsonReply *GetUserInfo(const QVariantMap ¶ms);
|
||||
Q_INVOKABLE JsonReply *GetTokens(const QVariantMap ¶ms);
|
||||
Q_INVOKABLE JsonReply *RemoveToken(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);
|
||||
|
||||
signals:
|
||||
void PushButtonAuthFinished(const QVariantMap ¶ms);
|
||||
void PushButtonAuthFinished(const QUuid &clientId, const QVariantMap ¶ms);
|
||||
|
||||
private slots:
|
||||
void onPushButtonAuthFinished(int transactionId, bool success, const QByteArray &token);
|
||||
|
||||
private:
|
||||
UserManager *m_userManager = nullptr;
|
||||
|
||||
58
libnymea/jsonrpc/jsoncontext.cpp
Normal file
58
libnymea/jsonrpc/jsoncontext.cpp
Normal file
@ -0,0 +1,58 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* 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 Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "jsoncontext.h"
|
||||
|
||||
JsonContext::JsonContext(const QUuid &clientId, const QLocale &locale):
|
||||
m_clientId(clientId),
|
||||
m_locale(locale)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QUuid JsonContext::clientId() const
|
||||
{
|
||||
return m_clientId;
|
||||
}
|
||||
|
||||
QLocale JsonContext::locale() const
|
||||
{
|
||||
return m_locale;
|
||||
}
|
||||
|
||||
QByteArray JsonContext::token() const
|
||||
{
|
||||
return m_token;
|
||||
}
|
||||
|
||||
void JsonContext::setToken(const QByteArray &token)
|
||||
{
|
||||
m_token = token;
|
||||
}
|
||||
54
libnymea/jsonrpc/jsoncontext.h
Normal file
54
libnymea/jsonrpc/jsoncontext.h
Normal file
@ -0,0 +1,54 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* 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 Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef JSONCONTEXT_H
|
||||
#define JSONCONTEXT_H
|
||||
|
||||
#include <QUuid>
|
||||
#include <QLocale>
|
||||
|
||||
class JsonContext
|
||||
{
|
||||
public:
|
||||
JsonContext(const QUuid &clientId, const QLocale &locale);
|
||||
|
||||
QUuid clientId() const;
|
||||
QLocale locale() const;
|
||||
|
||||
QByteArray token() const;
|
||||
void setToken(const QByteArray &token);
|
||||
|
||||
private:
|
||||
QUuid m_clientId;
|
||||
QLocale m_locale;
|
||||
QByteArray m_token;
|
||||
};
|
||||
|
||||
#endif // JSONCONTEXT_H
|
||||
@ -39,6 +39,7 @@
|
||||
#include <QDateTime>
|
||||
|
||||
#include "jsonreply.h"
|
||||
#include "jsoncontext.h"
|
||||
|
||||
class JsonHandler : public QObject
|
||||
{
|
||||
@ -108,7 +109,6 @@ protected:
|
||||
JsonReply *createAsyncReply(const QString &method) const;
|
||||
|
||||
private:
|
||||
|
||||
void registerObject(const QMetaObject &metaObject);
|
||||
void registerObject(const QMetaObject &metaObject, const QMetaObject &listMetaObject);
|
||||
|
||||
|
||||
@ -24,6 +24,7 @@ HEADERS += \
|
||||
devices/devicepairinginfo.h \
|
||||
devices/deviceactioninfo.h \
|
||||
devices/browseresult.h \
|
||||
jsonrpc/jsoncontext.h \
|
||||
jsonrpc/jsonhandler.h \
|
||||
jsonrpc/jsonreply.h \
|
||||
jsonrpc/jsonrpcserver.h \
|
||||
@ -106,6 +107,7 @@ SOURCES += \
|
||||
devices/devicepairinginfo.cpp \
|
||||
devices/deviceactioninfo.cpp \
|
||||
devices/browseresult.cpp \
|
||||
jsonrpc/jsoncontext.cpp \
|
||||
jsonrpc/jsonhandler.cpp \
|
||||
jsonrpc/jsonreply.cpp \
|
||||
jsonrpc/jsonrpcserver.cpp \
|
||||
|
||||
@ -34,6 +34,7 @@
|
||||
#include "version.h"
|
||||
#include "servers/mocktcpserver.h"
|
||||
#include "usermanager/usermanager.h"
|
||||
#include "nymeadbusservice.h"
|
||||
|
||||
using namespace nymeaserver;
|
||||
|
||||
@ -151,6 +152,7 @@ QStringList TestJSONRPC::extractRefs(const QVariant &variant)
|
||||
|
||||
void TestJSONRPC::initTestCase()
|
||||
{
|
||||
NymeaDBusService::setBusType(QDBusConnection::SessionBus);
|
||||
NymeaTestBase::initTestCase();
|
||||
QLoggingCategory::setFilterRules("*.debug=false\n"
|
||||
// "JsonRpcTraffic.debug=true\n"
|
||||
@ -182,7 +184,7 @@ void TestJSONRPC::testHandshake()
|
||||
|
||||
// Now register push button agent
|
||||
PushButtonAgent pushButtonAgent;
|
||||
pushButtonAgent.init();
|
||||
pushButtonAgent.init(QDBusConnection::SessionBus);
|
||||
|
||||
// And now check if it is sent again when calling JSONRPC.Hello
|
||||
handShake = injectAndWait("JSONRPC.Hello").toMap();
|
||||
@ -1141,7 +1143,7 @@ void TestJSONRPC::pluginConfigChangeEmitsNotification()
|
||||
void TestJSONRPC::testPushButtonAuth()
|
||||
{
|
||||
PushButtonAgent pushButtonAgent;
|
||||
pushButtonAgent.init();
|
||||
pushButtonAgent.init(QDBusConnection::SessionBus);
|
||||
|
||||
QVariantMap params;
|
||||
params.insert("deviceName", "pbtestdevice");
|
||||
@ -1167,7 +1169,7 @@ void TestJSONRPC::testPushButtonAuthInterrupt()
|
||||
{
|
||||
enableNotifications({});
|
||||
PushButtonAgent pushButtonAgent;
|
||||
pushButtonAgent.init();
|
||||
pushButtonAgent.init(QDBusConnection::SessionBus);
|
||||
|
||||
// m_clientId is registered in gutTestbase already, just using it here to improve readability of the test
|
||||
QUuid aliceId = m_clientId;
|
||||
@ -1272,7 +1274,7 @@ void TestJSONRPC::testPushButtonAuthInterrupt()
|
||||
void TestJSONRPC::testPushButtonAuthConnectionDrop()
|
||||
{
|
||||
PushButtonAgent pushButtonAgent;
|
||||
pushButtonAgent.init();
|
||||
pushButtonAgent.init(QDBusConnection::SessionBus);
|
||||
|
||||
// Snoop in on everything the TCP server sends to its clients.
|
||||
QSignalSpy clientSpy(m_mockTcpServer, &MockTcpServer::outgoingData);
|
||||
@ -1339,7 +1341,7 @@ void TestJSONRPC::testInitialSetupWithPushButtonAuth()
|
||||
QVERIFY(spy.isValid());
|
||||
|
||||
PushButtonAgent pushButtonAgent;
|
||||
pushButtonAgent.init();
|
||||
pushButtonAgent.init(QDBusConnection::SessionBus);
|
||||
|
||||
// Hello call should work in any case, telling us initial setup is required
|
||||
spy.clear();
|
||||
|
||||
@ -35,6 +35,9 @@
|
||||
#include "nymeatestbase.h"
|
||||
#include "usermanager/usermanager.h"
|
||||
#include "servers/mocktcpserver.h"
|
||||
#include "nymeadbusservice.h"
|
||||
|
||||
#include "../../utils/pushbuttonagent.h"
|
||||
|
||||
using namespace nymeaserver;
|
||||
|
||||
@ -45,14 +48,50 @@ public:
|
||||
TestUsermanager(QObject* parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
|
||||
void init();
|
||||
|
||||
void loginValidation_data();
|
||||
void loginValidation();
|
||||
|
||||
void createUser();
|
||||
|
||||
void authenticate();
|
||||
|
||||
/*
|
||||
Cases for push button auth:
|
||||
|
||||
Case 1: regular pushbutton
|
||||
- alice sends Users.RequestPushButtonAuth, gets "OK" back (if push button hardware is available)
|
||||
- alice pushes the hardware button and gets a notification on jsonrpc containing the token for local auth
|
||||
*/
|
||||
void authenticatePushButton();
|
||||
|
||||
/*
|
||||
Case 2: if we have an attacker in the network, he could try to call requestPushButtonAuth and
|
||||
hope someone would eventually press the button and give him a token. In order to prevent this,
|
||||
any previous attempt for a push button auth needs to be cancelled when a new request comes in:
|
||||
|
||||
* Mallory does RequestPushButtonAuth, gets OK back
|
||||
* Alice does RequestPushButtonAuth,
|
||||
* Mallory receives a "PushButtonFailed" notification
|
||||
* Alice receives OK
|
||||
* Alice presses the hardware button
|
||||
* Alice reveices a notification with token, mallory receives nothing
|
||||
|
||||
Case 3: Mallory tries to hijack it back again
|
||||
|
||||
* Mallory does RequestPushButtonAuth, gets OK back
|
||||
* Alice does RequestPusButtonAuth,
|
||||
* Alice gets ok reply, Mallory gets failed notification
|
||||
* Mallory quickly does RequestPushButtonAuth again to win the fight
|
||||
* Alice gets failed notification and can instruct the user to _not_ press the button now until procedure is restarted
|
||||
*/
|
||||
void authenticatePushButtonAuthInterrupt();
|
||||
|
||||
void authenticatePushButtonAuthConnectionDrop();
|
||||
|
||||
void createDuplicateUser();
|
||||
|
||||
void getTokens();
|
||||
@ -79,6 +118,12 @@ TestUsermanager::TestUsermanager(QObject *parent): NymeaTestBase(parent)
|
||||
QCoreApplication::instance()->setOrganizationName("nymea-test");
|
||||
}
|
||||
|
||||
void TestUsermanager::initTestCase()
|
||||
{
|
||||
NymeaDBusService::setBusType(QDBusConnection::SessionBus);
|
||||
NymeaTestBase::initTestCase();
|
||||
}
|
||||
|
||||
void TestUsermanager::init()
|
||||
{
|
||||
UserManager *userManager = NymeaCore::instance()->userManager();
|
||||
@ -155,7 +200,7 @@ void TestUsermanager::authenticate()
|
||||
params.insert("username", "valid@user.test");
|
||||
params.insert("password", "Bla1234*");
|
||||
params.insert("deviceName", "autotests");
|
||||
QVariant response = injectAndWait("JSONRPC.Authenticate", params);
|
||||
QVariant response = injectAndWait("Users.Authenticate", params);
|
||||
|
||||
m_apiToken = response.toMap().value("params").toMap().value("token").toByteArray();
|
||||
|
||||
@ -163,6 +208,192 @@ void TestUsermanager::authenticate()
|
||||
QVERIFY2(response.toMap().value("params").toMap().value("success").toString() == "true", "Error authenticating");
|
||||
}
|
||||
|
||||
void TestUsermanager::authenticatePushButton()
|
||||
{
|
||||
PushButtonAgent pushButtonAgent;
|
||||
pushButtonAgent.init(QDBusConnection::SessionBus);
|
||||
|
||||
QVariantMap params;
|
||||
params.insert("deviceName", "pbtestdevice");
|
||||
QVariant response = injectAndWait("Users.RequestPushButtonAuth", params);
|
||||
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true);
|
||||
int transactionId = response.toMap().value("params").toMap().value("transactionId").toInt();
|
||||
|
||||
// Setup connection to mock client
|
||||
QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
|
||||
|
||||
pushButtonAgent.sendButtonPressed();
|
||||
|
||||
if (clientSpy.count() == 0) clientSpy.wait();
|
||||
QVariantMap rsp = checkNotification(clientSpy, "Users.PushButtonAuthFinished").toMap();
|
||||
|
||||
QCOMPARE(rsp.value("params").toMap().value("transactionId").toInt(), transactionId);
|
||||
QVERIFY2(!rsp.value("params").toMap().value("token").toByteArray().isEmpty(), "Token not in push button auth notification");
|
||||
}
|
||||
|
||||
void TestUsermanager::authenticatePushButtonAuthInterrupt()
|
||||
{
|
||||
PushButtonAgent pushButtonAgent;
|
||||
pushButtonAgent.init(QDBusConnection::SessionBus);
|
||||
|
||||
// m_clientId is registered in gutTestbase already, just using it here to improve readability of the test
|
||||
QUuid aliceId = m_clientId;
|
||||
|
||||
// Create a new clientId for mallory and connect it to the server
|
||||
QUuid malloryId = QUuid::createUuid();
|
||||
m_mockTcpServer->clientConnected(malloryId);
|
||||
QSignalSpy responseSpy(m_mockTcpServer, &MockTcpServer::outgoingData);
|
||||
m_mockTcpServer->injectData(malloryId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}");
|
||||
if (responseSpy.count() == 0) responseSpy.wait();
|
||||
|
||||
// Snoop in on everything the TCP server sends to its clients.
|
||||
QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
|
||||
|
||||
// request push button auth for client 1 (alice) and check for OK reply
|
||||
QVariantMap params;
|
||||
params.insert("deviceName", "alice");
|
||||
QVariant response = injectAndWait("Users.RequestPushButtonAuth", params, aliceId);
|
||||
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true);
|
||||
int transactionId1 = response.toMap().value("params").toMap().value("transactionId").toInt();
|
||||
|
||||
|
||||
// Request push button auth for client 2 (mallory)
|
||||
clientSpy.clear();
|
||||
params.clear();
|
||||
params.insert("deviceName", "mallory");
|
||||
response = injectAndWait("Users.RequestPushButtonAuth", params, malloryId);
|
||||
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true);
|
||||
int transactionId2 = response.toMap().value("params").toMap().value("transactionId").toInt();
|
||||
|
||||
// Both clients should receive something. Wait for it
|
||||
if (clientSpy.count() < 2) {
|
||||
clientSpy.wait();
|
||||
}
|
||||
|
||||
// spy.at(0) should be the failed notification for alice
|
||||
// spy.at(1) shpuld be the OK reply for mallory
|
||||
|
||||
|
||||
// alice should have received a failed notification. She knows something's wrong.
|
||||
QVariantMap notification = QJsonDocument::fromJson(clientSpy.first().at(1).toByteArray()).toVariant().toMap();
|
||||
QCOMPARE(clientSpy.first().first().toUuid(), aliceId);
|
||||
QCOMPARE(notification.value("notification").toString(), QLatin1String("Users.PushButtonAuthFinished"));
|
||||
QCOMPARE(notification.value("params").toMap().value("transactionId").toInt(), transactionId1);
|
||||
QCOMPARE(notification.value("params").toMap().value("success").toBool(), false);
|
||||
|
||||
// Mallory instead should have received an OK
|
||||
QVariantMap reply = QJsonDocument::fromJson(clientSpy.at(1).at(1).toByteArray()).toVariant().toMap();
|
||||
QCOMPARE(clientSpy.at(1).first().toUuid(), malloryId);
|
||||
QCOMPARE(reply.value("params").toMap().value("success").toBool(), true);
|
||||
|
||||
|
||||
// Alice tries once more
|
||||
clientSpy.clear();
|
||||
params.clear();
|
||||
params.insert("deviceName", "alice");
|
||||
response = injectAndWait("Users.RequestPushButtonAuth", params, aliceId);
|
||||
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true);
|
||||
int transactionId3 = response.toMap().value("params").toMap().value("transactionId").toInt();
|
||||
|
||||
// Both clients should receive something. Wait for it
|
||||
if (clientSpy.count() < 2) {
|
||||
clientSpy.wait();
|
||||
}
|
||||
|
||||
// spy.at(0) should be the failed notification for mallory
|
||||
// spy.at(1) shpuld be the OK reply for alice
|
||||
|
||||
// mallory should have received a failed notification. She knows something's wrong.
|
||||
notification = QJsonDocument::fromJson(clientSpy.first().at(1).toByteArray()).toVariant().toMap();
|
||||
QCOMPARE(clientSpy.first().first().toUuid(), malloryId);
|
||||
QCOMPARE(notification.value("notification").toString(), QLatin1String("Users.PushButtonAuthFinished"));
|
||||
QCOMPARE(notification.value("params").toMap().value("transactionId").toInt(), transactionId2);
|
||||
QCOMPARE(notification.value("params").toMap().value("success").toBool(), false);
|
||||
|
||||
// Alice instead should have received an OK
|
||||
reply = QJsonDocument::fromJson(clientSpy.at(1).at(1).toByteArray()).toVariant().toMap();
|
||||
QCOMPARE(clientSpy.at(1).first().toUuid(), aliceId);
|
||||
QCOMPARE(reply.value("params").toMap().value("success").toBool(), true);
|
||||
|
||||
clientSpy.clear();
|
||||
|
||||
// do the button press
|
||||
pushButtonAgent.sendButtonPressed();
|
||||
|
||||
// Wait for things to happen
|
||||
if (clientSpy.count() == 0) {
|
||||
clientSpy.wait();
|
||||
}
|
||||
|
||||
// There should have been only exactly one message sent, the token for alice
|
||||
// Mallory should not have received anything
|
||||
QCOMPARE(clientSpy.count(), 1);
|
||||
notification = QJsonDocument::fromJson(clientSpy.first().at(1).toByteArray()).toVariant().toMap();
|
||||
QCOMPARE(clientSpy.first().first().toUuid(), aliceId);
|
||||
QCOMPARE(notification.value("notification").toString(), QLatin1String("Users.PushButtonAuthFinished"));
|
||||
QCOMPARE(notification.value("params").toMap().value("transactionId").toInt(), transactionId3);
|
||||
QCOMPARE(notification.value("params").toMap().value("success").toBool(), true);
|
||||
QVERIFY2(!notification.value("params").toMap().value("token").toByteArray().isEmpty(), "Token is empty while it shouldn't be");
|
||||
}
|
||||
|
||||
void TestUsermanager::authenticatePushButtonAuthConnectionDrop()
|
||||
{
|
||||
PushButtonAgent pushButtonAgent;
|
||||
pushButtonAgent.init(QDBusConnection::SessionBus);
|
||||
|
||||
// Snoop in on everything the TCP server sends to its clients.
|
||||
QSignalSpy clientSpy(m_mockTcpServer, &MockTcpServer::outgoingData);
|
||||
|
||||
// Create a new clientId for alice and connect it to the server
|
||||
QUuid aliceId = QUuid::createUuid();
|
||||
m_mockTcpServer->clientConnected(aliceId);
|
||||
m_mockTcpServer->injectData(aliceId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}");
|
||||
if (clientSpy.count() == 0) clientSpy.wait();
|
||||
|
||||
// request push button auth for client 1 (alice) and check for OK reply
|
||||
QVariantMap params;
|
||||
params.insert("deviceName", "alice");
|
||||
QVariant response = injectAndWait("Users.RequestPushButtonAuth", params, aliceId);
|
||||
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true);
|
||||
|
||||
// Disconnect alice
|
||||
m_mockTcpServer->clientDisconnected(aliceId);
|
||||
|
||||
// Now try with bob
|
||||
// Create a new clientId for bob and connect it to the server
|
||||
QUuid bobId = QUuid::createUuid();
|
||||
m_mockTcpServer->clientConnected(bobId);
|
||||
clientSpy.clear();
|
||||
m_mockTcpServer->injectData(bobId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}");
|
||||
if (clientSpy.count() == 0) clientSpy.wait();
|
||||
|
||||
// request push button auth for client 2 (bob) and check for OK reply
|
||||
params.clear();
|
||||
params.insert("deviceName", "bob");
|
||||
response = injectAndWait("Users.RequestPushButtonAuth", params, bobId);
|
||||
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true);
|
||||
int transactionId = response.toMap().value("params").toMap().value("transactionId").toInt();
|
||||
|
||||
clientSpy.clear();
|
||||
|
||||
pushButtonAgent.sendButtonPressed();
|
||||
|
||||
// Wait for things to happen
|
||||
if (clientSpy.count() == 0) {
|
||||
clientSpy.wait();
|
||||
}
|
||||
|
||||
// There should have been only exactly one message sent, the token for bob
|
||||
QCOMPARE(clientSpy.count(), 1);
|
||||
QVariantMap notification = QJsonDocument::fromJson(clientSpy.first().at(1).toByteArray()).toVariant().toMap();
|
||||
QCOMPARE(clientSpy.first().first().toUuid(), bobId);
|
||||
QCOMPARE(notification.value("notification").toString(), QLatin1String("Users.PushButtonAuthFinished"));
|
||||
QCOMPARE(notification.value("params").toMap().value("transactionId").toInt(), transactionId);
|
||||
QCOMPARE(notification.value("params").toMap().value("success").toBool(), true);
|
||||
QVERIFY2(!notification.value("params").toMap().value("token").toByteArray().isEmpty(), "Token is empty while it shouldn't be");
|
||||
|
||||
}
|
||||
|
||||
void TestUsermanager::createDuplicateUser()
|
||||
{
|
||||
authenticate();
|
||||
@ -223,7 +454,7 @@ void TestUsermanager::authenticateAfterPasswordChangeOK()
|
||||
params.insert("username", "valid@user.test");
|
||||
params.insert("password", "Blubb123"); // New password, should be ok
|
||||
params.insert("deviceName", "autotests");
|
||||
QVariant response = injectAndWait("JSONRPC.Authenticate", params);
|
||||
QVariant response = injectAndWait("Users.Authenticate", params);
|
||||
|
||||
m_apiToken = response.toMap().value("params").toMap().value("token").toByteArray();
|
||||
QVERIFY2(!m_apiToken.isEmpty(), "Token should not be empty");
|
||||
@ -239,7 +470,7 @@ void TestUsermanager::authenticateAfterPasswordChangeFail()
|
||||
params.insert("username", "valid@user.test");
|
||||
params.insert("password", "Bla1234*"); // Original password, should not be ok
|
||||
params.insert("deviceName", "autotests");
|
||||
QVariant response = injectAndWait("JSONRPC.Authenticate", params);
|
||||
QVariant response = injectAndWait("Users.Authenticate", params);
|
||||
|
||||
m_apiToken = response.toMap().value("params").toMap().value("token").toByteArray();
|
||||
QVERIFY2(m_apiToken.isEmpty(), "Token should be empty");
|
||||
|
||||
@ -2,4 +2,9 @@ include(../../../nymea.pri)
|
||||
include(../autotests.pri)
|
||||
|
||||
TARGET = testusermanager
|
||||
SOURCES += testusermanager.cpp
|
||||
|
||||
HEADERS += ../../utils/pushbuttonagent.h
|
||||
|
||||
SOURCES += testusermanager.cpp \
|
||||
../../utils/pushbuttonagent.cpp
|
||||
|
||||
|
||||
Reference in New Issue
Block a user