diff --git a/packages/database/features/getContext.feature b/packages/database/features/getContext.feature
index 532fb4700..e079a5d52 100644
--- a/packages/database/features/getContext.feature
+++ b/packages/database/features/getContext.feature
@@ -14,11 +14,13 @@ Feature: Get Context
When the user user1 opens the Roam plugin in space abc
Then the database should contain 2 PlatformAccount
And the database should contain 1 AgentIdentifier
- And the database should contain 2 SpaceAccess
+ And the database should contain 1 SpaceAccess
+ And the database should contain 1 LocalAccess
And the database should contain 1 Space
And a user logged in space abc should see 1 Space in the database
And a user logged in space abc should see 1 AgentIdentifier in the database
- And a user logged in space abc should see 2 SpaceAccess in the database
+ And a user logged in space abc should see 1 SpaceAccess in the database
+ And a user logged in space abc should see 1 LocalAccess in the database
And a user logged in space abc should see 2 PlatformAccount in the database
Scenario Outline: Calling getContext again
@@ -26,4 +28,5 @@ Feature: Get Context
Then the database should contain 2 PlatformAccount
And the database should contain 1 Space
And the database should contain 1 AgentIdentifier
- And the database should contain 2 SpaceAccess
+ And the database should contain 1 SpaceAccess
+ And the database should contain 1 LocalAccess
diff --git a/packages/database/schema.puml b/packages/database/schema.puml
index de8a0ee6c..9cac789fd 100644
--- a/packages/database/schema.puml
+++ b/packages/database/schema.puml
@@ -15,18 +15,21 @@ class "PlatformAccount" [[{An account for an agent on a platform}]] {
class "Users" [[{A database user account. Managed by the auth infrastructure.}]] {
{field} id : integer
}
-"PlatformAccount" --> "0..1" "Users" : "dg_account"
class "SpaceAccess" [[{An access control entry for a space}]] {
{field} editor : boolean
}
+"PlatformAccount" --> "0..1" "Users" : "dg_account"
+"SpaceAccess" --> "1" "Users" : "account"
class "Space" [[{A space on a platform representing a community engaged in a conversation}]] {
{field} id : integer
{field} url : string
{field} name : string
{field} platform : Platform
}
-"SpaceAccess" --> "1" "PlatformAccount" : "account"
"SpaceAccess" --> "0..1" "Space" : "space"
+class "LocalAccess" [[{A record of which platform accounts have used this space}]] {
+
+}
class "Content" [[{A unit of content}]] {
{field} id : integer
{field} source_local_id : string
@@ -57,6 +60,7 @@ class "Concept" [[{An abstract concept, claim or relation}]] {
{field} literal_content : JSON
{field} is_schema : boolean
}
+"LocalAccess" --> "0..1" "Space" : "space"
"Content" --> "0..1" "Space" : "space"
"Document" --> "0..1" "Space" : "space"
"Concept" --> "0..1" "Space" : "space"
@@ -73,6 +77,7 @@ class "AgentIdentifier" [[{An identifying attribute associated with an agent, ca
"Document" --> "0..*" "PlatformAccount" : "contributors"
"Concept" --> "0..*" "PlatformAccount" : "contributors"
"Content" --> "1" "PlatformAccount" : "creator"
+"LocalAccess" --> "1" "PlatformAccount" : "account"
"Content" --> "1" "Document" : "document"
abstract "ContentEmbedding" [[{An embedding for a piece of content.}]] {
{field} model : EmbeddingName
diff --git a/packages/database/schema.svg b/packages/database/schema.svg
index 78e2c9368..ab85a5238 100644
--- a/packages/database/schema.svg
+++ b/packages/database/schema.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/packages/database/schema.yaml b/packages/database/schema.yaml
index 9526ef1b7..5f44eef21 100644
--- a/packages/database/schema.yaml
+++ b/packages/database/schema.yaml
@@ -194,13 +194,21 @@ classes:
space_url:
unique_key_slots:
- url
+ LocalAccess:
+ description: A record of which platform accounts have used this space
+ slots:
+ - space
+ attributes:
+ account:
+ range: PlatformAccount
+ required: true
SpaceAccess:
description: An access control entry for a space
slots:
- space
attributes:
account:
- range: PlatformAccount
+ range: Users
required: true
editor:
range: boolean
diff --git a/packages/database/src/dbTypes.ts b/packages/database/src/dbTypes.ts
index 132a482cf..c28556115 100644
--- a/packages/database/src/dbTypes.ts
+++ b/packages/database/src/dbTypes.ts
@@ -532,6 +532,50 @@ export type Database = {
},
]
}
+ LocalAccess: {
+ Row: {
+ account_id: number
+ space_id: number
+ }
+ Insert: {
+ account_id: number
+ space_id: number
+ }
+ Update: {
+ account_id?: number
+ space_id?: number
+ }
+ Relationships: [
+ {
+ foreignKeyName: "LocalAccess_account_id_fkey"
+ columns: ["account_id"]
+ isOneToOne: false
+ referencedRelation: "my_accounts"
+ referencedColumns: ["id"]
+ },
+ {
+ foreignKeyName: "LocalAccess_account_id_fkey"
+ columns: ["account_id"]
+ isOneToOne: false
+ referencedRelation: "PlatformAccount"
+ referencedColumns: ["id"]
+ },
+ {
+ foreignKeyName: "LocalAccess_space_id_fkey"
+ columns: ["space_id"]
+ isOneToOne: false
+ referencedRelation: "my_spaces"
+ referencedColumns: ["id"]
+ },
+ {
+ foreignKeyName: "LocalAccess_space_id_fkey"
+ columns: ["space_id"]
+ isOneToOne: false
+ referencedRelation: "Space"
+ referencedColumns: ["id"]
+ },
+ ]
+ }
PlatformAccount: {
Row: {
account_local_id: string
@@ -603,35 +647,21 @@ export type Database = {
}
SpaceAccess: {
Row: {
- account_id: number
+ account_uid: string
editor: boolean
space_id: number
}
Insert: {
- account_id: number
+ account_uid: string
editor: boolean
space_id: number
}
Update: {
- account_id?: number
+ account_uid?: string
editor?: boolean
space_id?: number
}
Relationships: [
- {
- foreignKeyName: "SpaceAccess_account_id_fkey"
- columns: ["account_id"]
- isOneToOne: false
- referencedRelation: "my_accounts"
- referencedColumns: ["id"]
- },
- {
- foreignKeyName: "SpaceAccess_account_id_fkey"
- columns: ["account_id"]
- isOneToOne: false
- referencedRelation: "PlatformAccount"
- referencedColumns: ["id"]
- },
{
foreignKeyName: "SpaceAccess_space_id_fkey"
columns: ["space_id"]
@@ -1472,7 +1502,6 @@ export type Database = {
text_content: string
}[]
}
- my_account: { Args: never; Returns: number }
my_space_ids: { Args: never; Returns: number[] }
propose_sync_task: {
Args: {
diff --git a/packages/database/supabase/functions/create-space/index.ts b/packages/database/supabase/functions/create-space/index.ts
index 345c02176..cdc436b08 100644
--- a/packages/database/supabase/functions/create-space/index.ts
+++ b/packages/database/supabase/functions/create-space/index.ts
@@ -167,11 +167,11 @@ const processAndGetOrCreateSpace = async (
.upsert(
{
space_id,
- account_id: anonPlatformUserResult.data.id,
+ account_uid: anonymousUser.id,
editor: true,
},
{
- onConflict: "space_id,account_id",
+ onConflict: "account_uid,space_id",
ignoreDuplicates: false,
count: "estimated",
},
diff --git a/packages/database/supabase/migrations/20251231203152_migrate_space_access_to_user.sql b/packages/database/supabase/migrations/20251231203152_migrate_space_access_to_user.sql
new file mode 100644
index 000000000..12504a8ec
--- /dev/null
+++ b/packages/database/supabase/migrations/20251231203152_migrate_space_access_to_user.sql
@@ -0,0 +1,207 @@
+REVOKE ALL ON TABLE public."SpaceAccess" FROM anon;
+DROP POLICY IF EXISTS space_access_select_policy ON public."SpaceAccess";
+DROP POLICY IF EXISTS space_access_delete_policy ON public."SpaceAccess";
+DROP POLICY IF EXISTS space_access_insert_policy ON public."SpaceAccess";
+DROP POLICY IF EXISTS space_access_update_policy ON public."SpaceAccess";
+ALTER TABLE ONLY public."SpaceAccess" DROP CONSTRAINT "SpaceAccess_pkey";
+
+ALTER TABLE public."SpaceAccess" RENAME TO "LocalAccess";
+
+COMMENT ON TABLE public."LocalAccess" IS 'A record of which platform accounts have used this space';
+
+COMMENT ON COLUMN public."LocalAccess".account_id IS 'The identity of the local account in this space';
+
+ALTER TABLE ONLY public."LocalAccess"
+RENAME CONSTRAINT "SpaceAccess_account_id_fkey" TO "LocalAccess_account_id_fkey";
+
+ALTER TABLE ONLY public."LocalAccess"
+RENAME CONSTRAINT "SpaceAccess_space_id_fkey" TO "LocalAccess_space_id_fkey";
+
+ALTER TABLE public."LocalAccess" ADD CONSTRAINT "LocalAccess_pkey" PRIMARY KEY (account_id, space_id);
+
+
+CREATE TABLE IF NOT EXISTS public."SpaceAccess" (
+ account_uid UUID NOT NULL,
+ space_id bigint NOT NULL,
+ editor boolean NOT NULL
+);
+
+ALTER TABLE ONLY public."SpaceAccess"
+ADD CONSTRAINT "SpaceAccess_pkey" PRIMARY KEY (account_uid, space_id);
+
+ALTER TABLE public."SpaceAccess" OWNER TO "postgres";
+
+COMMENT ON TABLE public."SpaceAccess" IS 'An access control entry for a space';
+
+COMMENT ON COLUMN public."SpaceAccess".space_id IS 'The space in which the content is located';
+
+COMMENT ON COLUMN public."SpaceAccess".account_uid IS 'The identity of the user account';
+
+ALTER TABLE ONLY public."SpaceAccess"
+ADD CONSTRAINT "SpaceAccess_account_uid_fkey" FOREIGN KEY (
+ account_uid
+) REFERENCES auth.users (id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+ALTER TABLE ONLY public."SpaceAccess"
+ADD CONSTRAINT "SpaceAccess_space_id_fkey" FOREIGN KEY (
+ space_id
+) REFERENCES public."Space" (
+ id
+) ON UPDATE CASCADE ON DELETE CASCADE;
+
+GRANT ALL ON TABLE public."SpaceAccess" TO authenticated;
+GRANT ALL ON TABLE public."SpaceAccess" TO service_role;
+REVOKE ALL ON TABLE public."SpaceAccess" FROM anon;
+
+INSERT INTO public."SpaceAccess"
+SELECT pa.dg_account, la.space_id, la.editor
+FROM public."LocalAccess" AS la
+ JOIN public."PlatformAccount" AS pa ON (pa.id = la.account_id)
+WHERE pa.dg_account IS NOT NULL;
+
+ALTER TABLE public."LocalAccess" DROP COLUMN editor;
+DELETE FROM public."LocalAccess" WHERE account_id IN (
+ SELECT id FROM public."PlatformAccount"
+ WHERE agent_type = 'anonymous'
+);
+
+CREATE OR REPLACE FUNCTION public.is_my_account(account_id BIGINT) RETURNS boolean
+STABLE SECURITY DEFINER
+SET search_path = ''
+LANGUAGE sql
+AS $$
+ SELECT EXISTS (
+ SELECT 1 FROM public."PlatformAccount"
+ WHERE id = account_id AND dg_account = auth.uid()
+ LIMIT 1
+ );
+$$;
+
+CREATE OR REPLACE FUNCTION public.my_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"
+ WHERE account_uid = auth.uid();
+$$;
+
+CREATE OR REPLACE FUNCTION public.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
+ WHERE sa.space_id = in_space.space_id
+ AND sa.account_uid=auth.uid());
+$$;
+
+CREATE OR REPLACE FUNCTION public.account_in_shared_space(p_account_id BIGINT) RETURNS boolean
+STABLE SECURITY DEFINER
+SET search_path = ''
+LANGUAGE sql AS $$
+ SELECT EXISTS (
+ SELECT 1
+ FROM public."LocalAccess" AS la
+ JOIN public."SpaceAccess" AS sa USING (space_id)
+ WHERE la.account_id = p_account_id
+ AND sa.account_uid = auth.uid()
+ );
+$$;
+
+CREATE OR REPLACE FUNCTION public.unowned_account_in_shared_space(p_account_id BIGINT) RETURNS boolean
+STABLE SECURITY DEFINER
+SET search_path = ''
+LANGUAGE sql AS $$
+ SELECT EXISTS (
+ SELECT 1
+ FROM public."SpaceAccess" AS sa
+ JOIN public."LocalAccess" AS la USING (space_id)
+ JOIN public."PlatformAccount" AS pa ON (pa.id=la.account_id)
+ WHERE la.account_id = p_account_id
+ AND sa.account_uid = auth.uid()
+ AND pa.dg_account IS NULL
+ );
+$$;
+
+CREATE OR REPLACE FUNCTION public.upsert_account_in_space(
+ space_id_ BIGINT,
+ local_account public.account_local_input
+) RETURNS BIGINT
+SECURITY DEFINER
+SET search_path = ''
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ platform_ public."Platform";
+ account_id_ BIGINT;
+ user_uid UUID;
+BEGIN
+ SELECT platform INTO STRICT platform_ FROM public."Space" WHERE id = space_id_;
+ INSERT INTO public."PlatformAccount" AS pa (
+ account_local_id, name, platform
+ ) VALUES (
+ local_account.account_local_id, local_account.name, platform_
+ ) ON CONFLICT (account_local_id, platform) DO UPDATE SET
+ name = COALESCE(NULLIF(TRIM(EXCLUDED.name), ''), pa.name)
+ RETURNING id, dg_account INTO STRICT account_id_, user_uid;
+ IF user_uid IS NOT NULL THEN
+ INSERT INTO public."SpaceAccess" as sa (space_id, account_uid, editor)
+ VALUES (space_id_, user_uid, COALESCE(local_account.space_editor, true))
+ ON CONFLICT (space_id, account_uid)
+ DO UPDATE SET editor = COALESCE(local_account.space_editor, sa.editor, true);
+ END IF;
+ INSERT INTO public."LocalAccess" (space_id, account_id) values (space_id_, account_id_)
+ ON CONFLICT (space_id, account_id)
+ DO NOTHING;
+ IF local_account.email IS NOT NULL THEN
+ -- TODO: how to distinguish basic untrusted from platform placeholder email?
+ INSERT INTO public."AgentIdentifier" as ai (account_id, value, identifier_type, trusted) VALUES (account_id_, local_account.email, 'email', COALESCE(local_account.email_trusted, false))
+ ON CONFLICT (value, identifier_type, account_id)
+ DO UPDATE SET trusted = COALESCE(local_account.email_trusted, ai.trusted, false);
+ END IF;
+ RETURN account_id_;
+END;
+$$;
+
+CREATE OR REPLACE VIEW public.my_accounts AS
+SELECT
+ id,
+ name,
+ platform,
+ account_local_id,
+ write_permission,
+ active,
+ agent_type,
+ metadata,
+ dg_account
+FROM public."PlatformAccount"
+WHERE id IN (
+ SELECT "LocalAccess".account_id FROM public."LocalAccess"
+ JOIN public."SpaceAccess" USING (space_id)
+ WHERE "SpaceAccess".account_uid = auth.uid()
+);
+
+ALTER TABLE public."SpaceAccess" ENABLE ROW LEVEL SECURITY;
+
+CREATE POLICY space_access_select_policy ON public."SpaceAccess" FOR SELECT USING (public.in_space(space_id));
+CREATE POLICY space_access_delete_policy ON public."SpaceAccess" FOR DELETE USING (account_uid = auth.uid());
+CREATE POLICY space_access_insert_policy ON public."SpaceAccess" FOR INSERT WITH CHECK (account_uid = auth.uid());
+CREATE POLICY space_access_update_policy ON public."SpaceAccess" FOR UPDATE WITH CHECK (account_uid = auth.uid());
+
+CREATE POLICY local_access_select_policy ON public."LocalAccess" FOR SELECT USING (public.in_space(space_id));
+CREATE POLICY local_access_delete_policy ON public."LocalAccess" FOR DELETE USING (public.unowned_account_in_shared_space(account_id) OR public.is_my_account(account_id));
+CREATE POLICY local_access_insert_policy ON public."LocalAccess" FOR INSERT WITH CHECK (public.unowned_account_in_shared_space(account_id) OR public.is_my_account(account_id));
+CREATE POLICY local_access_update_policy ON public."LocalAccess" FOR UPDATE WITH CHECK (public.unowned_account_in_shared_space(account_id) OR public.is_my_account(account_id));
+
+DROP POLICY IF EXISTS agent_identifier_delete_policy ON public."AgentIdentifier";
+CREATE POLICY agent_identifier_delete_policy ON public."AgentIdentifier" FOR DELETE USING (public.unowned_account_in_shared_space(account_id) OR public.is_my_account(account_id));
+
+DROP POLICY IF EXISTS agent_identifier_insert_policy ON public."AgentIdentifier";
+CREATE POLICY agent_identifier_insert_policy ON public."AgentIdentifier" FOR INSERT WITH CHECK (public.unowned_account_in_shared_space(account_id) OR public.is_my_account(account_id));
+
+DROP POLICY IF EXISTS agent_identifier_update_policy ON public."AgentIdentifier";
+CREATE POLICY agent_identifier_update_policy ON public."AgentIdentifier" FOR UPDATE WITH CHECK (public.unowned_account_in_shared_space(account_id) OR public.is_my_account(account_id));
+
+DROP FUNCTION public.my_account();
diff --git a/packages/database/supabase/schemas/account.sql b/packages/database/supabase/schemas/account.sql
index 80944dc83..0182a2e9c 100644
--- a/packages/database/supabase/schemas/account.sql
+++ b/packages/database/supabase/schemas/account.sql
@@ -64,15 +64,45 @@ GRANT ALL ON TABLE public."AgentIdentifier" TO service_role;
CREATE INDEX platform_account_dg_account_idx ON public."PlatformAccount" (dg_account);
-CREATE TABLE IF NOT EXISTS public."SpaceAccess" (
- space_id bigint,
+CREATE TABLE IF NOT EXISTS public."LocalAccess" (
account_id bigint NOT NULL,
+ space_id bigint NOT NULL
+);
+
+ALTER TABLE public."LocalAccess" OWNER TO "postgres";
+
+ALTER TABLE public."LocalAccess" ADD CONSTRAINT "LocalAccess_pkey" PRIMARY KEY (account_id, space_id);
+
+COMMENT ON TABLE public."LocalAccess" IS 'A record of which platform accounts have used this space';
+
+COMMENT ON COLUMN public."LocalAccess".space_id IS 'The space in which the content is located';
+
+COMMENT ON COLUMN public."LocalAccess".account_id IS 'The identity of the local account in this space';
+
+ALTER TABLE ONLY public."LocalAccess"
+ADD CONSTRAINT "LocalAccess_account_id_fkey" FOREIGN KEY (
+ account_id
+) REFERENCES public."PlatformAccount" (id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+ALTER TABLE ONLY public."LocalAccess"
+ADD CONSTRAINT "LocalAccess_space_id_fkey" FOREIGN KEY (
+ space_id
+) REFERENCES public."Space" (
+ id
+) ON UPDATE CASCADE ON DELETE CASCADE;
+
+GRANT ALL ON TABLE public."LocalAccess" TO authenticated;
+GRANT ALL ON TABLE public."LocalAccess" TO service_role;
+REVOKE ALL ON TABLE public."LocalAccess" FROM anon;
+
+CREATE TABLE IF NOT EXISTS public."SpaceAccess" (
+ account_uid UUID NOT NULL,
+ space_id bigint NOT NULL,
editor boolean NOT NULL
);
ALTER TABLE ONLY public."SpaceAccess"
-ADD CONSTRAINT "SpaceAccess_pkey" PRIMARY KEY (space_id, account_id);
-
+ADD CONSTRAINT "SpaceAccess_pkey" PRIMARY KEY (account_uid, space_id);
ALTER TABLE public."SpaceAccess" OWNER TO "postgres";
@@ -80,12 +110,12 @@ COMMENT ON TABLE public."SpaceAccess" IS 'An access control entry for a space';
COMMENT ON COLUMN public."SpaceAccess".space_id IS 'The space in which the content is located';
-COMMENT ON COLUMN public."SpaceAccess".account_id IS 'The identity of the account in this space';
+COMMENT ON COLUMN public."SpaceAccess".account_uid IS 'The identity of the user account';
ALTER TABLE ONLY public."SpaceAccess"
-ADD CONSTRAINT "SpaceAccess_account_id_fkey" FOREIGN KEY (
- account_id
-) REFERENCES public."PlatformAccount" (id) ON UPDATE CASCADE ON DELETE CASCADE;
+ADD CONSTRAINT "SpaceAccess_account_uid_fkey" FOREIGN KEY (
+ account_uid
+) REFERENCES auth.users (id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY public."SpaceAccess"
ADD CONSTRAINT "SpaceAccess_space_id_fkey" FOREIGN KEY (
@@ -94,9 +124,9 @@ ADD CONSTRAINT "SpaceAccess_space_id_fkey" FOREIGN KEY (
id
) ON UPDATE CASCADE ON DELETE CASCADE;
-GRANT ALL ON TABLE public."SpaceAccess" TO anon;
GRANT ALL ON TABLE public."SpaceAccess" TO authenticated;
GRANT ALL ON TABLE public."SpaceAccess" TO service_role;
+REVOKE ALL ON TABLE public."SpaceAccess" FROM anon;
CREATE TYPE public.account_local_input AS (
-- PlatformAccount columns
@@ -119,6 +149,7 @@ AS $$
DECLARE
platform_ public."Platform";
account_id_ BIGINT;
+ user_uid UUID;
BEGIN
SELECT platform INTO STRICT platform_ FROM public."Space" WHERE id = space_id_;
INSERT INTO public."PlatformAccount" AS pa (
@@ -127,10 +158,16 @@ BEGIN
local_account.account_local_id, local_account.name, platform_
) ON CONFLICT (account_local_id, platform) DO UPDATE SET
name = COALESCE(NULLIF(TRIM(EXCLUDED.name), ''), pa.name)
- RETURNING id INTO STRICT account_id_;
- INSERT INTO public."SpaceAccess" as sa (space_id, account_id, editor) values (space_id_, account_id_, COALESCE(local_account.space_editor, true))
+ RETURNING id, dg_account INTO STRICT account_id_, user_uid;
+ IF user_uid IS NOT NULL THEN
+ INSERT INTO public."SpaceAccess" as sa (space_id, account_uid, editor)
+ VALUES (space_id_, user_uid, COALESCE(local_account.space_editor, true))
+ ON CONFLICT (space_id, account_uid)
+ DO UPDATE SET editor = COALESCE(local_account.space_editor, sa.editor, true);
+ END IF;
+ INSERT INTO public."LocalAccess" (space_id, account_id) values (space_id_, account_id_)
ON CONFLICT (space_id, account_id)
- DO UPDATE SET editor = COALESCE(local_account.space_editor, sa.editor, true);
+ DO NOTHING;
IF local_account.email IS NOT NULL THEN
-- TODO: how to distinguish basic untrusted from platform placeholder email?
INSERT INTO public."AgentIdentifier" as ai (account_id, value, identifier_type, trusted) VALUES (account_id_, local_account.email, 'email', COALESCE(local_account.email_trusted, false))
@@ -186,35 +223,23 @@ STABLE SECURITY DEFINER
SET search_path = ''
LANGUAGE sql
AS $$
- WITH u AS (SELECT auth.uid() LIMIT 1)
- SELECT true FROM public."PlatformAccount" pa
- JOIN u ON pa.dg_account = u.uid
- WHERE pa.id = account_id;
+ SELECT EXISTS (
+ SELECT 1 FROM public."PlatformAccount"
+ WHERE id = account_id AND dg_account = auth.uid()
+ LIMIT 1
+ );
$$;
COMMENT ON FUNCTION public.is_my_account IS 'security utility: is this my own account?';
-CREATE OR REPLACE FUNCTION public.my_account() RETURNS BIGINT
-STABLE SECURITY DEFINER
-SET search_path = ''
-LANGUAGE sql
-AS $$
- WITH u AS (SELECT auth.uid() LIMIT 1)
- SELECT id FROM public."PlatformAccount" pa
- JOIN u ON pa.dg_account = u.uid LIMIT 1;
-$$;
-
-COMMENT ON FUNCTION public.my_account IS 'security utility: id of my account';
-
CREATE OR REPLACE FUNCTION public.my_space_ids() RETURNS BIGINT []
STABLE SECURITY DEFINER
SET search_path = ''
LANGUAGE sql
AS $$
- WITH u AS (SELECT auth.uid() LIMIT 1)
- SELECT COALESCE(array_agg(distinct sa.space_id), '{}') AS ids FROM public."SpaceAccess" AS sa
- JOIN public."PlatformAccount" AS pa ON pa.id=sa.account_id
- JOIN u ON pa.dg_account = u.uid;
+ SELECT COALESCE(array_agg(distinct space_id), '{}') AS ids
+ FROM public."SpaceAccess"
+ WHERE account_uid = auth.uid();
$$;
COMMENT ON FUNCTION public.my_space_ids IS 'security utility: all spaces the user has access to';
@@ -224,11 +249,9 @@ STABLE SECURITY DEFINER
SET search_path = ''
LANGUAGE sql
AS $$
- WITH u AS (SELECT auth.uid() LIMIT 1),
- pa AS (SELECT sa.space_id AS id FROM public."SpaceAccess" AS sa
- JOIN public."PlatformAccount" AS pa ON pa.id=sa.account_id
- JOIN u ON pa.dg_account = u.uid)
- SELECT EXISTS (SELECT id FROM pa WHERE id = space_id );
+ SELECT EXISTS (SELECT 1 FROM public."SpaceAccess" AS sa
+ WHERE sa.space_id = in_space.space_id
+ AND sa.account_uid=auth.uid());
$$;
COMMENT ON FUNCTION public.in_space IS 'security utility: does current user have access to this space?';
@@ -240,9 +263,10 @@ SET search_path = ''
LANGUAGE sql AS $$
SELECT EXISTS (
SELECT 1
- FROM public."SpaceAccess" AS sa
- WHERE sa.account_id = p_account_id
- AND sa.space_id = ANY(public.my_space_ids())
+ FROM public."LocalAccess" AS la
+ JOIN public."SpaceAccess" AS sa USING (space_id)
+ WHERE la.account_id = p_account_id
+ AND sa.account_uid = auth.uid()
);
$$;
@@ -253,12 +277,13 @@ STABLE SECURITY DEFINER
SET search_path = ''
LANGUAGE sql AS $$
SELECT EXISTS (
- SELECT 1
- FROM public."SpaceAccess" AS sa
- JOIN public."PlatformAccount" AS pa ON (pa.id = sa.account_id)
- WHERE sa.account_id = p_account_id
- AND sa.space_id = ANY(public.my_space_ids())
- AND pa.dg_account IS NULL
+ SELECT 1
+ FROM public."SpaceAccess" AS sa
+ JOIN public."LocalAccess" AS la USING (space_id)
+ JOIN public."PlatformAccount" AS pa ON (pa.id=la.account_id)
+ WHERE la.account_id = p_account_id
+ AND sa.account_uid = auth.uid()
+ AND pa.dg_account IS NULL
);
$$;
@@ -290,22 +315,20 @@ ALTER TABLE public."PlatformAccount" ENABLE ROW LEVEL SECURITY;
CREATE OR REPLACE VIEW public.my_accounts AS
SELECT
- pa.id,
- pa.name,
- pa.platform,
- pa.account_local_id,
- pa.write_permission,
- pa.active,
- pa.agent_type,
- pa.metadata,
- pa.dg_account
-FROM public."PlatformAccount" AS pa
-WHERE EXISTS (
- SELECT 1
- FROM public."SpaceAccess" AS sa
- WHERE
- sa.account_id = pa.id
- AND sa.space_id = any(public.my_space_ids())
+ id,
+ name,
+ platform,
+ account_local_id,
+ write_permission,
+ active,
+ agent_type,
+ metadata,
+ dg_account
+FROM public."PlatformAccount"
+WHERE id IN (
+ SELECT "LocalAccess".account_id FROM public."LocalAccess"
+ JOIN public."SpaceAccess" USING (space_id)
+ WHERE "SpaceAccess".account_uid = auth.uid()
);
DROP POLICY IF EXISTS platform_account_policy ON public."PlatformAccount";
@@ -334,13 +357,29 @@ DROP POLICY IF EXISTS space_access_select_policy ON public."SpaceAccess";
CREATE POLICY space_access_select_policy ON public."SpaceAccess" FOR SELECT USING (public.in_space(space_id));
DROP POLICY IF EXISTS space_access_delete_policy ON public."SpaceAccess";
-CREATE POLICY space_access_delete_policy ON public."SpaceAccess" FOR DELETE USING (public.unowned_account_in_shared_space(account_id) OR account_id = public.my_account());
+CREATE POLICY space_access_delete_policy ON public."SpaceAccess" FOR DELETE USING (account_uid = auth.uid());
DROP POLICY IF EXISTS space_access_insert_policy ON public."SpaceAccess";
-CREATE POLICY space_access_insert_policy ON public."SpaceAccess" FOR INSERT WITH CHECK (public.unowned_account_in_shared_space(account_id) OR account_id = public.my_account());
+CREATE POLICY space_access_insert_policy ON public."SpaceAccess" FOR INSERT WITH CHECK (account_uid = auth.uid());
DROP POLICY IF EXISTS space_access_update_policy ON public."SpaceAccess";
-CREATE POLICY space_access_update_policy ON public."SpaceAccess" FOR UPDATE WITH CHECK (public.unowned_account_in_shared_space(account_id) OR account_id = public.my_account());
+CREATE POLICY space_access_update_policy ON public."SpaceAccess" FOR UPDATE WITH CHECK (account_uid = auth.uid());
+
+ALTER TABLE public."LocalAccess" ENABLE ROW LEVEL SECURITY;
+
+DROP POLICY IF EXISTS local_access_policy ON public."LocalAccess";
+
+DROP POLICY IF EXISTS local_access_select_policy ON public."LocalAccess";
+CREATE POLICY local_access_select_policy ON public."LocalAccess" FOR SELECT USING (public.in_space(space_id));
+
+DROP POLICY IF EXISTS local_access_delete_policy ON public."LocalAccess";
+CREATE POLICY local_access_delete_policy ON public."LocalAccess" FOR DELETE USING (public.unowned_account_in_shared_space(account_id) OR public.is_my_account(account_id));
+
+DROP POLICY IF EXISTS local_access_insert_policy ON public."LocalAccess";
+CREATE POLICY local_access_insert_policy ON public."LocalAccess" FOR INSERT WITH CHECK (public.unowned_account_in_shared_space(account_id) OR public.is_my_account(account_id));
+
+DROP POLICY IF EXISTS local_access_update_policy ON public."LocalAccess";
+CREATE POLICY local_access_update_policy ON public."LocalAccess" FOR UPDATE WITH CHECK (public.unowned_account_in_shared_space(account_id) OR public.is_my_account(account_id));
-- AgentIdentifier: Allow space members to do anything, to allow editing authors.
-- Eventually: Once the account is claimed by a user, only allow this user to modify it.
@@ -353,10 +392,10 @@ DROP POLICY IF EXISTS agent_identifier_select_policy ON public."AgentIdentifier"
CREATE POLICY agent_identifier_select_policy ON public."AgentIdentifier" FOR SELECT USING (public.account_in_shared_space(account_id));
DROP POLICY IF EXISTS agent_identifier_delete_policy ON public."AgentIdentifier";
-CREATE POLICY agent_identifier_delete_policy ON public."AgentIdentifier" FOR DELETE USING (public.unowned_account_in_shared_space(account_id) OR account_id = public.my_account());
+CREATE POLICY agent_identifier_delete_policy ON public."AgentIdentifier" FOR DELETE USING (public.unowned_account_in_shared_space(account_id) OR public.is_my_account(account_id));
DROP POLICY IF EXISTS agent_identifier_insert_policy ON public."AgentIdentifier";
-CREATE POLICY agent_identifier_insert_policy ON public."AgentIdentifier" FOR INSERT WITH CHECK (public.unowned_account_in_shared_space(account_id) OR account_id = public.my_account());
+CREATE POLICY agent_identifier_insert_policy ON public."AgentIdentifier" FOR INSERT WITH CHECK (public.unowned_account_in_shared_space(account_id) OR public.is_my_account(account_id));
DROP POLICY IF EXISTS agent_identifier_update_policy ON public."AgentIdentifier";
-CREATE POLICY agent_identifier_update_policy ON public."AgentIdentifier" FOR UPDATE WITH CHECK (public.unowned_account_in_shared_space(account_id) OR account_id = public.my_account());
+CREATE POLICY agent_identifier_update_policy ON public."AgentIdentifier" FOR UPDATE WITH CHECK (public.unowned_account_in_shared_space(account_id) OR public.is_my_account(account_id));