loggable_activity 0.1.56 → 0.1.59

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +10 -1
  3. data/CHANGELOG.md +9 -0
  4. data/GETTING-STARTED.md +8 -47
  5. data/PAYLOAD_EXAMPLE.md +63 -0
  6. data/docs/LoggableActivity/Activity.html +92 -241
  7. data/docs/LoggableActivity/Configuration.html +162 -15
  8. data/docs/LoggableActivity/ConfigurationError.html +148 -0
  9. data/docs/LoggableActivity/DataOwner.html +138 -0
  10. data/docs/LoggableActivity/Encryption.html +18 -14
  11. data/docs/LoggableActivity/EncryptionError.html +47 -1
  12. data/docs/LoggableActivity/EncryptionKey.html +54 -35
  13. data/docs/LoggableActivity/Error.html +48 -0
  14. data/docs/LoggableActivity/Hooks.html +221 -89
  15. data/docs/LoggableActivity/Payload.html +103 -27
  16. data/docs/LoggableActivity/Services/BasePayloadsBuilder.html +442 -0
  17. data/docs/LoggableActivity/Services/DestroyPayloadsBuilder.html +395 -0
  18. data/docs/LoggableActivity/Services/PayloadsBuilder.html +342 -0
  19. data/docs/LoggableActivity/Services/UpdatePayloadsBuilder.html +490 -0
  20. data/docs/LoggableActivity/Services.html +93 -0
  21. data/docs/created.rid +14 -10
  22. data/docs/index.html +10 -2
  23. data/docs/js/navigation.js.gz +0 -0
  24. data/docs/js/search_index.js +1 -1
  25. data/docs/js/search_index.js.gz +0 -0
  26. data/docs/js/searcher.js.gz +0 -0
  27. data/docs/table_of_contents.html +253 -85
  28. data/lib/generators/loggable_activity/install_generator.rb +2 -18
  29. data/lib/generators/loggable_activity/templates/.DS_Store +0 -0
  30. data/lib/generators/loggable_activity/templates/binary_ids/create_loggable_activities.rb +9 -10
  31. data/lib/generators/loggable_activity/templates/create_loggable_activities.rb +16 -13
  32. data/lib/loggable_activity/activity.rb +53 -102
  33. data/lib/loggable_activity/configuration.rb +60 -3
  34. data/lib/loggable_activity/data_owner.rb +18 -0
  35. data/lib/loggable_activity/encryption.rb +14 -14
  36. data/lib/loggable_activity/encryption_key.rb +26 -25
  37. data/lib/loggable_activity/error.rb +34 -0
  38. data/lib/loggable_activity/hooks.rb +78 -43
  39. data/lib/loggable_activity/payload.rb +70 -20
  40. data/lib/loggable_activity/services/base_payloads_builder.rb +127 -0
  41. data/lib/loggable_activity/services/destroy_payloads_builder.rb +127 -0
  42. data/lib/loggable_activity/services/payloads_builder.rb +98 -0
  43. data/lib/loggable_activity/services/update_payloads_builder.rb +170 -0
  44. data/lib/loggable_activity/version.rb +1 -1
  45. data/lib/loggable_activity.rb +6 -6
  46. data/lib/schemas/config_schema.json +90 -0
  47. metadata +37 -26
  48. data/docs/LoggableActivity/PayloadsBuilder.html +0 -441
  49. data/docs/LoggableActivity/UpdatePayloadsBuilder.html +0 -424
  50. data/lib/generators/loggable_activity/install_templates_generator.rb +0 -105
  51. data/lib/generators/loggable_activity/templates/helpers/activity_helper.rb +0 -37
  52. data/lib/generators/loggable_activity/templates/helpers/router.rb +0 -52
  53. data/lib/generators/loggable_activity/templates/helpers/routes_helper.rb +0 -20
  54. data/lib/generators/loggable_activity/templates/views/loggable_activity/templates/default/_create.html.erb +0 -23
  55. data/lib/generators/loggable_activity/templates/views/loggable_activity/templates/default/_create.html.slim +0 -18
  56. data/lib/generators/loggable_activity/templates/views/loggable_activity/templates/default/_destroy.html.erb +0 -18
  57. data/lib/generators/loggable_activity/templates/views/loggable_activity/templates/default/_destroy.html.slim +0 -17
  58. data/lib/generators/loggable_activity/templates/views/loggable_activity/templates/default/_show.html.erb +0 -18
  59. data/lib/generators/loggable_activity/templates/views/loggable_activity/templates/default/_show.html.slim +0 -17
  60. data/lib/generators/loggable_activity/templates/views/loggable_activity/templates/default/_update.html.erb +0 -18
  61. data/lib/generators/loggable_activity/templates/views/loggable_activity/templates/default/_update.html.slim +0 -12
  62. data/lib/generators/loggable_activity/templates/views/loggable_activity/templates/shared/_activity_info.html.erb +0 -12
  63. data/lib/generators/loggable_activity/templates/views/loggable_activity/templates/shared/_activity_info.html.slim +0 -11
  64. data/lib/generators/loggable_activity/templates/views/loggable_activity/templates/shared/_list_attrs.html.erb +0 -8
  65. data/lib/generators/loggable_activity/templates/views/loggable_activity/templates/shared/_list_attrs.html.slim +0 -6
  66. data/lib/generators/loggable_activity/templates/views/loggable_activity/templates/shared/_update_attrs.html.erb +0 -17
  67. data/lib/generators/loggable_activity/templates/views/loggable_activity/templates/shared/_update_attrs.html.slim +0 -14
  68. data/lib/generators/loggable_activity/templates/views/loggable_activity/templates/shared/_updated_relations.html.erb +0 -23
  69. data/lib/generators/loggable_activity/templates/views/loggable_activity/templates/shared/_updated_relations.html.slim +0 -21
  70. data/lib/loggable_activity/payloads_builder.rb +0 -153
  71. data/lib/loggable_activity/update_payloads_builder.rb +0 -127
