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

1294 categories for organisations #1315

Merged
merged 9 commits into from
Oct 24, 2024
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
-- SPDX-FileCopyrightText: 2023 - 2024 Felix Mühlbauer (GFZ) <[email protected]>
-- SPDX-FileCopyrightText: 2023 - 2024 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences
-- SPDX-FileCopyrightText: 2024 Christian Meeßen (GFZ) <[email protected]>
-- SPDX-FileCopyrightText: 2024 Dusan Mijatovic (Netherlands eScience Center)
--
-- SPDX-License-Identifier: Apache-2.0

Expand Down Expand Up @@ -88,29 +89,24 @@ CREATE TABLE category (
id UUID PRIMARY KEY,
parent UUID REFERENCES category DEFAULT NULL,
community UUID REFERENCES community(id) DEFAULT NULL,
organisation UUID REFERENCES organisation(id) DEFAULT NULL,
allow_software BOOLEAN NOT NULL DEFAULT FALSE,
allow_projects BOOLEAN NOT NULL DEFAULT FALSE,
short_name VARCHAR(100) NOT NULL,
name VARCHAR(250) NOT NULL,
properties JSONB NOT NULL DEFAULT '{}'::jsonb,
provenance_iri VARCHAR(250) DEFAULT NULL, -- e.g. https://www.w3.org/TR/skos-reference/#mapping

CONSTRAINT unique_short_name UNIQUE NULLS NOT DISTINCT (parent, short_name, community),
CONSTRAINT unique_name UNIQUE NULLS NOT DISTINCT (parent, name, community),
CONSTRAINT only_one_entity CHECK (community IS NULL OR organisation IS NULL),
CONSTRAINT unique_short_name UNIQUE NULLS NOT DISTINCT (parent, short_name, community, organisation),
CONSTRAINT unique_name UNIQUE NULLS NOT DISTINCT (parent, name, community, organisation),
CONSTRAINT invalid_value_for_properties CHECK (properties - '{icon, is_highlight, description, subtitle, tree_level_labels}'::text[] = '{}'::jsonb),
CONSTRAINT highlight_must_be_top_level_category CHECK (NOT ((properties->>'is_highlight')::boolean AND parent IS NOT NULL))
);

CREATE INDEX category_parent_idx ON category(parent);
CREATE INDEX category_community_idx ON category(community);


CREATE TABLE category_for_software (
software_id UUID REFERENCES software (id),
category_id UUID REFERENCES category (id),
PRIMARY KEY (software_id, category_id)
);

CREATE INDEX category_for_software_category_id_idx ON category_for_software(category_id);

CREATE INDEX category_organisation_idx ON category(organisation);

-- sanitize categories

Expand All @@ -126,6 +122,9 @@ BEGIN
IF NEW.parent IS NOT NULL AND (SELECT community FROM category WHERE id = NEW.parent) IS DISTINCT FROM NEW.community THEN
RAISE EXCEPTION USING MESSAGE = 'The community must be the same as of its parent.';
END IF;
IF NEW.parent IS NOT NULL AND (SELECT organisation FROM category WHERE id = NEW.parent) IS DISTINCT FROM NEW.organisation THEN
RAISE EXCEPTION USING MESSAGE = 'The organisation must be the same as of its parent.';
END IF;
NEW.id = gen_random_uuid();
RETURN NEW;
END
Expand All @@ -151,6 +150,12 @@ BEGIN
IF NEW.parent IS NOT NULL AND (SELECT community FROM category WHERE id = NEW.parent) IS DISTINCT FROM NEW.community THEN
RAISE EXCEPTION USING MESSAGE = 'The community must be the same as of its parent.';
END IF;
IF NEW.organisation IS DISTINCT FROM OLD.organisation THEN
RAISE EXCEPTION USING MESSAGE = 'The organisation this category belongs to may not be changed.';
END IF;
IF NEW.parent IS NOT NULL AND (SELECT organisation FROM category WHERE id = NEW.parent) IS DISTINCT FROM NEW.organisation THEN
RAISE EXCEPTION USING MESSAGE = 'The organisation must be the same as of its parent.';
END IF;
RETURN NEW;
END
$$;
Expand Down Expand Up @@ -224,6 +229,19 @@ $$
$$;


-- TABLE FOR software categories
-- includes organisation, community and general categories
-- Note! to filter specific categories of an community or organisation use join with community table

