From 316bd65296d8355c00a68fcf2aaa15d4e9fe3239 Mon Sep 17 00:00:00 2001 From: webplusai Date: Fri, 8 Nov 2024 11:09:42 +0100 Subject: [PATCH] Add success and error message feedback for user profile operations (#8716) * mws authentication * add more tests and permission checkers * add logic to ensure that only authenticated users' requests are handled * add custom login page * Implement user authentication as well as session handling * work on user operations authorization * add middleware to route handlers for bags & tiddlers routes * add feature that only returns the tiddlers and bags which the user has permission to access on index page * refactor auth routes & added user management page * fix Ci Test failure issue * fix users list page, add manage roles page * add commands and scripts to create new user & assign roles and permissions * resolved ci-test failure * add ACL permissions to bags & tiddlers on creation * fix comments and access control list bug * fix indentation issues * working on user profile edit * remove list users command & added support for database in server options * implement user profile update and password change feature * update plugin readme * implement command which triggers protected mode on the server * revert server-wide auth flag. Implement selective authorization * ACL management feature * Complete Access control list implementation * Added support to manage users' assigned role by admin * fix comments * fix comment * Add user profile management and account deletion functionality * add success and error message feedback for user profile operations * fix indentation issues * Add command to create admin user if none exists when the start command is executed * refactor annonymous user flow with create admin implementation * remove mws-add-user from start command --- editions/multiwikiserver/tiddlywiki.info | 9 - .../modules/commands/mws-add-permission.js | 2 - .../modules/commands/mws-add-role.js | 2 - .../modules/commands/mws-add-user.js | 6 +- .../commands/mws-assign-role-permission.js | 2 - .../modules/commands/mws-test-server.js | 14 -- .../multiwikiserver/modules/mws-server.js | 2 + .../routes/handlers/change-user-password.js | 42 +++-- .../routes/handlers/delete-user-account.js | 113 ++++++++----- .../modules/routes/handlers/get-acl.js | 158 +++++++++--------- .../modules/routes/handlers/get-index.js | 8 +- .../modules/routes/handlers/get-users.js | 13 +- .../modules/routes/handlers/manage-roles.js | 2 +- .../modules/routes/handlers/manage-user.js | 145 +++++++++------- .../modules/routes/handlers/post-acl.js | 81 +++++---- .../modules/routes/handlers/post-role.js | 32 ++-- .../modules/routes/handlers/post-user.js | 99 ++++++----- .../routes/handlers/update-user-profile.js | 36 ++-- .../modules/routes/helpers/acl-middleware.js | 2 +- .../modules/store/sql-tiddler-database.js | 37 ++-- .../multiwikiserver/templates/get-index.tid | 58 +++++++ .../multiwikiserver/templates/get-users.tid | 62 ++++--- .../templates/manage-user-account.tid | 37 ++++ .../multiwikiserver/templates/manage-user.tid | 6 +- .../multiwikiserver/templates/mws-header.tid | 2 +- 25 files changed, 580 insertions(+), 390 deletions(-) diff --git a/editions/multiwikiserver/tiddlywiki.info b/editions/multiwikiserver/tiddlywiki.info index abc3e460d..5b61d8576 100644 --- a/editions/multiwikiserver/tiddlywiki.info +++ b/editions/multiwikiserver/tiddlywiki.info @@ -11,15 +11,6 @@ "tiddlywiki/snowwhite" ], "build": { - "mws-add-user": [ - "--mws-add-permission", "READ", "Allows user to create tiddlers", - "--mws-add-permission", "WRITE", "Gives the user the permission to edit and delete tiddlers", - "--mws-add-role", "ADMIN", "System Administrator", - "--mws-assign-role-permission", "ADMIN", "READ", - "--mws-assign-role-permission", "ADMIN", "WRITE", - "--mws-add-user", "user", "pass123", - "--mws-assign-user-role", "user", "ADMIN" - ], "load-mws-demo-data": [ "--mws-load-wiki-folder","./editions/tw5.com","docs", "TiddlyWiki Documentation from https://tiddlywiki.com","docs","TiddlyWiki Documentation from https://tiddlywiki.com", "--mws-load-wiki-folder","./editions/dev","dev","TiddlyWiki Developer Documentation from https://tiddlywiki.com/dev","dev-docs", "TiddlyWiki Developer Documentation from https://tiddlywiki.com/dev", diff --git a/plugins/tiddlywiki/multiwikiserver/modules/commands/mws-add-permission.js b/plugins/tiddlywiki/multiwikiserver/modules/commands/mws-add-permission.js index ecaa13ba5..fe7429566 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/commands/mws-add-permission.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/commands/mws-add-permission.js @@ -38,8 +38,6 @@ Command.prototype.execute = function() { var description = this.params[1]; $tw.mws.store.sqlTiddlerDatabase.createPermission(permission_name, description); - - console.log(permission_name+" Permission Created Successfully!") self.callback(); return null; }; diff --git a/plugins/tiddlywiki/multiwikiserver/modules/commands/mws-add-role.js b/plugins/tiddlywiki/multiwikiserver/modules/commands/mws-add-role.js index 19ba2af9a..ec435a97f 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/commands/mws-add-role.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/commands/mws-add-role.js @@ -38,8 +38,6 @@ Command.prototype.execute = function() { var description = this.params[1]; $tw.mws.store.sqlTiddlerDatabase.createRole(role_name, description); - - console.log(role_name+" Role Created Successfully!") self.callback(null, "Role Created Successfully!"); return null; }; diff --git a/plugins/tiddlywiki/multiwikiserver/modules/commands/mws-add-user.js b/plugins/tiddlywiki/multiwikiserver/modules/commands/mws-add-user.js index 4c3a4168a..fc0c4e6e1 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/commands/mws-add-user.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/commands/mws-add-user.js @@ -43,11 +43,9 @@ Command.prototype.execute = function() { var user = $tw.mws.store.sqlTiddlerDatabase.getUserByUsername(username); - if(user) { - self.callback("WARNING: An account with the username (" + username + ") already exists"); - } else { + if(!user) { $tw.mws.store.sqlTiddlerDatabase.createUser(username, email, hashedPassword); - console.log("User Account Created Successfully!") + console.log("User Account Created Successfully with username: " + username + " and password: " + password); self.callback(); } return null; diff --git a/plugins/tiddlywiki/multiwikiserver/modules/commands/mws-assign-role-permission.js b/plugins/tiddlywiki/multiwikiserver/modules/commands/mws-assign-role-permission.js index 621a46e59..89ed568d9 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/commands/mws-assign-role-permission.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/commands/mws-assign-role-permission.js @@ -51,8 +51,6 @@ Command.prototype.execute = function() { $tw.mws.store.sqlTiddlerDatabase.addPermissionToRole(role.role_id, permission.permission_id); - - console.log(permission_name+" permission assigned to "+role_name+" role successfully!") self.callback(); return null; }; diff --git a/plugins/tiddlywiki/multiwikiserver/modules/commands/mws-test-server.js b/plugins/tiddlywiki/multiwikiserver/modules/commands/mws-test-server.js index 0c246c93e..5d03844aa 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/commands/mws-test-server.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/commands/mws-test-server.js @@ -119,20 +119,6 @@ TestRunner.prototype.runTest = function(testSpec,callback) { }; const testSpecs = [ - { - description: "Login Test User", - method: "POST", - path: "/login", - headers: { - "Accept": 'application/json', - "Content-Type": 'application/x-www-form-urlencoded', - "User-Agent": 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36' - }, - data: "username=user&password=pass123", - expectedResult: (jsonData,data,headers) => { - return !!jsonData.sessionId; - } - }, { description: "Check index page", method: "GET", diff --git a/plugins/tiddlywiki/multiwikiserver/modules/mws-server.js b/plugins/tiddlywiki/multiwikiserver/modules/mws-server.js index e594cd146..144b65b8d 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/mws-server.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/mws-server.js @@ -441,6 +441,8 @@ Server.prototype.requestHandler = function(request,response,options) { // Check whether anonymous access is granted state.allowAnon = false; //this.isAuthorized(state.authorizationType,null); + state.firstGuestUser = this.sqlTiddlerDatabase.listUsers().length === 0 && !state.authenticatedUser; + // Authorize with the authenticated username if(!this.isAuthorized(state.authorizationType,state.authenticatedUsername) && !response.headersSent) { response.writeHead(403,"'" + state.authenticatedUsername + "' is not authorized to access '" + this.servername + "'"); diff --git a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/change-user-password.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/change-user-password.js index 2633ce10f..303b8e4e6 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/change-user-password.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/change-user-password.js @@ -22,28 +22,44 @@ exports.bodyFormat = "www-form-urlencoded"; exports.csrfDisable = true; exports.handler = function (request, response, state) { + var userId = state.data.userId; + // Clean up any existing error/success messages + $tw.mws.store.adminWiki.deleteTiddler("$:/temp/mws/change-password/" + userId + "/error"); + $tw.mws.store.adminWiki.deleteTiddler("$:/temp/mws/change-password/" + userId + "/success"); + $tw.mws.store.adminWiki.deleteTiddler("$:/temp/mws/login/error"); + if(!state.authenticatedUser) { - response.writeHead(401, "Unauthorized", { "Content-Type": "text/plain" }); - response.end("Unauthorized"); + $tw.mws.store.adminWiki.addTiddler(new $tw.Tiddler({ + title: "$:/temp/mws/login/error", + text: "You must be logged in to change passwords" + })); + response.writeHead(302, { "Location": "/login" }); + response.end(); return; } - var auth = authenticator(state.server.sqlTiddlerDatabase); - var userId = state.data.userId; + var auth = authenticator(state.server.sqlTiddlerDatabase); var newPassword = state.data.newPassword; var confirmPassword = state.data.confirmPassword; var currentUserId = state.authenticatedUser.user_id; - var hasPermission = ($tw.utils.parseInt(userId, 10) === currentUserId) || state.authenticatedUser.isAdmin; + var hasPermission = ($tw.utils.parseInt(userId) === currentUserId) || state.authenticatedUser.isAdmin; if(!hasPermission) { - response.writeHead(403, "Forbidden", { "Content-Type": "text/plain" }); - response.end("Forbidden"); + $tw.mws.store.adminWiki.addTiddler(new $tw.Tiddler({ + title: "$:/temp/mws/change-password/" + userId + "/error", + text: "You don't have permission to change this user's password" + })); + response.writeHead(302, { "Location": "/admin/users/" + userId }); + response.end(); return; } if(newPassword !== confirmPassword) { - response.setHeader("Set-Cookie", "flashMessage=New passwords do not match; Path=/; HttpOnly; Max-Age=5"); + $tw.mws.store.adminWiki.addTiddler(new $tw.Tiddler({ + title: "$:/temp/mws/change-password/" + userId + "/error", + text: "New passwords do not match" + })); response.writeHead(302, { "Location": "/admin/users/" + userId }); response.end(); return; @@ -52,7 +68,10 @@ exports.handler = function (request, response, state) { var userData = state.server.sqlTiddlerDatabase.getUser(userId); if(!userData) { - response.setHeader("Set-Cookie", "flashMessage=User not found; Path=/; HttpOnly; Max-Age=5"); + $tw.mws.store.adminWiki.addTiddler(new $tw.Tiddler({ + title: "$:/temp/mws/change-password/" + userId + "/error", + text: "User not found" + })); response.writeHead(302, { "Location": "/admin/users/" + userId }); response.end(); return; @@ -61,7 +80,10 @@ exports.handler = function (request, response, state) { var newHash = auth.hashPassword(newPassword); var result = state.server.sqlTiddlerDatabase.updateUserPassword(userId, newHash); - response.setHeader("Set-Cookie", `flashMessage=${result.message}; Path=/; HttpOnly; Max-Age=5`); + $tw.mws.store.adminWiki.addTiddler(new $tw.Tiddler({ + title: "$:/temp/mws/change-password/" + userId + "/success", + text: result.message + })); response.writeHead(302, { "Location": "/admin/users/" + userId }); response.end(); }; diff --git a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/delete-user-account.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/delete-user-account.js index 426683a29..5fb0f219f 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/delete-user-account.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/delete-user-account.js @@ -8,51 +8,86 @@ POST /delete-user-account \*/ (function () { - /*jslint node: true, browser: true */ - /*global $tw: false */ - "use strict"; +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; - exports.method = "POST"; +exports.method = "POST"; - exports.path = /^\/delete-user-account\/?$/; +exports.path = /^\/delete-user-account\/?$/; - exports.bodyFormat = "www-form-urlencoded"; +exports.bodyFormat = "www-form-urlencoded"; - exports.csrfDisable = true; +exports.csrfDisable = true; - exports.handler = function (request, response, state) { - var sqlTiddlerDatabase = state.server.sqlTiddlerDatabase; - var userId = state.data.userId; +exports.handler = function (request, response, state) { + var sqlTiddlerDatabase = state.server.sqlTiddlerDatabase; + var userId = state.data.userId; - // Check if user is admin - if(!state.authenticatedUser || !state.authenticatedUser.isAdmin) { - response.writeHead(403, "Forbidden"); - response.end(); - return; - } - - // Prevent admin from deleting their own account - if(state.authenticatedUser.user_id === userId) { - response.writeHead(400, "Bad Request"); - response.end("Cannot delete your own account"); - return; - } - - // Check if the user exists - var user = sqlTiddlerDatabase.getUser(userId); - if(!user) { - response.writeHead(404, "Not Found"); - response.end("User not found"); - return; - } - - sqlTiddlerDatabase.deleteUserRolesByUserId(userId); - sqlTiddlerDatabase.deleteUserSessions(userId); - sqlTiddlerDatabase.deleteUser(userId); - - // Redirect back to the users management page - response.writeHead(302, { "Location": "/admin/users" }); + // Check if user is admin + if(!state.authenticatedUser || !state.authenticatedUser.isAdmin) { + $tw.mws.store.adminWiki.addTiddler(new $tw.Tiddler({ + title: "$:/temp/mws/delete-user/error", + text: "You must be an administrator to delete user accounts" + })); + response.writeHead(302, { "Location": '/admin/users/'+userId }); response.end(); - }; + return; + } + + // Prevent admin from deleting their own account + if(state.authenticatedUser.user_id === userId) { + $tw.mws.store.adminWiki.addTiddler(new $tw.Tiddler({ + title: "$:/temp/mws/delete-user/error", + text: "Cannot delete your own account" + })); + response.writeHead(302, { "Location": '/admin/users/'+userId }); + response.end(); + return; + } + + // Check if the user exists + var user = sqlTiddlerDatabase.getUser(userId); + if(!user) { + $tw.mws.store.adminWiki.addTiddler(new $tw.Tiddler({ + title: "$:/temp/mws/delete-user/error", + text: "User not found" + })); + response.writeHead(302, { "Location": '/admin/users/'+userId }); + response.end(); + return; + } + + // Check if this is the last admin account + var adminRole = sqlTiddlerDatabase.getRoleByName("ADMIN"); + if(!adminRole) { + $tw.mws.store.adminWiki.addTiddler(new $tw.Tiddler({ + title: "$:/temp/mws/delete-user/error", + text: "Admin role not found" + })); + response.writeHead(302, { "Location": '/admin/users/'+userId }); + response.end(); + return; + } + + var adminUsers = sqlTiddlerDatabase.listUsersByRoleId(adminRole.role_id); + if(adminUsers.length <= 1 && adminUsers.some(admin => admin.user_id === parseInt(userId))) { + $tw.mws.store.adminWiki.addTiddler(new $tw.Tiddler({ + title: "$:/temp/mws/delete-user/error", + text: "Cannot delete the last admin account" + })); + response.writeHead(302, { "Location": '/admin/users/'+userId }); + response.end(); + return; + } + + sqlTiddlerDatabase.deleteUserRolesByUserId(userId); + sqlTiddlerDatabase.deleteUserSessions(userId); + sqlTiddlerDatabase.deleteUser(userId); + + // Redirect back to the users management page + response.writeHead(302, { "Location": "/admin/users" }); + response.end(); +}; }()); \ No newline at end of file diff --git a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-acl.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-acl.js index fefcff8d2..6938dae0b 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-acl.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-acl.js @@ -7,91 +7,91 @@ GET /admin/acl \*/ (function () { - /*jslint node: true, browser: true */ - /*global $tw: false */ - "use strict"; +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; - exports.method = "GET"; +exports.method = "GET"; - exports.path = /^\/admin\/acl\/(.+)$/; +exports.path = /^\/admin\/acl\/(.+)$/; - exports.handler = function (request, response, state) { - var sqlTiddlerDatabase = state.server.sqlTiddlerDatabase; - var params = state.params[0].split("/") - var recipeName = params[0]; - var bagName = params[params.length - 1]; +exports.handler = function (request, response, state) { + var sqlTiddlerDatabase = state.server.sqlTiddlerDatabase; + var params = state.params[0].split("/") + var recipeName = params[0]; + var bagName = params[params.length - 1]; - var recipes = sqlTiddlerDatabase.listRecipes() - var bags = sqlTiddlerDatabase.listBags() + var recipes = sqlTiddlerDatabase.listRecipes() + var bags = sqlTiddlerDatabase.listBags() - var recipe = recipes.find((entry) => entry.recipe_name === recipeName && entry.bag_names.includes(bagName)) - var bag = bags.find((entry) => entry.bag_name === bagName); + var recipe = recipes.find((entry) => entry.recipe_name === recipeName && entry.bag_names.includes(bagName)) + var bag = bags.find((entry) => entry.bag_name === bagName); - if (!recipe || !bag) { - response.writeHead(500, "Unable to handle request", { "Content-Type": "text/html" }); - response.end(); - return; - } - - var recipeAclRecords = sqlTiddlerDatabase.getEntityAclRecords(recipe.recipe_name); - var bagAclRecords = sqlTiddlerDatabase.getEntityAclRecords(bag.bag_name); - var roles = state.server.sqlTiddlerDatabase.listRoles(); - var permissions = state.server.sqlTiddlerDatabase.listPermissions(); - - // This ensures that the user attempting to view the ACL management page has permission to do so - if(!state.authenticatedUser || (recipeAclRecords.length > 0 && !sqlTiddlerDatabase.hasRecipePermission(state.authenticatedUser.user_id, recipeName, 'WRITE'))){ - response.writeHead(403, "Forbidden"); - response.end(); - return - } - - // Enhance ACL records with role and permission details - recipeAclRecords = recipeAclRecords.map(record => { - var role = roles.find(role => role.role_id === record.role_id); - var permission = permissions.find(perm => perm.permission_id === record.permission_id); - return ({ - ...record, - role, - permission, - role_name: role.role_name, - role_description: role.description, - permission_name: permission.permission_name, - permission_description: permission.description - }) - }); - - bagAclRecords = bagAclRecords.map(record => { - var role = roles.find(role => role.role_id === record.role_id); - var permission = permissions.find(perm => perm.permission_id === record.permission_id); - return ({ - ...record, - role, - permission, - role_name: role.role_name, - role_description: role.description, - permission_name: permission.permission_name, - permission_description: permission.description - }) - }); - - response.writeHead(200, "OK", { "Content-Type": "text/html" }); - - var html = $tw.mws.store.adminWiki.renderTiddler("text/plain", "$:/plugins/tiddlywiki/multiwikiserver/templates/page", { - variables: { - "page-content": "$:/plugins/tiddlywiki/multiwikiserver/templates/manage-acl", - "roles-list": JSON.stringify(roles), - "permissions-list": JSON.stringify(permissions), - "bag": JSON.stringify(bag), - "recipe": JSON.stringify(recipe), - "recipe-acl-records": JSON.stringify(recipeAclRecords), - "bag-acl-records": JSON.stringify(bagAclRecords), - "username": state.authenticatedUser ? state.authenticatedUser.username : "Guest", - "user-is-admin": state.authenticatedUser && state.authenticatedUser.isAdmin ? "yes" : "no" - } - }); - - response.write(html); + if (!recipe || !bag) { + response.writeHead(500, "Unable to handle request", { "Content-Type": "text/html" }); response.end(); - }; + return; + } + + var recipeAclRecords = sqlTiddlerDatabase.getEntityAclRecords(recipe.recipe_name); + var bagAclRecords = sqlTiddlerDatabase.getEntityAclRecords(bag.bag_name); + var roles = state.server.sqlTiddlerDatabase.listRoles(); + var permissions = state.server.sqlTiddlerDatabase.listPermissions(); + + // This ensures that the user attempting to view the ACL management page has permission to do so + if(!state.authenticatedUser || (recipeAclRecords.length > 0 && !sqlTiddlerDatabase.hasRecipePermission(state.authenticatedUser.user_id, recipeName, 'WRITE'))){ + response.writeHead(403, "Forbidden"); + response.end(); + return + } + + // Enhance ACL records with role and permission details + recipeAclRecords = recipeAclRecords.map(record => { + var role = roles.find(role => role.role_id === record.role_id); + var permission = permissions.find(perm => perm.permission_id === record.permission_id); + return ({ + ...record, + role, + permission, + role_name: role.role_name, + role_description: role.description, + permission_name: permission.permission_name, + permission_description: permission.description + }) + }); + + bagAclRecords = bagAclRecords.map(record => { + var role = roles.find(role => role.role_id === record.role_id); + var permission = permissions.find(perm => perm.permission_id === record.permission_id); + return ({ + ...record, + role, + permission, + role_name: role.role_name, + role_description: role.description, + permission_name: permission.permission_name, + permission_description: permission.description + }) + }); + + response.writeHead(200, "OK", { "Content-Type": "text/html" }); + + var html = $tw.mws.store.adminWiki.renderTiddler("text/plain", "$:/plugins/tiddlywiki/multiwikiserver/templates/page", { + variables: { + "page-content": "$:/plugins/tiddlywiki/multiwikiserver/templates/manage-acl", + "roles-list": JSON.stringify(roles), + "permissions-list": JSON.stringify(permissions), + "bag": JSON.stringify(bag), + "recipe": JSON.stringify(recipe), + "recipe-acl-records": JSON.stringify(recipeAclRecords), + "bag-acl-records": JSON.stringify(bagAclRecords), + "username": state.authenticatedUser ? state.authenticatedUser.username : state.firstGuestUser ? "Annonymous User" : "Guest", + "user-is-admin": state.authenticatedUser && state.authenticatedUser.isAdmin ? "yes" : "no" + } + }); + + response.write(html); + response.end(); +}; }()); \ No newline at end of file diff --git a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-index.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-index.js index fc40e9852..63ee88708 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-index.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-index.js @@ -41,10 +41,10 @@ exports.handler = function(request,response,state) { "page-content": "$:/plugins/tiddlywiki/multiwikiserver/templates/get-index", "bag-list": JSON.stringify(allowedBags), "recipe-list": JSON.stringify(allowedRecipes), - "username": state.authenticatedUser ? state.authenticatedUser.username : "Guest", - "user-is-admin": state.authenticatedUser && state.authenticatedUser.isAdmin ? "yes" : "no" - } - }); + "username": state.authenticatedUser ? state.authenticatedUser.username : state.firstGuestUser ? "Annonymous User" : "Guest", + "user-is-admin": state.authenticatedUser && state.authenticatedUser.isAdmin ? "yes" : "no", + "first-guest-user": state.firstGuestUser ? "yes" : "no" + }}); response.write(html); response.end(); } diff --git a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-users.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-users.js index 380c04b04..06a4cf769 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-users.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-users.js @@ -18,19 +18,19 @@ exports.path = /^\/admin\/users$/; exports.handler = function(request,response,state) { var userList = state.server.sqlTiddlerDatabase.listUsers(); - + // Ensure userList is an array if (!Array.isArray(userList)) { userList = []; console.error("userList is not an array"); } - if(!state.authenticatedUser.isAdmin) { + if(!state.authenticatedUser.isAdmin && !state.firstGuestUser) { response.writeHead(403, "Forbidden", { "Content-Type": "text/plain" }); response.end("Forbidden"); return; } - + // Convert dates to strings and ensure all necessary fields are present userList = userList.map(user => ({ user_id: user.user_id || '', @@ -49,12 +49,13 @@ exports.handler = function(request,response,state) { variables: { "page-content": "$:/plugins/tiddlywiki/multiwikiserver/templates/get-users", "user-list": JSON.stringify(userList), - "username": state.authenticatedUser ? state.authenticatedUser.username : "Guest", - "user-is-admin": state.authenticatedUser && state.authenticatedUser.isAdmin ? "yes" : "no" + "username": state.authenticatedUser ? state.authenticatedUser.username : state.firstGuestUser ? "Annonymous User" : "Guest", + "user-is-admin": state.authenticatedUser && state.authenticatedUser.isAdmin ? "yes" : "no", + "first-guest-user": state.firstGuestUser ? "yes" : "no" } }); response.write(html); response.end(); }; - + }()); \ No newline at end of file diff --git a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/manage-roles.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/manage-roles.js index c76aa5a00..8519b002a 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/manage-roles.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/manage-roles.js @@ -31,7 +31,7 @@ exports.handler = function(request, response, state) { "page-content": "$:/plugins/tiddlywiki/multiwikiserver/templates/manage-roles", "roles-list": JSON.stringify(roles), "edit-role": editRole ? JSON.stringify(editRole) : "", - "username": state.authenticatedUser ? state.authenticatedUser.username : "Guest", + "username": state.authenticatedUser ? state.authenticatedUser.username : state.firstGuestUser ? "Annonymous User" : "Guest", "user-is-admin": state.authenticatedUser && state.authenticatedUser.isAdmin ? "yes" : "no" } }); diff --git a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/manage-user.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/manage-user.js index 771e0233e..739a6af56 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/manage-user.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/manage-user.js @@ -8,73 +8,92 @@ GET /admin/users/:user_id \*/ (function() { - /*jslint node: true, browser: true */ - /*global $tw: false */ - "use strict"; - - exports.method = "GET"; - - exports.path = /^\/admin\/users\/([^\/]+)\/?$/; - - exports.handler = function(request,response,state) { - var user_id = $tw.utils.decodeURIComponentSafe(state.params[0]); - var userData = state.server.sqlTiddlerDatabase.getUser(user_id); - - if(!userData) { - response.writeHead(404, "Not Found", {"Content-Type": "text/html"}); - var errorHtml = $tw.mws.store.adminWiki.renderTiddler("text/plain", "$:/plugins/tiddlywiki/multiwikiserver/templates/error", { - variables: { - "error-message": "User not found" - } - }); - response.write(errorHtml); - response.end(); - return; - } - - // Check if the user is trying to access their own profile or is an admin - var hasPermission = ($tw.utils.parseInt(user_id, 10) === state.authenticatedUser.user_id) || state.authenticatedUser.isAdmin; - if(!hasPermission) { - response.writeHead(403, "Forbidden", { "Content-Type": "text/plain" }); - response.end("Forbidden"); - return; - } +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; - // Convert dates to strings and ensure all necessary fields are present - var user = { - user_id: userData.user_id || "", - username: userData.username || "", - email: userData.email || "", - created_at: userData.created_at ? new Date(userData.created_at).toISOString() : "", - last_login: userData.last_login ? new Date(userData.last_login).toISOString() : "" - }; - - // Get all roles which the user has been assigned - var userRole = state.server.sqlTiddlerDatabase.getUserRoles(user_id); - var allRoles = state.server.sqlTiddlerDatabase.listRoles(); +exports.method = "GET"; - // sort allRoles by placing the user's role at the top of the list - allRoles.sort(function(a, b){ (a.role_id === userRole.role_id ? -1 : 1) }); - - response.writeHead(200, "OK", { - "Content-Type": "text/html" - }); - - // Render the html - var html = $tw.mws.store.adminWiki.renderTiddler("text/plain", "$:/plugins/tiddlywiki/multiwikiserver/templates/page", { +exports.path = /^\/admin\/users\/([^\/]+)\/?$/; + +exports.handler = function(request,response,state) { + var user_id = $tw.utils.decodeURIComponentSafe(state.params[0]); + var userData = state.server.sqlTiddlerDatabase.getUser(user_id); + + // Clean up any existing error/success messages if the user_id is different from the "$:/temp/mws/user-info/preview-user-id" + var lastPreviewedUser = $tw.wiki.getTiddlerText("$:/temp/mws/user-info/" + user_id + "/preview-user-id"); + + if(user_id !== lastPreviewedUser) { + $tw.mws.store.adminWiki.deleteTiddler("$:/temp/mws/change-password/" + user_id + "/ error"); + $tw.mws.store.adminWiki.deleteTiddler("$:/temp/mws/change-password/" + user_id + "/success"); + $tw.mws.store.adminWiki.deleteTiddler("$:/temp/mws/login/error"); + $tw.mws.store.adminWiki.deleteTiddler("$:/temp/mws/delete-user/" + user_id + "/error"); + $tw.mws.store.adminWiki.deleteTiddler("$:/temp/mws/delete-user/" + user_id + "/success"); + $tw.mws.store.adminWiki.deleteTiddler("$:/temp/mws/update-profile/" + user_id + "/error"); + $tw.mws.store.adminWiki.deleteTiddler("$:/temp/mws/update-profile/" + user_id + "/success"); + } + + if(!userData) { + response.writeHead(404, "Not Found", {"Content-Type": "text/html"}); + var errorHtml = $tw.mws.store.adminWiki.renderTiddler("text/plain", "$:/plugins/tiddlywiki/multiwikiserver/templates/error", { variables: { - "page-content": "$:/plugins/tiddlywiki/multiwikiserver/templates/manage-user", - "user": JSON.stringify(user), - "user-initials": user.username.split(" ").map(name => name[0]).join(""), - "user-role": JSON.stringify(userRole), - "all-roles": JSON.stringify(allRoles), - "is-current-user-profile": state.authenticatedUser && state.authenticatedUser.user_id === $tw.utils.parseInt(user_id, 10) ? "yes" : "no", - "username": state.authenticatedUser ? state.authenticatedUser.username : "Guest", - "user-is-admin": state.authenticatedUser && state.authenticatedUser.isAdmin ? "yes" : "no" + "error-message": "User not found" } }); - response.write(html); + response.write(errorHtml); response.end(); + return; + } + + // Check if the user is trying to access their own profile or is an admin + var hasPermission = ($tw.utils.parseInt(user_id) === state.authenticatedUser.user_id) || state.authenticatedUser.isAdmin; + if(!hasPermission) { + response.writeHead(403, "Forbidden", { "Content-Type": "text/plain" }); + response.end("Forbidden"); + return; + } + + // Convert dates to strings and ensure all necessary fields are present + var user = { + user_id: userData.user_id || "", + username: userData.username || "", + email: userData.email || "", + created_at: userData.created_at ? new Date(userData.created_at).toISOString() : "", + last_login: userData.last_login ? new Date(userData.last_login).toISOString() : "" }; - - }()); \ No newline at end of file + + // Get all roles which the user has been assigned + var userRole = state.server.sqlTiddlerDatabase.getUserRoles(user_id); + var allRoles = state.server.sqlTiddlerDatabase.listRoles(); + + // sort allRoles by placing the user's role at the top of the list + allRoles.sort(function(a, b){ return (a.role_id === userRole?.role_id ? -1 : 1) }); + + $tw.mws.store.adminWiki.addTiddler(new $tw.Tiddler({ + title: "$:/temp/mws/user-info/" + user_id + "/preview-user-id", + text: user_id + })); + + response.writeHead(200, "OK", { + "Content-Type": "text/html" + }); + + // Render the html + var html = $tw.mws.store.adminWiki.renderTiddler("text/plain", "$:/plugins/tiddlywiki/multiwikiserver/templates/page", { + variables: { + "page-content": "$:/plugins/tiddlywiki/multiwikiserver/templates/manage-user", + "user": JSON.stringify(user), + "user-initials": user.username.split(" ").map(name => name[0]).join(""), + "user-role": JSON.stringify(userRole), + "all-roles": JSON.stringify(allRoles), + "is-current-user-profile": state.authenticatedUser && state.authenticatedUser.user_id === $tw.utils.parseInt(user_id, 10) ? "yes" : "no", + "username": state.authenticatedUser ? state.authenticatedUser.username : state.firstGuestUser ? "Annonymous User" : "Guest", + "user-is-admin": state.authenticatedUser && state.authenticatedUser.isAdmin ? "yes" : "no", + "user-id": user_id, + } + }); + response.write(html); + response.end(); +}; + +}()); \ No newline at end of file diff --git a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-acl.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-acl.js index ae772360e..45982b079 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-acl.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-acl.js @@ -8,57 +8,56 @@ POST /admin/post-acl \*/ (function () { - /*jslint node: true, browser: true */ - /*global $tw: false */ - "use strict"; +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; - exports.method = "POST"; +exports.method = "POST"; - exports.path = /^\/admin\/post-acl\/?$/; +exports.path = /^\/admin\/post-acl\/?$/; +exports.bodyFormat = "www-form-urlencoded"; - exports.bodyFormat = "www-form-urlencoded"; +exports.csrfDisable = true; - exports.csrfDisable = true; +exports.handler = function (request, response, state) { + var sqlTiddlerDatabase = state.server.sqlTiddlerDatabase; + var entity_type = state.data.entity_type; + var recipe_name = state.data.recipe_name; + var bag_name = state.data.bag_name; + var role_id = state.data.role_id; + var permission_id = state.data.permission_id; + var isRecipe = entity_type === "recipe" - exports.handler = function (request, response, state) { - var sqlTiddlerDatabase = state.server.sqlTiddlerDatabase; - var entity_type = state.data.entity_type; - var recipe_name = state.data.recipe_name; - var bag_name = state.data.bag_name; - var role_id = state.data.role_id; - var permission_id = state.data.permission_id; - var isRecipe = entity_type === "recipe" + var entityAclRecords = sqlTiddlerDatabase.getACLByName(entity_type, isRecipe ? recipe_name : bag_name, true); - var entityAclRecords = sqlTiddlerDatabase.getACLByName(entity_type, isRecipe ? recipe_name : bag_name, true); + var aclExists = entityAclRecords.some((record) => ( + record.role_id == role_id && record.permission_id == permission_id + )) - var aclExists = entityAclRecords.some((record) => ( - record.role_id == role_id && record.permission_id == permission_id - )) - - // This ensures that the user attempting to modify the ACL has permission to do so - // if(!state.authenticatedUser || (entityAclRecords.length > 0 && !sqlTiddlerDatabase[isRecipe ? 'hasRecipePermission' : 'hasBagPermission'](state.authenticatedUser.user_id, isRecipe ? recipe_name : bag_name, 'WRITE'))){ - // response.writeHead(403, "Forbidden"); - // response.end(); - // return - // } - - if (aclExists) { - // do nothing, return the user back to the form - response.writeHead(302, { "Location": "/admin/acl/" + recipe_name + "/" + bag_name }); - response.end(); - return - } - - sqlTiddlerDatabase.createACL( - isRecipe ? recipe_name : bag_name, - entity_type, - role_id, - permission_id - ) + // This ensures that the user attempting to modify the ACL has permission to do so + // if(!state.authenticatedUser || (entityAclRecords.length > 0 && !sqlTiddlerDatabase[isRecipe ? 'hasRecipePermission' : 'hasBagPermission'](state.authenticatedUser.user_id, isRecipe ? recipe_name : bag_name, 'WRITE'))){ + // response.writeHead(403, "Forbidden"); + // response.end(); + // return + // } + if (aclExists) { + // do nothing, return the user back to the form response.writeHead(302, { "Location": "/admin/acl/" + recipe_name + "/" + bag_name }); response.end(); - }; + return + } + + sqlTiddlerDatabase.createACL( + isRecipe ? recipe_name : bag_name, + entity_type, + role_id, + permission_id + ) + + response.writeHead(302, { "Location": "/admin/acl/" + recipe_name + "/" + bag_name }); + response.end(); +}; }()); \ No newline at end of file diff --git a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-role.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-role.js index af62ec62b..c2c693131 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-role.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-role.js @@ -8,29 +8,29 @@ POST /admin/post-role \*/ (function () { - /*jslint node: true, browser: true */ - /*global $tw: false */ - "use strict"; +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; - exports.method = "POST"; +exports.method = "POST"; - exports.path = /^\/admin\/post-role\/?$/; +exports.path = /^\/admin\/post-role\/?$/; - exports.bodyFormat = "www-form-urlencoded"; +exports.bodyFormat = "www-form-urlencoded"; - exports.csrfDisable = true; +exports.csrfDisable = true; - exports.handler = function (request, response, state) { - var sqlTiddlerDatabase = state.server.sqlTiddlerDatabase; - var role_name = state.data.role_name; - var role_description = state.data.role_description; +exports.handler = function (request, response, state) { + var sqlTiddlerDatabase = state.server.sqlTiddlerDatabase; + var role_name = state.data.role_name; + var role_description = state.data.role_description; - // Add your authentication check here if needed + // Add your authentication check here if needed - sqlTiddlerDatabase.createRole(role_name, role_description); + sqlTiddlerDatabase.createRole(role_name, role_description); - response.writeHead(302, { "Location": "/admin/roles" }); - response.end(); - }; + response.writeHead(302, { "Location": "/admin/roles" }); + response.end(); +}; }()); \ No newline at end of file diff --git a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-user.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-user.js index e11f71742..df7f347f6 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-user.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-user.js @@ -8,56 +8,79 @@ POST /admin/post-user \*/ (function() { - /*jslint node: true, browser: true */ - /*global $tw: false */ - "use strict"; +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; +if($tw.node) { + var crypto = require("crypto"); +} +exports.method = "POST"; - exports.method = "POST"; +exports.path = /^\/admin\/post-user\/?$/; - exports.path = /^\/admin\/post-user\/?$/; +exports.bodyFormat = "www-form-urlencoded"; - exports.bodyFormat = "www-form-urlencoded"; +exports.csrfDisable = true; - exports.csrfDisable = true; +exports.handler = function(request, response, state) { + var sqlTiddlerDatabase = state.server.sqlTiddlerDatabase; + var username = state.data.username; + var email = state.data.email; + var password = state.data.password; + var confirmPassword = state.data.confirmPassword; - exports.handler = function(request, response, state) { - var sqlTiddlerDatabase = state.server.sqlTiddlerDatabase; - var username = state.data.username; - var email = state.data.email; - var password = state.data.password; - var confirmPassword = state.data.confirmPassword; + if(!state.authenticatedUser && !state.firstGuestUser) { + response.writeHead(401, "Unauthorized", { "Content-Type": "text/plain" }); + response.end("Unauthorized"); + return; + } - if(!state.authenticatedUser) { - response.writeHead(401, "Unauthorized", { "Content-Type": "text/plain" }); - response.end("Unauthorized"); - return; - } + if(!username || !email || !password || !confirmPassword) { + response.writeHead(400, {"Content-Type": "application/json"}); + response.end(JSON.stringify({error: "All fields are required"})); + return; + } - if(!username || !email || !password || !confirmPassword) { - response.writeHead(400, {"Content-Type": "application/json"}); - response.end(JSON.stringify({error: "All fields are required"})); - return; - } + if(password !== confirmPassword) { + response.writeHead(400, {"Content-Type": "application/json"}); + response.end(JSON.stringify({error: "Passwords do not match"})); + return; + } - if(password !== confirmPassword) { - response.writeHead(400, {"Content-Type": "application/json"}); - response.end(JSON.stringify({error: "Passwords do not match"})); - return; - } + // Check if user already exists + var existingUser = sqlTiddlerDatabase.getUser(username); + if(existingUser) { + response.writeHead(400, {"Content-Type": "application/json"}); + response.end(JSON.stringify({error: "Username already exists"})); + return; + } - // Check if user already exists - var existingUser = sqlTiddlerDatabase.getUser(username); - if(existingUser) { - response.writeHead(400, {"Content-Type": "application/json"}); - response.end(JSON.stringify({error: "Username already exists"})); - return; - } + var hasUsers = sqlTiddlerDatabase.listUsers().length > 0; + var hashedPassword = crypto.createHash("sha256").update(password).digest("hex"); - // Create new user - var userId = sqlTiddlerDatabase.createUser(username, email, password); + // Create new user + var userId = sqlTiddlerDatabase.createUser(username, email, hashedPassword); + if(!hasUsers) { + // If this is the first guest user, assign admin privileges + sqlTiddlerDatabase.setUserAdmin(userId, true); + + // Create a session for the new admin user + var auth = require('$:/plugins/tiddlywiki/multiwikiserver/auth/authentication.js').Authenticator; + var authenticator = auth(sqlTiddlerDatabase); + var sessionId = authenticator.createSession(userId); + + // Set the session cookie and redirect + response.setHeader('Set-Cookie', `session=${sessionId}; HttpOnly; Path=/`); + response.writeHead(302, { + 'Location': '/' + }); + response.end(); + return; + } else { response.writeHead(302, {"Location": "/admin/users/"+userId}); response.end(); - }; + } +}; }()); \ No newline at end of file diff --git a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/update-user-profile.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/update-user-profile.js index 95223db4a..3cbc06690 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/update-user-profile.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/update-user-profile.js @@ -22,8 +22,12 @@ exports.csrfDisable = true; exports.handler = function (request,response,state) { if(!state.authenticatedUser) { - response.writeHead(401, "Unauthorized", { "Content-Type": "text/plain" }); - response.end("Unauthorized"); + $tw.mws.store.adminWiki.addTiddler(new $tw.Tiddler({ + title: "$:/temp/mws/login/error", + text: "You must be logged in to update profiles" + })); + response.writeHead(302, { "Location": "/login" }); + response.end(); return; } @@ -32,29 +36,39 @@ exports.handler = function (request,response,state) { var email = state.data.email; var roleId = state.data.role; var currentUserId = state.authenticatedUser.user_id; - - var hasPermission = ($tw.utils.parseInt(userId, 10) === currentUserId) || state.authenticatedUser.isAdmin; + + var hasPermission = ($tw.utils.parseInt(userId) === currentUserId) || state.authenticatedUser.isAdmin; if(!hasPermission) { - response.writeHead(403, "Forbidden", { "Content-Type": "text/plain" }); - response.end("Forbidden"); + $tw.mws.store.adminWiki.addTiddler(new $tw.Tiddler({ + title: "$:/temp/mws/update-profile/" + userId + "/error", + text: "You don't have permission to update this profile" + })); + response.writeHead(302, { "Location": "/admin/users/" + userId }); + response.end(); return; } if(!state.authenticatedUser.isAdmin) { - var userRole = state.server.sqlTiddlerDatabase.getUserRoles(userId); + var userRole = state.server.sqlTiddlerDatabase.getUserRoles(userId); roleId = userRole.role_id; } var result = state.server.sqlTiddlerDatabase.updateUser(userId, username, email, roleId); if(result.success) { - response.setHeader("Set-Cookie", "flashMessage="+result.message+"; Path=/; HttpOnly; Max-Age=5"); - response.writeHead(302, { "Location": "/admin/users/" + userId }); + $tw.mws.store.adminWiki.addTiddler(new $tw.Tiddler({ + title: "$:/temp/mws/update-profile/" + userId + "/success", + text: result.message + })); } else { - response.setHeader("Set-Cookie", "flashMessage="+result.message+"; Path=/; HttpOnly; Max-Age=5"); - response.writeHead(302, { "Location": "/admin/users/" + userId }); + $tw.mws.store.adminWiki.addTiddler(new $tw.Tiddler({ + title: "$:/temp/mws/update-profile/" + userId + "/error", + text: result.message + })); } + + response.writeHead(302, { "Location": "/admin/users/" + userId }); response.end(); }; diff --git a/plugins/tiddlywiki/multiwikiserver/modules/routes/helpers/acl-middleware.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/helpers/acl-middleware.js index 582c5e93c..0ea93de00 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/routes/helpers/acl-middleware.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/helpers/acl-middleware.js @@ -42,7 +42,7 @@ exports.middleware = function (request, response, state, entityType, permissionN entityName = state.data ? (state.data[entityType+"_name"] || state.params[0]) : state.params[0]; // First, replace '%3A' with ':' to handle TiddlyWiki's system tiddlers - var partiallyDecoded = entityName.replace(/%3A/g, ":"); + var partiallyDecoded = entityName?.replace(/%3A/g, ":"); // Then use decodeURIComponent for the rest var decodedEntityName = decodeURIComponent(partiallyDecoded); var aclRecord = sqlTiddlerDatabase.getACLByName(entityType, decodedEntityName); diff --git a/plugins/tiddlywiki/multiwikiserver/modules/store/sql-tiddler-database.js b/plugins/tiddlywiki/multiwikiserver/modules/store/sql-tiddler-database.js index 09adb3ebf..a2ebda9fb 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/store/sql-tiddler-database.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/store/sql-tiddler-database.js @@ -224,14 +224,6 @@ SqlTiddlerDatabase.prototype.createBag = function(bag_name,description,accesscon $accesscontrol: accesscontrol, $description: description }); - - const admin = this.getRoleByName("ADMIN"); - if(admin) { - const readPermission = this.getPermissionByName("READ"); - const writePermission = this.getPermissionByName("WRITE"); - // this.createACL(bag_name, "bag", admin.role_id, readPermission.permission_id); - // this.createACL(bag_name, "bag", admin.role_id, writePermission.permission_id); - } return updateBags.lastInsertRowid; }; @@ -296,15 +288,6 @@ SqlTiddlerDatabase.prototype.createRecipe = function(recipe_name,bag_names,descr $bag_names: JSON.stringify(bag_names) }); - - // update the permissions on ACL records - const admin = this.getRoleByName("ADMIN"); - if(admin) { - const readPermission = this.getPermissionByName("READ"); - const writePermission = this.getPermissionByName("WRITE"); - // this.createACL(recipe_name, "recipe", admin.role_id, readPermission.permission_id); - // this.createACL(recipe_name, "recipe", admin.role_id, writePermission.permission_id); - } return updateRecipes.lastInsertRowid; }; @@ -825,6 +808,18 @@ SqlTiddlerDatabase.prototype.getUserByUsername = function(username) { }); }; +SqlTiddlerDatabase.prototype.listUsersByRoleId = function(roleId) { + return this.engine.runStatementGetAll(` + SELECT u.* + FROM users u + JOIN user_roles ur ON u.user_id = ur.user_id + WHERE ur.role_id = $roleId + ORDER BY u.username + `, { + $roleId: roleId + }); +}; + SqlTiddlerDatabase.prototype.updateUser = function (userId, username, email, roleId) { const existingUser = this.engine.runStatement(` SELECT user_id FROM users @@ -1018,6 +1013,14 @@ SqlTiddlerDatabase.prototype.deleteUserSessions = function(userId) { }); }; +// Set the user as an admin +SqlTiddlerDatabase.prototype.setUserAdmin = function(userId) { + var admin = this.getRoleByName("ADMIN"); + if(admin) { + this.addRoleToUser(userId, admin.role_id); + } +}; + // Group CRUD operations SqlTiddlerDatabase.prototype.createGroup = function(groupName, description) { const result = this.engine.runStatement(` diff --git a/plugins/tiddlywiki/multiwikiserver/templates/get-index.tid b/plugins/tiddlywiki/multiwikiserver/templates/get-index.tid index 897e54017..54bca73f6 100644 --- a/plugins/tiddlywiki/multiwikiserver/templates/get-index.tid +++ b/plugins/tiddlywiki/multiwikiserver/templates/get-index.tid @@ -27,6 +27,20 @@ title: $:/plugins/tiddlywiki/multiwikiserver/templates/get-index +<$list filter="[match[yes]]"> +
+
+
⚠️
+
+ Warning: TiddlyWiki is currently running in anonymous access mode which allows anyone with access to the server to read and modify data. +
+ +
+
+ +