@@ -1,32 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class CreateLoggableActivities < ActiveRecord::Migration[6.1]
3
+ class CreateLoggableActivities < ActiveRecord::Migration[7.1]
4
4
  def change
5
+ create_table :loggable_data_owners do |t|
6
+ t.references :record, polymorphic: true, null: true, index: true
7
+ t.references :encryption_key, null: false, foreign_key: { to_table: 'loggable_encryption_keys' }
8
+ end
9
+
5
10
  create_table :loggable_activities do |t|
6
11
  t.string :action
7
12
  t.references :actor, polymorphic: true, null: true
8
- t.string :encrypted_actor_display_name
9
- t.string :encrypted_record_display_name
13
+ t.string :encrypted_actor_name
10
14
  t.references :record, polymorphic: true, null: true
11
-
12
15
  t.timestamps
13
16
  end
14
17
 
15
18
  create_table :loggable_payloads do |t|
16
- t.references :record, polymorphic: true, null: true
19
+ t.references :activity, null: false, foreign_key: { to_table: 'loggable_activities' }
20
+ t.references :encryption_key, null: false, foreign_key: { to_table: 'loggable_encryption_keys' }
21
+ t.references :record, polymorphic: true, null: true, index: true
22
+ t.string :encrypted_record_name
17
23
  t.json :encrypted_attrs
18
- t.integer :payload_type, default: 0
24
+ t.integer :related_to_activity_as, default: 0
19
25
  t.boolean :data_owner, default: false
20
- t.string :route, default: nil
21
- t.references :activity, foreign_key: { to_table: 'loggable_activities', class_name: 'LoggableActivity::Activity' }
22
-
23
- t.timestamps
26
+ t.string :route
27
+ t.boolean :current_payload, default: true
24
28
  end
25
29
 
26
30
  create_table :loggable_encryption_keys do |t|
27
- t.references :parent_key, foreign_key: { to_table: 'loggable_encryption_keys', on_delete: :nullify }
28
- t.string :key
29
- t.references :record, polymorphic: true, on_delete: :nullify
31
+ t.references :record, polymorphic: true, null: true, index: true
32
+ t.string :secret_key
30
33
  end
31
34
  end
32
35
  end
@@ -7,9 +7,9 @@ module LoggableActivity
7
7
  class Activity < ActiveRecord::Base
8
8
  self.table_name = 'loggable_activities'
9
9
  # Associations
