action-audit 1.0.1

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.
@@ -0,0 +1,434 @@
1
+ # API Reference
2
+
3
+ Complete API documentation for the ActionAudit gem.
4
+
5
+ ## Module: ActionAudit
6
+
7
+ The main module that provides auditing functionality when included in controllers.
8
+
9
+ ### Class Methods
10
+
11
+ #### `ActionAudit.log_formatter`
12
+
13
+ **Type:** `Proc` or `nil`
14
+
15
+ **Description:** Custom formatter for audit log messages.
16
+
17
+ **Signature:**
18
+ ```ruby
19
+ ActionAudit.log_formatter = ->(controller_path, action_name, interpolated_message) { "formatted string" }
20
+ ```
21
+
22
+ **Parameters:**
23
+ - `controller_path` (String): The normalized controller path (e.g., "admin/users")
24
+ - `action_name` (String): The controller action name (e.g., "create")
25
+ - `interpolated_message` (String): The audit message with parameters interpolated
26
+
27
+ **Returns:** String that will be logged
28
+
29
+ **Default:** `nil` (uses default formatting)
30
+
31
+ **Example:**
32
+ ```ruby
33
+ ActionAudit.log_formatter = lambda do |controller, action, message|
34
+ "[#{Time.current.iso8601}] #{controller}/#{action} | #{message}"
35
+ end
36
+ ```
37
+
38
+ #### `ActionAudit.log_tag`
39
+
40
+ **Type:** `String`, `Array`, or `nil`
41
+
42
+ **Description:** Tag(s) to be used with `Rails.logger.tagged()`.
43
+
44
+ **Default:** `nil` (no tagging)
45
+
46
+ **Examples:**
47
+ ```ruby
48
+ ActionAudit.log_tag = "AUDIT"
49
+ ActionAudit.log_tag = ["AUDIT", "SECURITY"]
50
+ ActionAudit.log_tag = nil # Disable tagging
51
+ ```
52
+
53
+ ### Instance Methods (Private)
54
+
55
+ These methods are automatically added to controllers when `ActionAudit` is included.
56
+
57
+ #### `audit_request`
58
+
59
+ **Description:** The main auditing method called via `after_action` callback.
60
+
61
+ **Behavior:**
62
+ 1. Determines controller path and action name
63
+ 2. Looks up audit message from configuration
64
+ 3. Interpolates message with controller parameters
65
+ 4. Logs the formatted message
66
+
67
+ **Called automatically:** Yes (via `after_action`)
68
+
69
+ #### `interpolate_message(message, params)`
70
+
71
+ **Description:** Interpolates audit message with controller parameters.
72
+
73
+ **Parameters:**
74
+ - `message` (String): The audit message template
75
+ - `params` (ActionController::Parameters): Controller parameters for interpolation
76
+
77
+ **Returns:** String with interpolated values
78
+
79
+ **Error Handling:**
80
+ - Missing parameters: Logs interpolation error alongside original message
81
+ - Invalid message types: Converts to string
82
+
83
+ ---
84
+
85
+ ## Class: ActionAudit::AuditMessages
86
+
87
+ Registry class for managing audit messages, similar to I18n backend.
88
+
89
+ ### Class Methods
90
+
91
+ #### `messages`
92
+
93
+ **Returns:** Hash containing all loaded audit messages
94
+
95
+ **Example:**
96
+ ```ruby
97
+ ActionAudit::AuditMessages.messages
98
+ # => {
99
+ # "admin" => {
100
+ # "users" => {
101
+ # "create" => "Created user %{email}"
102
+ # }
103
+ # }
104
+ # }
105
+ ```
106
+
107
+ #### `lookup(controller_path, action_name)`
108
+
109
+ **Description:** Look up an audit message for a specific controller/action combination.
110
+
111
+ **Parameters:**
112
+ - `controller_path` (String): Controller path (e.g., "admin/users")
113
+ - `action_name` (String): Action name (e.g., "create")
114
+
115
+ **Returns:** String message template or `nil` if not found
116
+
117
+ **Example:**
118
+ ```ruby
119
+ ActionAudit::AuditMessages.lookup("admin/users", "create")
120
+ # => "Created user %{email}"
121
+
122
+ ActionAudit::AuditMessages.lookup("nonexistent", "action")
123
+ # => nil
124
+ ```
125
+
126
+ #### `load_from_file(file_path)`
127
+
128
+ **Description:** Load audit messages from a YAML file.
129
+
130
+ **Parameters:**
131
+ - `file_path` (String): Absolute path to YAML file
132
+
133
+ **Behavior:**
134
+ - Merges loaded messages with existing ones
135
+ - Handles missing files gracefully
136
+ - Validates YAML structure
137
+
138
+ **Example:**
139
+ ```ruby
140
+ ActionAudit::AuditMessages.load_from_file("/path/to/audit.yml")
141
+ ```
142
+
143
+ #### `load_from_engines`
144
+
145
+ **Description:** Automatically discover and load audit messages from all Rails engines.
146
+
147
+ **Behavior:**
148
+ - Loads from main application `config/audit.yml`
149
+ - Loads from each mounted engine's `config/audit.yml`
150
+ - Called automatically during Rails initialization
151
+
152
+ **Example:**
153
+ ```ruby
154
+ ActionAudit::AuditMessages.load_from_engines
155
+ ```
156
+
157
+ #### `add_message(controller_path, action_name, message)`
158
+
159
+ **Description:** Programmatically add an audit message.
160
+
161
+ **Parameters:**
162
+ - `controller_path` (String): Controller path (e.g., "admin/users")
163
+ - `action_name` (String): Action name (e.g., "create")
164
+ - `message` (String): Audit message template
165
+
166
+ **Example:**
167
+ ```ruby
168
+ ActionAudit::AuditMessages.add_message("admin/users", "create", "Created user %{email}")
169
+ ```
170
+
171
+ #### `clear!`
172
+
173
+ **Description:** Clear all loaded audit messages. Primarily used for testing.
174
+
175
+ **Example:**
176
+ ```ruby
177
+ ActionAudit::AuditMessages.clear!
178
+ ActionAudit::AuditMessages.messages
179
+ # => {}
180
+ ```
181
+
182
+ ---
183
+
184
+ ## Class: ActionAudit::Engine
185
+
186
+ Rails engine that provides automatic configuration loading and integration.
187
+
188
+ ### Behavior
189
+
190
+ - **Namespace:** Isolated as `ActionAudit`
191
+ - **Initializer:** Runs after `:load_config_initializers`
192
+ - **Development Mode:** Automatically reloads configurations when files change
193
+ - **Integration:** Seamlessly integrates with Rails application and engines
194
+
195
+ ### Initializers
196
+
197
+ #### `action_audit.load_audit_messages`
198
+
199
+ Automatically loads audit messages from all engines during Rails startup.
200
+
201
+ #### Development Mode Configuration
202
+
203
+ In development mode, configurations are reloaded on each request to support hot reloading.
204
+
205
+ ---
206
+
207
+ ## Configuration File Format
208
+
209
+ ### YAML Structure
210
+
211
+ Audit messages are defined in `config/audit.yml` using nested YAML structure:
212
+
213
+ ```yaml
214
+ # Top-level keys are controller namespaces or direct controllers
215
+ namespace:
216
+ controller:
217
+ action: "Message template with %{parameter}"
218
+
219
+ # Examples:
220
+ admin:
221
+ users:
222
+ create: "Admin created user %{email}"
223
+ update: "Admin updated user %{id}"
224
+
225
+ sessions:
226
+ create: "User logged in with %{email}"
227
+ destroy: "User logged out"
228
+ ```
229
+
230
+ ### Controller Path Mapping
231
+
232
+ Controller class names are automatically converted to audit message paths:
233
+
234
+ | Controller Class | Audit Path |
235
+ |------------------|------------|
236
+ | `UsersController` | `users` |
237
+ | `Admin::UsersController` | `admin/users` |
238
+ | `API::V1::WebhooksController` | `api/v1/webhooks` |
239
+ | `Manage::Settings::PreferencesController` | `manage/settings/preferences` |
240
+
241
+ ### Parameter Interpolation
242
+
243
+ Use `%{parameter_name}` syntax for parameter interpolation:
244
+
245
+ ```yaml
246
+ users:
247
+ create: "Created user %{email} with role %{role}"
248
+ update: "Updated user %{id}"
249
+ destroy: "Deleted user %{id}"
250
+ ```
251
+
252
+ **Supported Parameter Sources:**
253
+ - Direct controller parameters (`params[:email]`)
254
+ - Nested parameters (automatically flattened)
255
+ - Strong parameters (converted safely)
256
+
257
+ ---
258
+
259
+ ## Error Handling
260
+
261
+ ### Missing Messages
262
+
263
+ If no audit message is configured for a controller/action:
264
+ - **Behavior:** No log entry is created (silent skip)
265
+ - **Impact:** No performance penalty or errors
266
+
267
+ ### Missing Parameters
268
+
269
+ If a parameter referenced in the message template is missing:
270
+ - **Behavior:** Original message is logged with error information
271
+ - **Format:** `"Original message (interpolation error: key{param} not found)"`
272
+ - **Impact:** Audit still occurs with error context
273
+
274
+ ### Invalid YAML
275
+
276
+ If `config/audit.yml` has syntax errors:
277
+ - **Behavior:** Rails logs warning during startup
278
+ - **Impact:** That particular configuration file is skipped
279
+ - **Recovery:** Fix YAML syntax and restart (or reload in development)
280
+
281
+ ### File System Issues
282
+
283
+ If audit configuration files are unreadable:
284
+ - **Behavior:** Files are silently skipped
285
+ - **Impact:** Only affects that specific engine's configuration
286
+ - **Recovery:** Fix file permissions and restart
287
+
288
+ ---
289
+
290
+ ## Performance Characteristics
291
+
292
+ ### Memory Usage
293
+
294
+ - **Message Storage:** All audit messages loaded into memory at startup
295
+ - **Typical Usage:** < 1MB for large applications with extensive audit configurations
296
+ - **Scaling:** Linear with number of configured audit messages
297
+
298
+ ### Runtime Performance
299
+
300
+ - **Message Lookup:** O(1) hash lookup by controller path and action
301
+ - **Parameter Interpolation:** Standard Ruby string interpolation performance
302
+ - **Logging Overhead:** Equivalent to standard Rails logging
303
+
304
+ ### Initialization Time
305
+
306
+ - **Configuration Loading:** O(n) where n is number of engines with audit configs
307
+ - **YAML Parsing:** Standard Ruby YAML parsing performance
308
+ - **Typical Impact:** < 100ms additional startup time
309
+
310
+ ---
311
+
312
+ ## Testing Support
313
+
314
+ ### RSpec Integration
315
+
316
+ ActionAudit works seamlessly with RSpec controller tests:
317
+
318
+ ```ruby
319
+ RSpec.describe UsersController, type: :controller do
320
+ describe "#create" do
321
+ it "logs user creation" do
322
+ expect(Rails.logger).to receive(:info).with(/Created user.*john@example\.com/)
323
+ post :create, params: { user: { email: "john@example.com" } }
324
+ end
325
+ end
326
+ end
327
+ ```
328
+
329
+ ### Test Configuration
330
+
331
+ For testing environments:
332
+
333
+ ```ruby
334
+ # In test environment
335
+ ActionAudit::AuditMessages.clear! # Clear messages
336
+ ActionAudit.log_formatter = nil # Use default formatting
337
+ ActionAudit.log_tag = nil # Disable tagging
338
+ ```
339
+
340
+ ### Stubbing
341
+
342
+ To disable auditing in specific tests:
343
+
344
+ ```ruby
345
+ before do
346
+ allow(controller).to receive(:audit_request)
347
+ end
348
+ ```
349
+
350
+ ---
351
+
352
+ ## Thread Safety
353
+
354
+ ActionAudit is thread-safe:
355
+
356
+ - **Message Registry:** Read-only after initialization
357
+ - **Configuration Loading:** Happens during Rails initialization (single-threaded)
358
+ - **Runtime Usage:** No shared mutable state during request processing
359
+
360
+ ---
361
+
362
+ ## Rails Version Compatibility
363
+
364
+ | Rails Version | ActionAudit Support |
365
+ |---------------|-------------------|
366
+ | 6.0.x | ✅ Fully supported |
367
+ | 6.1.x | ✅ Fully supported |
368
+ | 7.0.x | ✅ Fully supported |
369
+ | 7.1.x | ✅ Fully supported |
370
+ | 8.0.x | ✅ Fully supported |
371
+
372
+ ---
373
+
374
+ ## Ruby Version Compatibility
375
+
376
+ | Ruby Version | ActionAudit Support |
377
+ |-------------|-------------------|
378
+ | 3.1.x | ✅ Fully supported |
379
+ | 3.2.x | ✅ Fully supported |
380
+ | 3.3.x | ✅ Fully supported |
381
+
382
+ ---
383
+
384
+ ## Dependencies
385
+
386
+ ### Runtime Dependencies
387
+
388
+ - **Rails** (`>= 6.0`): Core Rails framework
389
+ - **ActiveSupport** (`>= 6.0`): For `Concern` and core extensions
390
+
391
+ ### Development Dependencies
392
+
393
+ - **RSpec** (`~> 3.0`): Testing framework
394
+ - **RSpec-Rails** (`~> 6.0`): Rails integration for RSpec
395
+
396
+ ---
397
+
398
+ ## Migration from Other Solutions
399
+
400
+ ### From Manual Logging
401
+
402
+ ```ruby
403
+ # Before: Manual logging
404
+ def create
405
+ @user = User.create!(user_params)
406
+ Rails.logger.info "Created user #{@user.email}"
407
+ end
408
+
409
+ # After: ActionAudit
410
+ class UsersController < ApplicationController
411
+ include ActionAudit
412
+
413
+ def create
414
+ @user = User.create!(user_params)
415
+ # Automatic logging via audit.yml configuration
416
+ end
417
+ end
418
+ ```
419
+
420
+ ### From Other Audit Gems
421
+
422
+ ActionAudit provides a lightweight, configuration-based approach compared to database-backed audit solutions. Migration typically involves:
423
+
424
+ 1. Configuring audit messages in YAML
425
+ 2. Including `ActionAudit` in controllers
426
+ 3. Removing previous audit gem dependencies
427
+
428
+ ---
429
+
430
+ ## Next Steps
431
+
432
+ - [See real-world examples](examples.md)
433
+ - [Learn about troubleshooting](troubleshooting.md)
434
+ - [Check installation guide](installation.md)
@@ -0,0 +1,230 @@
1
+ # Configuration Guide
2
+
3
+ ActionAudit provides flexible configuration options for customizing how audit messages are formatted and logged.
4
+
5
+ ## Audit Messages Configuration
6
+
7
+ ### Basic Structure
8
+
9
+ Audit messages are defined in `config/audit.yml` using a nested structure that mirrors your controller hierarchy:
10
+
11
+ ```yaml
12
+ # config/audit.yml
13
+ namespace:
14
+ controller:
15
+ action: "Audit message with %{parameter} interpolation"
16
+ ```
17
+
18
+ ### Controller Path Mapping
19
+
20
+ ActionAudit automatically converts controller class names to audit message paths:
21
+
22
+ - `UsersController` → `users`
23
+ - `Admin::UsersController` → `admin/users`
24
+ - `API::V1::WebhooksController` → `api/v1/webhooks`
25
+
26
+ ### Parameter Interpolation
27
+
28
+ Use `%{parameter_name}` syntax to interpolate controller parameters into audit messages:
29
+
30
+ ```yaml
31
+ users:
32
+ create: "Created user %{email} with role %{role}"
33
+ update: "Updated user %{id}"
34
+ destroy: "Deleted user %{id}"
35
+ ```
36
+
37
+ The gem will automatically extract these values from `params` in your controller.
38
+
39
+ ### Example Configuration
40
+
41
+ ```yaml
42
+ # config/audit.yml
43
+
44
+ # Admin interface
45
+ admin:
46
+ users:
47
+ create: "Admin created user %{email}"
48
+ update: "Admin updated user %{id}"
49
+ destroy: "Admin deleted user %{id}"
50
+ activate: "Admin activated user %{id}"
51
+ deactivate: "Admin deactivated user %{id}"
52
+
53
+ accounts:
54
+ create: "Admin created account %{name}"
55
+ update: "Admin updated account %{id} with %{name}"
56
+ destroy: "Admin deleted account %{id}"
57
+
58
+ # User authentication
59
+ sessions:
60
+ create: "User logged in with %{email}"
61
+ destroy: "User %{user_id} logged out"
62
+
63
+ # API endpoints
64
+ api:
65
+ v1:
66
+ webhooks:
67
+ create: "Webhook received from %{source}"
68
+ users:
69
+ create: "API user created via %{client_id}"
70
+
71
+ # Regular controllers
72
+ posts:
73
+ create: "Created post '%{title}'"
74
+ update: "Updated post %{id}"
75
+ publish: "Published post %{id}"
76
+ unpublish: "Unpublished post %{id}"
77
+ ```
78
+
79
+ ## Logging Configuration
80
+
81
+ ### Custom Log Formatter
82
+
83
+ Configure custom log formatting in `config/initializers/action_audit.rb`:
84
+
85
+ ```ruby
86
+ # config/initializers/action_audit.rb
87
+
88
+ # Basic custom formatter
89
+ ActionAudit.log_formatter = lambda do |controller, action, message|
90
+ "[AUDIT] #{controller}/#{action} - #{message}"
91
+ end
92
+
93
+ # Advanced formatter with timestamp and user info
94
+ ActionAudit.log_formatter = lambda do |controller, action, message|
95
+ timestamp = Time.current.iso8601
96
+ user_info = defined?(current_user) && current_user ? "User: #{current_user.email}" : "User: anonymous"
97
+
98
+ "[#{timestamp}] #{controller}/#{action} | #{message} | #{user_info}"
99
+ end
100
+
101
+ # Formatter with request ID
102
+ ActionAudit.log_formatter = lambda do |controller, action, message|
103
+ request_id = defined?(request) && request ? request.request_id : SecureRandom.hex(8)
104
+ "AUDIT [#{request_id}] #{controller}/#{action}: #{message}"
105
+ end
106
+ ```
107
+
108
+ ### Log Tagging
109
+
110
+ Add consistent tags to all audit log entries:
111
+
112
+ ```ruby
113
+ # Simple tag
114
+ ActionAudit.log_tag = "AUDIT"
115
+
116
+ # Multiple tags (if your logger supports it)
117
+ ActionAudit.log_tag = ["AUDIT", "SECURITY"]
118
+
119
+ # Dynamic tag
120
+ ActionAudit.log_tag = "AUDIT-#{Rails.env.upcase}"
121
+ ```
122
+
123
+ ### Default Behavior
124
+
125
+ If no custom configuration is provided:
126
+
127
+ - **Default formatter**: `"#{controller_path}/#{action_name} - #{interpolated_message}"`
128
+ - **Default tag**: `nil` (no tagging)
129
+
130
+ ## Configuration Examples
131
+
132
+ ### Minimal Configuration
133
+
134
+ ```ruby
135
+ # config/initializers/action_audit.rb
136
+ ActionAudit.log_tag = "AUDIT"
137
+ ```
138
+
139
+ ### Production Configuration
140
+
141
+ ```ruby
142
+ # config/initializers/action_audit.rb
143
+ ActionAudit.log_tag = "AUDIT"
144
+
145
+ ActionAudit.log_formatter = lambda do |controller, action, message|
146
+ timestamp = Time.current.iso8601
147
+ request_id = defined?(request) && request ? request.request_id : "unknown"
148
+ user_id = defined?(current_user) && current_user ? current_user.id : "anonymous"
149
+
150
+ "[#{timestamp}] #{controller}/#{action} | #{message} | user_id=#{user_id} | request_id=#{request_id}"
151
+ end
152
+ ```
153
+
154
+ ### Development Configuration
155
+
156
+ ```ruby
157
+ # config/initializers/action_audit.rb
158
+ if Rails.env.development?
159
+ ActionAudit.log_tag = "DEV-AUDIT"
160
+
161
+ ActionAudit.log_formatter = lambda do |controller, action, message|
162
+ "🔍 [#{Time.current.strftime('%H:%M:%S')}] #{controller}/#{action} - #{message}"
163
+ end
164
+ end
165
+ ```
166
+
167
+ ## Environment-Specific Configuration
168
+
169
+ You can configure ActionAudit differently for each environment:
170
+
171
+ ```ruby
172
+ # config/initializers/action_audit.rb
173
+ case Rails.env
174
+ when 'development'
175
+ ActionAudit.log_tag = "DEV-AUDIT"
176
+ ActionAudit.log_formatter = ->(c, a, m) { "🔍 #{c}/#{a} - #{m}" }
177
+
178
+ when 'staging'
179
+ ActionAudit.log_tag = "STAGING-AUDIT"
180
+ ActionAudit.log_formatter = ->(c, a, m) { "[STAGING] #{c}/#{a} | #{m}" }
181
+
182
+ when 'production'
183
+ ActionAudit.log_tag = "AUDIT"
184
+ ActionAudit.log_formatter = lambda do |controller, action, message|
185
+ timestamp = Time.current.iso8601
186
+ "[#{timestamp}] #{controller}/#{action} | #{message}"
187
+ end
188
+ end
189
+ ```
190
+
191
+ ## Integration with Structured Logging
192
+
193
+ ActionAudit works well with structured logging solutions:
194
+
195
+ ```ruby
196
+ # For use with lograge or similar structured logging
197
+ ActionAudit.log_formatter = lambda do |controller, action, message|
198
+ {
199
+ type: 'audit',
200
+ controller: controller,
201
+ action: action,
202
+ message: message,
203
+ timestamp: Time.current.iso8601,
204
+ user_id: defined?(current_user) && current_user&.id,
205
+ request_id: defined?(request) && request&.request_id
206
+ }.to_json
207
+ end
208
+ ```
209
+
210
+ ## Testing Configuration
211
+
212
+ In your test environment, you might want to disable or modify auditing:
213
+
214
+ ```ruby
215
+ # config/environments/test.rb or config/initializers/action_audit.rb
216
+ if Rails.env.test?
217
+ # Option 1: Disable formatting for cleaner test output
218
+ ActionAudit.log_formatter = nil
219
+ ActionAudit.log_tag = nil
220
+
221
+ # Option 2: Use simple test-friendly format
222
+ ActionAudit.log_formatter = ->(c, a, m) { "TEST_AUDIT: #{c}/#{a} - #{m}" }
223
+ end
224
+ ```
225
+
226
+ ## Next Steps
227
+
228
+ - [Learn about usage patterns](usage.md)
229
+ - [Set up multi-engine configuration](multi-engine.md)
230
+ - [Explore real-world examples](examples.md)