Skip to content
Draft
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
3 changes: 1 addition & 2 deletions packages/database/features/step-definitions/stepdefs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -557,13 +557,12 @@ Then(
await expectWriteRejected(operation, {
insert: () =>
view.insert({
name: "written through my_accounts",
account_local_id: "written-through-my-accounts",
platform: "Roam",
}),
update: () =>
view
.update({ name: "written through my_accounts" })
.update({ account_local_id: "written-through-my-accounts" })
.eq("platform", "Roam"),
delete: () => view.delete().eq("platform", "Roam"),
});
Expand Down
6 changes: 4 additions & 2 deletions packages/database/src/dbTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -962,7 +962,7 @@ export type Database = {
active?: boolean | null
agent_type?: Database["public"]["Enums"]["AgentType"] | null
id?: number | null
name?: string | null
name?: never
platform?: Database["public"]["Enums"]["Platform"] | null
write_permission?: boolean | null
}
Expand All @@ -971,7 +971,7 @@ export type Database = {
active?: boolean | null
agent_type?: Database["public"]["Enums"]["AgentType"] | null
id?: number | null
name?: string | null
name?: never
platform?: Database["public"]["Enums"]["Platform"] | null
write_permission?: boolean | null
}
Expand Down Expand Up @@ -1817,6 +1817,7 @@ export type Database = {
}
Returns: Json
}
everyone_uid: { Args: never; Returns: string }
extract_references: { Args: { refs: Json }; Returns: number[] }
file_access: { Args: { hashvalue: string }; Returns: boolean }
file_exists: { Args: { hashvalue: string }; Returns: boolean }
Expand Down Expand Up @@ -1907,6 +1908,7 @@ export type Database = {
isSetofReturn: true
}
}
my_identity_accounts: { Args: never; Returns: string[] }
my_permissions_in_space: {
Args: { space_id_: number }
Returns: Database["public"]["Enums"]["SpaceAccessPermissions"]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
INSERT INTO auth.users (instance_id, id, aud, role, created_at, updated_at, is_super_admin, is_anonymous)
VALUES ('00000000-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000000', 'anon', 'anon', now(), now(), false, true);

CREATE OR REPLACE FUNCTION public.everyone_uid() RETURNS UUID
IMMUTABLE
SET search_path = ''
LANGUAGE sql
AS $$
SELECT '00000000-0000-0000-0000-000000000000'::uuid;
$$;

COMMENT ON FUNCTION public.everyone_uid IS 'The uid of the everyone pseudo-user. A grant to it applies to every caller, logged in or not.';

CREATE OR REPLACE FUNCTION public.my_user_accounts() RETURNS SETOF UUID
STABLE SECURITY DEFINER
SET search_path = ''
LANGUAGE sql
AS $$
SELECT auth.uid() WHERE auth.uid() IS NOT NULL UNION
SELECT public.everyone_uid() UNION
SELECT group_id FROM public.group_membership
WHERE member_id = auth.uid();
$$;

COMMENT ON FUNCTION public.my_user_accounts IS 'security utility: The uids which give me access, either as myself or as a group member.';

CREATE OR REPLACE FUNCTION public.my_identity_accounts() RETURNS SETOF UUID
STABLE SECURITY DEFINER
SET search_path = ''
LANGUAGE sql
AS $$
SELECT auth.uid() WHERE auth.uid() IS NOT NULL UNION
SELECT group_id FROM public.group_membership
WHERE member_id = auth.uid();
$$;

COMMENT ON FUNCTION public.my_identity_accounts IS 'security utility: The uids I act as, myself or a group I belong to. Excludes the everyone pseudo-user, so a public grant never counts as sharing a space.';

CREATE OR REPLACE FUNCTION public.account_in_shared_space(p_account_id BIGINT, access_level public."SpaceAccessPermissions" = 'reader') 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)
JOIN public.my_identity_accounts() ON (sa.account_uid = my_identity_accounts)
WHERE la.account_id = p_account_id
AND sa.permissions >= access_level
);
$$;

CREATE OR REPLACE FUNCTION public.unowned_account_in_shared_space(p_account_id BIGINT, access_level public."SpaceAccessPermissions" = 'reader') RETURNS boolean
STABLE SECURITY DEFINER
SET search_path = ''
LANGUAGE sql AS $$
SELECT EXISTS (
SELECT 1
FROM public."SpaceAccess" AS sa
JOIN public.my_identity_accounts() ON (sa.account_uid = my_identity_accounts)
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 pa.dg_account IS NULL
AND sa.permissions >= access_level
);
$$;

