Run specific test suites in serial matter and restrict file uploads

This commit is contained in:
thiloho
2024-09-01 16:51:21 +02:00
parent 8deefc41d1
commit 49c5b2a541
5 changed files with 109 additions and 10 deletions

View File

@@ -0,0 +1,76 @@
-- migrate:up
CREATE OR REPLACE FUNCTION api.upload_file (BYTEA, OUT file_id UUID)
AS $$
DECLARE
_headers JSON := CURRENT_SETTING('request.headers', TRUE)::JSON;
_website_id UUID := (_headers ->> 'x-website-id')::UUID;
_mimetype TEXT := _headers ->> 'x-mimetype';
_original_filename TEXT := _headers ->> 'x-original-filename';
_allowed_mimetypes TEXT[] := ARRAY['image/png', 'image/jpeg', 'image/webp'];
_max_file_size INT := 5 * 1024 * 1024;
_has_access BOOLEAN;
BEGIN
_has_access = internal.user_has_website_access (_website_id, 20);
IF OCTET_LENGTH($1) = 0 THEN
RAISE invalid_parameter_value
USING message = 'No file data was provided';
END IF;
IF _mimetype IS NULL OR _mimetype NOT IN (
SELECT
UNNEST(_allowed_mimetypes)) THEN
RAISE invalid_parameter_value
USING message = 'Invalid MIME type. Allowed types are: png, jpg, webp';
END IF;
IF OCTET_LENGTH($1) > _max_file_size THEN
RAISE program_limit_exceeded
USING message = FORMAT('File size exceeds the maximum limit of %s MB', _max_file_size / (1024 * 1024));
END IF;
INSERT INTO internal.media (website_id, blob, mimetype, original_name)
VALUES (_website_id, $1, _mimetype, _original_filename)
RETURNING
id INTO file_id;
END;
$$
LANGUAGE plpgsql
SECURITY DEFINER;
GRANT EXECUTE ON FUNCTION api.upload_file (BYTEA) TO authenticated_user;
-- migrate:down
DROP FUNCTION api.upload_file (BYTEA);
CREATE FUNCTION api.upload_file (BYTEA, OUT file_id UUID)
AS $$
DECLARE
_headers JSON := CURRENT_SETTING('request.headers', TRUE)::JSON;
_website_id UUID := (_headers ->> 'x-website-id')::UUID;
_mimetype TEXT := _headers ->> 'x-mimetype';
_original_filename TEXT := _headers ->> 'x-original-filename';
_allowed_mimetypes TEXT[] := ARRAY['image/png', 'image/jpeg', 'image/webp'];
_max_file_size INT := 5 * 1024 * 1024;
BEGIN
IF OCTET_LENGTH($1) = 0 THEN
RAISE invalid_parameter_value
USING message = 'No file data was provided';
END IF;
IF _mimetype IS NULL OR _mimetype NOT IN (
SELECT
UNNEST(_allowed_mimetypes)) THEN
RAISE invalid_parameter_value
USING message = 'Invalid MIME type. Allowed types are: png, jpg, webp';
END IF;
IF OCTET_LENGTH($1) > _max_file_size THEN
RAISE program_limit_exceeded
USING message = FORMAT('File size exceeds the maximum limit of %s MB', _max_file_size / (1024 * 1024));
END IF;
INSERT INTO internal.media (website_id, blob, mimetype, original_name)
VALUES (_website_id, $1, _mimetype, _original_filename)
RETURNING
id INTO file_id;
END;
$$
LANGUAGE plpgsql
SECURITY DEFINER;
GRANT EXECUTE ON FUNCTION api.upload_file (BYTEA) TO authenticated_user;

3
web-app/.gitignore vendored
View File

@@ -19,3 +19,6 @@ Thumbs.db
# Vite # Vite
vite.config.js.timestamp-* vite.config.js.timestamp-*
vite.config.ts.timestamp-* vite.config.ts.timestamp-*
# Playwright
test-results

View File

@@ -77,9 +77,19 @@
<label> <label>
Filter: Filter:
<select name="website_filter"> <select name="website_filter">
<option value="all">Show all</option> <option value="all" selected={"all" === $page.url.searchParams.get("website_filter")}
<option value="creations">Created by you</option> >Show all</option
<option value="shared">Shared with you</option> >
<option
value="creations"
selected={"creations" === $page.url.searchParams.get("website_filter")}
>Created by you</option
>
<option
value="shared"
selected={"shared" === $page.url.searchParams.get("website_filter")}
>Shared with you</option
>
</select> </select>
</label> </label>
<button type="submit">Submit</button> <button type="submit">Submit</button>

View File

@@ -66,9 +66,19 @@
<label> <label>
Filter: Filter:
<select name="article_filter"> <select name="article_filter">
<option value="all">Show all</option> <option value="all" selected={"all" === $page.url.searchParams.get("article_filter")}
<option value="creations">Created by you</option> >Show all</option
<option value="shared">Created by others</option> >
<option
value="creations"
selected={"creations" === $page.url.searchParams.get("article_filter")}
>Created by you</option
>
<option
value="shared"
selected={"shared" === $page.url.searchParams.get("article_filter")}
>Created by others</option
>
</select> </select>
</label> </label>
<button type="submit">Submit</button> <button type="submit">Submit</button>

View File

@@ -125,7 +125,7 @@ test.describe("Blog", () => {
}); });
}); });
test.describe("Articles", () => { test.describe.serial("Articles", () => {
test("Create article", async ({ authenticatedPage: page }) => { test("Create article", async ({ authenticatedPage: page }) => {
await page.getByRole("link", { name: "Blog" }).click(); await page.getByRole("link", { name: "Blog" }).click();
await page.getByRole("link", { name: "Articles" }).click(); await page.getByRole("link", { name: "Articles" }).click();
@@ -171,7 +171,7 @@ test.describe("Blog", () => {
}); });
}); });
test.describe("Collaborators", () => { test.describe.serial("Collaborators", () => {
test("Add collaborator", async ({ authenticatedPage: page }) => { test("Add collaborator", async ({ authenticatedPage: page }) => {
await page.getByRole("link", { name: "Blog" }).click(); await page.getByRole("link", { name: "Blog" }).click();
await page.getByRole("link", { name: "Collaborators" }).click(); await page.getByRole("link", { name: "Collaborators" }).click();
@@ -202,7 +202,7 @@ test.describe("Blog", () => {
}); });
test.describe("Docs", () => { test.describe("Docs", () => {
test.describe("Categories", () => { test.describe.serial("Categories", () => {
test("Create category", async ({ authenticatedPage: page }) => { test("Create category", async ({ authenticatedPage: page }) => {
await page.getByRole("link", { name: "Documentation" }).click(); await page.getByRole("link", { name: "Documentation" }).click();
await page.getByRole("link", { name: "Categories" }).click(); await page.getByRole("link", { name: "Categories" }).click();
@@ -299,7 +299,7 @@ test("Delete websites", async ({ authenticatedPage: page }) => {
await expect(page.getByRole("link", { name: "All websites" })).toBeHidden(); await expect(page.getByRole("link", { name: "All websites" })).toBeHidden();
}); });
test("Delete account", async ({ authenticatedPage: page }) => { test("Delete accounts", async ({ authenticatedPage: page }) => {
await page.getByRole("link", { name: "Account" }).click(); await page.getByRole("link", { name: "Account" }).click();
await page.getByRole("button", { name: "Delete account" }).click(); await page.getByRole("button", { name: "Delete account" }).click();
await page.getByLabel("Password:").click(); await page.getByLabel("Password:").click();