@fayz-ai/db 0.22.2 → 0.22.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
-- The reach walk UNIONs (id, depth): a branch the person reaches directly AND
|
|
2
|
+
-- through its HQ came back twice, and every unit picker listed it twice.
|
|
3
|
+
-- Same body as 017, deduplicated on the way out.
|
|
4
|
+
|
|
5
|
+
CREATE OR REPLACE FUNCTION app.user_unit_ids_in(p_tenant uuid)
|
|
6
|
+
RETURNS SETOF uuid
|
|
7
|
+
LANGUAGE sql STABLE SECURITY DEFINER
|
|
8
|
+
SET search_path TO ''
|
|
9
|
+
AS $$
|
|
10
|
+
WITH RECURSIVE seed AS (
|
|
11
|
+
SELECT a.unit_id AS id, 0 AS depth
|
|
12
|
+
FROM app.user_unit_access a
|
|
13
|
+
WHERE a.user_id = (SELECT auth.uid())
|
|
14
|
+
AND a.tenant_id = p_tenant
|
|
15
|
+
),
|
|
16
|
+
tree AS (
|
|
17
|
+
SELECT id, depth FROM seed
|
|
18
|
+
UNION
|
|
19
|
+
SELECT u.id, t.depth + 1
|
|
20
|
+
FROM app.units u
|
|
21
|
+
JOIN tree t ON u.parent_unit_id = t.id
|
|
22
|
+
WHERE u.tenant_id = p_tenant AND t.depth < 8
|
|
23
|
+
)
|
|
24
|
+
SELECT DISTINCT id FROM tree;
|
|
25
|
+
$$;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
-- ---------------------------------------------------------------------------
|
|
2
|
+
-- 174 — a conta de quem recebe é dado do financeiro.
|
|
3
|
+
--
|
|
4
|
+
-- Three permissive policies that let any member of a tenant do what the gated
|
|
5
|
+
-- policies next to them refuse. Postgres ORs permissive policies, so a
|
|
6
|
+
-- tenant-only policy beside an `app.has_permission` one wins every time.
|
|
7
|
+
--
|
|
8
|
+
-- · payee_accounts (149): bank/PIX of whoever the house pays. Any member,
|
|
9
|
+
-- viewer and SDR included, could rewrite where a payout lands. Now read
|
|
10
|
+
-- needs financial.read; insert/update financial.edit; delete
|
|
11
|
+
-- financial.delete (financial.manage satisfies all three).
|
|
12
|
+
-- · fulfillments: `fulfillments_member_all` (FOR ALL) shadowed the
|
|
13
|
+
-- fulfillments_authz_* write policies. The write side goes; a tenant-wide
|
|
14
|
+
-- SELECT stays so shop screens and plg_shop_rep_orders keep reading as today.
|
|
15
|
+
-- Every writer outside the authz policies is SECURITY DEFINER or the
|
|
16
|
+
-- service role.
|
|
17
|
+
-- · audit_logs: any member could insert any action, backdate it, stamp
|
|
18
|
+
-- before/after and pick a unit. The CRUD trail (admin stores/audit.ts) is
|
|
19
|
+
-- the only client writer and only writes `record.*`; server namespaces are
|
|
20
|
+
-- written by SECURITY DEFINER functions owned by postgres, which bypass RLS.
|
|
21
|
+
-- audit_log() leaves the client surface for the same reason.
|
|
22
|
+
--
|
|
23
|
+
-- Additive and replay-safe: DROP POLICY IF EXISTS + CREATE POLICY.
|
|
24
|
+
-- ---------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
-- ── payee_accounts ──────────────────────────────────────────────────────
|
|
27
|
+
DROP POLICY IF EXISTS payee_accounts_member_all ON public.payee_accounts;
|
|
28
|
+
|
|
29
|
+
DROP POLICY IF EXISTS payee_accounts_authz_select ON public.payee_accounts;
|
|
30
|
+
CREATE POLICY payee_accounts_authz_select ON public.payee_accounts
|
|
31
|
+
FOR SELECT TO authenticated
|
|
32
|
+
USING (tenant_id = (SELECT app.current_tenant_id())
|
|
33
|
+
AND (SELECT app.has_permission('financial.read')));
|
|
34
|
+
|
|
35
|
+
DROP POLICY IF EXISTS payee_accounts_authz_insert ON public.payee_accounts;
|
|
36
|
+
CREATE POLICY payee_accounts_authz_insert ON public.payee_accounts
|
|
37
|
+
FOR INSERT TO authenticated
|
|
38
|
+
WITH CHECK (tenant_id = (SELECT app.current_tenant_id())
|
|
39
|
+
AND (SELECT app.has_permission('financial.edit')));
|
|
40
|
+
|
|
41
|
+
-- trg_payee_accounts_one_default is SECURITY INVOKER: its demote-the-old-default
|
|
42
|
+
-- UPDATE runs under this policy too, which is why insert also asks for edit.
|
|
43
|
+
DROP POLICY IF EXISTS payee_accounts_authz_update ON public.payee_accounts;
|
|
44
|
+
CREATE POLICY payee_accounts_authz_update ON public.payee_accounts
|
|
45
|
+
FOR UPDATE TO authenticated
|
|
46
|
+
USING (tenant_id = (SELECT app.current_tenant_id())
|
|
47
|
+
AND (SELECT app.has_permission('financial.edit')))
|
|
48
|
+
WITH CHECK (tenant_id = (SELECT app.current_tenant_id())
|
|
49
|
+
AND (SELECT app.has_permission('financial.edit')));
|
|
50
|
+
|
|
51
|
+
DROP POLICY IF EXISTS payee_accounts_authz_delete ON public.payee_accounts;
|
|
52
|
+
CREATE POLICY payee_accounts_authz_delete ON public.payee_accounts
|
|
53
|
+
FOR DELETE TO authenticated
|
|
54
|
+
USING (tenant_id = (SELECT app.current_tenant_id())
|
|
55
|
+
AND (SELECT app.has_permission('financial.delete')));
|
|
56
|
+
|
|
57
|
+
-- ── fulfillments ────────────────────────────────────────────────────────
|
|
58
|
+
DROP POLICY IF EXISTS fulfillments_member_all ON public.fulfillments;
|
|
59
|
+
|
|
60
|
+
DROP POLICY IF EXISTS fulfillments_member_read ON public.fulfillments;
|
|
61
|
+
CREATE POLICY fulfillments_member_read ON public.fulfillments
|
|
62
|
+
FOR SELECT TO authenticated
|
|
63
|
+
USING (tenant_id = (SELECT app.current_tenant_id()));
|
|
64
|
+
|
|
65
|
+
-- ── audit_logs ──────────────────────────────────────────────────────────
|
|
66
|
+
-- Checked after the BEFORE trigger (handle_audit_actor), so user_id and
|
|
67
|
+
-- tenant_id are already the session's here.
|
|
68
|
+
DROP POLICY IF EXISTS audit_insert ON public.audit_logs;
|
|
69
|
+
CREATE POLICY audit_insert ON public.audit_logs
|
|
70
|
+
FOR INSERT TO authenticated
|
|
71
|
+
WITH CHECK (
|
|
72
|
+
tenant_id = (SELECT app.current_tenant_id())
|
|
73
|
+
AND user_id = (SELECT auth.uid())
|
|
74
|
+
AND action LIKE 'record.%'
|
|
75
|
+
AND before IS NULL
|
|
76
|
+
AND after IS NULL
|
|
77
|
+
AND (unit_id IS NULL OR (SELECT app.has_unit(unit_id)))
|
|
78
|
+
AND created_at BETWEEN now() - interval '1 minute' AND now() + interval '1 minute'
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
-- audit_log() is SECURITY DEFINER and takes any action, before/after and unit:
|
|
82
|
+
-- open to members it is the same forgery by another door. Nothing calls it
|
|
83
|
+
-- from a client or another function; server code keeps it via service_role.
|
|
84
|
+
REVOKE EXECUTE ON FUNCTION public.audit_log(text, text, uuid, text, jsonb, jsonb, uuid, jsonb, uuid)
|
|
85
|
+
FROM PUBLIC, anon, authenticated;
|
|
86
|
+
GRANT EXECUTE ON FUNCTION public.audit_log(text, text, uuid, text, jsonb, jsonb, uuid, jsonb, uuid)
|
|
87
|
+
TO service_role;
|
package/package.json
CHANGED