-- The real name only shows to callers who share a space with the account through their own grants.
-- An account visible only through a public grant shows as 'anonymous #<id>'.
CREATE OR REPLACE VIEW public.my_accounts AS
SELECT
id,
CASE WHEN id IN (
SELECT "LocalAccess".account_id FROM public."LocalAccess"
JOIN public."SpaceAccess" USING (space_id)
JOIN public.my_identity_accounts() ON (account_uid = my_identity_accounts)
WHERE permissions >= 'partial'
UNION
SELECT id FROM public."PlatformAccount" WHERE dg_account = auth.uid()
) THEN name ELSE ('anonymous #' || id)::varchar END AS name,
platform,
account_local_id,
write_permission,
active,
agent_type
FROM public."PlatformAccount"
WHERE id IN (
SELECT "LocalAccess".account_id FROM public."LocalAccess"
JOIN public."SpaceAccess" USING (space_id)
JOIN public.my_user_accounts() ON (account_uid = my_user_accounts)
WHERE permissions >= 'partial'
UNION
SELECT id FROM public."PlatformAccount" WHERE dg_account = auth.uid()
);

DROP POLICY IF EXISTS resource_access_select_policy ON public."ResourceAccess";
CREATE POLICY resource_access_select_policy ON public."ResourceAccess" FOR SELECT USING (
account_uid = public.everyone_uid()
OR public.in_space(space_id)
OR public.can_access_account(account_uid)
);

GRANT SELECT ON TABLE public."ResourceAccess" TO anon;
GRANT SELECT ON TABLE public."Document" TO anon;
GRANT SELECT ON TABLE public."Content" TO anon;
GRANT SELECT ON TABLE public."Concept" TO anon;
GRANT SELECT ON TABLE public."FileReference" TO anon;

DROP POLICY IF EXISTS "storage_select_assets_access" ON storage.objects;
CREATE POLICY "storage_select_assets_access"
ON storage.objects FOR SELECT TO anon, authenticated USING (
bucket_id = 'assets' AND file_access(name)
);
42 changes: 39 additions & 3 deletions packages/database/supabase/schemas/account.sql
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
-- Anonymous pseudo-user
INSERT INTO auth.users (instance_id, id, aud, role, created_at, updated_at, is_super_admin, is_anonymous)
VALUES ('00000000-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000000', 'anon', 'anon', now(), now(), false, true);

