Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions packages/database/src/dbTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,36 @@ export type Database = {
},
]
}
ConceptAccess: {
Row: {
account_uid: string
concept_id: number
}
Insert: {
account_uid: string
concept_id: number
}
Update: {
account_uid?: string
concept_id?: number
}
Relationships: [
{
foreignKeyName: "ConceptAccess_concept_id_fkey"
columns: ["concept_id"]
isOneToOne: false
referencedRelation: "Concept"
referencedColumns: ["id"]
},
{
foreignKeyName: "ConceptAccess_concept_id_fkey"
columns: ["concept_id"]
isOneToOne: false
referencedRelation: "my_concepts"
referencedColumns: ["id"]
},
]
}
Content: {
Row: {
author_id: number | null
Expand Down Expand Up @@ -424,6 +454,43 @@ export type Database = {
},
]
}
ContentAccess: {
Row: {
account_uid: string
content_id: number
}
Insert: {
account_uid: string
content_id: number
}
Update: {
account_uid?: string
content_id?: number
}
Relationships: [
{
foreignKeyName: "ContentAccess_content_id_fkey"
columns: ["content_id"]
isOneToOne: false
referencedRelation: "Content"
referencedColumns: ["id"]
},
{
foreignKeyName: "ContentAccess_content_id_fkey"
columns: ["content_id"]
isOneToOne: false
referencedRelation: "my_contents"
referencedColumns: ["id"]
},
{
foreignKeyName: "ContentAccess_content_id_fkey"
columns: ["content_id"]
isOneToOne: false
referencedRelation: "my_contents_with_embedding_openai_text_embedding_3_small_1536"
referencedColumns: ["id"]
},
]
}
ContentEmbedding_openai_text_embedding_3_small_1536: {
Row: {
model: Database["public"]["Enums"]["EmbeddingName"]
Expand Down Expand Up @@ -1320,10 +1387,17 @@ export type Database = {
isSetofReturn: true
}
}
can_access_account: { Args: { account_uid: string }; Returns: boolean }
can_view_specific_concept: { Args: { id: number }; Returns: boolean }
can_view_specific_content: { Args: { id: number }; Returns: boolean }
compute_arity_local: {
Args: { lit_content: Json; schema_id: number }
Returns: number
}
concept_in_editable_space: {
Args: { concept_id: number }
Returns: boolean
}
concept_in_relations:
| {
Args: { concept: Database["public"]["Tables"]["Concept"]["Row"] }
Expand Down Expand Up @@ -1377,6 +1451,10 @@ export type Database = {
isSetofReturn: true
}
}
content_in_editable_space: {
Args: { content_id: number }
Returns: boolean
}
content_in_space: { Args: { content_id: number }; Returns: boolean }
content_of_concept: {
Args: { concept: Database["public"]["Views"]["my_concepts"]["Row"] }
Expand Down Expand Up @@ -1434,6 +1512,7 @@ export type Database = {
isSetofReturn: true
}
}
editor_in_space: { Args: { space_id: number }; Returns: boolean }
end_sync_task: {
Args: {
s_function: string
Expand Down Expand Up @@ -1511,6 +1590,7 @@ export type Database = {
text_content: string
}[]
}
my_editable_space_ids: { Args: never; Returns: number[] }
my_space_ids: { Args: never; Returns: number[] }
my_user_accounts: { Args: never; Returns: string[] }
propose_sync_task: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
CREATE OR REPLACE FUNCTION public.can_access_account(account_uid UUID) RETURNS boolean
STABLE SECURITY DEFINER
SET search_path = ''
LANGUAGE sql
AS $$
SELECT account_uid = auth.uid() OR EXISTS (
SELECT 1 FROM public.group_membership
WHERE member_id = auth.uid() AND group_id=account_uid
LIMIT 1
);
$$;

COMMENT ON FUNCTION public.can_access_account IS 'security utility: Is this my account or one of my groups?';

CREATE OR REPLACE FUNCTION public.my_editable_space_ids() RETURNS BIGINT []
STABLE SECURITY DEFINER
SET search_path = ''
LANGUAGE sql
AS $$
SELECT COALESCE(array_agg(distinct space_id), '{}') AS ids
FROM public."SpaceAccess"
JOIN public.my_user_accounts() ON (account_uid = my_user_accounts)
WHERE editor;
$$;
COMMENT ON FUNCTION public.my_editable_space_ids IS 'security utility: all spaces the user has edit access to';


CREATE OR REPLACE FUNCTION public.editor_in_space(space_id BIGINT) RETURNS boolean
STABLE SECURITY DEFINER
SET search_path = ''
LANGUAGE sql
AS $$
SELECT EXISTS (SELECT 1 FROM public."SpaceAccess" AS sa
JOIN public.my_user_accounts() ON (sa.account_uid = my_user_accounts)
WHERE sa.space_id = editor_in_space.space_id AND sa.editor);
$$;

COMMENT ON FUNCTION public.editor_in_space IS 'security utility: does current user have edit access to this space?';

CREATE OR REPLACE FUNCTION public.content_in_editable_space(content_id BIGINT) RETURNS boolean
STABLE
SET search_path = ''
LANGUAGE sql
AS $$
SELECT public.editor_in_space(space_id) FROM public."Content" WHERE id=content_id
$$;

COMMENT ON FUNCTION public.content_in_editable_space IS 'security utility: does current user have editor access to this content''s space?';

CREATE OR REPLACE FUNCTION public.concept_in_editable_space(concept_id BIGINT) RETURNS boolean
STABLE
SET search_path = ''
LANGUAGE sql
AS $$
SELECT public.editor_in_space(space_id) FROM public."Concept" WHERE id=concept_id
$$;

COMMENT ON FUNCTION public.concept_in_editable_space IS 'security utility: does current user have editor access to this concept''s space?';

CREATE TABLE IF NOT EXISTS public."ContentAccess" (
account_uid UUID NOT NULL,
content_id bigint NOT NULL
);

ALTER TABLE ONLY public."ContentAccess"
ADD CONSTRAINT "ContentAccess_pkey" PRIMARY KEY (account_uid, content_id);

ALTER TABLE public."ContentAccess" OWNER TO "postgres";

COMMENT ON TABLE public."ContentAccess" IS 'An access control entry for a content';

COMMENT ON COLUMN public."ContentAccess".content_id IS 'The content item for which access is granted';

COMMENT ON COLUMN public."ContentAccess".account_uid IS 'The identity of the user account';

ALTER TABLE ONLY public."ContentAccess"
ADD CONSTRAINT "ContentAccess_account_uid_fkey" FOREIGN KEY (
account_uid
) REFERENCES auth.users (id) ON UPDATE CASCADE ON DELETE CASCADE;

CREATE INDEX content_access_content_id_idx ON public."ContentAccess" (content_id);

ALTER TABLE ONLY public."ContentAccess"
ADD CONSTRAINT "ContentAccess_content_id_fkey" FOREIGN KEY (
content_id
) REFERENCES public."Content" (
id
) ON UPDATE CASCADE ON DELETE CASCADE;

GRANT ALL ON TABLE public."ContentAccess" TO authenticated;
GRANT ALL ON TABLE public."ContentAccess" TO service_role;
REVOKE ALL ON TABLE public."ContentAccess" FROM anon;

CREATE OR REPLACE FUNCTION public.can_view_specific_content(id BIGINT) RETURNS BOOLEAN
STABLE SECURITY DEFINER
SET search_path = ''
LANGUAGE sql
AS $$
SELECT EXISTS(
SELECT true FROM public."ContentAccess"
JOIN public.my_user_accounts() ON (account_uid=my_user_accounts)
WHERE content_id=id
LIMIT 1);
$$;

CREATE OR REPLACE VIEW public.my_contents AS
SELECT
id,
document_id,
source_local_id,
variant,
author_id,
creator_id,
created,
text,
metadata,
scale,
space_id,
last_modified,
part_of_id
FROM public."Content"
WHERE (
space_id = any(public.my_space_ids())
OR public.can_view_specific_content(id)
);

DROP POLICY IF EXISTS content_policy ON public."Content";
CREATE POLICY content_select_policy ON public."Content" FOR SELECT USING (public.in_space(space_id) OR public.can_view_specific_content(id));
CREATE POLICY content_delete_policy ON public."Content" FOR DELETE USING (public.in_space(space_id));
CREATE POLICY content_insert_policy ON public."Content" FOR INSERT WITH CHECK (public.in_space(space_id));
CREATE POLICY content_update_policy ON public."Content" FOR UPDATE WITH CHECK (public.in_space(space_id));

ALTER TABLE public."ContentAccess" ENABLE ROW LEVEL SECURITY;

DROP POLICY IF EXISTS content_access_policy ON public."ContentAccess";
DROP POLICY IF EXISTS content_access_select_policy ON public."ContentAccess";
CREATE POLICY content_access_select_policy ON public."ContentAccess" FOR SELECT USING (public.content_in_space(content_id) OR public.can_access_account(account_uid));
DROP POLICY IF EXISTS content_access_delete_policy ON public."ContentAccess";
CREATE POLICY content_access_delete_policy ON public."ContentAccess" FOR DELETE USING (public.content_in_editable_space(content_id) OR public.can_access_account(account_uid));
DROP POLICY IF EXISTS content_access_insert_policy ON public."ContentAccess";
CREATE POLICY content_access_insert_policy ON public."ContentAccess" FOR INSERT WITH CHECK (public.content_in_editable_space(content_id));
DROP POLICY IF EXISTS content_access_update_policy ON public."ContentAccess";
CREATE POLICY content_access_update_policy ON public."ContentAccess" FOR UPDATE WITH CHECK (public.content_in_editable_space(content_id));


CREATE TABLE IF NOT EXISTS public."ConceptAccess" (
account_uid UUID NOT NULL,
concept_id bigint NOT NULL
);

ALTER TABLE ONLY public."ConceptAccess"
ADD CONSTRAINT "ConceptAccess_pkey" PRIMARY KEY (account_uid, concept_id);

ALTER TABLE public."ConceptAccess" OWNER TO "postgres";

COMMENT ON TABLE public."ConceptAccess" IS 'An access control entry for a concept';

COMMENT ON COLUMN public."ConceptAccess".concept_id IS 'The concept item for which access is granted';

COMMENT ON COLUMN public."ConceptAccess".account_uid IS 'The identity of the user account';

ALTER TABLE ONLY public."ConceptAccess"
ADD CONSTRAINT "ConceptAccess_account_uid_fkey" FOREIGN KEY (
account_uid
) REFERENCES auth.users (id) ON UPDATE CASCADE ON DELETE CASCADE;

CREATE INDEX concept_access_concept_id_idx ON public."ConceptAccess" (concept_id);

ALTER TABLE ONLY public."ConceptAccess"
ADD CONSTRAINT "ConceptAccess_concept_id_fkey" FOREIGN KEY (
concept_id
) REFERENCES public."Concept" (
id
) ON UPDATE CASCADE ON DELETE CASCADE;

GRANT ALL ON TABLE public."ConceptAccess" TO authenticated;
GRANT ALL ON TABLE public."ConceptAccess" TO service_role;
REVOKE ALL ON TABLE public."ConceptAccess" FROM anon;

CREATE OR REPLACE FUNCTION public.can_view_specific_concept(id BIGINT) RETURNS BOOLEAN
STABLE SECURITY DEFINER
SET search_path = ''
LANGUAGE sql
AS $$
SELECT EXISTS(
SELECT true FROM public."ConceptAccess"
JOIN public.my_user_accounts() ON (account_uid=my_user_accounts)
WHERE concept_id=id
LIMIT 1);
$$;

CREATE OR REPLACE VIEW public.my_concepts AS
SELECT
id,
epistemic_status,
name,
description,
author_id,
created,
last_modified,
space_id,
arity,
schema_id,
literal_content,
reference_content,
refs,
is_schema,
represented_by_id
FROM public."Concept"
WHERE (
space_id = any(public.my_space_ids())
OR public.can_view_specific_concept(id)
);

DROP POLICY IF EXISTS concept_policy ON public."Concept";
CREATE POLICY concept_select_policy ON public."Concept" FOR SELECT USING (public.in_space(space_id) OR public.can_view_specific_concept(id));
CREATE POLICY concept_delete_policy ON public."Concept" FOR DELETE USING (public.in_space(space_id));
CREATE POLICY concept_insert_policy ON public."Concept" FOR INSERT WITH CHECK (public.in_space(space_id));
CREATE POLICY concept_update_policy ON public."Concept" FOR UPDATE WITH CHECK (public.in_space(space_id));

ALTER TABLE public."ConceptAccess" ENABLE ROW LEVEL SECURITY;

DROP POLICY IF EXISTS concept_access_policy ON public."ConceptAccess";
DROP POLICY IF EXISTS concept_access_select_policy ON public."ConceptAccess";
CREATE POLICY concept_access_select_policy ON public."ConceptAccess" FOR SELECT USING (public.concept_in_space(concept_id) OR public.can_access_account(account_uid));
DROP POLICY IF EXISTS concept_access_delete_policy ON public."ConceptAccess";
CREATE POLICY concept_access_delete_policy ON public."ConceptAccess" FOR DELETE USING (public.concept_in_editable_space(concept_id) OR public.can_access_account(account_uid));
DROP POLICY IF EXISTS concept_access_insert_policy ON public."ConceptAccess";
CREATE POLICY concept_access_insert_policy ON public."ConceptAccess" FOR INSERT WITH CHECK (public.concept_in_editable_space(concept_id));
DROP POLICY IF EXISTS concept_access_update_policy ON public."ConceptAccess";
CREATE POLICY concept_access_update_policy ON public."ConceptAccess" FOR UPDATE WITH CHECK (public.concept_in_editable_space(concept_id));
Loading