10
- has_many :payloads, class_name: 'LoggableActivity::Payload', dependent: :destroy
11
- belongs_to :record, polymorphic: true, optional: true
10
+ has_many :payloads, class_name: '::LoggableActivity::Payload', dependent: :destroy
12
11
  belongs_to :actor, polymorphic: true, optional: true
12
+ belongs_to :record, polymorphic: true, optional: true
13
13
 
14
14
  accepts_nested_attributes_for :payloads
15
15
 
@@ -22,7 +22,7 @@ module LoggableActivity
22
22
  #
23
23
  # Each hash in the array contains:
24
24
  # - :record_type: The class name of the record.
25
- # - :payload_type: A descriptor of the payload's role (e.g., 'primary_payload' or 'current_association').
25
+ # - :related_to_activity_as: A descriptor of the payload's role (e.g., 'primary_payload' or 'current_association').
26
26
  # - :attrs: A hash of the record's attributes.
27
27
  #
28
28
  # Example usage:
@@ -30,65 +30,36 @@ module LoggableActivity
30
30
  #
31
31
  # Sample return value:
32
32
  # [
33
- # { record_type: MODEL_NAME, payload_type: "primary_payload", attrs: { "KEY" => "VALUE", ... } },
34
- # { record_type: MODEL_NAME, payload_type: "current_association", attrs: { "KEY" => "VALUE", ... } },
33
+ # { record_type: MODEL_NAME, related_to_activity_as: "primary_payload", attrs: { "KEY" => "VALUE", ... } },
34
+ # { record_type: MODEL_NAME, related_to_activity_as: "current_association", attrs: { "KEY" => "VALUE", ... } },
35
35
  # ...
36
36
  # ]
37
37
  def attrs
38
+ {
39
+ actor_type:,
40
+ actor_id:,
41
+ action:,
42
+ actor_display_name:,
43
+ record_display_name:,
44
+ payloads: payloads_attrs
45
+ }
46
+ end
47
+
48
+ def payloads_attrs
38
49
  ordered_payloads.map do |payload|
39
50
  {
51
+ related_to_activity_as: payload.related_to_activity_as,
40
52
  record_type: payload.record_type,
41
- record_id: payload.record_id,
42
- payload_type: payload.payload_type,
53
+ record_id: payload.deleted? ? nil : payload.record_id,
43
54
  attrs: payload.attrs,
44
- route: payload.payload_route
55
+ route: payload.payload_route,
56
+ record_display_name: payload.record_display_name,
57
+ current_payload: payload.current_payload,
58
+ data_owner: payload.data_owner
45
59
  }
46
60
  end
47
61
  end
48
62
 
49
- # Returns a hash describing the attributes of an update activity, including updated attributes for a record and any updated related attributes.
50
- #
51
- # Example:
52
- # @activity.update_activity_attrs
53
- #
54
- # The return value is structured as follows:
55
- # - :update_attrs contains the attributes of the record being updated, detailing changes from previous to new values.
56
- # - :updated_relations_attrs is an array of hashes, each representing an updated related record. Each hash details the previous and current attributes of the relation.
57
- #
58
- # Example Return Structure:
59
- # {
60
- # update_attrs: {
61
- # record_class: "CLASS.NAME",
62
- # attrs: [{ "KEY" => { from: "OLD_VALUE", to: "NEW_VALUE" } }]
63
- # },
64
- # updated_relations_attrs: [
65
- # {
66
- # record_class: "CLASS.NAME",
67
- # previous_attrs: { attrs: { "KEY" => "VALUE", ... } },
68
- # current_attrs: { attrs: { "KEY" => "VALUE", ... } }
69
- # }
70
- # ]
71
- # }
72
- def update_activity_attrs
73
- {
74
- update_attrs:,
75
- updated_relations_attrs:
76
- }
77
- end
78
-
79
- # Returns the attributes for the primary payload, without the relations.
80
- #
81
- # Example:
82
- #
83
- # @activity.primary_payload_attrs
84
- #
85
- # Returns:
86
- # { "KEY_A" => "VALUE_A", "KEY_B" => "VALUE_B", ... }
87
- #
88
- def primary_payload_attrs
89
- primary_payload ? primary_payload.attrs : {}
90
- end
91
-
92
63
  # Returns the attributes for the relations.
