pg_sql_triggers 1.2.0 → 1.3.0

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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +144 -0
  3. data/COVERAGE.md +26 -19
  4. data/Goal.md +276 -155
  5. data/README.md +27 -1
  6. data/app/assets/javascripts/pg_sql_triggers/trigger_actions.js +50 -0
  7. data/app/controllers/concerns/pg_sql_triggers/error_handling.rb +56 -0
  8. data/app/controllers/concerns/pg_sql_triggers/kill_switch_protection.rb +66 -0
  9. data/app/controllers/concerns/pg_sql_triggers/permission_checking.rb +117 -0
  10. data/app/controllers/pg_sql_triggers/application_controller.rb +10 -62
  11. data/app/controllers/pg_sql_triggers/audit_logs_controller.rb +102 -0
  12. data/app/controllers/pg_sql_triggers/dashboard_controller.rb +4 -9
  13. data/app/controllers/pg_sql_triggers/tables_controller.rb +30 -4
  14. data/app/controllers/pg_sql_triggers/triggers_controller.rb +3 -21
  15. data/app/helpers/pg_sql_triggers/permissions_helper.rb +43 -0
  16. data/app/models/pg_sql_triggers/audit_log.rb +106 -0
  17. data/app/models/pg_sql_triggers/trigger_registry.rb +178 -9
  18. data/app/views/layouts/pg_sql_triggers/application.html.erb +26 -6
  19. data/app/views/pg_sql_triggers/audit_logs/index.html.erb +177 -0
  20. data/app/views/pg_sql_triggers/dashboard/index.html.erb +33 -8
  21. data/app/views/pg_sql_triggers/tables/index.html.erb +76 -3
  22. data/app/views/pg_sql_triggers/tables/show.html.erb +17 -4
  23. data/app/views/pg_sql_triggers/triggers/_drop_modal.html.erb +16 -7
  24. data/app/views/pg_sql_triggers/triggers/_re_execute_modal.html.erb +16 -7
  25. data/app/views/pg_sql_triggers/triggers/show.html.erb +26 -6
  26. data/config/routes.rb +2 -0
  27. data/db/migrate/20260103000001_create_pg_sql_triggers_audit_log.rb +28 -0
  28. data/docs/README.md +15 -5
  29. data/docs/api-reference.md +191 -0
  30. data/docs/audit-trail.md +413 -0
  31. data/docs/configuration.md +6 -6
  32. data/docs/permissions.md +369 -0
  33. data/docs/troubleshooting.md +486 -0
  34. data/docs/ui-guide.md +211 -0
  35. data/docs/web-ui.md +257 -34
  36. data/lib/pg_sql_triggers/errors.rb +245 -0
  37. data/lib/pg_sql_triggers/generator/service.rb +32 -0
  38. data/lib/pg_sql_triggers/permissions/checker.rb +9 -2
  39. data/lib/pg_sql_triggers/registry.rb +141 -8
  40. data/lib/pg_sql_triggers/sql/kill_switch.rb +33 -5
  41. data/lib/pg_sql_triggers/testing/function_tester.rb +2 -0
  42. data/lib/pg_sql_triggers/version.rb +1 -1
  43. data/lib/pg_sql_triggers.rb +3 -6
  44. metadata +29 -6
  45. data/docs/screenshots/.gitkeep +0 -1
  46. data/docs/screenshots/Generate Trigger.png +0 -0
  47. data/docs/screenshots/Triggers Page.png +0 -0
  48. data/docs/screenshots/kill error.png +0 -0
  49. data/docs/screenshots/kill modal for migration down.png +0 -0
