Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: api delete for root documents #25

Merged
merged 9 commits into from
Nov 22, 2024
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not needed to be reviewed, since a second migration of the view was needed ;)

Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@

CREATE OR REPLACE VIEW view__all_document_user_permissions AS
SELECT
document_root_id,
user_id,
access,
document_id,
root_user_permission_id,
root_group_permission_id,
group_id,
ROW_NUMBER() OVER (PARTITION BY document_root_id, user_id, document_id ORDER BY access DESC) AS access_rank
FROM (
-- get all documents where the user **is the author**
-- including all child documents
WITH RECURSIVE
document_hierarchy AS (
-- Anchor member: select the root document
SELECT
document_roots.id AS document_root_id,
documents.id AS document_id,
documents.author_id AS user_id,
document_roots.access AS access,
NULL::uuid AS root_user_permission_id,
NULL::uuid AS root_group_permission_id,
NULL::uuid AS group_id
FROM
document_roots
INNER JOIN documents ON document_roots.id = documents.document_root_id
WHERE documents.parent_id IS NULL -- Assuming root documents have parent_id as NULL

UNION ALL -- keeps duplicates in the result set

-- Recursive member: select child documents with the parent's author as the user
SELECT
document_hierarchy.document_root_id AS document_root_id,
child_documents.id AS document_id,
document_hierarchy.user_id AS user_id,
document_hierarchy.access AS access,
NULL::uuid AS root_user_permission_id,
NULL::uuid AS root_group_permission_id,
NULL::uuid AS group_id
FROM
document_hierarchy
INNER JOIN documents AS child_documents ON document_hierarchy.document_id = child_documents.parent_id
),
-- get all documents where the user is **not the author**
-- but has been granted **shared access**
shared_doc_hierarchy AS (
-- Anchor member: select the root document
SELECT
document_roots.id AS document_root_id,
documents.id AS document_id,
all_users.id AS user_id,
CASE
WHEN document_roots.shared_access <= document_roots.access THEN document_roots.shared_access
ELSE document_roots.access
END AS access,
NULL::uuid AS root_user_permission_id,
NULL::uuid AS root_group_permission_id,
NULL::uuid AS group_id
FROM
document_roots
INNER JOIN documents ON document_roots.id = documents.document_root_id
CROSS JOIN users all_users
WHERE documents.parent_id IS NULL -- Assuming root documents have parent_id as NULL
AND documents.author_id != all_users.id
AND document_roots.access != 'None_DocumentRoot'
lebalz marked this conversation as resolved.
Show resolved Hide resolved
AND (
document_roots.shared_access='RO_DocumentRoot'
OR
document_roots.shared_access='RW_DocumentRoot'
)

UNION ALL -- keeps duplicates in the result set

-- Recursive member: select child documents with the parent's author as the user
SELECT
shared_doc_hierarchy.document_root_id AS document_root_id,
child_documents.id AS document_id,
shared_doc_hierarchy.user_id AS user_id,
shared_doc_hierarchy.access AS access,
NULL::uuid AS root_user_permission_id,
NULL::uuid AS root_group_permission_id,
NULL::uuid AS group_id
FROM
shared_doc_hierarchy
INNER JOIN documents AS child_documents ON shared_doc_hierarchy.document_id = child_documents.parent_id
)
SELECT
document_root_id,
user_id,
access,
document_id,
root_user_permission_id,
root_group_permission_id,
group_id
FROM document_hierarchy
UNION ALL
SELECT
document_root_id,
user_id,
access,
document_id,
root_user_permission_id,
root_group_permission_id,
group_id
FROM shared_doc_hierarchy
UNION ALL
-- get all documents where the user has been granted shared access
-- or the access has been extended by user permissions
SELECT
document_roots.id AS document_root_id,
rup.user_id AS user_id,
rup.access AS access,
documents.id AS document_id,
rup.id AS root_user_permission_id,
NULL::uuid AS root_group_permission_id,
NULL::uuid AS group_id
FROM
document_roots
LEFT JOIN documents ON document_roots.id=documents.document_root_id
LEFT JOIN root_user_permissions rup
ON (
document_roots.id = rup.document_root_id
AND (
documents.author_id = rup.user_id
OR
rup.access >= document_roots.shared_access
)
)
WHERE rup.user_id IS NOT NULL
UNION ALL
-- all group-based permissions for the documents author
SELECT
document_roots.id AS document_root_id,
sg_to_user."B" AS user_id,
rgp.access AS access,
documents.id AS document_id,
NULL::uuid AS root_user_permission_id,
rgp.id AS root_group_permission_id,
sg.id AS group_id
FROM
document_roots
INNER JOIN root_group_permissions rgp ON document_roots.id=rgp.document_root_id
INNER JOIN student_groups sg ON rgp.student_group_id=sg.id
LEFT JOIN documents ON document_roots.id=documents.document_root_id
LEFT JOIN "_StudentGroupToUser" sg_to_user
ON (
sg_to_user."A"=sg.id
AND (
sg_to_user."B"=documents.author_id
OR documents.author_id is null
)
)
WHERE sg_to_user."B" IS NOT NULL
UNION ALL
-- all group based permissions for the user, which is not the author
SELECT
document_roots.id AS document_root_id,
sg_to_user."B" AS user_id,
rgp.access AS access,
documents.id AS document_id,
NULL::uuid AS root_user_permission_id,
rgp.id AS root_group_permission_id,
sg.id AS group_id
FROM
document_roots
INNER JOIN root_group_permissions rgp
ON (
document_roots.id=rgp.document_root_id
AND rgp.access >= document_roots.shared_access
)
INNER JOIN student_groups sg ON rgp.student_group_id=sg.id
LEFT JOIN documents ON document_roots.id=documents.document_root_id
LEFT JOIN "_StudentGroupToUser" sg_to_user
ON (
sg_to_user."A"=sg.id
AND sg_to_user."B"!=documents.author_id
)
WHERE sg_to_user."B" IS NOT NULL
) as doc_user_permissions;
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@