93
64
  #
94
65
  # Example:
@@ -100,13 +71,13 @@ module LoggableActivity
100
71
  # {
101
72
  # record_type: CLASS.NAME,
102
73
  # record_id: INTEGER,
103
- # payload_type: ENUM
74
+ # related_to_activity_as: ENUM
104
75
  # attrs: { "KEY_A" => "VALUE_A", "KEY_B" => "VALUE_B", ... }
105
76
  # }
106
77
  # ]
107
- def relations_attrs
108
- attrs.filter { |p| p[:payload_type] == 'current_association' }
109
- end
78
+ # def relations_attrs
79
+ # attrs.filter { |p| p[:related_to_activity_as] == 'current_association' }
80
+ # end
110
81
 
111
82
  # Returns the display name for a record. what method to use if defined in '/config/loggable_activity.yaml'
112
83
  #
@@ -118,9 +89,7 @@ module LoggableActivity
118
89
  # "David Bowie"
119
90
  #
120
91
  def record_display_name
121
- return I18n.t('loggable.activity.deleted') if encrypted_record_display_name.nil?
122
-
123
- LoggableActivity::Encryption.decrypt(encrypted_record_display_name, record_key)
92
+ primary_payload.record_display_name
124
93
  end
125
94
 
126
95
  # Returns the path for the activity.
@@ -132,8 +101,8 @@ module LoggableActivity
132
101
  # Returns:
133
102
  # "/path/to/activity"
134
103
  #
135
- def primary_route
136
- primary_payload&.payload_route
104
+ def primary_route
105
+ primary_payload.payload_route
137
106
  end
138
107
 
139
108
  # Returns the display name for a actor. what method to use if defined in '/config/loggable_activity.yaml'
@@ -146,28 +115,30 @@ module LoggableActivity
146
115
  # "Elvis Presley"
147
116
  #
148
117
  def actor_display_name
149
- return I18n.t('loggable.activity.deleted') if encrypted_actor_display_name.nil?
118
+ return I18n.t('loggable.activity.deleted') if actor_deleted?
150
119
 
151
- LoggableActivity::Encryption.decrypt(encrypted_actor_display_name, actor_key)
120
+ ::LoggableActivity::Encryption.decrypt(encrypted_actor_name, actor_secret_key)
152
121
  end
153
122
 
154
123
  # Returns a list of activities for a given actor.
155
124
  def self.activities_for_actor(actor, limit = 20, params = { offset: 0 })
156
- LoggableActivity::Activity.latest(limit, params).where(actor:)
125
+ ::LoggableActivity::Activity.latest(limit, params).where(actor:)
157
126
  end
158
127
 
159
128
  # Returns a list of activities ordered by creation date.
129
+ # This is done to support UUID primary keys.
160
130
  def self.latest(limit = 20, params = { offset: 0 })
161
131
  offset = params[:offset] || 0
162
- LoggableActivity::Activity
132
+ ::LoggableActivity::Activity
163
133
  .all
164
134
  .order(created_at: :desc)
165
- .includes(:payloads)
135
+ .includes(payloads: :encryption_key)
166
136
  .offset(offset)
167
137
  .limit(limit)
168
138
  end
169
139
 
170
140
  # Returns the last activity.
141
+ # This is done to support of UUID primary keys.
171
142
  def self.last(limit = 1)
172
143
  return latest(1).first if limit == 1
173
144
 
@@ -180,57 +151,37 @@ module LoggableActivity
180
151
  #
181
152
  # Example usage:
182
153
  # payload = @activity.primary_payload
183
- # puts payload.record_type # => 'SOMD_MODEL_NAME'
154
+ # payload.record_type # => 'SOMD_MODEL_NAME'
184
155
  #
185
156
  def primary_payload
186
- ordered_payloads.find { |p| p.payload_type == 'primary_payload' }
157
+ related_to_activity_as = %w[primary_payload primary_update_payload primary_destroy_payload]
158
+ payloads.detect { |p| related_to_activity_as.include?(p.related_to_activity_as) }
187
159
  end
188
160
 
