UserManager: Add thing based user permissions

This commit is contained in:
Simon Stürz 2025-10-21 14:51:09 +02:00
parent cdabfb54a5
commit 88aa22f3a2
8 changed files with 94 additions and 21 deletions

View File

@ -38,6 +38,9 @@
#include "integrations/browseritemresult.h"
#include "ruleengine/ruleengine.h"
#include "nymeacore.h"
#include "usermanager/usermanager.h"
#include <QDebug>
#include <QJsonDocument>
#include <QCryptographicHash>
@ -769,29 +772,65 @@ JsonReply* IntegrationsHandler::GetThings(const QVariantMap &params, const JsonC
{
QVariantMap returns;
QVariantList things;
if (params.contains("thingId")) {
Thing *thing = m_thingManager->findConfiguredThing(ThingId(params.value("thingId").toString()));
if (!thing) {
returns.insert("thingError", enumValueName<Thing::ThingError>(Thing::ThingErrorThingNotFound));
return createReply(returns);
} else {
QVariantMap packedThing = pack(thing).toMap();
QString translatedSetupStatus = m_thingManager->translate(thing->pluginId(), thing->setupDisplayMessage(), context.locale());
if (!translatedSetupStatus.isEmpty()) {
packedThing["setupDisplayMessage"] = translatedSetupStatus;
if (NymeaCore::instance()->userManager()->restrictedThingAccess(context.token())) {
QList<ThingId> allowedThingIds = NymeaCore::instance()->userManager()->allowedThingIds(context.token());
if (params.contains("thingId")) {
ThingId thingId(params.value("thingId").toString());
Thing *thing = m_thingManager->findConfiguredThing(thingId);
if (!thing || !allowedThingIds.contains(thingId)) {
returns.insert("thingError", enumValueName<Thing::ThingError>(Thing::ThingErrorThingNotFound));
return createReply(returns);
} else {
QVariantMap packedThing = pack(thing).toMap();
QString translatedSetupStatus = m_thingManager->translate(thing->pluginId(), thing->setupDisplayMessage(), context.locale());
if (!translatedSetupStatus.isEmpty()) {
packedThing["setupDisplayMessage"] = translatedSetupStatus;
}
things.append(packedThing);
}
} else {
foreach (Thing *thing, m_thingManager->configuredThings()) {
if (!allowedThingIds.contains(thing->id()))
continue;
QVariantMap packedThing = pack(thing).toMap();
QString translatedSetupStatus = m_thingManager->translate(thing->pluginId(), thing->setupDisplayMessage(), context.locale());
if (!translatedSetupStatus.isEmpty()) {
packedThing["setupDisplayMessage"] = translatedSetupStatus;
}
things.append(packedThing);
}
things.append(packedThing);
}
} else {
foreach (Thing *thing, m_thingManager->configuredThings()) {
QVariantMap packedThing = pack(thing).toMap();
QString translatedSetupStatus = m_thingManager->translate(thing->pluginId(), thing->setupDisplayMessage(), context.locale());
if (!translatedSetupStatus.isEmpty()) {
packedThing["setupDisplayMessage"] = translatedSetupStatus;
// Unrestricted things access
if (params.contains("thingId")) {
Thing *thing = m_thingManager->findConfiguredThing(ThingId(params.value("thingId").toString()));
if (!thing) {
returns.insert("thingError", enumValueName<Thing::ThingError>(Thing::ThingErrorThingNotFound));
return createReply(returns);
} else {
QVariantMap packedThing = pack(thing).toMap();
QString translatedSetupStatus = m_thingManager->translate(thing->pluginId(), thing->setupDisplayMessage(), context.locale());
if (!translatedSetupStatus.isEmpty()) {
packedThing["setupDisplayMessage"] = translatedSetupStatus;
}
things.append(packedThing);
}
} else {
foreach (Thing *thing, m_thingManager->configuredThings()) {
QVariantMap packedThing = pack(thing).toMap();
QString translatedSetupStatus = m_thingManager->translate(thing->pluginId(), thing->setupDisplayMessage(), context.locale());
if (!translatedSetupStatus.isEmpty()) {
packedThing["setupDisplayMessage"] = translatedSetupStatus;
}
things.append(packedThing);
}
things.append(packedThing);
}
}
returns.insert("thingError", enumValueName<Thing::ThingError>(Thing::ThingErrorNoError));
returns.insert("things", things);
return createReply(returns);

View File

@ -42,12 +42,13 @@ UsersHandler::UsersHandler(UserManager *userManager, QObject *parent):
QString description;
params.clear(); returns.clear();
description = "Create a new user in the API with the given username and password. Use scopes to define the permissions for the new user. If no scopes are given, this user will be an admin user. Call Authenticate after this to obtain a device token for this user.";
description = "Create a new user in the API with the given username and password. Use scopes to define the permissions for the new user. If the user has not the permission \"PermissionScopeAccessAllThings\", the list of things this user has access to can be defined in the \"allowedThingIds\" property. If no scopes are given, this user will be an admin user. Call Authenticate after this to obtain a device token for this user.";
params.insert("username", enumValueName(String));
params.insert("password", enumValueName(String));
params.insert("o:email", enumValueName(String));
params.insert("o:displayName", enumValueName(String));
params.insert("o:scopes", flagRef<Types::PermissionScopes>());
params.insert("o:allowedThingIds", QVariantList() << enumValueName(Uuid));
returns.insert("error", enumRef<UserManager::UserError>());
registerMethod("CreateUser", description, params, returns);
@ -87,9 +88,10 @@ UsersHandler::UsersHandler(UserManager *userManager, QObject *parent):
registerMethod("RemoveUser", description, params, returns);
params.clear(); returns.clear();
description = "Set the permissions (scopes) for a given user.";
description = "Set the permissions (scopes) for a given user. If the user has not the permission \"PermissionScopeAccessAllThings\" the list of thing IDs this user has access to can be defined in the \"allowedThingIds\" property.";
params.insert("username", enumValueName(String));
params.insert("scopes", flagRef<Types::PermissionScopes>());
params.insert("o:allowedThingIds", QVariantList() << enumValueName(Uuid));
returns.insert("error", enumRef<UserManager::UserError>());
registerMethod("SetUserScopes", description, params, returns);

View File

@ -79,6 +79,16 @@ void UserInfo::setScopes(Types::PermissionScopes scopes)
m_scopes = scopes;
}
void UserInfo::setAllowedThingIds(const QList<ThingId> &allowedThingIds)
{
m_allowedThingIds = allowedThingIds;
}
QList<ThingId> UserInfo::allowedThingIds() const
{
return m_allowedThingIds;
}
QVariant UserInfoList::get(int index) const
{
return QVariant::fromValue(at(index));

View File

@ -56,11 +56,16 @@ public:
Types::PermissionScopes scopes() const;
void setScopes(Types::PermissionScopes scopes);
void setAllowedThingIds(const QList<ThingId> &allowedThingIds);
QList<ThingId> allowedThingIds() const;
private:
QString m_username;
QString m_email;
QString m_displayName;
Types::PermissionScopes m_scopes = Types::PermissionScopeNone;
QList<ThingId> m_allowedThingIds;
};
class UserInfoList: public QList<UserInfo>

View File

@ -539,6 +539,18 @@ bool UserManager::verifyToken(const QByteArray &token)
return true;
}
bool UserManager::restrictedThingAccess(const QByteArray &token) const
{
UserInfo ui = userInfo(tokenInfo(token).username());
return !ui.scopes().testFlag(Types::PermissionScopeAccessAllThings);
}
QList<ThingId> UserManager::allowedThingIds(const QByteArray &token) const
{
UserInfo ui = userInfo(tokenInfo(token).username());
return ui.allowedThingIds();
}
bool UserManager::initDB()
{
m_db.close();

View File

@ -74,9 +74,11 @@ public:
UserError removeToken(const QUuid &tokenId);
bool verifyToken(const QByteArray &token);
bool restrictedThingAccess(const QByteArray &token) const;
QList<ThingId> allowedThingIds(const QByteArray &token) const;
signals:
void userAdded(const QString &username);
void userRemoved(const QString &username);
@ -102,7 +104,9 @@ private:
QPair<int, QString> m_pushButtonTransaction;
};
}
Q_DECLARE_METATYPE(nymeaserver::UserManager::UserError)
#endif // USERMANAGER_H

View File

@ -87,7 +87,7 @@ public:
ThingErrorItemNotFound,
ThingErrorItemNotExecutable,
ThingErrorUnsupportedFeature,
ThingErrorTimeout,
ThingErrorTimeout
};
Q_ENUM(ThingError)

View File

@ -202,6 +202,7 @@ public:
PermissionScopeNone = 0x0000,
PermissionScopeControlThings = 0x0001,
PermissionScopeConfigureThings = 0x0003,
PermissionScopeAccessAllThings = 0x0004,
PermissionScopeExecuteRules = 0x0010,
PermissionScopeConfigureRules = 0x0030,
PermissionScopeAdmin = 0xFFFF,