Skip to content

Commit

Permalink
TiddlyWiki#8756 complete private tiddler with ACL middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
webplusai committed Nov 21, 2024
1 parent 644dc00 commit 73c9cbd
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,14 @@ exports.useACL = true;
exports.entityName = "recipe"

exports.handler = function(request,response,state) {
var server = state.server,
sqlTiddlerDatabase = server.sqlTiddlerDatabase
if(state.data.recipe_name && state.data.bag_names) {
const result = $tw.mws.store.createRecipe(state.data.recipe_name,$tw.utils.parseStringArray(state.data.bag_names),state.data.description,state.authenticatedUser?.user_id);
const result = $tw.mws.store.createRecipe(state.data.recipe_name,$tw.utils.parseStringArray(state.data.bag_names),state.data.description);
if(!result) {
if(state.authenticatedUser) {
sqlTiddlerDatabase.assignRecipeToUser(state.data.recipe_name,state.authenticatedUser.user_id);
}
state.sendResponse(302,{
"Content-Type": "text/plain",
"Location": "/"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ exports.handler = function (request, response, state) {
var recipe_name = $tw.utils.decodeURIComponentSafe(state.params[0]),
data = $tw.utils.parseJSONSafe(state.data);
if(recipe_name && data) {
var result = $tw.mws.store.createRecipe(recipe_name, data.bag_names, data.description, state.authenticatedUser?.user_id);
var result = $tw.mws.store.createRecipe(recipe_name, data.bag_names, data.description);
if(!result) {
state.sendResponse(204, {
"Content-Type": "text/plain"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,33 +48,44 @@ exports.middleware = function (request, response, state, entityType, permissionN
var aclRecord = sqlTiddlerDatabase.getACLByName(entityType, decodedEntityName);
var isGetRequest = request.method === "GET";
var hasAnonymousAccess = isGetRequest ? state.allowAnonReads : state.allowAnonWrites;
// Get permission record
const permission = sqlTiddlerDatabase.getPermissionByName(permissionName);
// ACL Middleware will only apply if the entity has a middleware record
if(aclRecord && aclRecord?.permission_id === permission?.permission_id) {
// If not authenticated and anonymous access is not allowed, request authentication
if(!state.authenticatedUsername && !state.allowAnon) {
if(state.urlInfo.pathname !== '/login') {
redirectToLogin(response, request.url);
return;
}
}
// Check if user is authenticated
if(!state.authenticatedUser && !hasAnonymousAccess && !response.headersSent) {
response.writeHead(401, "Unauthorized");
response.end();
return;
}

// Check ACL permission
var hasPermission = request.method === "POST" || sqlTiddlerDatabase.checkACLPermission(state.authenticatedUser.user_id, entityType, decodedEntityName, permissionName)
if(!hasPermission && !hasAnonymousAccess) {
var entity = sqlTiddlerDatabase.getEntityByName(entityType, decodedEntityName);
if(entity?.owner_id) {
if(state.authenticatedUser?.user_id !== entity.owner_id) {
if(!response.headersSent) {
response.writeHead(403, "Forbidden");
response.end();
}
return;
}
} else {
// Get permission record
const permission = sqlTiddlerDatabase.getPermissionByName(permissionName);
// ACL Middleware will only apply if the entity has a middleware record
if(aclRecord && aclRecord?.permission_id === permission?.permission_id) {
// If not authenticated and anonymous access is not allowed, request authentication
if(!state.authenticatedUsername && !state.allowAnon) {
if(state.urlInfo.pathname !== '/login') {
redirectToLogin(response, request.url);
return;
}
}
// Check if user is authenticated
if(!state.authenticatedUser && !hasAnonymousAccess && !response.headersSent) {
response.writeHead(401, "Unauthorized");
response.end();
return;
}

// Check ACL permission
var hasPermission = request.method === "POST" || sqlTiddlerDatabase.checkACLPermission(state.authenticatedUser.user_id, entityType, decodedEntityName, permissionName)
if(!hasPermission && !hasAnonymousAccess) {
if(!response.headersSent) {
response.writeHead(403, "Forbidden");
response.end();
}
return;
}
}
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ SqlTiddlerDatabase.prototype.listRecipes = function() {
Create or update a recipe
Returns the recipe_id of the recipe
*/
SqlTiddlerDatabase.prototype.createRecipe = function(recipe_name,bag_names,description,owner_id) {
SqlTiddlerDatabase.prototype.createRecipe = function(recipe_name,bag_names,description) {
// Run the queries
this.engine.runStatement(`
-- Delete existing recipe_bags entries for this recipe
Expand All @@ -272,12 +272,11 @@ SqlTiddlerDatabase.prototype.createRecipe = function(recipe_name,bag_names,descr
});
const updateRecipes = this.engine.runStatement(`
-- Create the entry in the recipes table if required
INSERT OR REPLACE INTO recipes (recipe_name, description, owner_id)
VALUES ($recipe_name, $description, $owner_id)
INSERT OR REPLACE INTO recipes (recipe_name, description)
VALUES ($recipe_name, $description)
`,{
$recipe_name: recipe_name,
$description: description,
$owner_id: owner_id
$description: description
});
this.engine.runStatement(`
INSERT INTO recipe_bags (recipe_id, bag_id, position)
Expand All @@ -294,6 +293,18 @@ SqlTiddlerDatabase.prototype.createRecipe = function(recipe_name,bag_names,descr
return updateRecipes.lastInsertRowid;
};

/*
Assign a recipe to a user
*/
SqlTiddlerDatabase.prototype.assignRecipeToUser = function(recipe_name,user_id) {
this.engine.runStatement(`
UPDATE recipes SET owner_id = $user_id WHERE recipe_name = $recipe_name
`,{
$recipe_name: recipe_name,
$user_id: user_id
});
};

/*
Returns {tiddler_id:}
*/
Expand Down Expand Up @@ -593,6 +604,19 @@ SqlTiddlerDatabase.prototype.getEntityAclRecords = function(entityName) {
return aclRecords
}

/*
Get the entity by name
*/
SqlTiddlerDatabase.prototype.getEntityByName = function(entityType, entityName) {
const entityInfo = this.entityTypeToTableMap[entityType];
if (entityInfo) {
return this.engine.runStatementGet(`SELECT * FROM ${entityInfo.table} WHERE ${entityInfo.column} = $entity_name`, {
$entity_name: entityName
});
}
return null;
}

/*
Get the titles of the tiddlers in a bag. Returns an empty array for bags that do not exist
*/
Expand Down

0 comments on commit 73c9cbd

Please sign in to comment.