189
- # Returns the attributes for the update+payload.
190
- def update_attrs
191
- update_payload_attrs = attrs.find { |p| p[:payload_type] == 'update_payload' }
192
- return nil unless update_payload_attrs
193
-
194
- update_payload_attrs.delete(:payload_type)
195
- update_payload_attrs
196
- end
197
-
198
-
199
- # Returns the attributes for the updated relations.
200
- def updated_relations_attrs
201
- grouped_associations = attrs.group_by { |p| p[:record_type] }
202
-
203
- grouped_associations.map do |record_type, payloads|
204
- previous_attrs = payloads.find { |p| p[:payload_type] == 'previous_association' }
205
- current_attrs = payloads.find { |p| p[:payload_type] == 'current_association' }
206
- next if previous_attrs.nil? && current_attrs.nil?
207
-
208
- { record_type:, previous_attrs:, current_attrs: }
209
- end.compact
210
- end
211
-
212
- # Returns the previous association attributes.
213
- def previous_associations_attrs
214
- attrs.select { |p| p[:payload_type] == 'previous_association' }
215
- end
216
-
217
- # Returns payloads sorted by :payload_type.
161
+ # # Returns payloads sorted by :related_to_activity_as.
218
162
  def ordered_payloads
219
- payloads.order(:payload_type)
163
+ # payloads.order(related_to_activity_as: :desc)
164
+ payloads
220
165
  end
221
166
 
222
167
  # Returns the key for the logged record.
223
168
  def record_key
224
169
  return nil if record.nil?
225
170
 
226
- LoggableActivity::EncryptionKey.for_record(record)&.key
171
+ ::LoggableActivity::EncryptionKey.for_record(record)&.secret_key
227
172
  end
228
173
 
229
- # Returns the key for the actor.
230
- def actor_key
231
- return nil if actor.nil?
174
+ # Check if the actor is deleted.
175
+ # If the actor is deleted, it will return true.
176
+ # This way we don't rely on access to the main DB to check if the actor is deleted.
177
+ def actor_deleted?
178
+ actor_secret_key.nil?
179
+ end
232
180
 
233
- LoggableActivity::EncryptionKey.for_record(actor)&.key
181
+ # Returns the key for the actor.
182
+ def actor_secret_key
183
+ # @actor_key_secret_key ||=
184
+ ::LoggableActivity::EncryptionKey.for_record_by_type_and_id(actor_type, actor_id)&.secret_key
234
185
  end
235
186
 
236
187
  # Validates that the activity has at least one payload.
@@ -1,20 +1,66 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'json-schema'
4
+ require 'json'
5
+
3
6
  module LoggableActivity
4
7
  # This class is used to load the configuration file located at config/loggable_activity.yml
8
+ class ConfigurationError < StandardError
9
+ def initialize(msg = '')
10
+ # https://api.loggable_activity.com/msg
11
+ puts '---------------- LOGGABLE ACTIVITY -----------------'
12
+ puts msg
13
+ puts '----------------------------------------------------'
14
+ super(msg)
15
+ end
16
+ end
17
+
18
+ # This class is used to load the configuration file located at config/loggable_activity.yml
19
+ # When the LoggableActivity::Hook is included in a model
20
+ # it takes the model's name and find the configuration for that model in the configuration file.
5
21
  class Configuration
6
22
  # Loads the configuration file
7
23
  def self.load_config_file(config_file_path)
8
24
  @config_data = YAML.load_file(config_file_path)
25
+ validate_config_file
26
+ rescue Errno::ENOENT
27
+ raise ConfigurationError, 'config/loggable_activity.yaml not found'
28
+ end
29
+
30
+ # Loads the schema file for the configuration file
31
+ def self.load_schema
32
+ schema_path = File.join(__dir__, '..', 'schemas', 'config_schema.json')
33
+ JSON.parse(File.read(schema_path))
34
+ end
35
+
36
+ # Validates the configuration file againss the schema
37
+ def self.validate_config_file
38
+ schema = load_schema
39
+ errors = JSON::Validator.fully_validate(schema, @config_data)
40
+ return unless errors.any?
41
+
42
+ raise ConfigurationError,
43
+ "config/loggable_activity.yaml is invalid: #{errors.join(', ')}"
44
+ end
45
+
46
+ # Returns true if the configuration file has been loaded
47
+ def self.loaded?
48
+ !@config_data.nil?
49
+ end
50
+
51
+ # Returns the configuration data
52
+ class << self
53
+ # @return [Hash]
54
+ attr_reader :config_data
9
55
  end
