diff --git a/libnymea-app/libnymea-app-core.h b/libnymea-app/libnymea-app-core.h index cbc0ae1f..89498a89 100644 --- a/libnymea-app/libnymea-app-core.h +++ b/libnymea-app/libnymea-app-core.h @@ -372,7 +372,7 @@ void registerQmlTypes() { qmlRegisterType(uri, 1, 0, "ScriptAutoSaver"); qmlRegisterType(uri, 1, 0, "UserManager"); - qmlRegisterUncreatableType(uri, 1, 0, "UserInfo", "Get it from UserManager"); + qmlRegisterType(uri, 1, 0, "UserInfo"); qmlRegisterUncreatableType(uri, 1, 0, "TokenInfo", "Get it from TokenInfos"); qmlRegisterUncreatableType(uri, 1, 0, "TokenInfos", "Get it from UserManager"); qmlRegisterUncreatableType(uri, 1, 0, "Users", "Get it from UserManager"); diff --git a/libnymea-app/types/userinfo.h b/libnymea-app/types/userinfo.h index 5e180e51..d2db28fa 100644 --- a/libnymea-app/types/userinfo.h +++ b/libnymea-app/types/userinfo.h @@ -31,18 +31,18 @@ class UserInfo : public QObject { Q_OBJECT - Q_PROPERTY(QString username READ username NOTIFY usernameChanged) - Q_PROPERTY(QString email READ email NOTIFY emailChanged) - Q_PROPERTY(QString displayName READ displayName NOTIFY displayNameChanged) - Q_PROPERTY(PermissionScopes scopes READ scopes NOTIFY scopesChanged) - Q_PROPERTY(QList allowedThingIds READ allowedThingIds NOTIFY allowedThingIdsChanged) + Q_PROPERTY(QString username READ username WRITE setUsername NOTIFY usernameChanged) + Q_PROPERTY(QString email READ email WRITE setEmail NOTIFY emailChanged) + Q_PROPERTY(QString displayName READ displayName WRITE setDisplayName NOTIFY displayNameChanged) + Q_PROPERTY(PermissionScopes scopes READ scopes WRITE setScopes NOTIFY scopesChanged) + Q_PROPERTY(QList allowedThingIds READ allowedThingIds WRITE setAllowedThingIds NOTIFY allowedThingIdsChanged) public: enum PermissionScope { PermissionScopeNone = 0x0000, PermissionScopeControlThings = 0x0001, PermissionScopeConfigureThings = 0x0003, - PermissionScopeAccessAllThings = 0x0004, + PermissionScopeAccessAllThings = 0x0004, // Since 8.4 PermissionScopeExecuteRules = 0x0010, PermissionScopeConfigureRules = 0x0030, PermissionScopeAdmin = 0xFFFF, diff --git a/libnymea-app/usermanager.cpp b/libnymea-app/usermanager.cpp index 5cb403b5..c3d1b2a0 100644 --- a/libnymea-app/usermanager.cpp +++ b/libnymea-app/usermanager.cpp @@ -103,7 +103,14 @@ int UserManager::createUser(const QString &username, const QString &password, co if (m_engine->jsonRpcClient()->ensureServerVersion("6.0")) { params.insert("displayName", displayName); params.insert("email", email); - params.insert("scopes", UserInfo::scopesToList((UserInfo::PermissionScopes)permissionScopes)); + + // Backports compatibility for pre 8.4 + UserInfo::PermissionScopes scopes = static_cast(permissionScopes); + if (!m_engine->jsonRpcClient()->ensureServerVersion("8.4")) + scopes.setFlag(UserInfo::PermissionScopeAccessAllThings, false); + + + params.insert("scopes", UserInfo::scopesToList(scopes)); } if (m_engine->jsonRpcClient()->ensureServerVersion("8.4") && !allowedThingIds.isEmpty()) { @@ -145,7 +152,14 @@ int UserManager::setUserScopes(const QString &username, int scopes, const QList< { QVariantMap params; params.insert("username", username); - params.insert("scopes", UserInfo::scopesToList((UserInfo::PermissionScopes)scopes)); + + // Backports compatibility for pre 8.4 + UserInfo::PermissionScopes finalScopes = static_cast(scopes); + if (!m_engine->jsonRpcClient()->ensureServerVersion("8.4")) + finalScopes.setFlag(UserInfo::PermissionScopeAccessAllThings, false); + + params.insert("scopes", UserInfo::scopesToList(finalScopes)); + if (m_engine->jsonRpcClient()->ensureServerVersion("8.4") && !allowedThingIds.isEmpty()) { QVariantList thingIds; foreach (const QUuid &thingId, allowedThingIds) diff --git a/nymea-app/ui/system/UsersSettingsPage.qml b/nymea-app/ui/system/UsersSettingsPage.qml index 17ccdabe..50c1fde1 100644 --- a/nymea-app/ui/system/UsersSettingsPage.qml +++ b/nymea-app/ui/system/UsersSettingsPage.qml @@ -98,7 +98,7 @@ SettingsPageBase { Layout.fillWidth: true text: qsTr("Change password") iconName: "qrc:/icons/key.svg" - visible: !engine.jsonRpcClient.pushButtonAuthAvailable + visible: NymeaUtils.hasPermissionScope(engine.jsonRpcClient.permissions, UserInfo.PermissionScopeAdmin) && !engine.jsonRpcClient.pushButtonAuthAvailable onClicked: { var page = pageStack.push(changePasswordComponent) page.confirmed.connect(function(newPassword) { @@ -112,16 +112,15 @@ SettingsPageBase { text: qsTr("Edit user information") iconName: "qrc:/icons/edit.svg" onClicked: pageStack.push(editUserInfoComponent) - visible: !engine.jsonRpcClient.pushButtonAuthAvailable + visible: NymeaUtils.hasPermissionScope(engine.jsonRpcClient.permissions, UserInfo.PermissionScopeAdmin) && !engine.jsonRpcClient.pushButtonAuthAvailable } NymeaItemDelegate { Layout.fillWidth: true text: qsTr("Manage authorized devices") iconName: "qrc:/icons/smartphone.svg" - onClicked: { - pageStack.push(manageTokensComponent) - } + visible: NymeaUtils.hasPermissionScope(engine.jsonRpcClient.permissions, UserInfo.PermissionScopeAdmin) + onClicked: pageStack.push(manageTokensComponent) } SettingsPageSectionHeader { @@ -134,12 +133,9 @@ SettingsPageBase { text: qsTr("Manage users") visible: NymeaUtils.hasPermissionScope(engine.jsonRpcClient.permissions, UserInfo.PermissionScopeAdmin) && !engine.jsonRpcClient.pushButtonAuthAvailable iconName: "qrc:/icons/contact-group.svg" - onClicked: { - pageStack.push(manageUsersComponent) - } + onClicked: pageStack.push(manageUsersComponent) } - Component { id: editUserInfoComponent SettingsPageBase { @@ -199,14 +195,16 @@ SettingsPageBase { id: configureAllowedThingsPage property UserInfo userInfo: null + property bool existingUser: true + + title: qsTr("Accessable things for") + " \"" + userInfo.username + "\"" header: NymeaHeader { - text: root.title + text: configureAllowedThingsPage.title backButtonVisible: true onBackPressed: pageStack.pop() } - title: qsTr("Allowed things for") + " \"" + userInfo.username + "\"" ColumnLayout { anchors.fill: parent @@ -236,7 +234,10 @@ SettingsPageBase { checked: configureAllowedThingsPage.userInfo.thingAllowed(thingDelegate.thing.id) onCheckedChanged: { configureAllowedThingsPage.userInfo.allowThingId(thingDelegate.thing.id, checked) - userManager.setUserScopes(configureAllowedThingsPage.userInfo.username, configureAllowedThingsPage.userInfo.scopes, configureAllowedThingsPage.userInfo.allowedThingIds) + if (configureAllowedThingsPage.existingUser) { + // Only update if this user already exists + userManager.setUserScopes(configureAllowedThingsPage.userInfo.username, configureAllowedThingsPage.userInfo.scopes, configureAllowedThingsPage.userInfo.allowedThingIds) + } } } } @@ -438,7 +439,7 @@ SettingsPageBase { Repeater { id: permissionRepeater - model: NymeaUtils.scopesModel + model: engine.jsonRpcClient.ensureServerVersion("8.4") ? NymeaUtils.scopesModel : NymeaUtils.scopesModelPre8dot4 delegate: NymeaSwipeDelegate { Layout.fillWidth: true @@ -478,24 +479,22 @@ SettingsPageBase { } } - SettingsPageSectionHeader { text: qsTr("Acessable things") - visible: (userDetailsPage.userInfo.scopes & UserInfo.PermissionScopeAccessAllThings) !== UserInfo.PermissionScopeAccessAllThings + visible: engine.jsonRpcClient.ensureServerVersion("8.4") && + (userDetailsPage.userInfo.scopes & UserInfo.PermissionScopeAccessAllThings) !== UserInfo.PermissionScopeAccessAllThings Layout.fillWidth: true } - NymeaSwipeDelegate { id: allowedThingsEntry Layout.fillWidth: true text: qsTr("Allowed things for this user") subText: userDetailsPage.userInfo.allowedThingIds.length + " " + qsTr("things accessable") - visible: (userDetailsPage.userInfo.scopes & UserInfo.PermissionScopeAccessAllThings) !== UserInfo.PermissionScopeAccessAllThings + visible: engine.jsonRpcClient.ensureServerVersion("8.4") && + (userDetailsPage.userInfo.scopes & UserInfo.PermissionScopeAccessAllThings) !== UserInfo.PermissionScopeAccessAllThings progressive: true - onClicked: { - pageStack.push(configureAllowedThingsComponent, {userInfo: userDetailsPage.userInfo}) - } + onClicked: pageStack.push(configureAllowedThingsComponent, {userInfo: userDetailsPage.userInfo}) } SettingsPageSectionHeader { @@ -537,7 +536,13 @@ SettingsPageBase { id: createUserPage title: qsTr("Add a user") - property var permissionScopes: UserInfo.PermissionScopeNone + UserInfo { + id: newUserInfo + username: usernameTextField.text + email: emailTextField.text + displayName: displayNameTextField.text + + } SettingsPageSectionHeader { text: qsTr("User information") @@ -588,30 +593,59 @@ SettingsPageBase { text: qsTr("Permissions") } - Repeater { id: scopesRepeater - model: NymeaUtils.scopesModel - delegate: CheckDelegate { + model: engine.jsonRpcClient.ensureServerVersion("8.4") ? NymeaUtils.scopesModel : NymeaUtils.scopesModelPre8dot4 + + delegate: NymeaSwipeDelegate { + Layout.fillWidth: true - text: model.text - checked: (createUserPage.permissionScopes & model.scope) === model.scope - onClicked: { - var scopes = createUserPage.permissionScopes - 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) - createUserPage.permissionScopes = scopes + text: model.text + subText: model.description + progressive: false + + CheckBox { + anchors.right: parent.right + anchors.rightMargin: app.margins + anchors.verticalCenter: parent.verticalCenter + enabled: model.scope === UserInfo.PermissionScopeAdmin || ((newUserInfo.scopes & UserInfo.PermissionScopeAdmin) !== UserInfo.PermissionScopeAdmin) + checked: (newUserInfo.scopes & model.scope) === model.scope + onClicked: { + var scopes = newUserInfo.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) + newUserInfo.scopes = scopes + } } } } + SettingsPageSectionHeader { + text: qsTr("Acessable things") + visible: engine.jsonRpcClient.ensureServerVersion("8.4") && + (newUserInfo.scopes & UserInfo.PermissionScopeAccessAllThings) !== UserInfo.PermissionScopeAccessAllThings + Layout.fillWidth: true + } + + NymeaSwipeDelegate { + id: allowedThingsEntry + Layout.fillWidth: true + text: qsTr("Allowed things for this user") + subText: newUserInfo.allowedThingIds.length + " " + qsTr("things accessable") + visible: engine.jsonRpcClient.ensureServerVersion("8.4") && + (newUserInfo.scopes & UserInfo.PermissionScopeAccessAllThings) !== UserInfo.PermissionScopeAccessAllThings + progressive: true + onClicked: pageStack.push(configureAllowedThingsComponent, {userInfo: newUserInfo, existingUser: false}) + } + Button { text: qsTr("Create new user") Layout.fillWidth: true @@ -620,7 +654,7 @@ SettingsPageBase { enabled: usernameTextField.displayText.length >= 3 && passwordTextField.isValid onClicked: { createUserPage.busy = true - userManager.createUser(usernameTextField.displayText, passwordTextField.password, displayNameTextField.text, emailTextField.text, createUserPage.permissionScopes) + userManager.createUser(usernameTextField.displayText, passwordTextField.password, displayNameTextField.text, emailTextField.text, newUserInfo.scopes, newUserInfo.allowedThingIds) } } Connections { diff --git a/nymea-app/ui/utils/NymeaUtils.qml b/nymea-app/ui/utils/NymeaUtils.qml index b4c56ea1..0c274bc9 100644 --- a/nymea-app/ui/utils/NymeaUtils.qml +++ b/nymea-app/ui/utils/NymeaUtils.qml @@ -166,6 +166,35 @@ Item { return namedIcons[name] } + property ListModel scopesModelPre8dot4: ListModel { + 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("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 + } + } + + property ListModel scopesModel: ListModel { ListElement { text: qsTr("Admin")