| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305 |
- -- =====================================================
- -- Row Level Security (RLS) Policies
- -- =====================================================
- -- This migration adds row-level security policies to protect data access
- -- based on user roles and ownership
- -- Enable Row Level Security on all system tables
- ALTER TABLE __sys_users ENABLE ROW LEVEL SECURITY;
- ALTER TABLE __sys_sessions ENABLE ROW LEVEL SECURITY;
- ALTER TABLE __sys_api_keys ENABLE ROW LEVEL SECURITY;
- ALTER TABLE __sys_applications ENABLE ROW LEVEL SECURITY;
- ALTER TABLE __sys_deployments ENABLE ROW LEVEL SECURITY;
- ALTER TABLE __sys_audit_logs ENABLE ROW LEVEL SECURITY;
- ALTER TABLE __sys_email_queue ENABLE ROW LEVEL SECURITY;
- ALTER TABLE __sys_smtp_settings ENABLE ROW LEVEL SECURITY;
- -- Add role column to users table if it doesn't exist
- DO $$
- BEGIN
- IF NOT EXISTS (SELECT 1 FROM information_schema.columns
- WHERE table_name='__sys_users' AND column_name='role') THEN
- ALTER TABLE __sys_users ADD COLUMN role VARCHAR(50) DEFAULT 'user';
- END IF;
- END $$;
- -- Update existing users to have admin role (for the first user)
- UPDATE __sys_users SET role = 'admin' WHERE role IS NULL LIMIT 1;
- -- Create function to get current user ID from JWT claims
- CREATE OR REPLACE FUNCTION current_user_id() RETURNS UUID AS $$
- BEGIN
- -- Try to get user_id from current_setting
- -- This will be set by the application layer
- RETURN current_setting('app.current_user_id', true)::UUID;
- EXCEPTION
- WHEN OTHERS THEN
- RETURN NULL;
- END;
- $$ LANGUAGE plpgsql STABLE;
- -- Create function to check if current user is admin
- CREATE OR REPLACE FUNCTION is_admin() RETURNS BOOLEAN AS $$
- DECLARE
- user_role VARCHAR(50);
- BEGIN
- SELECT role INTO user_role
- FROM __sys_users
- WHERE id = current_user_id();
- RETURN user_role = 'admin';
- EXCEPTION
- WHEN OTHERS THEN
- RETURN false;
- END;
- $$ LANGUAGE plpgsql STABLE;
- -- =====================================================
- -- USERS TABLE POLICIES
- -- =====================================================
- -- Users can see their own profile, admins can see all users
- CREATE POLICY users_select_policy ON __sys_users
- FOR SELECT
- USING (id = current_user_id() OR is_admin());
- -- Users can update their own profile, admins can update any user
- CREATE POLICY users_update_policy ON __sys_users
- FOR UPDATE
- USING (id = current_user_id() OR is_admin());
- -- Only admins can insert users
- CREATE POLICY users_insert_policy ON __sys_users
- FOR INSERT
- WITH CHECK (is_admin());
- -- Only admins can delete users
- CREATE POLICY users_delete_policy ON __sys_users
- FOR DELETE
- USING (is_admin());
- -- =====================================================
- -- SESSIONS TABLE POLICIES
- -- =====================================================
- -- Users can see their own sessions, admins can see all
- CREATE POLICY sessions_select_policy ON __sys_sessions
- FOR SELECT
- USING (user_id = current_user_id() OR is_admin());
- -- Users can insert their own sessions
- CREATE POLICY sessions_insert_policy ON __sys_sessions
- FOR INSERT
- WITH CHECK (user_id = current_user_id() OR is_admin());
- -- Users can delete their own sessions, admins can delete any
- CREATE POLICY sessions_delete_policy ON __sys_sessions
- FOR DELETE
- USING (user_id = current_user_id() OR is_admin());
- -- =====================================================
- -- API KEYS TABLE POLICIES
- -- =====================================================
- -- Users can see their own API keys, admins can see all
- CREATE POLICY api_keys_select_policy ON __sys_api_keys
- FOR SELECT
- USING (created_by = current_user_id() OR is_admin());
- -- Users can create their own API keys
- CREATE POLICY api_keys_insert_policy ON __sys_api_keys
- FOR INSERT
- WITH CHECK (created_by = current_user_id() OR is_admin());
- -- Users can update their own API keys, admins can update any
- CREATE POLICY api_keys_update_policy ON __sys_api_keys
- FOR UPDATE
- USING (created_by = current_user_id() OR is_admin());
- -- Users can delete their own API keys, admins can delete any
- CREATE POLICY api_keys_delete_policy ON __sys_api_keys
- FOR DELETE
- USING (created_by = current_user_id() OR is_admin());
- -- =====================================================
- -- APPLICATIONS TABLE POLICIES
- -- =====================================================
- -- Users can see their own applications, admins can see all
- CREATE POLICY applications_select_policy ON __sys_applications
- FOR SELECT
- USING (created_by = current_user_id() OR is_admin());
- -- Users can create applications
- CREATE POLICY applications_insert_policy ON __sys_applications
- FOR INSERT
- WITH CHECK (created_by = current_user_id() OR is_admin());
- -- Users can update their own applications, admins can update any
- CREATE POLICY applications_update_policy ON __sys_applications
- FOR UPDATE
- USING (created_by = current_user_id() OR is_admin());
- -- Users can delete their own applications, admins can delete any
- CREATE POLICY applications_delete_policy ON __sys_applications
- FOR DELETE
- USING (created_by = current_user_id() OR is_admin());
- -- =====================================================
- -- DEPLOYMENTS TABLE POLICIES
- -- =====================================================
- -- Users can see deployments of their applications, admins can see all
- CREATE POLICY deployments_select_policy ON __sys_deployments
- FOR SELECT
- USING (
- EXISTS (
- SELECT 1 FROM __sys_applications
- WHERE __sys_applications.id = __sys_deployments.application_id
- AND (__sys_applications.created_by = current_user_id() OR is_admin())
- )
- );
- -- Users can create deployments for their applications
- CREATE POLICY deployments_insert_policy ON __sys_deployments
- FOR INSERT
- WITH CHECK (
- EXISTS (
- SELECT 1 FROM __sys_applications
- WHERE __sys_applications.id = application_id
- AND (__sys_applications.created_by = current_user_id() OR is_admin())
- )
- );
- -- Users can update deployments of their applications, admins can update any
- CREATE POLICY deployments_update_policy ON __sys_deployments
- FOR UPDATE
- USING (
- EXISTS (
- SELECT 1 FROM __sys_applications
- WHERE __sys_applications.id = __sys_deployments.application_id
- AND (__sys_applications.created_by = current_user_id() OR is_admin())
- )
- );
- -- Users can delete deployments of their applications, admins can delete any
- CREATE POLICY deployments_delete_policy ON __sys_deployments
- FOR DELETE
- USING (
- EXISTS (
- SELECT 1 FROM __sys_applications
- WHERE __sys_applications.id = __sys_deployments.application_id
- AND (__sys_applications.created_by = current_user_id() OR is_admin())
- )
- );
- -- =====================================================
- -- AUDIT LOGS TABLE POLICIES
- -- =====================================================
- -- Users can see their own audit logs, admins can see all
- CREATE POLICY audit_logs_select_policy ON __sys_audit_logs
- FOR SELECT
- USING (user_id = current_user_id() OR is_admin());
- -- Only the system can insert audit logs (through application)
- CREATE POLICY audit_logs_insert_policy ON __sys_audit_logs
- FOR INSERT
- WITH CHECK (true);
- -- No one can update audit logs (immutable)
- CREATE POLICY audit_logs_update_policy ON __sys_audit_logs
- FOR UPDATE
- USING (false);
- -- Only admins can delete audit logs (for cleanup)
- CREATE POLICY audit_logs_delete_policy ON __sys_audit_logs
- FOR DELETE
- USING (is_admin());
- -- =====================================================
- -- EMAIL QUEUE TABLE POLICIES
- -- =====================================================
- -- Only admins can see email queue
- CREATE POLICY email_queue_select_policy ON __sys_email_queue
- FOR SELECT
- USING (is_admin());
- -- Only admins can insert into email queue
- CREATE POLICY email_queue_insert_policy ON __sys_email_queue
- FOR INSERT
- WITH CHECK (is_admin());
- -- Only admins can update email queue
- CREATE POLICY email_queue_update_policy ON __sys_email_queue
- FOR UPDATE
- USING (is_admin());
- -- Only admins can delete from email queue
- CREATE POLICY email_queue_delete_policy ON __sys_email_queue
- FOR DELETE
- USING (is_admin());
- -- =====================================================
- -- SMTP SETTINGS TABLE POLICIES
- -- =====================================================
- -- Only admins can see SMTP settings
- CREATE POLICY smtp_settings_select_policy ON __sys_smtp_settings
- FOR SELECT
- USING (is_admin());
- -- Only admins can insert SMTP settings
- CREATE POLICY smtp_settings_insert_policy ON __sys_smtp_settings
- FOR INSERT
- WITH CHECK (is_admin());
- -- Only admins can update SMTP settings
- CREATE POLICY smtp_settings_update_policy ON __sys_smtp_settings
- FOR UPDATE
- USING (is_admin());
- -- Only admins can delete SMTP settings
- CREATE POLICY smtp_settings_delete_policy ON __sys_smtp_settings
- FOR DELETE
- USING (is_admin());
- -- =====================================================
- -- BYPASS RLS FOR SERVICE ROLE
- -- =====================================================
- -- Allow the service user to bypass RLS for system operations
- -- This is important for the application to function while RLS infrastructure is being built
- -- Note: RLS is ENABLED but not FORCED, meaning:
- -- - The service role (saas_user) can bypass RLS and access all data
- -- - When we set up proper user context in the future, RLS will be enforced
- -- - This allows gradual migration to full RLS enforcement
- -- If you want to FORCE RLS (enforce for all roles including service role),
- -- uncomment the lines below:
- -- ALTER TABLE __sys_users FORCE ROW LEVEL SECURITY;
- -- ALTER TABLE __sys_sessions FORCE ROW LEVEL SECURITY;
- -- ALTER TABLE __sys_api_keys FORCE ROW LEVEL SECURITY;
- -- ALTER TABLE __sys_applications FORCE ROW LEVEL SECURITY;
- -- ALTER TABLE __sys_deployments FORCE ROW LEVEL SECURITY;
- -- ALTER TABLE __sys_audit_logs FORCE ROW LEVEL SECURITY;
- -- ALTER TABLE __sys_email_queue FORCE ROW LEVEL SECURITY;
- -- ALTER TABLE __sys_smtp_settings FORCE ROW LEVEL SECURITY;
- -- Create indexes for better RLS performance
- CREATE INDEX IF NOT EXISTS idx_users_role ON __sys_users(role);
- CREATE INDEX IF NOT EXISTS idx_sessions_user_id ON __sys_sessions(user_id);
- CREATE INDEX IF NOT EXISTS idx_api_keys_created_by ON __sys_api_keys(created_by);
- CREATE INDEX IF NOT EXISTS idx_applications_created_by ON __sys_applications(created_by);
- CREATE INDEX IF NOT EXISTS idx_deployments_application_id ON __sys_deployments(application_id);
- CREATE INDEX IF NOT EXISTS idx_audit_logs_user_id ON __sys_audit_logs(user_id);
- -- Grant necessary permissions to the service user
- GRANT ALL ON ALL TABLES IN SCHEMA public TO saas_user;
- GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO saas_user;
- GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO saas_user;
- COMMENT ON FUNCTION current_user_id() IS 'Returns the current user ID from session context';
- COMMENT ON FUNCTION is_admin() IS 'Returns true if the current user has admin role';
|