From fbadbb18a40c10b77918ff766978e55d5466dcec Mon Sep 17 00:00:00 2001 From: thiloho <123883702+thiloho@users.noreply.github.com> Date: Wed, 7 Aug 2024 19:13:39 +0200 Subject: [PATCH] Manage collaborators via RLS --- .../20240724191017_row_level_security.sql | 261 ++++++------------ .../src/lib/components/WebsiteEditor.svelte | 5 +- .../[websiteId]/publish/+page.server.ts | 29 +- .../website/[websiteId]/publish/+page.svelte | 3 +- 4 files changed, 105 insertions(+), 193 deletions(-) diff --git a/rest-api/db/migrations/20240724191017_row_level_security.sql b/rest-api/db/migrations/20240724191017_row_level_security.sql index 4599f47..e4e5f48 100644 --- a/rest-api/db/migrations/20240724191017_row_level_security.sql +++ b/rest-api/db/migrations/20240724191017_row_level_security.sql @@ -9,244 +9,157 @@ ALTER TABLE internal.article ENABLE ROW LEVEL SECURITY; ALTER TABLE internal.footer ENABLE ROW LEVEL SECURITY; ALTER TABLE internal.collab ENABLE ROW LEVEL SECURITY; +CREATE FUNCTION internal.user_has_website_access(website_id UUID, required_permission INTEGER DEFAULT 10) +RETURNS BOOLEAN AS $$ +DECLARE + _user_id UUID; + _has_access BOOLEAN; +BEGIN + _user_id := (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID; + + SELECT EXISTS ( + SELECT 1 + FROM internal.website + WHERE id = website_id AND owner_id = _user_id + ) INTO _has_access; + + IF _has_access THEN + RETURN _has_access; + END IF; + + SELECT EXISTS ( + SELECT 1 + FROM internal.collab c + WHERE c.website_id = user_has_website_access.website_id + AND c.user_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID + AND c.permission_level >= user_has_website_access.required_permission + ) INTO _has_access; + + RETURN _has_access; +END; +$$ LANGUAGE plpgsql SECURITY DEFINER; + + CREATE POLICY view_user ON internal.user FOR SELECT USING (true); -CREATE POLICY view_own_websites ON internal.website +CREATE POLICY view_websites ON internal.website FOR SELECT -USING (owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID); +USING (internal.user_has_website_access(id, 10)); -CREATE POLICY update_own_website ON internal.website +CREATE POLICY update_website ON internal.website FOR UPDATE -USING (owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID); +USING (internal.user_has_website_access(id, 20)); -CREATE POLICY delete_own_website ON internal.website +CREATE POLICY delete_website ON internal.website FOR DELETE -USING (owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID); +USING (internal.user_has_website_access(id, 40)); -CREATE POLICY view_own_media ON internal.media +CREATE POLICY view_media ON internal.media FOR SELECT -USING (user_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID); +USING (internal.user_has_website_access(website_id, 10)); -CREATE POLICY insert_own_media ON internal.media +CREATE POLICY insert_media ON internal.media FOR INSERT -WITH CHECK ( - EXISTS ( - SELECT 1 - FROM internal.website - WHERE internal.website.id = internal.media.website_id - AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID - ) -); +WITH CHECK (internal.user_has_website_access(website_id, 20)); -CREATE POLICY view_own_settings ON internal.settings +CREATE POLICY view_settings ON internal.settings FOR SELECT -USING ( - EXISTS ( - SELECT 1 - FROM internal.website - WHERE internal.website.id = internal.settings.website_id - AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID - ) -); +USING (internal.user_has_website_access(website_id, 10)); -CREATE POLICY update_own_settings ON internal.settings +CREATE POLICY update_settings ON internal.settings FOR UPDATE -USING ( - EXISTS ( - SELECT 1 - FROM internal.website - WHERE internal.website.id = internal.settings.website_id - AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID - ) -); +USING (internal.user_has_website_access(website_id, 20)); -CREATE POLICY view_own_header ON internal.header +CREATE POLICY view_header ON internal.header FOR SELECT -USING ( - EXISTS ( - SELECT 1 - FROM internal.website - WHERE internal.website.id = internal.header.website_id - AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID - ) -); +USING (internal.user_has_website_access(website_id, 10)); -CREATE POLICY update_own_header ON internal.header +CREATE POLICY update_header ON internal.header FOR UPDATE -USING ( - EXISTS ( - SELECT 1 - FROM internal.website - WHERE internal.website.id = internal.header.website_id - AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID - ) -); +USING (internal.user_has_website_access(website_id, 20)); -CREATE POLICY view_own_home ON internal.home +CREATE POLICY view_home ON internal.home FOR SELECT -USING ( - EXISTS ( - SELECT 1 - FROM internal.website - WHERE internal.website.id = internal.home.website_id - AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID - ) -); +USING (internal.user_has_website_access(website_id, 10)); -CREATE POLICY update_own_home ON internal.home +CREATE POLICY update_home ON internal.home FOR UPDATE -USING ( - EXISTS ( - SELECT 1 - FROM internal.website - WHERE internal.website.id = internal.home.website_id - AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID - ) -); +USING (internal.user_has_website_access(website_id, 20)); -CREATE POLICY view_own_articles ON internal.article +CREATE POLICY view_articles ON internal.article FOR SELECT -USING ( - EXISTS ( - SELECT 1 - FROM internal.website - WHERE internal.website.id = internal.article.website_id - AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID - ) -); +USING (internal.user_has_website_access(website_id, 10)); -CREATE POLICY update_own_article ON internal.article +CREATE POLICY update_article ON internal.article FOR UPDATE -USING ( - EXISTS ( - SELECT 1 - FROM internal.website - WHERE internal.website.id = internal.article.website_id - AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID - ) -); +USING (internal.user_has_website_access(website_id, 20)); -CREATE POLICY delete_own_article ON internal.article +CREATE POLICY delete_article ON internal.article FOR DELETE -USING ( - EXISTS ( - SELECT 1 - FROM internal.website - WHERE internal.website.id = internal.article.website_id - AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID - ) -); +USING (internal.user_has_website_access(website_id, 30)); -CREATE POLICY insert_own_article ON internal.article +CREATE POLICY insert_article ON internal.article FOR INSERT -WITH CHECK ( - EXISTS ( - SELECT 1 - FROM internal.website - WHERE internal.website.id = internal.article.website_id - AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID - ) -); +WITH CHECK (internal.user_has_website_access(website_id, 20)); -CREATE POLICY view_own_footer ON internal.footer +CREATE POLICY view_footer ON internal.footer FOR SELECT -USING ( - EXISTS ( - SELECT 1 - FROM internal.website - WHERE internal.website.id = internal.footer.website_id - AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID - ) -); +USING (internal.user_has_website_access(website_id, 10)); -CREATE POLICY update_own_footer ON internal.footer +CREATE POLICY update_footer ON internal.footer FOR UPDATE -USING ( - EXISTS ( - SELECT 1 - FROM internal.website - WHERE internal.website.id = internal.footer.website_id - AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID - ) -); +USING (internal.user_has_website_access(website_id, 20)); CREATE POLICY view_collaborations ON internal.collab FOR SELECT -USING ( - EXISTS ( - SELECT 1 - FROM internal.website - WHERE internal.website.id = internal.collab.website_id - AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID - ) -); +USING (internal.user_has_website_access(website_id, 10)); CREATE POLICY insert_collaborations ON internal.collab FOR INSERT -WITH CHECK ( - EXISTS ( - SELECT 1 - FROM internal.website - WHERE internal.website.id = internal.collab.website_id - AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID - ) -); +WITH CHECK (internal.user_has_website_access(website_id, 30)); CREATE POLICY update_collaborations ON internal.collab FOR UPDATE -USING ( - EXISTS ( - SELECT 1 - FROM internal.website - WHERE internal.website.id = internal.collab.website_id - AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID - ) -); +USING (internal.user_has_website_access(website_id, 30)); CREATE POLICY delete_collaborations ON internal.collab FOR DELETE -USING ( - EXISTS ( - SELECT 1 - FROM internal.website - WHERE internal.website.id = internal.collab.website_id - AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID - ) -); +USING (internal.user_has_website_access(website_id, 30)); -- migrate:down DROP POLICY view_user ON internal.user; -DROP POLICY view_own_websites ON internal.website; -DROP POLICY delete_own_website ON internal.website; -DROP POLICY update_own_website ON internal.website; -DROP POLICY view_own_media ON internal.media; -DROP POLICY insert_own_media ON internal.media; -DROP POLICY view_own_settings ON internal.settings; -DROP POLICY update_own_settings ON internal.settings; -DROP POLICY view_own_header ON internal.header; -DROP POLICY update_own_header ON internal.header; -DROP POLICY view_own_home ON internal.home; -DROP POLICY update_own_home ON internal.home; -DROP POLICY view_own_articles ON internal.article; -DROP POLICY update_own_article ON internal.article; -DROP POLICY delete_own_article ON internal.article; -DROP POLICY insert_own_article ON internal.article; -DROP POLICY view_own_footer ON internal.footer; -DROP POLICY update_own_footer ON internal.footer; +DROP POLICY view_websites ON internal.website; +DROP POLICY delete_website ON internal.website; +DROP POLICY update_website ON internal.website; +DROP POLICY view_media ON internal.media; +DROP POLICY insert_media ON internal.media; +DROP POLICY view_settings ON internal.settings; +DROP POLICY update_settings ON internal.settings; +DROP POLICY view_header ON internal.header; +DROP POLICY update_header ON internal.header; +DROP POLICY view_home ON internal.home; +DROP POLICY update_home ON internal.home; +DROP POLICY view_articles ON internal.article; +DROP POLICY update_article ON internal.article; +DROP POLICY delete_article ON internal.article; +DROP POLICY insert_article ON internal.article; +DROP POLICY view_footer ON internal.footer; +DROP POLICY update_footer ON internal.footer; DROP POLICY view_collaborations ON internal.collab; DROP POLICY insert_collaborations ON internal.collab; DROP POLICY update_collaborations ON internal.collab; DROP POLICY delete_collaborations ON internal.collab; +DROP FUNCTION internal.user_has_website_access(UUID, INTEGER); ALTER TABLE internal.user DISABLE ROW LEVEL SECURITY; ALTER TABLE internal.website DISABLE ROW LEVEL SECURITY; diff --git a/web-app/src/lib/components/WebsiteEditor.svelte b/web-app/src/lib/components/WebsiteEditor.svelte index 4570981..4c8b051 100644 --- a/web-app/src/lib/components/WebsiteEditor.svelte +++ b/web-app/src/lib/components/WebsiteEditor.svelte @@ -32,10 +32,7 @@
${article.publication_date}
`) - .replace("{{main_content}}", md.render(article.main_content)) - .replace("{{additional_text}}", md.render(websiteData.additional_text)); + .replace("{{main_content}}", md.render(article.main_content || "")) + .replace("{{additional_text}}", md.render(websiteData.additional_text || "")); await writeFile(join(uploadDir, "articles", `${articleFileName}.html`), articleFileContents); } diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/publish/+page.svelte b/web-app/src/routes/(authenticated)/website/[websiteId]/publish/+page.svelte index 05638d5..64b12c6 100644 --- a/web-app/src/routes/(authenticated)/website/[websiteId]/publish/+page.svelte +++ b/web-app/src/routes/(authenticated)/website/[websiteId]/publish/+page.svelte @@ -12,7 +12,8 @@