mirror of
https://github.com/thiloho/archtika.git
synced 2025-11-22 10:51:36 +01:00
Update policies for collaborators for stricter rules
This commit is contained in:
@@ -22,9 +22,9 @@ CREATE TABLE internal.user (
|
||||
|
||||
CREATE TABLE internal.website (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
owner_id UUID REFERENCES internal.user(id) ON DELETE CASCADE NOT NULL DEFAULT (current_setting('request.jwt.claims', true)::JSON->>'user_id')::UUID,
|
||||
user_id UUID REFERENCES internal.user(id) ON DELETE CASCADE NOT NULL DEFAULT (current_setting('request.jwt.claims', true)::JSON->>'user_id')::UUID,
|
||||
content_type VARCHAR(10) CHECK (content_type IN ('Blog', 'Docs')) NOT NULL,
|
||||
title VARCHAR(50) NOT NULL CHECK (trim(title) <> ''),
|
||||
title VARCHAR(50) NOT NULL CHECK (trim(title) != ''),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CLOCK_TIMESTAMP(),
|
||||
last_modified_at TIMESTAMPTZ NOT NULL DEFAULT CLOCK_TIMESTAMP(),
|
||||
last_modified_by UUID REFERENCES internal.user(id) ON DELETE SET NULL
|
||||
@@ -56,14 +56,14 @@ CREATE TABLE internal.header (
|
||||
last_modified_at TIMESTAMPTZ NOT NULL DEFAULT CLOCK_TIMESTAMP(),
|
||||
last_modified_by UUID REFERENCES internal.user(id) ON DELETE SET NULL,
|
||||
CONSTRAINT logo_content_check CHECK (
|
||||
(logo_type = 'text' AND logo_text IS NOT NULL AND trim(logo_text) <> '') OR
|
||||
(logo_type = 'text' AND logo_text IS NOT NULL AND trim(logo_text) != '') OR
|
||||
(logo_type = 'image' AND logo_image IS NOT NULL)
|
||||
)
|
||||
);
|
||||
|
||||
CREATE TABLE internal.home (
|
||||
website_id UUID PRIMARY KEY REFERENCES internal.website(id) ON DELETE CASCADE,
|
||||
main_content TEXT NOT NULL CHECK (trim(main_content) <> ''),
|
||||
main_content TEXT NOT NULL CHECK (trim(main_content) != ''),
|
||||
last_modified_at TIMESTAMPTZ NOT NULL DEFAULT CLOCK_TIMESTAMP(),
|
||||
last_modified_by UUID REFERENCES internal.user(id) ON DELETE SET NULL
|
||||
);
|
||||
@@ -72,12 +72,12 @@ CREATE TABLE internal.article (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
website_id UUID REFERENCES internal.website(id) ON DELETE CASCADE NOT NULL,
|
||||
user_id UUID REFERENCES internal.user(id) ON DELETE SET NULL,
|
||||
title VARCHAR(100) NOT NULL CHECK (trim(title) <> ''),
|
||||
meta_description VARCHAR(250) CHECK (trim(meta_description) <> ''),
|
||||
meta_author VARCHAR(100) CHECK (trim(meta_author) <> ''),
|
||||
title VARCHAR(100) NOT NULL CHECK (trim(title) != ''),
|
||||
meta_description VARCHAR(250) CHECK (trim(meta_description) != ''),
|
||||
meta_author VARCHAR(100) CHECK (trim(meta_author) != ''),
|
||||
cover_image UUID REFERENCES internal.media(id) ON DELETE SET NULL,
|
||||
publication_date DATE NOT NULL DEFAULT CURRENT_DATE,
|
||||
main_content TEXT CHECK (trim(main_content) <> ''),
|
||||
main_content TEXT CHECK (trim(main_content) != ''),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CLOCK_TIMESTAMP(),
|
||||
last_modified_at TIMESTAMPTZ NOT NULL DEFAULT CLOCK_TIMESTAMP(),
|
||||
last_modified_by UUID REFERENCES internal.user(id) ON DELETE SET NULL
|
||||
@@ -85,7 +85,7 @@ CREATE TABLE internal.article (
|
||||
|
||||
CREATE TABLE internal.footer (
|
||||
website_id UUID PRIMARY KEY REFERENCES internal.website(id) ON DELETE CASCADE,
|
||||
additional_text VARCHAR(250) NOT NULL CHECK (trim(additional_text) <> ''),
|
||||
additional_text VARCHAR(250) NOT NULL CHECK (trim(additional_text) != ''),
|
||||
last_modified_at TIMESTAMPTZ NOT NULL DEFAULT CLOCK_TIMESTAMP(),
|
||||
last_modified_by UUID REFERENCES internal.user(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
@@ -23,7 +23,7 @@ EXECUTE FUNCTION internal.check_role_exists();
|
||||
CREATE FUNCTION
|
||||
internal.encrypt_pass() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF TG_OP = 'INSERT' OR NEW.password_hash <> OLD.password_hash THEN
|
||||
IF TG_OP = 'INSERT' OR NEW.password_hash != OLD.password_hash THEN
|
||||
NEW.password_hash = crypt(NEW.password_hash, gen_salt('bf'));
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
|
||||
@@ -17,7 +17,7 @@ WITH (security_invoker = on)
|
||||
AS
|
||||
SELECT
|
||||
id,
|
||||
owner_id,
|
||||
user_id,
|
||||
content_type,
|
||||
title,
|
||||
created_at,
|
||||
|
||||
@@ -20,7 +20,7 @@ BEGIN
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM internal.website
|
||||
WHERE id = website_id AND owner_id = _user_id
|
||||
WHERE id = website_id AND user_id = _user_id
|
||||
) INTO _has_access;
|
||||
|
||||
IF _has_access THEN
|
||||
@@ -103,7 +103,15 @@ USING (internal.user_has_website_access(website_id, 20));
|
||||
|
||||
CREATE POLICY delete_article ON internal.article
|
||||
FOR DELETE
|
||||
USING (internal.user_has_website_access(website_id, 30));
|
||||
USING (
|
||||
internal.user_has_website_access(website_id, 30)
|
||||
OR
|
||||
(
|
||||
internal.user_has_website_access(website_id, 20)
|
||||
AND
|
||||
user_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY insert_article ON internal.article
|
||||
FOR INSERT
|
||||
@@ -125,15 +133,48 @@ USING (internal.user_has_website_access(website_id, 10));
|
||||
|
||||
CREATE POLICY insert_collaborations ON internal.collab
|
||||
FOR INSERT
|
||||
WITH CHECK (internal.user_has_website_access(website_id, 30));
|
||||
WITH CHECK (
|
||||
CASE
|
||||
WHEN internal.user_has_website_access(website_id, 40) THEN
|
||||
true
|
||||
WHEN internal.user_has_website_access(website_id, 30) THEN
|
||||
(user_id != (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID)
|
||||
AND
|
||||
(permission_level < 30)
|
||||
ELSE
|
||||
false
|
||||
END
|
||||
);
|
||||
|
||||
CREATE POLICY update_collaborations ON internal.collab
|
||||
FOR UPDATE
|
||||
USING (internal.user_has_website_access(website_id, 30));
|
||||
USING (
|
||||
CASE
|
||||
WHEN internal.user_has_website_access(website_id, 40) THEN
|
||||
true
|
||||
WHEN internal.user_has_website_access(website_id, 30) THEN
|
||||
(user_id != (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID)
|
||||
AND
|
||||
(permission_level < 30)
|
||||
ELSE
|
||||
false
|
||||
END
|
||||
);
|
||||
|
||||
CREATE POLICY delete_collaborations ON internal.collab
|
||||
FOR DELETE
|
||||
USING (internal.user_has_website_access(website_id, 30));
|
||||
USING (
|
||||
CASE
|
||||
WHEN internal.user_has_website_access(website_id, 40) THEN
|
||||
TRUE
|
||||
WHEN internal.user_has_website_access(website_id, 30) THEN
|
||||
(user_id != (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID)
|
||||
AND
|
||||
(permission_level < 30)
|
||||
ELSE
|
||||
FALSE
|
||||
END
|
||||
);
|
||||
|
||||
|
||||
-- migrate:down
|
||||
|
||||
@@ -4,7 +4,7 @@ WITH (security_invoker = on)
|
||||
AS
|
||||
SELECT
|
||||
w.id,
|
||||
w.owner_id,
|
||||
w.user_id,
|
||||
w.content_type,
|
||||
w.title,
|
||||
s.accent_color_light_theme,
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
-- migrate:up
|
||||
CREATE FUNCTION internal.check_user_not_website_owner()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1
|
||||
FROM internal.website
|
||||
WHERE id = NEW.website_id AND user_id = NEW.user_id
|
||||
) THEN
|
||||
RAISE foreign_key_violation USING MESSAGE = 'User cannot be added as a collaborator to their own website';
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
CREATE CONSTRAINT TRIGGER check_user_not_website_owner
|
||||
AFTER INSERT ON internal.collab
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION internal.check_user_not_website_owner();
|
||||
|
||||
-- migrate:down
|
||||
DROP TRIGGER check_user_not_website_owner ON internal.collab;
|
||||
DROP FUNCTION internal.check_user_not_website_owner();
|
||||
@@ -81,15 +81,9 @@ const generateStaticFiles = async (websiteData: any, isPreview: boolean = true)
|
||||
let uploadDir = "";
|
||||
|
||||
if (isPreview) {
|
||||
uploadDir = join(
|
||||
process.cwd(),
|
||||
"static",
|
||||
"user-websites",
|
||||
websiteData.owner_id,
|
||||
websiteData.id
|
||||
);
|
||||
uploadDir = join(process.cwd(), "static", "user-websites", websiteData.user_id, websiteData.id);
|
||||
} else {
|
||||
uploadDir = join("/", "var", "www", "archtika-websites", websiteData.owner_id, websiteData.id);
|
||||
uploadDir = join("/", "var", "www", "archtika-websites", websiteData.user_id, websiteData.id);
|
||||
}
|
||||
|
||||
await mkdir(uploadDir, { recursive: true });
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<WebsiteEditor
|
||||
id={data.website.id}
|
||||
title={data.website.title}
|
||||
previewContent="http://localhost:5173/user-websites/{data.websiteOverview.owner_id}/{data
|
||||
previewContent="http://localhost:5173/user-websites/{data.websiteOverview.user_id}/{data
|
||||
.websiteOverview.id}/index.html"
|
||||
fullPreview={true}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user