10
56
 
11
57
  # Returns the configuration data for the given class
12
58
  #
13
59
  # Example:
14
- # LoggableActivity::Configuration.for_class('User')
60
+ # ::LoggableActivity::Configuration.for_class('User')
15
61
  # Returns:
16
62
  # {
17
- # "record_display_name": "full_name",
63
+ # "fetch_record_name_from": "full_name",
18
64
  # "loggable_attrs": [
19
65
  # "first_name",
20
66
  # "last_name",
@@ -23,10 +69,21 @@ module LoggableActivity
23
69
  # "create",
24
70
  # "update",
25
71
  # "destroy"
26
- # ]
72
+ # ],
73
+ # ....
27
74
  # }
28
75
  def self.for_class(class_name)
29
76
  @config_data[class_name]
30
77
  end
78
+
79
+ # Returns the name of the field or method to use for the actor's display name.
80
+ def self.fetch_current_user_name_from
81
+ @config_data['fetch_current_user_name_from']
82
+ end
83
+
84
+ # Returns the name of the model to use for the current user.
85
+ def self.current_user_model_name
86
+ @config_data['current_user_model_name']
87
+ end
31
88
  end
32
89
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_record'
4
+
5
+ module LoggableActivity
6
+ # This class represends an additional data owner for a record.
7
+ # For it to kick in, the data_owner configuration has to be set to true in the loggable_activity.yaml file.
8
+ class DataOwner < ActiveRecord::Base
9
+ self.table_name = 'loggable_data_owners'
10
+ belongs_to :record, polymorphic: true, optional: true
11
+ belongs_to :encryption_key, class_name: '::LoggableActivity::EncryptionKey'
12
+
13
+ # When a record is deleted, all data owner added to the record is also deleted.
14
+ def mark_as_deleted!
15
+ encryption_key.mark_as_deleted!
16
+ end
17
+ end
18
+ end
@@ -5,24 +5,21 @@ require 'openssl'
5
5
  require 'base64'
6
6
 
7
7
  module LoggableActivity
8
- # This error is raised when encryption or decryption fails
9
- class EncryptionError < StandardError
10
- end
11
-
12
8
  # This module is used to encrypt and decrypt attributes
13
9
  module Encryption
14
10
  # Encrypts the given data using the given encryption key
15
11
  #
16
12
  # Example:
17
- # LoggableActivity::Encryption.encrypt('my secret data', 'my secret key')
13
+ # ::LoggableActivity::Encryption.encrypt('my secret data', 'my secret key')
18
14
  #
19
15
  # Returns:
20
16
  # "SOME_ENCRYPTED_STRING"
21
17
  #
22
- def self.encrypt(data, encoded_key)
23
- return nil if data.nil? || encoded_key.nil?
18
+ def self.encrypt(data, secret_key)
19
+ return nil if secret_key.nil?
20
+ return nil if data.nil?
24
21
 
25
- encryption_key = Base64.decode64(encoded_key)
22
+ encryption_key = Base64.decode64(secret_key)
26
23
  raise EncryptionError, "Encryption failed: Invalid encoded_key length #{encryption_key.bytesize}" unless encryption_key.bytesize == 32
27
24
 
28
25
  cipher = OpenSSL::Cipher.new('AES-256-CBC').encrypt
@@ -39,19 +36,21 @@ module LoggableActivity
39
36
  # Decrypts the given data using the given encryption key
40
37
  #
41
38
  # Example:
42
- # LoggableActivity::Encryption.decrypt('SOME_ENCRYPTED_STRING', 'SECRET_KEY')
39
+ # ::LoggableActivity::Encryption.decrypt('SOME_ENCRYPTED_STRING', 'SECRET_KEY')
43
40
  #
44
41
  # Returns:
45
42
  # "my secret data"
46
43
  #