CREATE TABLE category_for_software (
software_id UUID REFERENCES software (id),
category_id UUID REFERENCES category (id),
PRIMARY KEY (software_id, category_id)
);

CREATE INDEX category_for_software_category_id_idx ON category_for_software(category_id);

-- RPC for software page to show all software categories
CREATE FUNCTION category_paths_by_software_expanded(software_id UUID)
RETURNS JSON
LANGUAGE SQL STABLE AS
Expand All @@ -238,3 +256,45 @@ $$
ELSE '[]'::json
END AS result
$$;


-- TABLE FOR project categories
-- currently used only for organisation categories
CREATE TABLE category_for_project (
project_id UUID REFERENCES project (id),
category_id UUID REFERENCES category (id),
PRIMARY KEY (project_id, category_id)
);

CREATE INDEX category_for_project_category_id_idx ON category_for_project(category_id);

-- RPC for project page to show all project categories
CREATE FUNCTION category_paths_by_project_expanded(project_id UUID)
RETURNS JSON
LANGUAGE SQL STABLE AS
$$
WITH
cat_ids AS
(SELECT
category_id
FROM
category_for_project
WHERE
category_for_project.project_id = category_paths_by_project_expanded.project_id
),
paths AS
(
SELECT
category_path_expanded(category_id) AS path
FROM cat_ids
)
SELECT
CASE
WHEN EXISTS(
SELECT 1 FROM cat_ids
) THEN (
SELECT json_agg(path) FROM paths
)
ELSE '[]'::json
END AS result
$$;
30 changes: 26 additions & 4 deletions database/020-row-level-security.sql
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,6 @@ CREATE POLICY admin_all_rights ON testimonial TO rsd_admin


-- categories

ALTER TABLE category ENABLE ROW LEVEL SECURITY;

-- allow everybody to read
Expand All @@ -290,7 +289,11 @@ CREATE POLICY anyone_can_read ON category

CREATE POLICY maintainer_all_rights ON category
TO rsd_user
USING (community IN (SELECT * FROM communities_of_current_maintainer()));
USING (
(community IS NOT NULL AND community IN (SELECT * FROM communities_of_current_maintainer()))
OR
(organisation IS NOT NULL AND organisation IN (SELECT * FROM organisations_of_current_maintainer()))
);

-- allow admins to have full read/write access
CREATE POLICY admin_all_rights ON category
Expand All @@ -299,14 +302,13 @@ CREATE POLICY admin_all_rights ON category


-- categories for software

ALTER TABLE category_for_software ENABLE ROW LEVEL SECURITY;

-- allow everybody to read metadata of published software
CREATE POLICY anyone_can_read ON category_for_software
FOR SELECT
TO rsd_web_anon, rsd_user
USING (EXISTS(SELECT 1 FROM software WHERE id = software_id));
USING (software_id IN (SELECT id FROM software));

-- allow software maintainers to have read/write access to their software
CREATE POLICY maintainer_all_rights ON category_for_software
Expand All @@ -319,6 +321,26 @@ CREATE POLICY admin_all_rights ON category_for_software
USING (TRUE);


-- categories for project
ALTER TABLE category_for_project ENABLE ROW LEVEL SECURITY;

-- allow everybody to read metadata of published projects
CREATE POLICY anyone_can_read ON category_for_project
FOR SELECT
TO rsd_web_anon, rsd_user
USING (project_id IN (SELECT id FROM project));

-- allow software maintainers to have read/write access to their project
CREATE POLICY maintainer_all_rights ON category_for_project
TO rsd_user
USING (project_id IN (SELECT * FROM projects_of_current_maintainer()));

-- allow admins to have full read/write access
CREATE POLICY admin_all_rights ON category_for_project
TO rsd_admin
USING (TRUE);


-- keywords
ALTER TABLE keyword ENABLE ROW LEVEL SECURITY;

Expand Down
39 changes: 37 additions & 2 deletions database/109-category-functions.sql
Original file line number Diff line number Diff line change
@@ -1,13 +1,48 @@
-- SPDX-FileCopyrightText: 2024 Dusan Mijatovic (Netherlands eScience Center)
-- SPDX-FileCopyrightText: 2024 Ewan Cahen (Netherlands eScience Center) <[email protected]>
-- SPDX-FileCopyrightText: 2024 Netherlands eScience Center
--
-- SPDX-License-Identifier: Apache-2.0