@@ -0,0 +1,369 @@
1
+ # Permissions Guide
2
+
3
+ PgSqlTriggers includes a comprehensive three-tier permission system to control access to trigger operations. This guide explains how to configure and use permissions.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Overview](#overview)
8
+ - [Permission Levels](#permission-levels)
9
+ - [Actions and Required Roles](#actions-and-required-roles)
10
+ - [Configuration](#configuration)
11
+ - [Integration Examples](#integration-examples)
12
+ - [UI Behavior](#ui-behavior)
13
+ - [Console API](#console-api)
14
+
15
+ ## Overview
16
+
17
+ The permission system provides three levels of access:
18
+
19
+ - **Viewer**: Read-only access to view triggers and their status
20
+ - **Operator**: Can enable/disable triggers, apply migrations, generate triggers
21
+ - **Admin**: Full access including drop, re-execute, and SQL capsule execution
22
+
23
+ By default, all permissions are allowed (permissive mode). **You must configure permissions in production** to enforce access controls.
24
+
25
+ ## Permission Levels
26
+
27
+ ### Viewer
28
+
29
+ Viewers have read-only access and can:
30
+ - View the dashboard
31
+ - View trigger details
32
+ - View table listings
33
+ - View audit logs
34
+ - View drift information
35
+
36
+ ### Operator
37
+
38
+ Operators can perform routine operations:
39
+ - All Viewer permissions
40
+ - Enable/disable triggers
41
+ - Apply trigger migrations
42
+ - Generate new triggers (via UI)
43
+ - Perform dry-run operations
44
+ - Test triggers
45
+
46
+ ### Admin
47
+
48
+ Admins have full access:
49
+ - All Operator permissions
50
+ - Drop triggers
51
+ - Re-execute triggers
52
+ - Execute SQL capsules
53
+ - Override drift detection
54
+
55
+ ## Actions and Required Roles
56
+
57
+ The following actions are mapped to permission levels:
58
+
59
+ | Action | Required Role | Description |
60
+ |--------|--------------|-------------|
61
+ | `view_triggers` | Viewer | View trigger list and details |
62
+ | `view_diffs` | Viewer | View SQL differences and drift |
63
+ | `enable_trigger` | Operator | Enable a trigger |
64
+ | `disable_trigger` | Operator | Disable a trigger |
65
+ | `apply_trigger` | Operator | Apply a trigger migration |
66
+ | `dry_run_sql` | Operator | Perform dry-run validation |
67
+ | `generate_trigger` | Operator | Generate triggers via UI |
68
+ | `test_trigger` | Operator | Test trigger functions |
69
+ | `drop_trigger` | Admin | Drop a trigger from database |
70
+ | `execute_sql` | Admin | Execute SQL capsules |
71
+ | `override_drift` | Admin | Override drift detection warnings |
72
+
73
+ ## Configuration
74
+
75
+ ### Basic Configuration
76
+
77
+ Configure permissions in your initializer:
78
+
79
+ ```ruby
80
+ # config/initializers/pg_sql_triggers.rb
81
+ PgSqlTriggers.configure do |config|
82
+ config.permission_checker = ->(actor, action, environment) {
83
+ # Your permission logic here
84
+ # Return true if allowed, false if denied
85
+ }
86
+ end
87
+ ```
88
+
89
+ ### Simple Role-Based Example
90
+
91
+ ```ruby
92
+ PgSqlTriggers.configure do |config|
93
+ config.permission_checker = ->(actor, action, environment) {
94
+ # Assume actor has a :role method or attribute
95
+ user_role = actor.role.to_s.downcase
96
+
97
+ case action
98
+ when :view_triggers, :view_diffs
99
+ true # Everyone can view
100
+ when :enable_trigger, :disable_trigger, :apply_trigger,
101
+ :dry_run_sql, :generate_trigger, :test_trigger
102
+ user_role.in?(%w[operator admin])
103
+ when :drop_trigger, :execute_sql, :override_drift
104
+ user_role == 'admin'
105
+ else
106
+ false
107
+ end
108
+ }
109
+ end
110
+ ```
111
+
112
+ ### Pundit Integration
113
+
114
+ ```ruby
115
+ PgSqlTriggers.configure do |config|
116
+ config.permission_checker = ->(actor, action, environment) {
117
+ resource = PgSqlTriggers::TriggerPolicy.new(actor, nil)
118
+
119
+ case action
120
+ when :view_triggers, :view_diffs
121
+ resource.view?
122
+ when :enable_trigger, :disable_trigger
123
+ resource.enable? || resource.disable?
124
+ when :drop_trigger, :execute_sql
125
+ resource.admin?
126
+ else
127
+ false
128
+ end
129
+ }
130
+ end
131
+ ```
132
+
133
+ ### CanCanCan Integration
134
+
135
+ ```ruby
136
+ PgSqlTriggers.configure do |config|
137
+ config.permission_checker = ->(actor, action, environment) {
138
+ ability = Ability.new(actor)
139
+
140
+ case action
141
+ when :view_triggers, :view_diffs
142
+ ability.can?(:read, :trigger)
143
+ when :enable_trigger, :disable_trigger
144
+ ability.can?(:manage, :trigger)
145
+ when :drop_trigger, :execute_sql
146
+ ability.can?(:destroy, :trigger)
147
+ else
148
+ false
149
+ end
150
+ }
151
+ end
152
+ ```
153
+
154
+ ### Environment-Based Permissions
155
+
156
+ You can restrict permissions by environment:
157
+
158
+ ```ruby
159
+ PgSqlTriggers.configure do |config|
160
+ config.permission_checker = ->(actor, action, environment) {
161
+ # Stricter permissions in production
162
+ if environment == 'production'
163
+ case action
164
+ when :drop_trigger, :execute_sql
165
+ actor.role == 'admin' && actor.super_admin?
166
+ else
167
+ actor.role.in?(%w[operator admin])
168
+ end
169
+ else
170
+ # More permissive in development
171
+ actor.role.in?(%w[viewer operator admin])
172
+ end
173
+ }
174
+ end
175
+ ```
176
+
177
+ ## Integration Examples
178
+
179
+ ### Controller Integration
180
+
181
+ The permission system integrates with controllers automatically. Override `current_actor` in your application controller:
182
+
183
+ ```ruby
184
+ # app/controllers/application_controller.rb
185
+ class ApplicationController < ActionController::Base
186
+ private
187
+
188
+ def current_actor
189
+ {
190
+ type: current_user.class.name,
191
+ id: current_user.id.to_s,
192
+ role: current_user.role # Add role if needed
193
+ }
194
+ end
195
+ end
196
+ ```
197
+
198
+ For PgSqlTriggers controllers, override the ApplicationController:
199
+
200
+ ```ruby
201
+ # app/controllers/pg_sql_triggers/application_controller.rb
202
+ module PgSqlTriggers
203
+ class ApplicationController < ::PgSqlTriggers::ApplicationController
204
+ private
205
+
206
+ def current_actor
207
+ {
208
+ type: current_user.class.name,
209
+ id: current_user.id.to_s,
210
+ role: current_user.role
211
+ }
212
+ end
213
+ end
214
+ end
215
+ ```
216
+
217
+ ### Custom Actor Format
218
+
219
+ Your actor can be any format as long as your permission checker understands it:
220
+
221
+ ```ruby
222
+ # Actor as User object
223
+ config.permission_checker = ->(actor, action, environment) {
224
+ return false unless actor.is_a?(User)
225
+ actor.role >= required_role_for(action)
226
+ }
227
+
228
+ # Actor as Hash
229
+ config.permission_checker = ->(actor, action, environment) {
230
+ return false unless actor.is_a?(Hash)
231
+ actor[:role] >= required_role_for(action)
232
+ }
233
+ ```
234
+
235
+ ## UI Behavior
236
+
237
+ The UI automatically adjusts based on permissions:
238
+
239
+ ### Button Visibility
240
+
241
+ - **Enable/Disable buttons**: Only visible to Operator+ roles
242
+ - **Drop button**: Only visible to Admin roles
243
+ - **Re-execute button**: Only visible to Admin roles
244
+ - **Execute SQL button**: Only visible to Admin roles
245
+ - **Generate Trigger button**: Only visible to Operator+ roles
246
+
247
+ ### Permission Errors
248
+
249
+ When a user attempts an unauthorized action:
250
+
251
+ 1. **UI Actions**: User is redirected with an alert message
252
+ 2. **API Calls**: `PermissionError` is raised with recovery suggestions
253
+ 3. **Error Messages**: Include the required role and recovery steps
254
+
255
+ ## Console API
256
+
257
+ All console API methods check permissions:
258
+
259
+ ```ruby
260
+ # Enable trigger (requires Operator+)
261
+ PgSqlTriggers::Registry.enable(
262
+ "trigger_name",
263
+ actor: current_user,
264
+ confirmation: "EXECUTE TRIGGER_ENABLE"
265
+ )
266
+
267
+ # Drop trigger (requires Admin)
268
+ PgSqlTriggers::Registry.drop(
269
+ "trigger_name",
270
+ actor: current_user,
271
+ reason: "No longer needed",
272
+ confirmation: "EXECUTE TRIGGER_DROP"
273
+ )
274
+
275
+ # Execute SQL capsule (requires Admin)
276
+ PgSqlTriggers::SQL::Executor.execute(
277
+ capsule,
278
+ actor: current_user,
279
+ confirmation: "EXECUTE SQL"
280
+ )
281
+ ```
282
+
283
+ ### Permission Errors in Console
284
+
285
+ When permission is denied, a `PermissionError` is raised:
286
+
287
+ ```ruby
288
+ begin
289
+ PgSqlTriggers::Registry.drop("trigger_name", actor: user)
290
+ rescue PgSqlTriggers::PermissionError => e
291
+ puts "Permission denied: #{e.message}"
292
+ puts "Recovery: #{e.recovery_suggestion}"
293
+ # Error code: e.error_code => "PERMISSION_DENIED"
294
+ end
295
+ ```
296
+
297
+ ## Testing Permissions
298
+
299
+ ### In Tests
300
+
301
+ ```ruby
302
+ # RSpec example
303
+ RSpec.describe "Trigger permissions" do
304
+ let(:operator_user) { create(:user, role: 'operator') }
305
+ let(:admin_user) { create(:user, role: 'admin') }
306
+ let(:viewer_user) { create(:user, role: 'viewer') }
307
+
308
+ it "allows operators to enable triggers" do
309
+ actor = { type: "User", id: operator_user.id.to_s, role: 'operator' }
310
+ expect {
311
+ PgSqlTriggers::Registry.enable("trigger_name", actor: actor)
312
+ }.not_to raise_error
313
+ end
314
+
315
+ it "denies viewers from dropping triggers" do
316
+ actor = { type: "User", id: viewer_user.id.to_s, role: 'viewer' }
317
+ expect {
318
+ PgSqlTriggers::Registry.drop("trigger_name", actor: actor)
319
+ }.to raise_error(PgSqlTriggers::PermissionError)
320
+ end
321
+ end
322
+ ```
323
+
324
+ ## Best Practices
325
+
326
+ 1. **Always configure permissions in production** - Default permissive mode is unsafe
327
+ 2. **Use environment-based permissions** - Stricter in production, permissive in development
328
+ 3. **Test permission scenarios** - Ensure your permission checker works correctly
329
+ 4. **Log permission denials** - Monitor unauthorized access attempts
330
+ 5. **Document your permission model** - Help team members understand access levels
331
+
332
+ ## Troubleshooting
333
+
334
+ ### All users have full access
335
+
336
+ **Problem**: Permissions are not being enforced.
337
+
338
+ **Solution**: Check that `permission_checker` is configured. The default is permissive (allows all).
339
+
340
+ ```ruby
341
+ # Verify configuration
342
+ PgSqlTriggers.permission_checker # Should not be nil in production
343
+ ```
344
+
345
+ ### Permission errors in development
346
+
347
+ **Problem**: Permission checks are blocking development work.
348
+
349
+ **Solution**: Use environment-based permissions or disable checks in development:
350
+
351
+ ```ruby
352
+ config.permission_checker = ->(actor, action, environment) {
353
+ return true if Rails.env.development?
354
+ # Production permission logic
355
+ }
356
+ ```
357
+
358
+ ### Actor format errors
359
+
360
+ **Problem**: Permission checker receives unexpected actor format.
361
+
362
+ **Solution**: Ensure your permission checker handles the actor format you're using. Check controller `current_actor` method.
363
+
364
+ ## Related Documentation
365
+
366
+ - [Configuration Reference](configuration.md#permission-system) - Complete configuration options
367
+ - [API Reference](api-reference.md) - Console API methods
368
+ - [Web UI Guide](web-ui.md) - UI features and behavior
369
+