diff --git a/plugins/tiddlywiki/multiwikiserver/modules/mws-server.js b/plugins/tiddlywiki/multiwikiserver/modules/mws-server.js
index be46a53d2d3..febcd97d781 100644
--- a/plugins/tiddlywiki/multiwikiserver/modules/mws-server.js
+++ b/plugins/tiddlywiki/multiwikiserver/modules/mws-server.js
@@ -414,11 +414,13 @@ Server.prototype.requestAuthentication = function(response) {
Server.prototype.getAnonymousAccessConfig = function() {
const allowReadsTiddler = this.wiki.getTiddlerText("$:/config/MultiWikiServer/AllowAnonymousReads", "undefined");
const allowWritesTiddler = this.wiki.getTiddlerText("$:/config/MultiWikiServer/AllowAnonymousWrites", "undefined");
+ const showAnonymousAccessModal = this.wiki.getTiddlerText("$:/config/MultiWikiServer/ShowAnonymousAccessModal", "undefined");
return {
allowReads: allowReadsTiddler === "yes",
allowWrites: allowWritesTiddler === "yes",
- isEnabled: allowReadsTiddler !== "undefined" && allowWritesTiddler !== "undefined"
+ isEnabled: allowReadsTiddler !== "undefined" && allowWritesTiddler !== "undefined",
+ showAnonConfig: showAnonymousAccessModal === "yes"
};
}
@@ -452,11 +454,12 @@ Server.prototype.requestHandler = function(request,response,options) {
// Check whether anonymous access is granted
state.allowAnon = false; //this.isAuthorized(state.authorizationType,null);
- var {allowReads, allowWrites, isEnabled} = this.getAnonymousAccessConfig();
+ var {allowReads, allowWrites, isEnabled, showAnonConfig} = this.getAnonymousAccessConfig();
+ state.anonAccessConfigured = isEnabled;
state.allowAnon = isEnabled && (request.method === 'GET' ? allowReads : allowWrites);
state.allowAnonReads = allowReads;
state.allowAnonWrites = allowWrites;
- state.showAnonConfig = !!state.authenticatedUser?.isAdmin && !isEnabled;
+ state.showAnonConfig = !!state.authenticatedUser?.isAdmin && showAnonConfig;
state.firstGuestUser = this.sqlTiddlerDatabase.listUsers().length === 0 && !state.authenticatedUser;
// Authorize with the authenticated username
diff --git a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-recipe-tiddler.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-recipe-tiddler.js
index 037f7489daf..a50657ce58a 100644
--- a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-recipe-tiddler.js
+++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-recipe-tiddler.js
@@ -20,7 +20,7 @@ exports.method = "GET";
exports.path = /^\/recipes\/([^\/]+)\/tiddlers\/(.+)$/;
-exports.useACL = true;
+// exports.useACL = true;
exports.entityName = "recipe"
diff --git a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-acl.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-acl.js
index 45982b07997..63a9f414f1b 100644
--- a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-acl.js
+++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-acl.js
@@ -29,35 +29,40 @@ exports.handler = function (request, response, state) {
var permission_id = state.data.permission_id;
var isRecipe = entity_type === "recipe"
- var entityAclRecords = sqlTiddlerDatabase.getACLByName(entity_type, isRecipe ? recipe_name : bag_name, true);
+ try {
+ 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
- // }
+ // 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
+ 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();
+ } catch (error) {
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-anon-config.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-anon-config.js
index c20806540c1..e1e841516c4 100644
--- a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-anon-config.js
+++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-anon-config.js
@@ -42,6 +42,10 @@ exports.handler = function(request, response, state) {
text: allowWrites ? "yes" : "no"
});
+ wiki.addTiddler({
+ title: "$:/config/MultiWikiServer/ShowAnonymousAccessModal",
+ text: "no"
+ });
// Redirect back to admin page
response.writeHead(302, {"Location": "/"});
response.end();
diff --git a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-anon.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-anon.js
index 288a81bc779..911b6ef971c 100644
--- a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-anon.js
+++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-anon.js
@@ -32,12 +32,8 @@ exports.handler = function(request, response, state) {
// Update the configuration tiddlers
var wiki = $tw.wiki;
wiki.addTiddler({
- title: "$:/config/MultiWikiServer/AllowAnonymousReads",
- text: "undefined"
- });
- wiki.addTiddler({
- title: "$:/config/MultiWikiServer/AllowAnonymousWrites",
- text: "undefined"
+ title: "$:/config/MultiWikiServer/ShowAnonymousAccessModal",
+ text: "yes"
});
// Redirect back to admin page
diff --git a/plugins/tiddlywiki/multiwikiserver/modules/routes/helpers/acl-middleware.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/helpers/acl-middleware.js
index adbdcff7a02..6b9095ac467 100644
--- a/plugins/tiddlywiki/multiwikiserver/modules/routes/helpers/acl-middleware.js
+++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/helpers/acl-middleware.js
@@ -36,9 +36,10 @@ function redirectToLogin(response, returnUrl) {
};
exports.middleware = function (request, response, state, entityType, permissionName) {
+ var extensionRegex = /\.[A-Za-z0-9]{1,4}$/;
var server = state.server,
- sqlTiddlerDatabase = server.sqlTiddlerDatabase,
+ sqlTiddlerDatabase = $tw.mws.store.sqlTiddlerDatabase || server.sqlTiddlerDatabase,
entityName = state.data ? (state.data[entityType+"_name"] || state.params[0]) : state.params[0];
// First, replace '%3A' with ':' to handle TiddlyWiki's system tiddlers
@@ -48,10 +49,15 @@ exports.middleware = function (request, response, state, entityType, permissionN
var aclRecord = sqlTiddlerDatabase.getACLByName(entityType, decodedEntityName);
var isGetRequest = request.method === "GET";
var hasAnonymousAccess = state.allowAnon ? (isGetRequest ? state.allowAnonReads : state.allowAnonWrites) : false;
+ var anonymousAccessConfigured = state.anonAccessConfigured;
var entity = sqlTiddlerDatabase.getEntityByName(entityType, decodedEntityName);
if(entity?.owner_id) {
if(state.authenticatedUser?.user_id && (state.authenticatedUser?.user_id !== entity.owner_id) || !state.authenticatedUser?.user_id && !hasAnonymousAccess) {
- if(!response.headersSent) {
+ const hasPermission = state.authenticatedUser?.user_id ?
+ entityType === 'recipe' ? sqlTiddlerDatabase.hasRecipePermission(state.authenticatedUser?.user_id, decodedEntityName, isGetRequest ? 'READ' : 'WRITE')
+ : sqlTiddlerDatabase.hasBagPermission(state.authenticatedUser?.user_id, decodedEntityName, isGetRequest ? 'READ' : 'WRITE')
+ : false
+ if(!response.headersSent && !hasPermission) {
response.writeHead(403, "Forbidden");
response.end();
}
@@ -59,8 +65,8 @@ exports.middleware = function (request, response, state, entityType, permissionN
}
} else {
// First, we need to check if anonymous access is allowed
- if(!state.authenticatedUser?.user_id && !hasAnonymousAccess) {
- if(!response.headersSent) {
+ if(!state.authenticatedUser?.user_id && (anonymousAccessConfigured && !hasAnonymousAccess)) {
+ if(!response.headersSent && !extensionRegex.test(request.url)) {
response.writeHead(401, "Unauthorized");
response.end();
}
@@ -80,7 +86,7 @@ exports.middleware = function (request, response, state, entityType, permissionN
}
// Check ACL permission
- var hasPermission = request.method === "POST" || sqlTiddlerDatabase.checkACLPermission(state.authenticatedUser.user_id, entityType, decodedEntityName, permissionName)
+ var hasPermission = request.method === "POST" || sqlTiddlerDatabase.checkACLPermission(state.authenticatedUser.user_id, entityType, decodedEntityName, permissionName, entity?.owner_id)
if(!hasPermission && !hasAnonymousAccess) {
if(!response.headersSent) {
response.writeHead(403, "Forbidden");
diff --git a/plugins/tiddlywiki/multiwikiserver/modules/store/sql-tiddler-database.js b/plugins/tiddlywiki/multiwikiserver/modules/store/sql-tiddler-database.js
index df68b08df57..c4be2b66dde 100644
--- a/plugins/tiddlywiki/multiwikiserver/modules/store/sql-tiddler-database.js
+++ b/plugins/tiddlywiki/multiwikiserver/modules/store/sql-tiddler-database.js
@@ -500,19 +500,27 @@ SqlTiddlerDatabase.prototype.getRecipeTiddler = function(title,recipe_name) {
Checks if a user has permission to access a recipe
*/
SqlTiddlerDatabase.prototype.hasRecipePermission = function(userId, recipeName, permissionName) {
- // check if the user is the owner of the entity
- const recipe = this.engine.runStatementGet(`
- SELECT owner_id
- FROM recipes
- WHERE recipe_name = $recipe_name
- `, {
- $recipe_name: recipeName
- });
+ try {
+ // check if the user is the owner of the entity
+ const recipe = this.engine.runStatementGet(`
+ SELECT owner_id
+ FROM recipes
+ WHERE recipe_name = $recipe_name
+ `, {
+ $recipe_name: recipeName
+ });
- if(recipe?.owner_id) {
- return recipe.owner_id === userId;
+ if(!!recipe?.owner_id && recipe?.owner_id === userId) {
+ return true;
+ } else {
+ var permission = this.checkACLPermission(userId, "recipe", recipeName, permissionName, recipe?.owner_id)
+ return permission;
+ }
+
+ } catch (error) {
+ console.error(error)
+ return false
}
- return this.checkACLPermission(userId, "recipe", recipeName, permissionName)
};
/*
@@ -530,10 +538,11 @@ SqlTiddlerDatabase.prototype.getACLByName = function(entityType, entityName, fet
// First, check if there's an ACL record for the entity and get the permission_id
var checkACLExistsQuery = `
- SELECT *
+ SELECT acl.*, permissions.permission_name
FROM acl
- WHERE entity_type = $entity_type
- AND entity_name = $entity_name
+ LEFT JOIN permissions ON acl.permission_id = permissions.permission_id
+ WHERE acl.entity_type = $entity_type
+ AND acl.entity_name = $entity_name
`;
if (!fetchAll) {
@@ -548,43 +557,50 @@ SqlTiddlerDatabase.prototype.getACLByName = function(entityType, entityName, fet
return aclRecord;
}
-SqlTiddlerDatabase.prototype.checkACLPermission = function(userId, entityType, entityName) {
- // if the entityName starts with "$:/", we'll assume its a system bag/recipe, then grant the user permission
- if(entityName.startsWith("$:/")) {
- return true;
- }
+SqlTiddlerDatabase.prototype.checkACLPermission = function(userId, entityType, entityName, permissionName, ownerId) {
+ try {
+ // if the entityName starts with "$:/", we'll assume its a system bag/recipe, then grant the user permission
+ if(entityName.startsWith("$:/")) {
+ return true;
+ }
- const aclRecord = this.getACLByName(entityType, entityName);
+ const aclRecords = this.getACLByName(entityType, entityName, true);
+ const aclRecord = aclRecords.find(record => record.permission_name === permissionName);
- // If no ACL record exists, return true for hasPermission
- if (!aclRecord) {
- return true;
- }
+ // If no ACL record exists, return true for hasPermission
+ if ((!aclRecord && !ownerId) || ((!!aclRecord && !!ownerId) && ownerId === userId)) {
+ return true;
+ }
- // If ACL record exists, check for user permission using the retrieved permission_id
- const checkPermissionQuery = `
- SELECT 1
- FROM users u
- JOIN user_roles ur ON u.user_id = ur.user_id
- JOIN roles r ON ur.role_id = r.role_id
- JOIN acl a ON r.role_id = a.role_id
- WHERE u.user_id = $user_id
- AND a.entity_type = $entity_type
- AND a.entity_name = $entity_name
- AND a.permission_id = $permission_id
- LIMIT 1
- `;
+ // If ACL record exists, check for user permission using the retrieved permission_id
+ const checkPermissionQuery = `
+ SELECT *
+ FROM users u
+ JOIN user_roles ur ON u.user_id = ur.user_id
+ JOIN roles r ON ur.role_id = r.role_id
+ JOIN acl a ON r.role_id = a.role_id
+ WHERE u.user_id = $user_id
+ AND a.entity_type = $entity_type
+ AND a.entity_name = $entity_name
+ AND a.permission_id = $permission_id
+ LIMIT 1
+ `;
- const result = this.engine.runStatementGet(checkPermissionQuery, {
- $user_id: userId,
- $entity_type: entityType,
- $entity_name: entityName,
- $permission_id: aclRecord.permission_id
- });
-
- let hasPermission = result !== undefined;
+ const result = this.engine.runStatementGet(checkPermissionQuery, {
+ $user_id: userId,
+ $entity_type: entityType,
+ $entity_name: entityName,
+ $permission_id: aclRecord?.permission_id
+ });
+
+ let hasPermission = result !== undefined;
- return hasPermission;
+ return hasPermission;
+
+ } catch (error) {
+ console.error(error);
+ return false
+ }
};
/**
diff --git a/plugins/tiddlywiki/multiwikiserver/templates/manage-acl.tid b/plugins/tiddlywiki/multiwikiserver/templates/manage-acl.tid
index 024f5a3a781..696e02f1e3e 100644
--- a/plugins/tiddlywiki/multiwikiserver/templates/manage-acl.tid
+++ b/plugins/tiddlywiki/multiwikiserver/templates/manage-acl.tid
@@ -16,7 +16,7 @@ title: $:/plugins/tiddlywiki/multiwikiserver/templates/manage-acl
jsonget[recipe_name]] }}}/>
jsonget[bag_name]] }}}/>
-