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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +144 -0
- data/COVERAGE.md +26 -19
- data/Goal.md +276 -155
- data/README.md +27 -1
- data/app/assets/javascripts/pg_sql_triggers/trigger_actions.js +50 -0
- data/app/controllers/concerns/pg_sql_triggers/error_handling.rb +56 -0
- data/app/controllers/concerns/pg_sql_triggers/kill_switch_protection.rb +66 -0
- data/app/controllers/concerns/pg_sql_triggers/permission_checking.rb +117 -0
- data/app/controllers/pg_sql_triggers/application_controller.rb +10 -62
- data/app/controllers/pg_sql_triggers/audit_logs_controller.rb +102 -0
- data/app/controllers/pg_sql_triggers/dashboard_controller.rb +4 -9
- data/app/controllers/pg_sql_triggers/tables_controller.rb +30 -4
- data/app/controllers/pg_sql_triggers/triggers_controller.rb +3 -21
- data/app/helpers/pg_sql_triggers/permissions_helper.rb +43 -0
- data/app/models/pg_sql_triggers/audit_log.rb +106 -0
- data/app/models/pg_sql_triggers/trigger_registry.rb +178 -9
- data/app/views/layouts/pg_sql_triggers/application.html.erb +26 -6
- data/app/views/pg_sql_triggers/audit_logs/index.html.erb +177 -0
- data/app/views/pg_sql_triggers/dashboard/index.html.erb +33 -8
- data/app/views/pg_sql_triggers/tables/index.html.erb +76 -3
- data/app/views/pg_sql_triggers/tables/show.html.erb +17 -4
- data/app/views/pg_sql_triggers/triggers/_drop_modal.html.erb +16 -7
- data/app/views/pg_sql_triggers/triggers/_re_execute_modal.html.erb +16 -7
- data/app/views/pg_sql_triggers/triggers/show.html.erb +26 -6
- data/config/routes.rb +2 -0
- data/db/migrate/20260103000001_create_pg_sql_triggers_audit_log.rb +28 -0
- data/docs/README.md +15 -5
- data/docs/api-reference.md +191 -0
- data/docs/audit-trail.md +413 -0
- data/docs/configuration.md +6 -6
- data/docs/permissions.md +369 -0
- data/docs/troubleshooting.md +486 -0
- data/docs/ui-guide.md +211 -0
- data/docs/web-ui.md +257 -34
- data/lib/pg_sql_triggers/errors.rb +245 -0
- data/lib/pg_sql_triggers/generator/service.rb +32 -0
- data/lib/pg_sql_triggers/permissions/checker.rb +9 -2
- data/lib/pg_sql_triggers/registry.rb +141 -8
- data/lib/pg_sql_triggers/sql/kill_switch.rb +33 -5
- data/lib/pg_sql_triggers/testing/function_tester.rb +2 -0
- data/lib/pg_sql_triggers/version.rb +1 -1
- data/lib/pg_sql_triggers.rb +3 -6
- metadata +29 -6
- data/docs/screenshots/.gitkeep +0 -1
- data/docs/screenshots/Generate Trigger.png +0 -0
- data/docs/screenshots/Triggers Page.png +0 -0
- data/docs/screenshots/kill error.png +0 -0
- data/docs/screenshots/kill modal for migration down.png +0 -0
data/docs/api-reference.md
CHANGED
|
@@ -9,6 +9,7 @@ Complete reference for using PgSqlTriggers programmatically from the Rails conso
|
|
|
9
9
|
- [Kill Switch API](#kill-switch-api)
|
|
10
10
|
- [DSL API](#dsl-api)
|
|
11
11
|
- [TriggerRegistry Model](#triggerregistry-model)
|
|
12
|
+
- [Audit Log API](#audit-log-api)
|
|
12
13
|
|
|
13
14
|
## Registry API
|
|
14
15
|
|
|
@@ -97,6 +98,76 @@ end
|
|
|
97
98
|
|
|
98
99
|
**Returns**: Hash with drift categories
|
|
99
100
|
|
|
101
|
+
### `PgSqlTriggers::Registry.drifted`
|
|
102
|
+
|
|
103
|
+
Returns all triggers that have drifted from their expected state.
|
|
104
|
+
|
|
105
|
+
```ruby
|
|
106
|
+
drifted_triggers = PgSqlTriggers::Registry.drifted
|
|
107
|
+
# => [
|
|
108
|
+
# { state: "drifted", trigger_name: "users_email_validation", ... },
|
|
109
|
+
# ...
|
|
110
|
+
# ]
|
|
111
|
+
|
|
112
|
+
drifted_triggers.each do |trigger|
|
|
113
|
+
puts "Drifted: #{trigger[:trigger_name]}"
|
|
114
|
+
end
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Returns**: Array of drift result hashes for drifted triggers
|
|
118
|
+
|
|
119
|
+
### `PgSqlTriggers::Registry.in_sync`
|
|
120
|
+
|
|
121
|
+
Returns all triggers that are in sync with their expected state.
|
|
122
|
+
|
|
123
|
+
```ruby
|
|
124
|
+
in_sync_triggers = PgSqlTriggers::Registry.in_sync
|
|
125
|
+
# => [
|
|
126
|
+
# { state: "in_sync", trigger_name: "billing_trigger", ... },
|
|
127
|
+
# ...
|
|
128
|
+
# ]
|
|
129
|
+
|
|
130
|
+
puts "In sync triggers: #{in_sync_triggers.count}"
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Returns**: Array of drift result hashes for in-sync triggers
|
|
134
|
+
|
|
135
|
+
### `PgSqlTriggers::Registry.unknown_triggers`
|
|
136
|
+
|
|
137
|
+
Returns all unknown (external) triggers not managed by this gem.
|
|
138
|
+
|
|
139
|
+
```ruby
|
|
140
|
+
unknown = PgSqlTriggers::Registry.unknown_triggers
|
|
141
|
+
# => [
|
|
142
|
+
# { state: "unknown", trigger_name: "external_trigger", ... },
|
|
143
|
+
# ...
|
|
144
|
+
# ]
|
|
145
|
+
|
|
146
|
+
unknown.each do |trigger|
|
|
147
|
+
puts "External trigger: #{trigger[:trigger_name]}"
|
|
148
|
+
end
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Returns**: Array of drift result hashes for unknown triggers
|
|
152
|
+
|
|
153
|
+
### `PgSqlTriggers::Registry.dropped`
|
|
154
|
+
|
|
155
|
+
Returns all triggers that have been dropped from the database.
|
|
156
|
+
|
|
157
|
+
```ruby
|
|
158
|
+
dropped_triggers = PgSqlTriggers::Registry.dropped
|
|
159
|
+
# => [
|
|
160
|
+
# { state: "dropped", trigger_name: "old_trigger", ... },
|
|
161
|
+
# ...
|
|
162
|
+
# ]
|
|
163
|
+
|
|
164
|
+
dropped_triggers.each do |trigger|
|
|
165
|
+
puts "Dropped: #{trigger[:trigger_name]}"
|
|
166
|
+
end
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Returns**: Array of drift result hashes for dropped triggers
|
|
170
|
+
|
|
100
171
|
### `PgSqlTriggers::Registry.validate!`
|
|
101
172
|
|
|
102
173
|
Validates all triggers and raises an error if any are invalid.
|
|
@@ -836,6 +907,14 @@ puts "Total triggers: #{triggers.count}"
|
|
|
836
907
|
drift = PgSqlTriggers::Registry.diff
|
|
837
908
|
puts "Drifted triggers: #{drift[:drifted].count}"
|
|
838
909
|
|
|
910
|
+
# Alternative: Use dedicated query methods
|
|
911
|
+
drifted = PgSqlTriggers::Registry.drifted
|
|
912
|
+
in_sync = PgSqlTriggers::Registry.in_sync
|
|
913
|
+
unknown = PgSqlTriggers::Registry.unknown_triggers
|
|
914
|
+
dropped = PgSqlTriggers::Registry.dropped
|
|
915
|
+
|
|
916
|
+
puts "Drifted: #{drifted.count}, In Sync: #{in_sync.count}, Unknown: #{unknown.count}, Dropped: #{dropped.count}"
|
|
917
|
+
|
|
839
918
|
# 5. Enable specific trigger
|
|
840
919
|
trigger = PgSqlTriggers::TriggerRegistry.find_by(trigger_name: "users_email_validation")
|
|
841
920
|
trigger.enable!(confirmation: "EXECUTE TRIGGER_ENABLE") if trigger
|
|
@@ -922,6 +1001,118 @@ rescue StandardError => e
|
|
|
922
1001
|
end
|
|
923
1002
|
```
|
|
924
1003
|
|
|
1004
|
+
## Audit Log API
|
|
1005
|
+
|
|
1006
|
+
The Audit Log API provides methods for querying and managing audit log entries.
|
|
1007
|
+
|
|
1008
|
+
### `PgSqlTriggers::AuditLog`
|
|
1009
|
+
|
|
1010
|
+
The `AuditLog` model provides methods for querying audit log entries.
|
|
1011
|
+
|
|
1012
|
+
#### Class Methods
|
|
1013
|
+
|
|
1014
|
+
##### `AuditLog.for_trigger_name(trigger_name)`
|
|
1015
|
+
|
|
1016
|
+
Get audit log entries for a specific trigger.
|
|
1017
|
+
|
|
1018
|
+
**Parameters:**
|
|
1019
|
+
- `trigger_name` (String): The trigger name to query
|
|
1020
|
+
|
|
1021
|
+
**Returns:** `ActiveRecord::Relation` - Audit log entries for the trigger, ordered by most recent first
|
|
1022
|
+
|
|
1023
|
+
**Example:**
|
|
1024
|
+
```ruby
|
|
1025
|
+
# Get all audit log entries for a specific trigger
|
|
1026
|
+
entries = PgSqlTriggers::AuditLog.for_trigger_name("users_email_validation")
|
|
1027
|
+
entries.each do |entry|
|
|
1028
|
+
puts "#{entry.operation}: #{entry.status} at #{entry.created_at}"
|
|
1029
|
+
end
|
|
1030
|
+
```
|
|
1031
|
+
|
|
1032
|
+
##### `AuditLog.log_success(...)`
|
|
1033
|
+
|
|
1034
|
+
Log a successful operation to the audit log.
|
|
1035
|
+
|
|
1036
|
+
**Parameters:**
|
|
1037
|
+
- `operation:` (Symbol, String): The operation being performed (e.g., `:trigger_enable`)
|
|
1038
|
+
- `trigger_name:` (String, nil): The trigger name (if applicable)
|
|
1039
|
+
- `actor:` (Hash): Information about who performed the action (e.g., `{ type: "UI", id: "123" }`)
|
|
1040
|
+
- `environment:` (String, nil): The environment
|
|
1041
|
+
- `reason:` (String, nil): Reason for the operation
|
|
1042
|
+
- `confirmation_text:` (String, nil): Confirmation text used
|
|
1043
|
+
- `before_state:` (Hash, nil): State before operation
|
|
1044
|
+
- `after_state:` (Hash, nil): State after operation
|
|
1045
|
+
- `diff:` (String, nil): Diff information
|
|
1046
|
+
|
|
1047
|
+
**Returns:** `AuditLog` instance or `nil` if logging fails
|
|
1048
|
+
|
|
1049
|
+
**Example:**
|
|
1050
|
+
```ruby
|
|
1051
|
+
PgSqlTriggers::AuditLog.log_success(
|
|
1052
|
+
operation: :trigger_enable,
|
|
1053
|
+
trigger_name: "users_email_validation",
|
|
1054
|
+
actor: { type: "Console", id: "admin@example.com" },
|
|
1055
|
+
environment: "production",
|
|
1056
|
+
before_state: { enabled: false },
|
|
1057
|
+
after_state: { enabled: true }
|
|
1058
|
+
)
|
|
1059
|
+
```
|
|
1060
|
+
|
|
1061
|
+
##### `AuditLog.log_failure(...)`
|
|
1062
|
+
|
|
1063
|
+
Log a failed operation to the audit log.
|
|
1064
|
+
|
|
1065
|
+
**Parameters:**
|
|
1066
|
+
- `operation:` (Symbol, String): The operation being performed
|
|
1067
|
+
- `trigger_name:` (String, nil): The trigger name (if applicable)
|
|
1068
|
+
- `actor:` (Hash): Information about who performed the action
|
|
1069
|
+
- `environment:` (String, nil): The environment
|
|
1070
|
+
- `error_message:` (String): Error message (required)
|
|
1071
|
+
- `reason:` (String, nil): Reason for the operation (if provided before failure)
|
|
1072
|
+
- `confirmation_text:` (String, nil): Confirmation text used
|
|
1073
|
+
- `before_state:` (Hash, nil): State before operation
|
|
1074
|
+
|
|
1075
|
+
**Returns:** `AuditLog` instance or `nil` if logging fails
|
|
1076
|
+
|
|
1077
|
+
**Example:**
|
|
1078
|
+
```ruby
|
|
1079
|
+
PgSqlTriggers::AuditLog.log_failure(
|
|
1080
|
+
operation: :trigger_enable,
|
|
1081
|
+
trigger_name: "users_email_validation",
|
|
1082
|
+
actor: { type: "UI", id: "user_123" },
|
|
1083
|
+
environment: "production",
|
|
1084
|
+
error_message: "Trigger does not exist in database",
|
|
1085
|
+
before_state: { enabled: false }
|
|
1086
|
+
)
|
|
1087
|
+
```
|
|
1088
|
+
|
|
1089
|
+
#### Scopes
|
|
1090
|
+
|
|
1091
|
+
The `AuditLog` model provides several useful scopes:
|
|
1092
|
+
|
|
1093
|
+
- `AuditLog.for_trigger(trigger_name)` - Filter by trigger name
|
|
1094
|
+
- `AuditLog.for_operation(operation)` - Filter by operation type
|
|
1095
|
+
- `AuditLog.for_environment(env)` - Filter by environment
|
|
1096
|
+
- `AuditLog.successful` - Only successful operations
|
|
1097
|
+
- `AuditLog.failed` - Only failed operations
|
|
1098
|
+
- `AuditLog.recent` - Order by most recent first
|
|
1099
|
+
|
|
1100
|
+
**Example:**
|
|
1101
|
+
```ruby
|
|
1102
|
+
# Get all failed operations in production
|
|
1103
|
+
failed_ops = PgSqlTriggers::AuditLog
|
|
1104
|
+
.failed
|
|
1105
|
+
.for_environment("production")
|
|
1106
|
+
.recent
|
|
1107
|
+
.limit(10)
|
|
1108
|
+
|
|
1109
|
+
# Get all enable operations for a trigger
|
|
1110
|
+
enable_ops = PgSqlTriggers::AuditLog
|
|
1111
|
+
.for_trigger("users_email_validation")
|
|
1112
|
+
.for_operation("trigger_enable")
|
|
1113
|
+
.recent
|
|
1114
|
+
```
|
|
1115
|
+
|
|
925
1116
|
## Next Steps
|
|
926
1117
|
|
|
927
1118
|
- [Usage Guide](usage-guide.md) - Learn the DSL and migration system
|
data/docs/audit-trail.md
ADDED
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
# Audit Trail Guide
|
|
2
|
+
|
|
3
|
+
PgSqlTriggers provides comprehensive audit logging for all trigger operations. This guide explains how to view, filter, and export audit logs.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Overview](#overview)
|
|
8
|
+
- [Accessing Audit Logs](#accessing-audit-logs)
|
|
9
|
+
- [Viewing Audit Logs](#viewing-audit-logs)
|
|
10
|
+
- [Filtering Logs](#filtering-logs)
|
|
11
|
+
- [Exporting Logs](#exporting-logs)
|
|
12
|
+
- [Console API](#console-api)
|
|
13
|
+
- [Logged Operations](#logged-operations)
|
|
14
|
+
- [Audit Log Structure](#audit-log-structure)
|
|
15
|
+
|
|
16
|
+
## Overview
|
|
17
|
+
|
|
18
|
+
The audit trail captures:
|
|
19
|
+
|
|
20
|
+
- **Who**: Actor information (user type and ID)
|
|
21
|
+
- **What**: Operation performed (enable, disable, drop, re-execute, etc.)
|
|
22
|
+
- **When**: Timestamp of the operation
|
|
23
|
+
- **Where**: Environment where operation occurred
|
|
24
|
+
- **Result**: Success or failure status
|
|
25
|
+
- **Context**: Before/after state, reasons, confirmation text, error messages
|
|
26
|
+
|
|
27
|
+
All operations are automatically logged, providing a complete audit trail for compliance and debugging.
|
|
28
|
+
|
|
29
|
+
## Accessing Audit Logs
|
|
30
|
+
|
|
31
|
+
### Via Web UI
|
|
32
|
+
|
|
33
|
+
Navigate to the Audit Log page:
|
|
34
|
+
|
|
35
|
+
1. Go to the PgSqlTriggers dashboard
|
|
36
|
+
2. Click "Audit Log" in the navigation menu
|
|
37
|
+
3. URL: `http://localhost:3000/pg_sql_triggers/audit_logs`
|
|
38
|
+
|
|
39
|
+
### Via Console API
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
# Get all audit logs
|
|
43
|
+
PgSqlTriggers::AuditLog.all
|
|
44
|
+
|
|
45
|
+
# Get logs for a specific trigger
|
|
46
|
+
PgSqlTriggers::AuditLog.for_trigger_name("users_email_validation")
|
|
47
|
+
|
|
48
|
+
# Get recent logs
|
|
49
|
+
PgSqlTriggers::AuditLog.recent.limit(100)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Viewing Audit Logs
|
|
53
|
+
|
|
54
|
+
### Audit Log Table
|
|
55
|
+
|
|
56
|
+
The audit log table displays:
|
|
57
|
+
|
|
58
|
+
- **Timestamp**: When the operation occurred
|
|
59
|
+
- **Operation**: Type of operation (enable, disable, drop, etc.)
|
|
60
|
+
- **Trigger Name**: Affected trigger (if applicable)
|
|
61
|
+
- **Actor**: Who performed the operation (type and ID)
|
|
62
|
+
- **Environment**: Environment where operation occurred
|
|
63
|
+
- **Status**: Success or failure
|
|
64
|
+
- **Reason**: Reason provided (for drop/re-execute operations)
|
|
65
|
+
|
|
66
|
+
### Viewing Details
|
|
67
|
+
|
|
68
|
+
Click on any audit log entry to view detailed information:
|
|
69
|
+
|
|
70
|
+
- **Before State**: State before the operation (enabled/disabled, function body, etc.)
|
|
71
|
+
- **After State**: State after the operation
|
|
72
|
+
- **Diff**: Changes made (for re-execute operations)
|
|
73
|
+
- **Confirmation Text**: Confirmation text used (if applicable)
|
|
74
|
+
- **Error Message**: Error details (for failed operations)
|
|
75
|
+
|
|
76
|
+
## Filtering Logs
|
|
77
|
+
|
|
78
|
+
### Filter Options
|
|
79
|
+
|
|
80
|
+
Filter audit logs by:
|
|
81
|
+
|
|
82
|
+
- **Trigger Name**: Filter by specific trigger
|
|
83
|
+
- **Operation**: Filter by operation type (enable, disable, drop, etc.)
|
|
84
|
+
- **Status**: Filter by success or failure
|
|
85
|
+
- **Environment**: Filter by environment (production, staging, etc.)
|
|
86
|
+
- **Sort Order**: Newest first or oldest first
|
|
87
|
+
|
|
88
|
+
### Using Filters in UI
|
|
89
|
+
|
|
90
|
+
1. Navigate to Audit Log page
|
|
91
|
+
2. Use filter dropdowns at the top
|
|
92
|
+
3. Click "Apply Filters"
|
|
93
|
+
4. Use "Clear" to reset filters
|
|
94
|
+
|
|
95
|
+
### Filtering via Console API
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
# Filter by trigger
|
|
99
|
+
PgSqlTriggers::AuditLog.for_trigger("users_email_validation")
|
|
100
|
+
|
|
101
|
+
# Filter by operation
|
|
102
|
+
PgSqlTriggers::AuditLog.for_operation("trigger_enable")
|
|
103
|
+
|
|
104
|
+
# Filter by environment
|
|
105
|
+
PgSqlTriggers::AuditLog.for_environment("production")
|
|
106
|
+
|
|
107
|
+
# Filter by status
|
|
108
|
+
PgSqlTriggers::AuditLog.successful
|
|
109
|
+
PgSqlTriggers::AuditLog.failed
|
|
110
|
+
|
|
111
|
+
# Combine filters
|
|
112
|
+
PgSqlTriggers::AuditLog
|
|
113
|
+
.for_trigger("users_email_validation")
|
|
114
|
+
.for_operation("trigger_enable")
|
|
115
|
+
.successful
|
|
116
|
+
.recent
|
|
117
|
+
.limit(50)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Advanced Filtering
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
# Get failed operations in production
|
|
124
|
+
PgSqlTriggers::AuditLog
|
|
125
|
+
.for_environment("production")
|
|
126
|
+
.failed
|
|
127
|
+
.recent
|
|
128
|
+
|
|
129
|
+
# Get all drop operations
|
|
130
|
+
PgSqlTriggers::AuditLog
|
|
131
|
+
.for_operation("trigger_drop")
|
|
132
|
+
.includes(:trigger_name)
|
|
133
|
+
|
|
134
|
+
# Get operations by specific actor
|
|
135
|
+
PgSqlTriggers::AuditLog
|
|
136
|
+
.where("actor->>'type' = ? AND actor->>'id' = ?", "User", "123")
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Exporting Logs
|
|
140
|
+
|
|
141
|
+
### CSV Export via UI
|
|
142
|
+
|
|
143
|
+
1. Apply any desired filters
|
|
144
|
+
2. Click "Export CSV" button
|
|
145
|
+
3. CSV file downloads with all visible entries
|
|
146
|
+
4. Filters are preserved in the export
|
|
147
|
+
|
|
148
|
+
### CSV Export Format
|
|
149
|
+
|
|
150
|
+
The CSV includes all columns:
|
|
151
|
+
|
|
152
|
+
- Timestamp
|
|
153
|
+
- Operation
|
|
154
|
+
- Trigger Name
|
|
155
|
+
- Actor Type
|
|
156
|
+
- Actor ID
|
|
157
|
+
- Environment
|
|
158
|
+
- Status
|
|
159
|
+
- Reason
|
|
160
|
+
- Error Message (for failures)
|
|
161
|
+
- Created At
|
|
162
|
+
|
|
163
|
+
### Programmatic Export
|
|
164
|
+
|
|
165
|
+
```ruby
|
|
166
|
+
# Export to CSV
|
|
167
|
+
require 'csv'
|
|
168
|
+
|
|
169
|
+
logs = PgSqlTriggers::AuditLog.recent.limit(1000)
|
|
170
|
+
|
|
171
|
+
CSV.open("audit_logs.csv", "w") do |csv|
|
|
172
|
+
csv << ["Timestamp", "Operation", "Trigger", "Actor", "Environment", "Status", "Reason"]
|
|
173
|
+
|
|
174
|
+
logs.each do |log|
|
|
175
|
+
csv << [
|
|
176
|
+
log.created_at,
|
|
177
|
+
log.operation,
|
|
178
|
+
log.trigger_name,
|
|
179
|
+
"#{log.actor['type']}:#{log.actor['id']}",
|
|
180
|
+
log.environment,
|
|
181
|
+
log.status,
|
|
182
|
+
log.reason
|
|
183
|
+
]
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Console API
|
|
189
|
+
|
|
190
|
+
### Querying Audit Logs
|
|
191
|
+
|
|
192
|
+
```ruby
|
|
193
|
+
# Get logs for a trigger
|
|
194
|
+
PgSqlTriggers::AuditLog.for_trigger_name("users_email_validation")
|
|
195
|
+
|
|
196
|
+
# Returns: ActiveRecord::Relation ordered by most recent first
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Logging Operations
|
|
200
|
+
|
|
201
|
+
Operations are automatically logged, but you can also log manually:
|
|
202
|
+
|
|
203
|
+
```ruby
|
|
204
|
+
# Log a successful operation
|
|
205
|
+
PgSqlTriggers::AuditLog.log_success(
|
|
206
|
+
operation: :trigger_enable,
|
|
207
|
+
trigger_name: "users_email_validation",
|
|
208
|
+
actor: { type: "Console", id: "admin@example.com" },
|
|
209
|
+
environment: "production",
|
|
210
|
+
before_state: { enabled: false },
|
|
211
|
+
after_state: { enabled: true }
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
# Log a failed operation
|
|
215
|
+
PgSqlTriggers::AuditLog.log_failure(
|
|
216
|
+
operation: :trigger_drop,
|
|
217
|
+
trigger_name: "old_trigger",
|
|
218
|
+
actor: { type: "UI", id: "user_123" },
|
|
219
|
+
environment: "production",
|
|
220
|
+
error_message: "Trigger not found",
|
|
221
|
+
reason: "Cleanup"
|
|
222
|
+
)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Logged Operations
|
|
226
|
+
|
|
227
|
+
The following operations are automatically logged:
|
|
228
|
+
|
|
229
|
+
### Trigger Operations
|
|
230
|
+
|
|
231
|
+
- **`trigger_enable`**: Trigger enabled
|
|
232
|
+
- **`trigger_disable`**: Trigger disabled
|
|
233
|
+
- **`trigger_drop`**: Trigger dropped from database
|
|
234
|
+
- **`trigger_re_execute`**: Trigger re-executed (drop and recreate)
|
|
235
|
+
|
|
236
|
+
### Migration Operations
|
|
237
|
+
|
|
238
|
+
- **`migration_up`**: Migration applied (up)
|
|
239
|
+
- **`migration_down`**: Migration rolled back (down)
|
|
240
|
+
|
|
241
|
+
### SQL Capsule Operations
|
|
242
|
+
|
|
243
|
+
- **`sql_capsule_execute`**: SQL capsule executed
|
|
244
|
+
- **`sql_capsule_dry_run`**: SQL capsule dry-run performed
|
|
245
|
+
|
|
246
|
+
### Generator Operations
|
|
247
|
+
|
|
248
|
+
- **`trigger_generate`**: Trigger generated via UI
|
|
249
|
+
|
|
250
|
+
## Audit Log Structure
|
|
251
|
+
|
|
252
|
+
### Database Schema
|
|
253
|
+
|
|
254
|
+
The audit log table (`pg_sql_triggers_audit_log`) contains:
|
|
255
|
+
|
|
256
|
+
| Column | Type | Description |
|
|
257
|
+
|--------|------|-------------|
|
|
258
|
+
| `id` | integer | Primary key |
|
|
259
|
+
| `trigger_name` | string | Trigger name (nullable) |
|
|
260
|
+
| `operation` | string | Operation type |
|
|
261
|
+
| `actor` | jsonb | Actor information (type, id) |
|
|
262
|
+
| `environment` | string | Environment name |
|
|
263
|
+
| `status` | string | "success" or "failure" |
|
|
264
|
+
| `reason` | text | Reason for operation (nullable) |
|
|
265
|
+
| `confirmation_text` | text | Confirmation text used (nullable) |
|
|
266
|
+
| `before_state` | jsonb | State before operation (nullable) |
|
|
267
|
+
| `after_state` | jsonb | State after operation (nullable) |
|
|
268
|
+
| `diff` | text | Diff information (nullable) |
|
|
269
|
+
| `error_message` | text | Error message (for failures) |
|
|
270
|
+
| `created_at` | timestamp | When operation occurred |
|
|
271
|
+
| `updated_at` | timestamp | Last update time |
|
|
272
|
+
|
|
273
|
+
### Actor Format
|
|
274
|
+
|
|
275
|
+
Actor information is stored as JSON:
|
|
276
|
+
|
|
277
|
+
```json
|
|
278
|
+
{
|
|
279
|
+
"type": "User",
|
|
280
|
+
"id": "123"
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Or for console operations:
|
|
285
|
+
|
|
286
|
+
```json
|
|
287
|
+
{
|
|
288
|
+
"type": "Console",
|
|
289
|
+
"id": "admin@example.com"
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### State Format
|
|
294
|
+
|
|
295
|
+
Before and after states are stored as JSON:
|
|
296
|
+
|
|
297
|
+
```json
|
|
298
|
+
{
|
|
299
|
+
"enabled": true,
|
|
300
|
+
"function_body": "CREATE FUNCTION...",
|
|
301
|
+
"version": 1,
|
|
302
|
+
"drift_state": "in_sync"
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
## Use Cases
|
|
307
|
+
|
|
308
|
+
### Compliance Auditing
|
|
309
|
+
|
|
310
|
+
Track who performed what operations for compliance:
|
|
311
|
+
|
|
312
|
+
```ruby
|
|
313
|
+
# Get all admin operations in production
|
|
314
|
+
PgSqlTriggers::AuditLog
|
|
315
|
+
.for_environment("production")
|
|
316
|
+
.where("actor->>'type' = ?", "Admin")
|
|
317
|
+
.recent
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Debugging Failed Operations
|
|
321
|
+
|
|
322
|
+
Find and analyze failed operations:
|
|
323
|
+
|
|
324
|
+
```ruby
|
|
325
|
+
# Get recent failures
|
|
326
|
+
failures = PgSqlTriggers::AuditLog.failed.recent.limit(50)
|
|
327
|
+
|
|
328
|
+
failures.each do |log|
|
|
329
|
+
puts "#{log.created_at}: #{log.operation} on #{log.trigger_name}"
|
|
330
|
+
puts "Error: #{log.error_message}"
|
|
331
|
+
puts "Actor: #{log.actor}"
|
|
332
|
+
puts "---"
|
|
333
|
+
end
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Trigger History
|
|
337
|
+
|
|
338
|
+
View complete history of a trigger:
|
|
339
|
+
|
|
340
|
+
```ruby
|
|
341
|
+
history = PgSqlTriggers::AuditLog.for_trigger_name("users_email_validation")
|
|
342
|
+
|
|
343
|
+
history.each do |log|
|
|
344
|
+
puts "#{log.created_at}: #{log.operation} - #{log.status}"
|
|
345
|
+
if log.reason
|
|
346
|
+
puts " Reason: #{log.reason}"
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Operation Analysis
|
|
352
|
+
|
|
353
|
+
Analyze operation patterns:
|
|
354
|
+
|
|
355
|
+
```ruby
|
|
356
|
+
# Count operations by type
|
|
357
|
+
PgSqlTriggers::AuditLog
|
|
358
|
+
.group(:operation)
|
|
359
|
+
.count
|
|
360
|
+
|
|
361
|
+
# Count failures by operation
|
|
362
|
+
PgSqlTriggers::AuditLog
|
|
363
|
+
.failed
|
|
364
|
+
.group(:operation)
|
|
365
|
+
.count
|
|
366
|
+
|
|
367
|
+
# Operations by environment
|
|
368
|
+
PgSqlTriggers::AuditLog
|
|
369
|
+
.group(:environment)
|
|
370
|
+
.count
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
## Best Practices
|
|
374
|
+
|
|
375
|
+
1. **Regular Reviews**: Review audit logs regularly for anomalies
|
|
376
|
+
2. **Retention Policy**: Implement a retention policy for old logs
|
|
377
|
+
3. **Monitoring**: Set up alerts for failed operations
|
|
378
|
+
4. **Backup**: Include audit logs in your backup strategy
|
|
379
|
+
5. **Analysis**: Use audit logs to understand usage patterns
|
|
380
|
+
|
|
381
|
+
## Troubleshooting
|
|
382
|
+
|
|
383
|
+
### Audit logs not appearing
|
|
384
|
+
|
|
385
|
+
**Problem**: Operations are not being logged.
|
|
386
|
+
|
|
387
|
+
**Solution**:
|
|
388
|
+
- Check that migrations are run (`rails db:migrate`)
|
|
389
|
+
- Verify the audit log table exists
|
|
390
|
+
- Check Rails logs for audit logging errors
|
|
391
|
+
|
|
392
|
+
### Performance issues with large logs
|
|
393
|
+
|
|
394
|
+
**Problem**: Audit log queries are slow.
|
|
395
|
+
|
|
396
|
+
**Solution**:
|
|
397
|
+
- Add indexes on frequently queried columns
|
|
398
|
+
- Implement pagination
|
|
399
|
+
- Archive old logs regularly
|
|
400
|
+
- Use filtering to reduce result set size
|
|
401
|
+
|
|
402
|
+
### Missing actor information
|
|
403
|
+
|
|
404
|
+
**Problem**: Actor shows as "unknown".
|
|
405
|
+
|
|
406
|
+
**Solution**: Ensure `current_actor` method is properly implemented in your controllers.
|
|
407
|
+
|
|
408
|
+
## Related Documentation
|
|
409
|
+
|
|
410
|
+
- [Web UI Guide](web-ui.md#audit-log) - Using the audit log UI
|
|
411
|
+
- [API Reference](api-reference.md#audit-log-api) - Console API methods
|
|
412
|
+
- [Configuration Reference](configuration.md) - Configuration options
|
|
413
|
+
|
data/docs/configuration.md
CHANGED
|
@@ -208,12 +208,12 @@ config.permission_checker = ->(actor, action, environment) {
|
|
|
208
208
|
return false unless user
|
|
209
209
|
|
|
210
210
|
case action
|
|
211
|
-
when :
|
|
212
|
-
user.present?
|
|
213
|
-
when :
|
|
214
|
-
user.operator? || user.admin?
|
|
215
|
-
when :
|
|
216
|
-
user.admin?
|
|
211
|
+
when :view_triggers, :view_diffs
|
|
212
|
+
user.present? # Viewer level
|
|
213
|
+
when :enable_trigger, :disable_trigger, :apply_trigger, :generate_trigger, :test_trigger, :dry_run_sql
|
|
214
|
+
user.operator? || user.admin? # Operator level
|
|
215
|
+
when :drop_trigger, :execute_sql, :override_drift
|
|
216
|
+
user.admin? # Admin level
|
|
217
217
|
else
|
|
218
218
|
false
|
|
219
219
|
end
|