mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2025-01-09 17:00:27 +00:00
parent
1c5648826e
commit
0198eb2a1a
@ -415,11 +415,13 @@ Server.prototype.requestAuthentication = function(response) {
|
|||||||
Server.prototype.getAnonymousAccessConfig = function() {
|
Server.prototype.getAnonymousAccessConfig = function() {
|
||||||
const allowReadsTiddler = this.wiki.getTiddlerText("$:/config/MultiWikiServer/AllowAnonymousReads", "undefined");
|
const allowReadsTiddler = this.wiki.getTiddlerText("$:/config/MultiWikiServer/AllowAnonymousReads", "undefined");
|
||||||
const allowWritesTiddler = this.wiki.getTiddlerText("$:/config/MultiWikiServer/AllowAnonymousWrites", "undefined");
|
const allowWritesTiddler = this.wiki.getTiddlerText("$:/config/MultiWikiServer/AllowAnonymousWrites", "undefined");
|
||||||
|
const showAnonymousAccessModal = this.wiki.getTiddlerText("$:/config/MultiWikiServer/ShowAnonymousAccessModal", "undefined");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
allowReads: allowReadsTiddler === "yes",
|
allowReads: allowReadsTiddler === "yes",
|
||||||
allowWrites: allowWritesTiddler === "yes",
|
allowWrites: allowWritesTiddler === "yes",
|
||||||
isEnabled: allowReadsTiddler !== "undefined" && allowWritesTiddler !== "undefined"
|
isEnabled: allowReadsTiddler !== "undefined" && allowWritesTiddler !== "undefined",
|
||||||
|
showAnonConfig: showAnonymousAccessModal === "yes"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -453,11 +455,12 @@ Server.prototype.requestHandler = function(request,response,options) {
|
|||||||
|
|
||||||
// Check whether anonymous access is granted
|
// Check whether anonymous access is granted
|
||||||
state.allowAnon = false; //this.isAuthorized(state.authorizationType,null);
|
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.allowAnon = isEnabled && (request.method === 'GET' ? allowReads : allowWrites);
|
||||||
state.allowAnonReads = allowReads;
|
state.allowAnonReads = allowReads;
|
||||||
state.allowAnonWrites = allowWrites;
|
state.allowAnonWrites = allowWrites;
|
||||||
state.showAnonConfig = !!state.authenticatedUser?.isAdmin && !isEnabled;
|
state.showAnonConfig = !!state.authenticatedUser?.isAdmin && showAnonConfig;
|
||||||
state.firstGuestUser = this.sqlTiddlerDatabase.listUsers().length === 0 && !state.authenticatedUser;
|
state.firstGuestUser = this.sqlTiddlerDatabase.listUsers().length === 0 && !state.authenticatedUser;
|
||||||
|
|
||||||
// Authorize with the authenticated username
|
// Authorize with the authenticated username
|
||||||
|
@ -20,7 +20,7 @@ exports.method = "GET";
|
|||||||
|
|
||||||
exports.path = /^\/recipes\/([^\/]+)\/tiddlers\/(.+)$/;
|
exports.path = /^\/recipes\/([^\/]+)\/tiddlers\/(.+)$/;
|
||||||
|
|
||||||
exports.useACL = true;
|
// exports.useACL = true;
|
||||||
|
|
||||||
exports.entityName = "recipe"
|
exports.entityName = "recipe"
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ exports.handler = function (request, response, state) {
|
|||||||
var permission_id = state.data.permission_id;
|
var permission_id = state.data.permission_id;
|
||||||
var isRecipe = entity_type === "recipe"
|
var isRecipe = entity_type === "recipe"
|
||||||
|
|
||||||
|
try {
|
||||||
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) => (
|
var aclExists = entityAclRecords.some((record) => (
|
||||||
@ -58,6 +59,10 @@ exports.handler = function (request, response, state) {
|
|||||||
|
|
||||||
response.writeHead(302, { "Location": "/admin/acl/" + recipe_name + "/" + bag_name });
|
response.writeHead(302, { "Location": "/admin/acl/" + recipe_name + "/" + bag_name });
|
||||||
response.end();
|
response.end();
|
||||||
|
} catch (error) {
|
||||||
|
response.writeHead(302, { "Location": "/admin/acl/" + recipe_name + "/" + bag_name });
|
||||||
|
response.end();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}());
|
}());
|
@ -42,6 +42,10 @@ exports.handler = function(request, response, state) {
|
|||||||
text: allowWrites ? "yes" : "no"
|
text: allowWrites ? "yes" : "no"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
wiki.addTiddler({
|
||||||
|
title: "$:/config/MultiWikiServer/ShowAnonymousAccessModal",
|
||||||
|
text: "no"
|
||||||
|
});
|
||||||
// Redirect back to admin page
|
// Redirect back to admin page
|
||||||
response.writeHead(302, {"Location": "/"});
|
response.writeHead(302, {"Location": "/"});
|
||||||
response.end();
|
response.end();
|
||||||
|
@ -32,12 +32,8 @@ exports.handler = function(request, response, state) {
|
|||||||
// Update the configuration tiddlers
|
// Update the configuration tiddlers
|
||||||
var wiki = $tw.wiki;
|
var wiki = $tw.wiki;
|
||||||
wiki.addTiddler({
|
wiki.addTiddler({
|
||||||
title: "$:/config/MultiWikiServer/AllowAnonymousReads",
|
title: "$:/config/MultiWikiServer/ShowAnonymousAccessModal",
|
||||||
text: "undefined"
|
text: "yes"
|
||||||
});
|
|
||||||
wiki.addTiddler({
|
|
||||||
title: "$:/config/MultiWikiServer/AllowAnonymousWrites",
|
|
||||||
text: "undefined"
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Redirect back to admin page
|
// Redirect back to admin page
|
||||||
|
@ -36,9 +36,10 @@ function redirectToLogin(response, returnUrl) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
exports.middleware = function (request, response, state, entityType, permissionName) {
|
exports.middleware = function (request, response, state, entityType, permissionName) {
|
||||||
|
var extensionRegex = /\.[A-Za-z0-9]{1,4}$/;
|
||||||
|
|
||||||
var server = state.server,
|
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];
|
entityName = state.data ? (state.data[entityType+"_name"] || state.params[0]) : state.params[0];
|
||||||
|
|
||||||
// First, replace '%3A' with ':' to handle TiddlyWiki's system tiddlers
|
// 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 aclRecord = sqlTiddlerDatabase.getACLByName(entityType, decodedEntityName);
|
||||||
var isGetRequest = request.method === "GET";
|
var isGetRequest = request.method === "GET";
|
||||||
var hasAnonymousAccess = state.allowAnon ? (isGetRequest ? state.allowAnonReads : state.allowAnonWrites) : false;
|
var hasAnonymousAccess = state.allowAnon ? (isGetRequest ? state.allowAnonReads : state.allowAnonWrites) : false;
|
||||||
|
var anonymousAccessConfigured = state.anonAccessConfigured;
|
||||||
var entity = sqlTiddlerDatabase.getEntityByName(entityType, decodedEntityName);
|
var entity = sqlTiddlerDatabase.getEntityByName(entityType, decodedEntityName);
|
||||||
if(entity?.owner_id) {
|
if(entity?.owner_id) {
|
||||||
if(state.authenticatedUser?.user_id && (state.authenticatedUser?.user_id !== entity.owner_id) || !state.authenticatedUser?.user_id && !hasAnonymousAccess) {
|
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.writeHead(403, "Forbidden");
|
||||||
response.end();
|
response.end();
|
||||||
}
|
}
|
||||||
@ -59,8 +65,8 @@ exports.middleware = function (request, response, state, entityType, permissionN
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// First, we need to check if anonymous access is allowed
|
// First, we need to check if anonymous access is allowed
|
||||||
if(!state.authenticatedUser?.user_id && !hasAnonymousAccess) {
|
if(!state.authenticatedUser?.user_id && (anonymousAccessConfigured && !hasAnonymousAccess)) {
|
||||||
if(!response.headersSent) {
|
if(!response.headersSent && !extensionRegex.test(request.url)) {
|
||||||
response.writeHead(401, "Unauthorized");
|
response.writeHead(401, "Unauthorized");
|
||||||
response.end();
|
response.end();
|
||||||
}
|
}
|
||||||
@ -80,7 +86,7 @@ exports.middleware = function (request, response, state, entityType, permissionN
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check ACL permission
|
// 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(!hasPermission && !hasAnonymousAccess) {
|
||||||
if(!response.headersSent) {
|
if(!response.headersSent) {
|
||||||
response.writeHead(403, "Forbidden");
|
response.writeHead(403, "Forbidden");
|
||||||
|
@ -500,6 +500,7 @@ SqlTiddlerDatabase.prototype.getRecipeTiddler = function(title,recipe_name) {
|
|||||||
Checks if a user has permission to access a recipe
|
Checks if a user has permission to access a recipe
|
||||||
*/
|
*/
|
||||||
SqlTiddlerDatabase.prototype.hasRecipePermission = function(userId, recipeName, permissionName) {
|
SqlTiddlerDatabase.prototype.hasRecipePermission = function(userId, recipeName, permissionName) {
|
||||||
|
try {
|
||||||
// check if the user is the owner of the entity
|
// check if the user is the owner of the entity
|
||||||
const recipe = this.engine.runStatementGet(`
|
const recipe = this.engine.runStatementGet(`
|
||||||
SELECT owner_id
|
SELECT owner_id
|
||||||
@ -509,10 +510,17 @@ SqlTiddlerDatabase.prototype.hasRecipePermission = function(userId, recipeName,
|
|||||||
$recipe_name: recipeName
|
$recipe_name: recipeName
|
||||||
});
|
});
|
||||||
|
|
||||||
if(recipe?.owner_id) {
|
if(!!recipe?.owner_id && recipe?.owner_id === userId) {
|
||||||
return 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
|
// First, check if there's an ACL record for the entity and get the permission_id
|
||||||
var checkACLExistsQuery = `
|
var checkACLExistsQuery = `
|
||||||
SELECT *
|
SELECT acl.*, permissions.permission_name
|
||||||
FROM acl
|
FROM acl
|
||||||
WHERE entity_type = $entity_type
|
LEFT JOIN permissions ON acl.permission_id = permissions.permission_id
|
||||||
AND entity_name = $entity_name
|
WHERE acl.entity_type = $entity_type
|
||||||
|
AND acl.entity_name = $entity_name
|
||||||
`;
|
`;
|
||||||
|
|
||||||
if (!fetchAll) {
|
if (!fetchAll) {
|
||||||
@ -548,22 +557,24 @@ SqlTiddlerDatabase.prototype.getACLByName = function(entityType, entityName, fet
|
|||||||
return aclRecord;
|
return aclRecord;
|
||||||
}
|
}
|
||||||
|
|
||||||
SqlTiddlerDatabase.prototype.checkACLPermission = function(userId, entityType, entityName) {
|
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 the entityName starts with "$:/", we'll assume its a system bag/recipe, then grant the user permission
|
||||||
if(entityName.startsWith("$:/")) {
|
if(entityName.startsWith("$:/")) {
|
||||||
return true;
|
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 no ACL record exists, return true for hasPermission
|
||||||
if (!aclRecord) {
|
if ((!aclRecord && !ownerId) || ((!!aclRecord && !!ownerId) && ownerId === userId)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If ACL record exists, check for user permission using the retrieved permission_id
|
// If ACL record exists, check for user permission using the retrieved permission_id
|
||||||
const checkPermissionQuery = `
|
const checkPermissionQuery = `
|
||||||
SELECT 1
|
SELECT *
|
||||||
FROM users u
|
FROM users u
|
||||||
JOIN user_roles ur ON u.user_id = ur.user_id
|
JOIN user_roles ur ON u.user_id = ur.user_id
|
||||||
JOIN roles r ON ur.role_id = r.role_id
|
JOIN roles r ON ur.role_id = r.role_id
|
||||||
@ -579,12 +590,17 @@ SqlTiddlerDatabase.prototype.checkACLPermission = function(userId, entityType, e
|
|||||||
$user_id: userId,
|
$user_id: userId,
|
||||||
$entity_type: entityType,
|
$entity_type: entityType,
|
||||||
$entity_name: entityName,
|
$entity_name: entityName,
|
||||||
$permission_id: aclRecord.permission_id
|
$permission_id: aclRecord?.permission_id
|
||||||
});
|
});
|
||||||
|
|
||||||
let hasPermission = result !== undefined;
|
let hasPermission = result !== undefined;
|
||||||
|
|
||||||
return hasPermission;
|
return hasPermission;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return false
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,7 +16,7 @@ title: $:/plugins/tiddlywiki/multiwikiserver/templates/manage-acl
|
|||||||
<input type="hidden" name="entity_type" value="recipe" />
|
<input type="hidden" name="entity_type" value="recipe" />
|
||||||
<input type="hidden" name="recipe_name" value={{{ [<recipe>jsonget[recipe_name]] }}}/>
|
<input type="hidden" name="recipe_name" value={{{ [<recipe>jsonget[recipe_name]] }}}/>
|
||||||
<input type="hidden" name="bag_name" value={{{ [<bag>jsonget[bag_name]] }}}/>
|
<input type="hidden" name="bag_name" value={{{ [<bag>jsonget[bag_name]] }}}/>
|
||||||
<select name="role_id" class="tc-select">
|
<select name="role_id" class="tc-select" required>
|
||||||
<option value="">Select Role</option>
|
<option value="">Select Role</option>
|
||||||
<$list filter="[<roles-list>jsonindexes[]]" variable="role-index">
|
<$list filter="[<roles-list>jsonindexes[]]" variable="role-index">
|
||||||
<$let role={{{ [<roles-list>jsonextract<role-index>] }}}>
|
<$let role={{{ [<roles-list>jsonextract<role-index>] }}}>
|
||||||
@ -25,7 +25,7 @@ title: $:/plugins/tiddlywiki/multiwikiserver/templates/manage-acl
|
|||||||
</$list>
|
</$list>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select name="permission_id" class="tc-select">
|
<select name="permission_id" class="tc-select" required>
|
||||||
<option value="">Select Permission</option>
|
<option value="">Select Permission</option>
|
||||||
<$list filter="[<permissions-list>jsonindexes[]]" variable="permission-index">
|
<$list filter="[<permissions-list>jsonindexes[]]" variable="permission-index">
|
||||||
<$let permission={{{ [<permissions-list>jsonextract<permission-index>] }}}>
|
<$let permission={{{ [<permissions-list>jsonextract<permission-index>] }}}>
|
||||||
@ -86,7 +86,7 @@ title: $:/plugins/tiddlywiki/multiwikiserver/templates/manage-acl
|
|||||||
<input type="hidden" name="entity_type" value="bag" />
|
<input type="hidden" name="entity_type" value="bag" />
|
||||||
<input type="hidden" name="recipe_name" value={{{ [<recipe>jsonget[recipe_name]] }}}/>
|
<input type="hidden" name="recipe_name" value={{{ [<recipe>jsonget[recipe_name]] }}}/>
|
||||||
<input type="hidden" name="bag_name" value={{{ [<bag>jsonget[bag_name]] }}}/>
|
<input type="hidden" name="bag_name" value={{{ [<bag>jsonget[bag_name]] }}}/>
|
||||||
<select name="role_id" class="tc-select">
|
<select name="role_id" class="tc-select" required>
|
||||||
<option value="">Select Role</option>
|
<option value="">Select Role</option>
|
||||||
<$list filter="[<roles-list>jsonindexes[]]" variable="role-index">
|
<$list filter="[<roles-list>jsonindexes[]]" variable="role-index">
|
||||||
<$let role={{{ [<roles-list>jsonextract<role-index>] }}}>
|
<$let role={{{ [<roles-list>jsonextract<role-index>] }}}>
|
||||||
@ -95,7 +95,7 @@ title: $:/plugins/tiddlywiki/multiwikiserver/templates/manage-acl
|
|||||||
</$list>
|
</$list>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select name="permission_id" class="tc-select">
|
<select name="permission_id" class="tc-select" required>
|
||||||
<option value="">Select Permission</option>
|
<option value="">Select Permission</option>
|
||||||
<$list filter="[<permissions-list>jsonindexes[]]" variable="permission-index">
|
<$list filter="[<permissions-list>jsonindexes[]]" variable="permission-index">
|
||||||
<$let permission={{{ [<permissions-list>jsonextract<permission-index>] }}}>
|
<$let permission={{{ [<permissions-list>jsonextract<permission-index>] }}}>
|
||||||
|
Loading…
Reference in New Issue
Block a user