trading-platform-database-v2/ddl/schemas/rbac/tables/003_role_permissions.sql
rckrdmrd e520268348 Migración desde trading-platform/apps/database - Estándar multi-repo v2
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 08:32:52 -06:00

181 lines
6.8 KiB
PL/PgSQL

-- ============================================================================
-- 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';