CREATE OR REPLACE VIEW view__all_document_user_permissions AS
SELECT
document_root_id,
user_id,
access,
document_id,
root_user_permission_id,
root_group_permission_id,
group_id,
ROW_NUMBER() OVER (PARTITION BY document_root_id, user_id, document_id ORDER BY access DESC) AS access_rank
FROM (
-- get all documents where the user **is the author**
-- including all child documents
WITH RECURSIVE
document_hierarchy AS (
-- Anchor member: select the root document
SELECT
document_roots.id AS document_root_id,
documents.id AS document_id,
documents.author_id AS user_id,
document_roots.access AS access,
NULL::uuid AS root_user_permission_id,
NULL::uuid AS root_group_permission_id,
NULL::uuid AS group_id
FROM
document_roots
INNER JOIN documents ON document_roots.id = documents.document_root_id
WHERE documents.parent_id IS NULL -- Assuming root documents have parent_id as NULL

UNION ALL -- keeps duplicates in the result set

-- Recursive member: select child documents with the parent's author as the user
SELECT
document_hierarchy.document_root_id AS document_root_id,
child_documents.id AS document_id,
document_hierarchy.user_id AS user_id,
document_hierarchy.access AS access,
NULL::uuid AS root_user_permission_id,
NULL::uuid AS root_group_permission_id,
NULL::uuid AS group_id
FROM
document_hierarchy
INNER JOIN documents AS child_documents ON document_hierarchy.document_id = child_documents.parent_id
),
-- get all documents where the user is **not the author**
-- but has been granted **shared access**
shared_doc_hierarchy AS (
-- Anchor member: select the root document
SELECT
document_roots.id AS document_root_id,
documents.id AS document_id,
all_users.id AS user_id,
CASE
WHEN document_roots.shared_access <= document_roots.access THEN document_roots.shared_access
ELSE document_roots.access
END AS access,
NULL::uuid AS root_user_permission_id,
NULL::uuid AS root_group_permission_id,
NULL::uuid AS group_id
FROM
document_roots
INNER JOIN documents ON document_roots.id = documents.document_root_id
CROSS JOIN users all_users
WHERE documents.parent_id IS NULL -- Assuming root documents have parent_id as NULL
AND documents.author_id != all_users.id
AND document_roots.access != 'None_DocumentRoot'
AND document_roots.shared_access != 'None_DocumentRoot'

UNION ALL -- keeps duplicates in the result set

-- Recursive member: select child documents with the parent's author as the user
SELECT
shared_doc_hierarchy.document_root_id AS document_root_id,
child_documents.id AS document_id,
shared_doc_hierarchy.user_id AS user_id,
shared_doc_hierarchy.access AS access,
NULL::uuid AS root_user_permission_id,
NULL::uuid AS root_group_permission_id,
NULL::uuid AS group_id
FROM
shared_doc_hierarchy
INNER JOIN documents AS child_documents ON shared_doc_hierarchy.document_id = child_documents.parent_id
)
SELECT
document_root_id,
user_id,
access,
document_id,
root_user_permission_id,
root_group_permission_id,
group_id
FROM document_hierarchy
UNION ALL
SELECT
document_root_id,
user_id,
access,
document_id,
root_user_permission_id,
root_group_permission_id,
group_id
FROM shared_doc_hierarchy
UNION ALL
-- get all documents where the user has been granted shared access
-- or the access has been extended by user permissions
SELECT
document_roots.id AS document_root_id,
rup.user_id AS user_id,
CASE
WHEN documents.author_id = rup.user_id THEN rup.access
WHEN document_roots.shared_access = 'RO_DocumentRoot' THEN 'RO_User'
WHEN document_roots.shared_access = 'RW_DocumentRoot' THEN 'RW_User'
ELSE document_roots.shared_access
END AS access,
Comment on lines +110 to +115
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added: Distinguish between own and shared documents

documents.id AS document_id,
rup.id AS root_user_permission_id,
NULL::uuid AS root_group_permission_id,
NULL::uuid AS group_id
FROM
document_roots
LEFT JOIN documents ON document_roots.id=documents.document_root_id
LEFT JOIN root_user_permissions rup
ON (
document_roots.id = rup.document_root_id
AND (
documents.author_id = rup.user_id
OR -- shared access
(
rup.access >= document_roots.shared_access
AND document_roots.shared_access != 'None_DocumentRoot'
)
Comment on lines +129 to +132
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+: ensure the shared_access is not None

)
)
WHERE rup.user_id IS NOT NULL
UNION ALL
-- all group-based permissions for the documents author
SELECT
document_roots.id AS document_root_id,
sg_to_user."B" AS user_id,
rgp.access AS access,
documents.id AS document_id,
NULL::uuid AS root_user_permission_id,
rgp.id AS root_group_permission_id,
sg.id AS group_id
FROM
document_roots
INNER JOIN root_group_permissions rgp ON document_roots.id=rgp.document_root_id
INNER JOIN student_groups sg ON rgp.student_group_id=sg.id
LEFT JOIN documents ON document_roots.id=documents.document_root_id
LEFT JOIN "_StudentGroupToUser" sg_to_user
ON (
sg_to_user."A"=sg.id
AND (
sg_to_user."B"=documents.author_id
OR documents.author_id is null
)
)
WHERE sg_to_user."B" IS NOT NULL
UNION ALL
-- all group based permissions for the user, which is not the author
SELECT
document_roots.id AS document_root_id,
sg_to_user."B" AS user_id,
CASE
WHEN document_roots.shared_access = 'RO_DocumentRoot' THEN 'RO_StudentGroup'
WHEN document_roots.shared_access = 'RW_DocumentRoot' THEN 'RW_StudentGroup'
ELSE document_roots.shared_access
END AS access,
Comment on lines +165 to +169
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+: same for group based access

documents.id AS document_id,
NULL::uuid AS root_user_permission_id,
rgp.id AS root_group_permission_id,
sg.id AS group_id
FROM
document_roots
INNER JOIN root_group_permissions rgp
ON (
document_roots.id=rgp.document_root_id
AND rgp.access >= document_roots.shared_access
AND document_roots.shared_access != 'None_DocumentRoot'
)
Comment on lines +177 to +181
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+: and here aswell, don't provide None Access

INNER JOIN student_groups sg ON rgp.student_group_id=sg.id
LEFT JOIN documents ON document_roots.id=documents.document_root_id
LEFT JOIN "_StudentGroupToUser" sg_to_user
ON (
sg_to_user."A"=sg.id
AND sg_to_user."B"!=documents.author_id
)
WHERE sg_to_user."B" IS NOT NULL
) as doc_user_permissions;
12 changes: 7 additions & 5 deletions src/auth/guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,6 @@ const requestHasRequiredAttributes = (
method: string,
isAdmin: boolean
) => {
if (isAdmin) {
return true;
}
Comment on lines -103 to -105
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huch, shouldn't admins be checked through the guard too!?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is ringing a bell now... I think we decided that admins should just be able to do whatever they want - but I don't think this was a great decision back then :D

const accessRules = Object.values(accessMatrix);
const accessRule = accessRules
.filter((accessRule) => accessRule.regex.test(path))
Expand All @@ -111,9 +108,14 @@ const requestHasRequiredAttributes = (
if (!accessRule) {
return false;
}
return accessRule.access.some(
(rule) => !rule.adminOnly && rule.methods.includes(method as 'GET' | 'POST' | 'PUT' | 'DELETE')
const hasAccess = accessRule.access.some(
(rule) =>
(isAdmin || !rule.adminOnly) && rule.methods.includes(method as 'GET' | 'POST' | 'PUT' | 'DELETE')
);
Logger.info(
`${hasAccess ? '✅' : '❌'} Access Rule for ${isAdmin ? 'Admin' : 'User'}: [${method}:${path}] ${JSON.stringify(accessRule)}`
);
return hasAccess;
};

export default routeGuard;
Loading