CREATE TYPE public."AgentType" AS ENUM (
'person',
'organization',
Expand Down Expand Up @@ -213,18 +217,41 @@ $$;

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

CREATE OR REPLACE FUNCTION public.everyone_uid() RETURNS UUID
IMMUTABLE
SET search_path = ''
LANGUAGE sql
AS $$
SELECT '00000000-0000-0000-0000-000000000000'::uuid;
$$;

COMMENT ON FUNCTION public.everyone_uid IS 'The uid of the everyone pseudo-user. A grant to it applies to every caller, logged in or not.';

CREATE OR REPLACE FUNCTION public.my_user_accounts() RETURNS SETOF UUID
STABLE SECURITY DEFINER
SET search_path = ''
LANGUAGE sql
AS $$
SELECT auth.uid() WHERE auth.uid() IS NOT NULL UNION
SELECT public.everyone_uid() UNION
SELECT group_id FROM public.group_membership
WHERE member_id = auth.uid();
$$;

COMMENT ON FUNCTION public.my_user_accounts IS 'security utility: The uids which give me access, either as myself or as a group member.';

CREATE OR REPLACE FUNCTION public.my_identity_accounts() RETURNS SETOF UUID
STABLE SECURITY DEFINER
SET search_path = ''
LANGUAGE sql
AS $$
SELECT auth.uid() WHERE auth.uid() IS NOT NULL UNION
SELECT group_id FROM public.group_membership
WHERE member_id = auth.uid();
$$;

COMMENT ON FUNCTION public.my_identity_accounts IS 'security utility: The uids I act as, myself or a group I belong to. Excludes the everyone pseudo-user, so a public grant never counts as sharing a space.';

CREATE OR REPLACE FUNCTION public.my_permissions_in_space(
space_id_ BIGINT
) RETURNS public."SpaceAccessPermissions"
Expand Down Expand Up @@ -294,7 +321,7 @@ LANGUAGE sql AS $$
SELECT 1
FROM public."LocalAccess" AS la
JOIN public."SpaceAccess" AS sa USING (space_id)
JOIN public.my_user_accounts() ON (sa.account_uid = my_user_accounts)
JOIN public.my_identity_accounts() ON (sa.account_uid = my_identity_accounts)
WHERE la.account_id = p_account_id
AND sa.permissions >= access_level
);
Expand All @@ -309,7 +336,7 @@ LANGUAGE sql AS $$
SELECT EXISTS (
SELECT 1
FROM public."SpaceAccess" AS sa
JOIN public.my_user_accounts() ON (sa.account_uid = my_user_accounts)
JOIN public.my_identity_accounts() ON (sa.account_uid = my_identity_accounts)
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
Expand Down Expand Up @@ -441,10 +468,19 @@ REVOKE INSERT, UPDATE, DELETE, TRUNCATE ON public.my_spaces FROM anon, authentic
ALTER TABLE public."PlatformAccount" ENABLE ROW LEVEL SECURITY;

-- Leaves out dg_account and metadata: peers have no use for them, and a known auth uid should not be handed out.
-- The real name only shows to callers who share a space with the account through their own grants.
-- An account visible only through a public grant shows as 'anonymous #<id>'.
CREATE OR REPLACE VIEW public.my_accounts AS
SELECT
id,
name,
CASE WHEN id IN (
SELECT "LocalAccess".account_id FROM public."LocalAccess"
JOIN public."SpaceAccess" USING (space_id)
JOIN public.my_identity_accounts() ON (account_uid = my_identity_accounts)
WHERE permissions >= 'partial'
UNION
SELECT id FROM public."PlatformAccount" WHERE dg_account = auth.uid()
) THEN name ELSE ('anonymous #' || id)::varchar END AS name,
platform,
account_local_id,
write_permission,
Expand Down
3 changes: 2 additions & 1 deletion packages/database/supabase/schemas/assets.sql
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ REVOKE INSERT, UPDATE, DELETE, TRUNCATE ON public.my_file_references FROM anon,
GRANT ALL ON TABLE public."FileReference" TO authenticated;
GRANT ALL ON TABLE public."FileReference" TO service_role;
REVOKE ALL ON TABLE public."FileReference" FROM anon;
GRANT SELECT ON TABLE public."FileReference" TO anon;

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

Expand Down Expand Up @@ -143,7 +144,7 @@ ON storage.objects FOR INSERT TO authenticated WITH CHECK (

DROP POLICY IF EXISTS "storage_select_assets_access" ON storage.objects;
CREATE POLICY "storage_select_assets_access"
ON storage.objects FOR SELECT TO authenticated USING (
ON storage.objects FOR SELECT TO anon, authenticated USING (
bucket_id = 'assets' AND file_access(name)
);

Expand Down
1 change: 1 addition & 0 deletions packages/database/supabase/schemas/concept.sql
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ ADD CONSTRAINT "Concept_space_id_fkey" FOREIGN KEY (


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

Expand Down
9 changes: 8 additions & 1 deletion packages/database/supabase/schemas/content.sql
Original file line number Diff line number Diff line change
Expand Up @@ -191,12 +191,15 @@ CREATE INDEX resource_access_content_local_id_idx ON public."ResourceAccess" (so
GRANT ALL ON TABLE public."ResourceAccess" TO authenticated;
GRANT ALL ON TABLE public."ResourceAccess" TO service_role;
REVOKE ALL ON TABLE public."ResourceAccess" FROM anon;
GRANT SELECT ON TABLE public."ResourceAccess" TO anon;

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

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

Expand Down Expand Up @@ -742,7 +745,11 @@ ALTER TABLE public."ResourceAccess" ENABLE ROW LEVEL SECURITY;

DROP POLICY IF EXISTS resource_access_policy ON public."ResourceAccess";
DROP POLICY IF EXISTS resource_access_select_policy ON public."ResourceAccess";
CREATE POLICY resource_access_select_policy ON public."ResourceAccess" FOR SELECT USING (public.in_space(space_id) OR public.can_access_account(account_uid));
CREATE POLICY resource_access_select_policy ON public."ResourceAccess" FOR SELECT USING (
account_uid = public.everyone_uid()
OR public.in_space(space_id)
OR public.can_access_account(account_uid)
);
DROP POLICY IF EXISTS resource_access_delete_policy ON public."ResourceAccess";
CREATE POLICY resource_access_delete_policy ON public."ResourceAccess" FOR DELETE USING (public.in_space(space_id, 'editor') OR public.can_access_account(account_uid));
DROP POLICY IF EXISTS resource_access_insert_policy ON public."ResourceAccess";
Expand Down
Loading