-- ============================================================================ -- RBAC Schema: Role Permissions Table -- Maps permissions to roles -- ============================================================================ -- ============================================================================ -- ROLE_PERMISSIONS TABLE -- Junction table linking roles to their permissions -- ============================================================================ CREATE TABLE IF NOT EXISTS rbac.role_permissions ( -- Primary key id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- Role reference role_id UUID NOT NULL REFERENCES rbac.roles(id) ON DELETE CASCADE, -- Permission reference permission_id UUID NOT NULL REFERENCES rbac.permissions(id) ON DELETE CASCADE, -- Grant type: allow or deny (for permission inheritance override) grant_type VARCHAR(10) NOT NULL DEFAULT 'allow' CHECK (grant_type IN ('allow', 'deny')), -- Conditions for permission (JSON for field-level or conditional access) conditions JSONB DEFAULT NULL, -- Audit fields created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, created_by UUID REFERENCES users.users(id), -- Constraints CONSTRAINT uq_role_permission UNIQUE (role_id, permission_id) ); -- ============================================================================ -- INDEXES -- ============================================================================ CREATE INDEX IF NOT EXISTS idx_role_permissions_role_id ON rbac.role_permissions(role_id); CREATE INDEX IF NOT EXISTS idx_role_permissions_permission_id ON rbac.role_permissions(permission_id); CREATE INDEX IF NOT EXISTS idx_role_permissions_grant_type ON rbac.role_permissions(grant_type); -- ============================================================================ -- FUNCTION: Assign default permissions to system roles -- ============================================================================ CREATE OR REPLACE FUNCTION rbac.assign_default_role_permissions(p_tenant_id UUID) RETURNS void AS $$ DECLARE v_owner_role_id UUID; v_admin_role_id UUID; v_manager_role_id UUID; v_member_role_id UUID; v_viewer_role_id UUID; v_perm RECORD; BEGIN -- Get role IDs SELECT id INTO v_owner_role_id FROM rbac.roles WHERE tenant_id = p_tenant_id AND slug = 'owner'; SELECT id INTO v_admin_role_id FROM rbac.roles WHERE tenant_id = p_tenant_id AND slug = 'admin'; SELECT id INTO v_manager_role_id FROM rbac.roles WHERE tenant_id = p_tenant_id AND slug = 'manager'; SELECT id INTO v_member_role_id FROM rbac.roles WHERE tenant_id = p_tenant_id AND slug = 'member'; SELECT id INTO v_viewer_role_id FROM rbac.roles WHERE tenant_id = p_tenant_id AND slug = 'viewer'; -- Owner gets ALL permissions FOR v_perm IN SELECT id FROM rbac.permissions WHERE is_active = true LOOP INSERT INTO rbac.role_permissions (role_id, permission_id) VALUES (v_owner_role_id, v_perm.id) ON CONFLICT (role_id, permission_id) DO NOTHING; END LOOP; -- Admin gets all except org:delete and org:billing:manage FOR v_perm IN SELECT id FROM rbac.permissions WHERE is_active = true AND code NOT IN ('org:delete', 'org:billing:manage') LOOP INSERT INTO rbac.role_permissions (role_id, permission_id) VALUES (v_admin_role_id, v_perm.id) ON CONFLICT (role_id, permission_id) DO NOTHING; END LOOP; -- Manager gets user viewing, role viewing, and standard features FOR v_perm IN SELECT id FROM rbac.permissions WHERE is_active = true AND code IN ( 'org:read', 'users:read', 'users:invite', 'roles:read', 'wallet:read', 'wallet:deposit', 'wallet:withdraw', 'products:read', 'products:purchase', 'vip:read', 'vip:subscribe', 'agents:read', 'agents:allocate', 'predictions:read', 'predictions:purchase', 'reports:read' ) LOOP INSERT INTO rbac.role_permissions (role_id, permission_id) VALUES (v_manager_role_id, v_perm.id) ON CONFLICT (role_id, permission_id) DO NOTHING; END LOOP; -- Member gets standard user features FOR v_perm IN SELECT id FROM rbac.permissions WHERE is_active = true AND code IN ( 'org:read', 'wallet:read', 'wallet:deposit', 'wallet:withdraw', 'products:read', 'products:purchase', 'vip:read', 'vip:subscribe', 'agents:read', 'agents:allocate', 'predictions:read', 'predictions:purchase' ) LOOP INSERT INTO rbac.role_permissions (role_id, permission_id) VALUES (v_member_role_id, v_perm.id) ON CONFLICT (role_id, permission_id) DO NOTHING; END LOOP; -- Viewer gets read-only access FOR v_perm IN SELECT id FROM rbac.permissions WHERE is_active = true AND code IN ( 'org:read', 'wallet:read', 'products:read', 'vip:read', 'agents:read', 'predictions:read' ) LOOP INSERT INTO rbac.role_permissions (role_id, permission_id) VALUES (v_viewer_role_id, v_perm.id) ON CONFLICT (role_id, permission_id) DO NOTHING; END LOOP; END; $$ LANGUAGE plpgsql; -- ============================================================================ -- VIEW: Role with permissions -- ============================================================================ CREATE OR REPLACE VIEW rbac.v_role_permissions AS SELECT r.id AS role_id, r.tenant_id, r.name AS role_name, r.slug AS role_slug, r.hierarchy_level, p.id AS permission_id, p.code AS permission_code, p.name AS permission_name, p.module, p.action, p.resource, rp.grant_type, rp.conditions FROM rbac.roles r JOIN rbac.role_permissions rp ON r.id = rp.role_id JOIN rbac.permissions p ON rp.permission_id = p.id WHERE r.is_active = true AND p.is_active = true; -- ============================================================================ -- GRANTS -- ============================================================================ GRANT SELECT, INSERT, DELETE ON rbac.role_permissions TO trading_user; GRANT SELECT ON rbac.v_role_permissions TO trading_user; -- ============================================================================ -- COMMENTS -- ============================================================================ COMMENT ON TABLE rbac.role_permissions IS 'Junction table mapping permissions to roles'; COMMENT ON COLUMN rbac.role_permissions.grant_type IS 'allow = grants permission, deny = explicitly denies (for override)'; COMMENT ON COLUMN rbac.role_permissions.conditions IS 'JSON conditions for field-level or conditional access control';