47
- def self.decrypt(data, encoded_key)
48
- return '' if data.nil? || encoded_key.nil?
44
+ def self.decrypt(data, secret_key)
45
+ return I18n.t('loggable.activity.deleted') if secret_key.nil?
46
+ return '' if data.blank?
49
47
 
50
- encryption_key = Base64.decode64(encoded_key)
48
+ encryption_key = Base64.decode64(secret_key)
51
49
  raise EncryptionError, "Decryption failed: Invalid encoded_key length: #{encryption_key.bytesize}" unless encryption_key.bytesize == 32
52
50
 
53
51
  cipher = OpenSSL::Cipher.new('AES-256-CBC').decrypt
54
52
  cipher.key = encryption_key
53
+ raise EncryptionError, 'Decryption failed: Invalid data length' unless data.bytesize > cipher.iv_len
55
54
 
56
55
  raw_data = Base64.decode64(data)
57
56
  cipher.iv = raw_data[0...cipher.iv_len] # Extract IV from the beginning of raw_data
@@ -59,9 +58,10 @@ module LoggableActivity
59
58
 
60
59
  decrypted_data.force_encoding('UTF-8')
61
60
  rescue OpenSSL::Cipher::CipherError => e
62
- raise EncryptionError, "Decryption failed: #{e.message}"
61
+ puts "CipherError Decryption failed: #{e.message}"
62
+ '*** DECRYPTION FAILED ***'
63
63
  rescue EncryptionError => e
64
- puts e.message
64
+ puts "EncryptionError failed: #{e.message}"
65
65
  '*** DECRYPTION FAILED ***'
66
66
  end
67
67
 
@@ -10,24 +10,29 @@ module LoggableActivity
10
10
 
11
11
  # Associations
12
12
  belongs_to :record, polymorphic: true, optional: true
13
- belongs_to :parent_key, class_name: 'LoggableActivity::EncryptionKey', optional: true,
14
- foreign_key: 'parent_key_id'
13
+ # belongs_to :payload, class_name: '::LoggableActivity::Payload', optional: true
14
+ # belongs_to :parent_key, class_name: '::LoggableActivity::EncryptionKey', optional: true,
15
+ # foreign_key: 'parent_key_id'
15
16
 
16
17
  # Marks the encryption key as deleted by updating the key to nil.
17
- def mark_as_deleted
18
- update(key: nil)
19
- parent_key.mark_as_deleted if parent_key.present?
18
+ def mark_as_deleted!
19
+ update(secret_key: nil)
20
+ end
21
+
22
+ # check if the encryption key is deleted
23
+ def deleted?
24
+ secret_key.nil?
20
25
  end
21
26
 
22
27
  # Returns an encryption key for a record by its type and ID, optionally using a parent key.
23
28
  #
24
29
  # @param record_type [String] The type of the record.
25
30
  # @param record_id [Integer] The ID of the record.
26
- # @param parent_key [LoggableActivity::EncryptionKey, nil] The parent encryption key, if any.
27
- # @return [LoggableActivity::EncryptionKey] The encryption key for the record.
31
+ # @param parent_key [::LoggableActivity::EncryptionKey, nil] The parent encryption key, if any.
32
+ # @return [::LoggableActivity::EncryptionKey] The encryption key for the record.
28
33
  #
29
34
  # Example:
30
- # LoggableActivity::EncryptionKey.for_record_by_type_and_id('User', 1)
35
+ # ::LoggableActivity::EncryptionKey.for_record_by_type_and_id('User', 1)
31
36
  #
32
37
  # Returns:
33
38
  # {
@@ -38,22 +43,22 @@ module LoggableActivity
38
43
  # :record_id => 1
39
44
  # }
40
45
  #
41
- def self.for_record_by_type_and_id(record_type, record_id, parent_key = nil)
46
+ def self.for_record_by_type_and_id(record_type, record_id)
42
47
  encryption_key = find_by(record_type:, record_id:)
43
48
  return encryption_key if encryption_key
44
49
 
45
- create_encryption_key(record_type, record_id, parent_key)
50
+ create_encryption_key(record_type, record_id)
46
51
  end
47
52
 
48
53
  # Returns an encryption key for a record, optionally using a parent key.
49
54
  #
50
55
  # @param record [ActiveRecord::Base] The record for which to get the encryption key.