-- DELETE organisation categories for specific community
CREATE FUNCTION delete_community_categories_from_software(software_id UUID, community_id UUID)
RETURNS VOID
LANGUAGE sql AS
$$
DELETE FROM category_for_software
USING category
WHERE category_for_software.category_id = category.id AND category_for_software.software_id = software_id AND category.community = community_id;
USING
category
WHERE
category_for_software.category_id = category.id AND
category_for_software.software_id = delete_community_categories_from_software.software_id AND
category.community = delete_community_categories_from_software.community_id;
$$;


-- DELETE organisation categories for specific software
CREATE FUNCTION delete_organisation_categories_from_software(software_id UUID, organisation_id UUID)
RETURNS VOID
LANGUAGE sql AS
$$
DELETE FROM category_for_software
USING
category
WHERE
category_for_software.category_id = category.id AND
category_for_software.software_id = delete_organisation_categories_from_software.software_id AND
category.organisation = delete_organisation_categories_from_software.organisation_id;
$$;

-- DELETE organisation categories for specific project
CREATE FUNCTION delete_organisation_categories_from_project(project_id UUID, organisation_id UUID)
RETURNS VOID
LANGUAGE sql AS
$$
DELETE FROM category_for_project
USING
category
WHERE
category_for_project.category_id = category.id AND
category_for_project.project_id = delete_organisation_categories_from_project.project_id AND
category.organisation = delete_organisation_categories_from_project.organisation_id;
$$;
70 changes: 66 additions & 4 deletions frontend/auth/permissions/isMaintainerOfOrganisation.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
// SPDX-FileCopyrightText: 2022 - 2023 Dusan Mijatovic (dv4all)
// SPDX-FileCopyrightText: 2022 - 2023 dv4all
// SPDX-FileCopyrightText: 2023 Dusan Mijatovic (Netherlands eScience Center)
// SPDX-FileCopyrightText: 2023 Netherlands eScience Center
// SPDX-FileCopyrightText: 2023 - 2024 Dusan Mijatovic (Netherlands eScience Center)
// SPDX-FileCopyrightText: 2023 - 2024 Netherlands eScience Center
//
// SPDX-License-Identifier: Apache-2.0

import {createJsonHeaders, getBaseUrl} from '../../utils/fetchHelpers'
import logger from '../../utils/logger'
import {OrganisationsOfProject} from '~/types/Project'
import {EditOrganisation, OrganisationsForSoftware} from '~/types/Organisation'
import {createJsonHeaders, getBaseUrl} from '~/utils/fetchHelpers'
import logger from '~/utils/logger'
import {RsdRole} from '../index'

type IsOrganisationMaintainerProps = {
Expand Down Expand Up @@ -92,5 +94,65 @@ export async function getMaintainerOrganisations({token}:
}
}

type CanEditOrganisationsProps={
account?: string
token?: string
organisations: OrganisationsOfProject[]| OrganisationsForSoftware[]
}

export async function canEditOrganisations({organisations,account,token}:CanEditOrganisationsProps){
try{
// collect isMaintainerRequests
const promises:Promise<boolean>[] = []
// prepare organisation list
const orgList = organisations.map((item, pos) => {
// save isMaintainer request
promises.push(isMaintainerOfOrganisation({
organisation: item.id,
account,
token
}))
// extract only needed props
const org: EditOrganisation = {
...item,
// additional props for edit type
position: pos + 1,
logo_b64: null,
logo_mime_type: null,
source: 'RSD' as 'RSD',
status: item.status,
// false by default
canEdit: false
}
return org
})
// run all isMaintainer requests in parallel
const isMaintainer = await Promise.all(promises)
const canEditOrganisations = orgList.map((item, pos) => {
// update canEdit based on isMaintainer requests
if (isMaintainer[pos]) item.canEdit = isMaintainer[pos]
return item
})
return canEditOrganisations
}catch(e:any){
logger(`canEditOrganisations: ${e.message}`, 'error')
// on error all items set to false
return organisations.map((item, pos) => {
// extract only needed props
const org: EditOrganisation = {
...item,
// additional props for edit type
position: pos + 1,
logo_b64: null,
logo_mime_type: null,
source: 'RSD' as 'RSD',
status: item.status,
// false by default
canEdit: false
}
return org
})
}
}

export default isMaintainerOfOrganisation
1 change: 1 addition & 0 deletions frontend/components/admin/categories/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export default function AdminCategories() {
<CategoryEditTree
roots={roots}
community={null}
organisation={null}
onMutation={onMutation}
/>
}
Expand Down
Loading