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, PermissionScopeNone = 0x0000,
PermissionScopeControlThings = 0x0001, PermissionScopeControlThings = 0x0001,
PermissionScopeConfigureThings = 0x0003, PermissionScopeConfigureThings = 0x0003,
PermissionScopeAccessAllThings = 0x0004,
PermissionScopeExecuteRules = 0x0010, PermissionScopeExecuteRules = 0x0010,
PermissionScopeConfigureRules = 0x0030, PermissionScopeConfigureRules = 0x0030,
PermissionScopeAdmin = 0xFFFF, PermissionScopeAdmin = 0xFFFF,

View File

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

View File

@ -323,6 +323,7 @@ SettingsPageBase {
title: qsTr("Manage %1").arg(userInfo.username) title: qsTr("Manage %1").arg(userInfo.username)
property UserInfo userInfo: null property UserInfo userInfo: null
property bool restrictedThingAccess: userDetailsPage.userInfo.scopes & UserInfo.PermissionScopeAccessAllThings === 0
Component { Component {
id: confirmUserDeletionComponent id: confirmUserDeletionComponent
@ -382,30 +383,66 @@ SettingsPageBase {
Repeater { Repeater {
model: NymeaUtils.scopesModel model: NymeaUtils.scopesModel
delegate: CheckDelegate { delegate: NymeaSwipeDelegate {
Layout.fillWidth: true
text: model.text
checked: (userDetailsPage.userInfo.scopes & model.scope) === model.scope
enabled: model.scope === UserInfo.PermissionScopeAdmin && userDetailsPage.userInfo.username == userManager.userInfo.username ? Layout.fillWidth: true
false : model.scope === UserInfo.PermissionScopeAdmin ||
((userDetailsPage.userInfo.scopes & UserInfo.PermissionScopeAdmin) !== UserInfo.PermissionScopeAdmin) text: model.text
onClicked: { subText: model.description
print("scopes:", userDetailsPage.userInfo.scopes) progressive: false
var scopes = userDetailsPage.userInfo.scopes
if (checked) { CheckBox {
scopes |= model.scope anchors.right: parent.right
} else { anchors.rightMargin: app.margins
scopes &= ~model.scope anchors.verticalCenter: parent.verticalCenter
scopes |= model.resetOnUnset
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)
}
}
onClicked: {
var scopes = userDetailsPage.userInfo.scopes
if (checked) {
scopes |= model.scope
} else {
scopes &= ~model.scope
}
// 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)
} }
print("username:", userDetailsPage.userInfo.username)
print("new scopes:", scopes, UserInfo.PermissionScopeAdmin)
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 { SettingsPageSectionHeader {
text: qsTr("Remove") text: qsTr("Remove")
} }
@ -496,6 +533,7 @@ SettingsPageBase {
text: qsTr("Permissions") text: qsTr("Permissions")
} }
Repeater { Repeater {
id: scopesRepeater id: scopesRepeater
model: NymeaUtils.scopesModel model: NymeaUtils.scopesModel
@ -510,8 +548,10 @@ SettingsPageBase {
scopes |= model.scope scopes |= model.scope
} else { } else {
scopes &= ~model.scope 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 createUserPage.permissionScopes = scopes
} }
} }

View File

@ -32,7 +32,7 @@ Item {
id: root id: root
function pad(num, size, base) { function pad(num, size, base) {
if (base == undefined) { if (base === undefined) {
base = 10 base = 10
} }
@ -167,11 +167,102 @@ Item {
} }
property ListModel scopesModel: ListModel { property ListModel scopesModel: ListModel {
ListElement { text: qsTr("Admin"); scope: UserInfo.PermissionScopeAdmin; resetOnUnset: UserInfo.PermissionScopeNone } ListElement {
ListElement { text: qsTr("Control things"); scope: UserInfo.PermissionScopeControlThings; resetOnUnset: UserInfo.PermissionScopeNone } text: qsTr("Admin")
ListElement { text: qsTr("Configure things"); scope: UserInfo.PermissionScopeConfigureThings; resetOnUnset: UserInfo.PermissionScopeControlThings } description: qsTr("Full access to the system.")
ListElement { text: qsTr("Execute magic"); scope: UserInfo.PermissionScopeExecuteRules; resetOnUnset: UserInfo.PermissionScopeNone } scope: UserInfo.PermissionScopeAdmin
ListElement { text: qsTr("Configure magic"); scope: UserInfo.PermissionScopeConfigureRules; resetOnUnset: UserInfo.PermissionScopeExecuteRules } }
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) { function hasPermissionScope(permissions, requestedScope) {
@ -201,9 +292,9 @@ Item {
} }
function rgb2hsv(r,g,b) { function rgb2hsv(r,g,b) {
var v=Math.max(r,g,b), c=v-Math.min(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]; return [60*(h<0?h+6:h), v&&c/v, v];
} }
readonly property var sensorInterfaceStateMap: { readonly property var sensorInterfaceStateMap: {