51
- # @param parent_key [LoggableActivity::EncryptionKey, nil] The parent encryption key, if any.
52
- # @return [LoggableActivity::EncryptionKey] The encryption key for the record.
56
+ # @param parent_key [::LoggableActivity::EncryptionKey, nil] The parent encryption key, if any.
57
+ # @return [::LoggableActivity::EncryptionKey] The encryption key for the record.
53
58
  #
54
59
  # Example:
55
60
  # user = User.find(1)
56
- # LoggableActivity::EncryptionKey.for_record(user)
61
+ # ::LoggableActivity::EncryptionKey.for_record(user)
57
62
  #
58
63
  # Returns:
59
64
  # {
@@ -64,24 +69,24 @@ module LoggableActivity
64
69
  # :record_id => 1
65
70
  # }
66
71
  #
67
- def self.for_record(record, parent_key = nil)
72
+ def self.for_record(record)
68
73
  return nil if record.nil?
69
74
 
70
75
  encryption_key = find_by(record:)
71
76
  return encryption_key if encryption_key
72
77
 
73
- create_encryption_key(record.class.name, record.id, parent_key)
78
+ create_encryption_key(record.class.name, record.id)
74
79
  end
75
80
 
76
81
  # Creates an encryption key for a record, optionally using a parent key.
77
82
  #
78
83
  # @param record_type [String] The type of the record.
79
84
  # @param record_id [Integer] The ID of the record.
80
- # @param parent_key [LoggableActivity::EncryptionKey, nil] The parent encryption key, if any.
81
- # @return [LoggableActivity::EncryptionKey] The created encryption key.
85
+ # @param parent_key [::LoggableActivity::EncryptionKey, nil] The parent encryption key, if any.
86
+ # @return [::LoggableActivity::EncryptionKey] The created encryption key.
82
87
  #
83
88
  # Example:
84
- # LoggableActivity::EncryptionKey.create_encryption_key('User', 1)
89
+ # ::LoggableActivity::EncryptionKey.create_encryption_key('User', 1)
85
90
  #
86
91
  # Returns:
87
92
  # {
@@ -92,12 +97,8 @@ module LoggableActivity
92
97
  # :record_id => 1
93
98
  # }
94
99
  #
95
- def self.create_encryption_key(record_type, record_id, parent_key = nil)
96
- if parent_key
97
- create(record_type:, record_id:, key: random_key, parent_key:)
98
- else
99
- create(record_type:, record_id:, key: random_key)
100
- end
100
+ def self.create_encryption_key(record_type, record_id)
101
+ create(record_type:, record_id:, secret_key: random_key)
101
102
  end
102
103
 
103
104
  # Generates a random encryption key.
@@ -105,7 +106,7 @@ module LoggableActivity
105
106
  # @return [String] The generated encryption key.
106
107
  #
107
108
  # Example:
108
- # LoggableActivity::EncryptionKey.random_key
109
+ # ::LoggableActivity::EncryptionKey.random_key
109
110
  #
110
111
  # Returns:
111
112
  # "a8f4774e7f42eb253045a4db7de7b79e"
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LoggableActivity
4
+ # Error class for loggable activity.
5
+ class Error < StandardError
6
+ def initialize(msg = '')
7
+ puts '---------------- LOGGABLE ACTIVITY -----------------'
8
+ puts msg
9
+ puts '----------------------------------------------------'
10
+ super(msg)
11
+ end
12
+ end
13
+
14
+ # Error class for encryption.
15
+ class EncryptionError < StandardError
16
+ def initialize(msg = '')
17
+ puts '---------------- LOGGABLE ACTIVITY -----------------'
18
+ puts msg
19
+ puts '----------------------------------------------------'
20
+ super(msg)
21
+ end
22
+ end
23
+
24
+ # This class is used to load the configuration file located at config/loggable_activity.yml
25
+ class ConfigurationError < StandardError
26
+ def initialize(msg = '')
27
+ # https://api.loggable_activity.com/msg
28
+ puts '---------------- LOGGABLE ACTIVITY -----------------'
29
+ puts msg
30
+ puts '----------------------------------------------------'
31
+ super(msg)
32
+ end
33
+ end
34
+ end