mirror of
https://github.com/thiloho/archtika.git
synced 2025-11-22 10:51:36 +01:00
Manage collaborators via RLS
This commit is contained in:
@@ -9,244 +9,157 @@ ALTER TABLE internal.article ENABLE ROW LEVEL SECURITY;
|
|||||||
ALTER TABLE internal.footer ENABLE ROW LEVEL SECURITY;
|
ALTER TABLE internal.footer ENABLE ROW LEVEL SECURITY;
|
||||||
ALTER TABLE internal.collab 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
|
CREATE POLICY view_user ON internal.user
|
||||||
FOR SELECT
|
FOR SELECT
|
||||||
USING (true);
|
USING (true);
|
||||||
|
|
||||||
CREATE POLICY view_own_websites ON internal.website
|
CREATE POLICY view_websites ON internal.website
|
||||||
FOR SELECT
|
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
|
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
|
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
|
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
|
FOR INSERT
|
||||||
WITH CHECK (
|
WITH CHECK (internal.user_has_website_access(website_id, 20));
|
||||||
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
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
CREATE POLICY view_own_settings ON internal.settings
|
CREATE POLICY view_settings ON internal.settings
|
||||||
FOR SELECT
|
FOR SELECT
|
||||||
USING (
|
USING (internal.user_has_website_access(website_id, 10));
|
||||||
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
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE POLICY update_own_settings ON internal.settings
|
CREATE POLICY update_settings ON internal.settings
|
||||||
FOR UPDATE
|
FOR UPDATE
|
||||||
USING (
|
USING (internal.user_has_website_access(website_id, 20));
|
||||||
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
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
CREATE POLICY view_own_header ON internal.header
|
CREATE POLICY view_header ON internal.header
|
||||||
FOR SELECT
|
FOR SELECT
|
||||||
USING (
|
USING (internal.user_has_website_access(website_id, 10));
|
||||||
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
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE POLICY update_own_header ON internal.header
|
CREATE POLICY update_header ON internal.header
|
||||||
FOR UPDATE
|
FOR UPDATE
|
||||||
USING (
|
USING (internal.user_has_website_access(website_id, 20));
|
||||||
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
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
CREATE POLICY view_own_home ON internal.home
|
CREATE POLICY view_home ON internal.home
|
||||||
FOR SELECT
|
FOR SELECT
|
||||||
USING (
|
USING (internal.user_has_website_access(website_id, 10));
|
||||||
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
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE POLICY update_own_home ON internal.home
|
CREATE POLICY update_home ON internal.home
|
||||||
FOR UPDATE
|
FOR UPDATE
|
||||||
USING (
|
USING (internal.user_has_website_access(website_id, 20));
|
||||||
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
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
CREATE POLICY view_own_articles ON internal.article
|
CREATE POLICY view_articles ON internal.article
|
||||||
FOR SELECT
|
FOR SELECT
|
||||||
USING (
|
USING (internal.user_has_website_access(website_id, 10));
|
||||||
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
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE POLICY update_own_article ON internal.article
|
CREATE POLICY update_article ON internal.article
|
||||||
FOR UPDATE
|
FOR UPDATE
|
||||||
USING (
|
USING (internal.user_has_website_access(website_id, 20));
|
||||||
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
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE POLICY delete_own_article ON internal.article
|
CREATE POLICY delete_article ON internal.article
|
||||||
FOR DELETE
|
FOR DELETE
|
||||||
USING (
|
USING (internal.user_has_website_access(website_id, 30));
|
||||||
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
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE POLICY insert_own_article ON internal.article
|
CREATE POLICY insert_article ON internal.article
|
||||||
FOR INSERT
|
FOR INSERT
|
||||||
WITH CHECK (
|
WITH CHECK (internal.user_has_website_access(website_id, 20));
|
||||||
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
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
CREATE POLICY view_own_footer ON internal.footer
|
CREATE POLICY view_footer ON internal.footer
|
||||||
FOR SELECT
|
FOR SELECT
|
||||||
USING (
|
USING (internal.user_has_website_access(website_id, 10));
|
||||||
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
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE POLICY update_own_footer ON internal.footer
|
CREATE POLICY update_footer ON internal.footer
|
||||||
FOR UPDATE
|
FOR UPDATE
|
||||||
USING (
|
USING (internal.user_has_website_access(website_id, 20));
|
||||||
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
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
CREATE POLICY view_collaborations ON internal.collab
|
CREATE POLICY view_collaborations ON internal.collab
|
||||||
FOR SELECT
|
FOR SELECT
|
||||||
USING (
|
USING (internal.user_has_website_access(website_id, 10));
|
||||||
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
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE POLICY insert_collaborations ON internal.collab
|
CREATE POLICY insert_collaborations ON internal.collab
|
||||||
FOR INSERT
|
FOR INSERT
|
||||||
WITH CHECK (
|
WITH CHECK (internal.user_has_website_access(website_id, 30));
|
||||||
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
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE POLICY update_collaborations ON internal.collab
|
CREATE POLICY update_collaborations ON internal.collab
|
||||||
FOR UPDATE
|
FOR UPDATE
|
||||||
USING (
|
USING (internal.user_has_website_access(website_id, 30));
|
||||||
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
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE POLICY delete_collaborations ON internal.collab
|
CREATE POLICY delete_collaborations ON internal.collab
|
||||||
FOR DELETE
|
FOR DELETE
|
||||||
USING (
|
USING (internal.user_has_website_access(website_id, 30));
|
||||||
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
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
-- migrate:down
|
-- migrate:down
|
||||||
DROP POLICY view_user ON internal.user;
|
DROP POLICY view_user ON internal.user;
|
||||||
DROP POLICY view_own_websites ON internal.website;
|
DROP POLICY view_websites ON internal.website;
|
||||||
DROP POLICY delete_own_website ON internal.website;
|
DROP POLICY delete_website ON internal.website;
|
||||||
DROP POLICY update_own_website ON internal.website;
|
DROP POLICY update_website ON internal.website;
|
||||||
DROP POLICY view_own_media ON internal.media;
|
DROP POLICY view_media ON internal.media;
|
||||||
DROP POLICY insert_own_media ON internal.media;
|
DROP POLICY insert_media ON internal.media;
|
||||||
DROP POLICY view_own_settings ON internal.settings;
|
DROP POLICY view_settings ON internal.settings;
|
||||||
DROP POLICY update_own_settings ON internal.settings;
|
DROP POLICY update_settings ON internal.settings;
|
||||||
DROP POLICY view_own_header ON internal.header;
|
DROP POLICY view_header ON internal.header;
|
||||||
DROP POLICY update_own_header ON internal.header;
|
DROP POLICY update_header ON internal.header;
|
||||||
DROP POLICY view_own_home ON internal.home;
|
DROP POLICY view_home ON internal.home;
|
||||||
DROP POLICY update_own_home ON internal.home;
|
DROP POLICY update_home ON internal.home;
|
||||||
DROP POLICY view_own_articles ON internal.article;
|
DROP POLICY view_articles ON internal.article;
|
||||||
DROP POLICY update_own_article ON internal.article;
|
DROP POLICY update_article ON internal.article;
|
||||||
DROP POLICY delete_own_article ON internal.article;
|
DROP POLICY delete_article ON internal.article;
|
||||||
DROP POLICY insert_own_article ON internal.article;
|
DROP POLICY insert_article ON internal.article;
|
||||||
DROP POLICY view_own_footer ON internal.footer;
|
DROP POLICY view_footer ON internal.footer;
|
||||||
DROP POLICY update_own_footer ON internal.footer;
|
DROP POLICY update_footer ON internal.footer;
|
||||||
DROP POLICY view_collaborations ON internal.collab;
|
DROP POLICY view_collaborations ON internal.collab;
|
||||||
DROP POLICY insert_collaborations ON internal.collab;
|
DROP POLICY insert_collaborations ON internal.collab;
|
||||||
DROP POLICY update_collaborations ON internal.collab;
|
DROP POLICY update_collaborations ON internal.collab;
|
||||||
DROP POLICY delete_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.user DISABLE ROW LEVEL SECURITY;
|
||||||
ALTER TABLE internal.website DISABLE ROW LEVEL SECURITY;
|
ALTER TABLE internal.website DISABLE ROW LEVEL SECURITY;
|
||||||
|
|||||||
@@ -32,10 +32,7 @@
|
|||||||
|
|
||||||
<div class="preview">
|
<div class="preview">
|
||||||
{#if fullPreview}
|
{#if fullPreview}
|
||||||
<iframe
|
<iframe src={previewContent} title="Preview"></iframe>
|
||||||
src="http://localhost:5173/user-websites/e6710116-f2b7-4318-82de-35a25d22ed2e/0015130f-3024-402b-8421-aaee4a6f0890/index.html"
|
|
||||||
title="Preview"
|
|
||||||
></iframe>
|
|
||||||
{:else}
|
{:else}
|
||||||
{@html md.render(previewContent)}
|
{@html md.render(previewContent)}
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export const load: PageServerLoad = async ({ params, fetch, cookies, locals }) =
|
|||||||
|
|
||||||
const websiteOverview = await websiteOverviewData.json();
|
const websiteOverview = await websiteOverviewData.json();
|
||||||
|
|
||||||
generateStaticFiles(websiteOverview, locals.user.id, params.websiteId);
|
generateStaticFiles(websiteOverview);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
websiteOverview
|
websiteOverview
|
||||||
@@ -30,16 +30,11 @@ export const actions: Actions = {
|
|||||||
const data = await request.formData();
|
const data = await request.formData();
|
||||||
const websiteOverview = JSON.parse(data.get("website-overview") as string);
|
const websiteOverview = JSON.parse(data.get("website-overview") as string);
|
||||||
|
|
||||||
generateStaticFiles(websiteOverview, locals.user.id, params.websiteId, false);
|
generateStaticFiles(websiteOverview, false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateStaticFiles = async (
|
const generateStaticFiles = async (websiteData: any, isPreview: boolean = true) => {
|
||||||
websiteData: any,
|
|
||||||
userId: string,
|
|
||||||
websiteId: string,
|
|
||||||
isPreview: boolean = true
|
|
||||||
) => {
|
|
||||||
const templatePath = join(
|
const templatePath = join(
|
||||||
process.cwd(),
|
process.cwd(),
|
||||||
"..",
|
"..",
|
||||||
@@ -60,7 +55,7 @@ const generateStaticFiles = async (
|
|||||||
: `<img src="https://picsum.photos/32/32" />`
|
: `<img src="https://picsum.photos/32/32" />`
|
||||||
)
|
)
|
||||||
.replace("{{title}}", `<h1>${websiteData.title}</h1>`)
|
.replace("{{title}}", `<h1>${websiteData.title}</h1>`)
|
||||||
.replace("{{main_content}}", md.render(websiteData.main_content))
|
.replace("{{main_content}}", md.render(websiteData.main_content || ""))
|
||||||
.replace(
|
.replace(
|
||||||
"{{articles}}",
|
"{{articles}}",
|
||||||
websiteData.articles
|
websiteData.articles
|
||||||
@@ -81,14 +76,20 @@ const generateStaticFiles = async (
|
|||||||
})
|
})
|
||||||
.join("")
|
.join("")
|
||||||
)
|
)
|
||||||
.replace("{{additional_text}}", md.render(websiteData.additional_text));
|
.replace("{{additional_text}}", md.render(websiteData.additional_text || ""));
|
||||||
|
|
||||||
let uploadDir = "";
|
let uploadDir = "";
|
||||||
|
|
||||||
if (isPreview) {
|
if (isPreview) {
|
||||||
uploadDir = join(process.cwd(), "static", "user-websites", userId, websiteId);
|
uploadDir = join(
|
||||||
|
process.cwd(),
|
||||||
|
"static",
|
||||||
|
"user-websites",
|
||||||
|
websiteData.owner_id,
|
||||||
|
websiteData.id
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
uploadDir = join("/", "var", "www", "archtika-websites", userId, websiteId);
|
uploadDir = join("/", "var", "www", "archtika-websites", websiteData.owner_id, websiteData.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
await mkdir(uploadDir, { recursive: true });
|
await mkdir(uploadDir, { recursive: true });
|
||||||
@@ -110,8 +111,8 @@ const generateStaticFiles = async (
|
|||||||
.replace("{{cover_image}}", `<img src="https://picsum.photos/600/200" />`)
|
.replace("{{cover_image}}", `<img src="https://picsum.photos/600/200" />`)
|
||||||
.replace("{{title}}", `<h1>${article.title}</h1>`)
|
.replace("{{title}}", `<h1>${article.title}</h1>`)
|
||||||
.replace("{{publication_date}}", `<p>${article.publication_date}</p>`)
|
.replace("{{publication_date}}", `<p>${article.publication_date}</p>`)
|
||||||
.replace("{{main_content}}", md.render(article.main_content))
|
.replace("{{main_content}}", md.render(article.main_content || ""))
|
||||||
.replace("{{additional_text}}", md.render(websiteData.additional_text));
|
.replace("{{additional_text}}", md.render(websiteData.additional_text || ""));
|
||||||
|
|
||||||
await writeFile(join(uploadDir, "articles", `${articleFileName}.html`), articleFileContents);
|
await writeFile(join(uploadDir, "articles", `${articleFileName}.html`), articleFileContents);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,8 @@
|
|||||||
<WebsiteEditor
|
<WebsiteEditor
|
||||||
id={data.website.id}
|
id={data.website.id}
|
||||||
title={data.website.title}
|
title={data.website.title}
|
||||||
previewContent="https://aurora.thilohohlt.com"
|
previewContent="http://localhost:5173/user-websites/{data.websiteOverview.owner_id}/{data
|
||||||
|
.websiteOverview.id}/index.html"
|
||||||
fullPreview={true}
|
fullPreview={true}
|
||||||
>
|
>
|
||||||
<section>
|
<section>
|
||||||
|
|||||||
Reference in New Issue
Block a user