Improve permission handling and add new Thing based permission basics

pull/1127/head
Simon Stürz 2025-10-22 14:01:14 +02:00
parent ecea0eae26
commit e14137296e
4 changed files with 162 additions and 29 deletions

View File

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

View File

@ -53,7 +53,8 @@ public:
UserErrorDuplicateUserId,
UserErrorBadPassword,
UserErrorTokenNotFound,
UserErrorPermissionDenied
UserErrorPermissionDenied,
UserErrorInconsistantScopes
};
Q_ENUM(UserError)

View File

@ -323,6 +323,7 @@ SettingsPageBase {
title: qsTr("Manage %1").arg(userInfo.username)
property UserInfo userInfo: null
property bool restrictedThingAccess: userDetailsPage.userInfo.scopes & UserInfo.PermissionScopeAccessAllThings === 0
Component {
id: confirmUserDeletionComponent
@ -382,29 +383,65 @@ SettingsPageBase {
Repeater {
model: NymeaUtils.scopesModel
delegate: CheckDelegate {
Layout.fillWidth: true
text: model.text
checked: (userDetailsPage.userInfo.scopes & model.scope) === model.scope
delegate: NymeaSwipeDelegate {
Layout.fillWidth: true
text: model.text
subText: model.description
progressive: false
CheckBox {
anchors.right: parent.right
anchors.rightMargin: app.margins
anchors.verticalCenter: parent.verticalCenter
checked: (userDetailsPage.userInfo.scopes & model.scope) === model.scope
enabled: {
// Prevent an admin to lock himself out as admin
if (model.scope === UserInfo.PermissionScopeAdmin && userDetailsPage.userInfo.username == userManager.userInfo.username) {
return false
} else {
return model.scope === UserInfo.PermissionScopeAdmin || ((userDetailsPage.userInfo.scopes & UserInfo.PermissionScopeAdmin) !== UserInfo.PermissionScopeAdmin)
}
}
enabled: model.scope === UserInfo.PermissionScopeAdmin && userDetailsPage.userInfo.username == userManager.userInfo.username ?
false : model.scope === UserInfo.PermissionScopeAdmin ||
((userDetailsPage.userInfo.scopes & UserInfo.PermissionScopeAdmin) !== UserInfo.PermissionScopeAdmin)
onClicked: {
print("scopes:", userDetailsPage.userInfo.scopes)
var scopes = userDetailsPage.userInfo.scopes
if (checked) {
scopes |= model.scope
} else {
scopes &= ~model.scope
scopes |= model.resetOnUnset
}
print("username:", userDetailsPage.userInfo.username)
print("new scopes:", scopes, UserInfo.PermissionScopeAdmin)
// make sure the new permissions are consistant before sending them to the core
scopes = NymeaUtils.getPermissionScopeAdjustments(model.scope, checked, scopes)
userManager.setUserScopes(userDetailsPage.userInfo.username, scopes)
}
}
}
}
SettingsPageSectionHeader {
text: qsTr("Acessable things")
visible: userDetailsPage.restrictedThingAccess
Layout.fillWidth: true
}
NymeaSwipeDelegate {
id: allowedThingsEntry
Layout.fillWidth: true
text: qsTr("Allowed things for this user")
visible: userDetailsPage.restrictedThingAccess
progressive: true
onClicked: {
}
}
SettingsPageSectionHeader {
text: qsTr("Remove")
@ -496,6 +533,7 @@ SettingsPageBase {
text: qsTr("Permissions")
}
Repeater {
id: scopesRepeater
model: NymeaUtils.scopesModel
@ -510,8 +548,10 @@ SettingsPageBase {
scopes |= model.scope
} else {
scopes &= ~model.scope
scopes |= model.resetOnUnset
}
// make sure the new permissions are consistant before sending them to the core
scopes = NymeaUtils.getPermissionScopeAdjustments(model.scope, checked, scopes)
createUserPage.permissionScopes = scopes
}
}

View File

@ -32,7 +32,7 @@ Item {
id: root
function pad(num, size, base) {
if (base == undefined) {
if (base === undefined) {
base = 10
}
@ -167,11 +167,102 @@ Item {
}
property ListModel scopesModel: ListModel {
ListElement { text: qsTr("Admin"); scope: UserInfo.PermissionScopeAdmin; resetOnUnset: UserInfo.PermissionScopeNone }
ListElement { text: qsTr("Control things"); scope: UserInfo.PermissionScopeControlThings; resetOnUnset: UserInfo.PermissionScopeNone }
ListElement { text: qsTr("Configure things"); scope: UserInfo.PermissionScopeConfigureThings; resetOnUnset: UserInfo.PermissionScopeControlThings }
ListElement { text: qsTr("Execute magic"); scope: UserInfo.PermissionScopeExecuteRules; resetOnUnset: UserInfo.PermissionScopeNone }
ListElement { text: qsTr("Configure magic"); scope: UserInfo.PermissionScopeConfigureRules; resetOnUnset: UserInfo.PermissionScopeExecuteRules }
ListElement {
text: qsTr("Admin")
description: qsTr("Full access to the system.")
scope: UserInfo.PermissionScopeAdmin
}
ListElement {
text: qsTr("Control things")
description: qsTr("Execute actions and use things and services.")
scope: UserInfo.PermissionScopeControlThings
}
ListElement {
text: qsTr("Configure things")
description: qsTr("Add new things and change settings.")
scope: UserInfo.PermissionScopeConfigureThings
}
ListElement {
text: qsTr("Access all things")
description: qsTr("Allow to see and use all things of the system.")
scope: UserInfo.PermissionScopeAccessAllThings
}
ListElement {
text: qsTr("Execute magic")
description: qsTr("Execute rules, scenes and scripts.")
scope: UserInfo.PermissionScopeExecuteRules
}
ListElement {
text: qsTr("Configure magic")
description: qsTr("Create new rules and scripts in the system.")
scope: UserInfo.PermissionScopeConfigureRules
}
}
function getPermissionScopeAdjustments(scope, enabled, currentScopes) {
var adjustedScopes = currentScopes;
console.warn("Adjust permissions", scope, "->", enabled, currentScopes)
if (enabled) {
// Scope has been enabled
switch (scope) {
case UserInfo.PermissionScopeAdmin:
adjustedScopes = UserInfo.PermissionScopeAdmin
break;
case UserInfo.PermissionScopeControlThings:
break;
case UserInfo.PermissionScopeConfigureThings:
adjustedScopes |= UserInfo.PermissionScopeControlThings
adjustedScopes |= UserInfo.PermissionScopeAccessAllThings
break;
case UserInfo.PermissionScopeAccessAllThings:
adjustedScopes |= UserInfo.PermissionScopeControlThings
break;
case UserInfo.PermissionScopeExecuteRules:
adjustedScopes |= UserInfo.PermissionScopeAccessAllThings
break;
case UserInfo.PermissionScopeConfigureRules:
adjustedScopes |= UserInfo.PermissionScopeExecuteRules
adjustedScopes |= UserInfo.PermissionScopeAccessAllThings
break;
}
} else {
// Scope has been disabled
switch (scope) {
case UserInfo.PermissionScopeAdmin:
// Set the default permission for non admin
adjustedScopes = UserInfo.PermissionScopeAccessAllThings | UserInfo.PermissionScopeControlThings | UserInfo.PermissionScopeExecuteRules
break;
case UserInfo.PermissionScopeControlThings:
adjustedScopes &= ~UserInfo.PermissionScopeConfigureThings
break;
case UserInfo.PermissionScopeConfigureThings:
// Note: PermissionScopeConfigureThings is 3 and unsets therefore also the abbility to control things.
adjustedScopes |= UserInfo.PermissionScopeControlThings
break;
case UserInfo.PermissionScopeAccessAllThings:
adjustedScopes &= ~UserInfo.PermissionScopeConfigureThings
adjustedScopes &= ~UserInfo.PermissionScopeExecuteRules
adjustedScopes &= ~UserInfo.PermissionScopeConfigureRules
// Make sure we still can controll those things we added
adjustedScopes |= UserInfo.PermissionScopeControlThings
break;
case UserInfo.PermissionScopeExecuteRules:
adjustedScopes &= ~UserInfo.PermissionScopeConfigureRules
break;
case UserInfo.PermissionScopeConfigureRules:
// Note: PermissionScopeConfigureRules constand unsets therefore also the abbility to execute rules (screnes).
adjustedScopes |= UserInfo.PermissionScopeExecuteRules
break;
}
}
return adjustedScopes
}
function hasPermissionScope(permissions, requestedScope) {
@ -202,7 +293,7 @@ Item {
function rgb2hsv(r,g,b) {
var v=Math.max(r,g,b), c=v-Math.min(r,g,b);
var h= c && ((v==r) ? (g-b)/c : ((v==g) ? 2+(b-r)/c : 4+(r-g)/c));
var h= c && ((v===r) ? (g-b)/c : ((v===g) ? 2+(b-r)/c : 4+(r-g)/c));
return [60*(h<0?h+6:h), v&&c/v, v];
}