loggable_activity 0.5.4 → 0.5.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 24bd885b926bbd2d1b0795d5265b0121b03a99ea3057da14c60669de72d5ef98
4
- data.tar.gz: b5d04e92f45bb6d7a1af4d1f6e22489cb4f88798771403ee6ad1cf97ae0433fd
3
+ metadata.gz: e8d0d096b34f0b29dc9d0af6fb1c44e7fb4ba79b85b92e0e6aee4d895e5dcc57
4
+ data.tar.gz: 35ffb9e0e97409e8da7d659a37a37a66e2305f441505c4852af03d9681b8d90d
5
5
  SHA512:
6
- metadata.gz: 5a88d29b6655e37f8713ece59f8cbd48f36da48e2d1f1945e7de7f08572db4591109bd88b4804e6523d9a8c428c12799f04e7d220d221be4b48fc38c6761eee3
7
- data.tar.gz: a6e037dc4d8732af308fc162134144a164815d5f1915701cad9cb5b543353fceac2b7bd0a56ae19bd028163eb42b78316f0a16d533da6f24bc0447e7f967bf3c
6
+ metadata.gz: f2cae0fe288f7ac429004f9318e833af33d2b629b57041dfa6d2da11e81b6cf10cd282ee0672b5a8aab4a0529ff9637f682979c9b91749559827863b42281c10
7
+ data.tar.gz: 7f068e3bf66ac331a646e909c631d79ef9e455643f13b2ac9a3e574d99e9dfefc046d76885a6f38bba275a5763c8c7f64980076a86cbc4e230dd4d65aaa64d2c
data/.rubocop.yml CHANGED
@@ -1,4 +1,5 @@
1
1
  AllCops:
2
+ NewCops: enable
2
3
  Exclude:
3
4
  - 'db/migrate/20240702092648_create_loggable_activity_tables.rb'
4
5
  Layout/LineLength:
@@ -9,6 +10,8 @@ Layout/LineLength:
9
10
  Metrics/AbcSize:
10
11
  Exclude:
11
12
  - 'lib/loggable_activity/encryption.rb'
13
+ - 'lib/loggable_activity/services/update_payloads_builder.rb'
14
+ - 'lib/generators/loggable_activity/install/templates/create_loggable_activities.rb'
12
15
  Metrics/BlockLength:
13
16
  Exclude:
14
17
  - 'test/dummy/config/environments/development.rb'
@@ -18,6 +21,7 @@ Metrics/ClassLength:
18
21
  Exclude:
19
22
  - 'lib/loggable_activity/services/update_payloads_builder.rb'
20
23
  Metrics/MethodLength:
24
+ Enabled: true
21
25
  Exclude:
22
26
  - 'lib/loggable_activity/services/update_payloads_builder.rb'
23
27
  - 'lib/loggable_activity/services/destroy_payloads_builder.rb'
@@ -27,6 +31,9 @@ Metrics/MethodLength:
27
31
  - 'lib/loggable_activity/activity.rb'
28
32
  - 'lib/loggable_activity/services/payloads_builder.rb'
29
33
  - 'lib/loggable_activity/hooks.rb'
34
+ - 'test/dummy/db/migrate/20240629154538_create_users.rb'
35
+ - 'lib/loggable_activity/services/custom_payloads_builder.rb'
36
+ - 'lib/generators/loggable_activity/install/templates/create_loggable_activities.rb'
30
37
  Metrics/ModuleLength:
31
38
  Exclude:
32
39
  - 'lib/loggable_activity/hooks.rb'
@@ -36,3 +43,6 @@ Style/Documentation:
36
43
  - 'app/models/loggable_activity/application_record.rb'
37
44
  - 'app/mailers/loggable_activity/application_mailer.rb'
38
45
  - 'app/helpers/loggable_activity/application_helper.rb'
46
+ Metrics/CyclomaticComplexity:
47
+ Exclude:
48
+ - 'lib/loggable_activity/hooks.rb'
data/CHANGELOG.md CHANGED
@@ -1,10 +1,16 @@
1
- ## [0.5.3] - 2024-03-21
1
+ ## [0.5.6] - 2024-07-18
2
+ - Documentation updated
3
+ - Generator updated
4
+
5
+ ## [0.5.5] - 2024-07-16
6
+ - Custom attrs added
2
7
  ### Breaking change
3
- - Styling changed to bootstrap
4
8
  - Configuration.yml changed
5
9
 
10
+ ## [0.5.4] - 2024-03-21
11
+ - Styling with bootstrap css
12
+
6
13
  ## [0.5.2] - 2024-03-21
7
- ### Breaking change
8
14
  - Styling with pico css
9
15
  - Public attrs added
10
16
 
@@ -0,0 +1,60 @@
1
+ # Getting started
2
+ This document should you getting started with `LoggableActivity`
3
+
4
+ ## Prerequisite
5
+ 1 A running Rails Application<br/>
6
+ 2 An authorization system where a current user or similar is available.
7
+
8
+ ## Installation
9
+ Add the loggable_activity gem to the Gemfile `gem 'loggable_activity', '~> 0.5.4` and then<br/>
10
+ ```
11
+ $ bundle install
12
+ ```
13
+ Then we have to generate some migrations and execute them<br/>
14
+ ```
15
+ $ bin/rails generate loggable_activity:install
16
+ $ bin/rails db:migrate
17
+ ```
18
+
19
+ ## Add hoks to models
20
+ ```
21
+ class User < ApplicationRecord
22
+ include LoggableActivity::Hooks
23
+ ```
24
+
25
+ ## Configuration
26
+ Update `config/initializers/loggable_activity.rb` documentation is in the file.
27
+
28
+ ## loggable_activity.yaml
29
+ You have to update the configuration file installed inside the `config/loggable_activity.yaml`
30
+ This file defines how data are logged.
31
+
32
+ ## Automatically log show from controllers
33
+ This assumes that there is a current_user. E.g provided by the Devise gem</br>
34
+ Add a this to the application_controller.rb if you want automatically log show
35
+ ```
36
+ class ApplicationController < ActionController::Base
37
+ include LoggableActivity::CurrentUser
38
+ ```
39
+ Otherwise you might create `app/controllers/concerns/current_user.rb` and alter it .
40
+
41
+ ## Manually log show from controllers
42
+ If you want to log the show action you can add this to your controllers show method
43
+ ```
44
+ def show
45
+ @user.log(:show)
46
+ ```
47
+
48
+ ## For developers and contributors
49
+ You can download and play around with a demo app
50
+ - 1 Download the project from [github](https://github.com/LoggableActivity/LoggableActivity)
51
+ - 1 Download the demo application from [github](https://github.com/LoggableActivity/LoggableActivityDemoApp)
52
+ - 2 Update the Gemfile in the demo project so it points to your localhost.
53
+ ```
54
+ Gemfile
55
+
56
+ gem 'loggable_activity', '~> VERSION', path: '/PATH_TO_PROJECT/LoggableActivityEngine/LoggableActivityEngine'
57
+ # gem 'loggable_activity', '~> VERSION'
58
+ ```
59
+ *VERSION is the version number found in the gemfile*<br/>
60
+ *PATH_TO_PROJECT* is where you have stored the project on your local drive
data/README.md CHANGED
@@ -1,11 +1,8 @@
1
1
  # Loggable Activity 🌟
2
- Secure protect data and log how it is handled
3
- - Keep an activity log of how data in the db are handled.
4
- - Protect and secure the privacy of data stored in Activity Logs
5
- - Prepare for General Data Protection Regulation (GDPR) compliance.
6
- - Handles activities that involves more than one table in the DB.
7
- - Encrypt data stored in the activity log
8
- - Support binary_ids
2
+ Super easy to use activity log with views out of the box
3
+ - Just add the gem and mount the engine and you have a cool looking activity log.
4
+ - Easy and simple to customize and configure
5
+ - All data are encrypted, and
9
6
 
10
7
  ### Important!
11
8
  This project is under development and not ready for production. There might be breaking changes, so please consult the CHANGELOG.md
@@ -1,13 +1,13 @@
1
1
 
2
2
  .row
3
3
  .col-3
4
- b.text-secondary Action
4
+ b.text-secondary = t('loggable_activity.activity.action')
5
5
  .col-3
6
- b.text-secondary Record
6
+ b.text-secondary = t('loggable_activity.activity.record')
7
7
  .col-3
8
- b.text-secondary Actor
8
+ b.text-secondary = t('loggable_activity.activity.actor')
9
9
  .col-3
10
- b.text-secondary Created At
10
+ b.text-secondary = t('loggable_activity.activity.created_at')
11
11
 
12
12
  - @activities.each do |activity|
13
13
  .activity.activity-info[type="button" data-bs-toggle="collapse" data-bs-target="#activity_#{activity.id}" aria-expanded="false" aria-controls="collapseExample"]
@@ -15,9 +15,9 @@
15
15
  .col-3
16
16
  = activity_action(activity)
17
17
  .col-3
18
- = t(activity.record_display_name)
18
+ = activity.record_display_name
19
19
  .col-3
20
- = t(activity.actor_display_name)
20
+ = activity.actor_display_name
21
21
  .col-3
22
22
  = l(activity.created_at, format: :long)
23
23
  .collapse id="activity_#{activity.id}"
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This module provides generators for installing and configuring LoggableActivity.
4
+ # It includes generators for creating initializer, migration, and locale files.
5
+ #
6
+ # Example usage:
7
+ # rails generate loggable_activity:install
8
+ #
9
+ # This will:
10
+ # - Create an initializer file in config/initializers/loggable_activity.rb
11
+ # - Create a migration file in db/migrate/
12
+ # - Create a locale file in config/locales/loggable_activity.en.yml
13
+ #
14
+ # After running the generator, remember to:
15
+ # - Add `mount LoggableActivity::Engine => '/loggable_activity'` to your routes.rb file.
16
+ # - Run `rails db:migrate` to create the necessary database tables.
17
+ # - Include `LoggableActivity::Hook` in the models you want to track.
18
+ # - Update the config/loggable_activity.yaml file with the fields you want to track.
19
+ # - Ensure the locale files are properly set up in config/locales/loggable_activity.en.yml.
20
+ module LoggableActivity
21
+ module Generators
22
+ # The InstallGenerator class is responsible for copying the necessary
23
+ # files to set up LoggableActivity in a Rails application.
24
+ class InstallGenerator < Rails::Generators::Base
25
+ source_root File.expand_path('templates', __dir__)
26
+
27
+ desc 'Creates a LoggableActivity initializer in your application.'
28
+ def copy_initializer
29
+ template 'loggable_activity.rb', 'config/initializers/loggable_activity.rb'
30
+ end
31
+
32
+ desc 'Creates a migration file for LoggableActivity in your application.'
33
+ def copy_migration
34
+ template 'create_loggable_activities.rb', "db/migrate/#{migration_number}_create_loggable_activities.rb"
35
+ end
36
+
37
+ desc 'Creates a locale file for LoggableActivity in your application.'
38
+ def copy_locale
39
+ template 'loggable_activity.en.yml', 'config/locales/loggable_activity.en.yml'
40
+ end
41
+
42
+ # Generates a timestamp to use in the migration filename.
43
+ #
44
+ # @return [String] the current UTC time formatted as a timestamp
45
+ def migration_number
46
+ Time.now.utc.strftime('%Y%m%d%H%M%S')
47
+ end
48
+
49
+ puts ''
50
+ puts "\e[1m\e[32m* ----------------------------- LoggableActivity ----------------------------- *\e[0m"
51
+ puts "\e[1m\e[32m* *\e[0m"
52
+ puts "\e[1m\e[32m* LoggableActivity has been successfully installed. *\e[0m"
53
+ puts "\e[1m\e[32m* Add the following to your routes.rb file. *\e[0m"
54
+ puts "\e[1m\e[32m* Mount LoggableActivity::Engine => '/loggable_activity' *\e[0m"
55
+ puts "\e[1m\e[32m* *\e[0m"
56
+ puts "\e[1m\e[32m* $ rails db:migrate to create the tables. *\e[0m"
57
+ puts "\e[1m\e[32m* *\e[0m"
58
+ puts "\e[1m\e[32m* Add include LoggableActivity::Hook to the models you want to track. *\e[0m"
59
+ puts "\e[1m\e[32m* *\e[0m"
60
+ puts "\e[1m\e[32m* Update the config/loggable_activity.yaml file with fields to track. *\e[0m"
61
+ puts "\e[1m\e[32m* *\e[0m"
62
+ puts "\e[1m\e[32m* Locale files are found in config/locale/loggable_activity.en.yaml. *\e[0m"
63
+ puts "\e[1m\e[32m* *\e[0m"
64
+ puts "\e[1m\e[32m* ---------------------------------------------------------------------------- *\e[0m"
65
+ puts ''
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This migration creates the necessary tables for LoggableActivity.
4
+ class CreateLoggableActivities < ActiveRecord::Migration[7.1]
5
+ def change
6
+ create_table :loggable_activity_encryption_keys do |t|
7
+ t.references :record, polymorphic: true, null: true, index: true
8
+ t.string :secret_key
9
+ t.datetime :delete_at
10
+
11
+ t.timestamps
12
+ end
13
+
14
+ create_table :loggable_activity_activities do |t|
15
+ t.string :action
16
+ t.references :actor, polymorphic: true, null: true
17
+ t.string :encrypted_actor_name
18
+ t.references :record, polymorphic: true, null: true
19
+
20
+ t.timestamps
21
+ end
22
+
23
+ create_table :loggable_activity_payloads do |t|
24
+ t.references :activity, null: false, foreign_key: { to_table: 'loggable_activity_activities' }
25
+ t.references :encryption_key, null: false, foreign_key: { to_table: 'loggable_activity_encryption_keys' }
26
+ t.references :record, polymorphic: true, null: true, index: true
27
+ t.string :encrypted_record_name
28
+ t.json :encrypted_attrs
29
+ t.integer :related_to_activity_as, default: 0
30
+ t.boolean :data_owner, default: false
31
+ t.string :route
32
+ t.boolean :current_payload, default: true
33
+ t.json :public_attrs, default: {}
34
+
35
+ t.timestamps
36
+ end
37
+
38
+ create_table :loggable_activity_data_owners do |t|
39
+ t.references :record, polymorphic: true, null: true, index: true
40
+ t.references :encryption_key, null: false, foreign_key: { to_table: 'loggable_activity_encryption_keys' }
41
+
42
+ t.timestamps
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,32 @@
1
+
2
+ # This file was generated when you installed Loggable Activity and is used for internationalization.
3
+ # When a model 'includes LoggableActivity::Hooks' tags for translations are generated.
4
+ # Example:
5
+ # class User < ApplicationRecord
6
+ # include LoggableActivity::Hooks
7
+ # end
8
+ # This will make the following tags for translations available:
9
+ # - loggable_activity.user.create
10
+ # - loggable_activity.user.show
11
+ # - loggable_activity.user.update
12
+ # - loggable_activity.user.destroy
13
+ # - loggable_activity.user.login
14
+ # - loggable_activity.user.logout
15
+ #
16
+ en:
17
+ loggable_activity:
18
+ activity:
19
+ actor: Actor
20
+ action: Action
21
+ record: Record
22
+ created_at: Created at
23
+ deleted: "*** DELETED ***"
24
+ user:
25
+ update: A user was updated
26
+ create: A user was created
27
+ show: "A user was shown"
28
+ destroy: A user was deleted
29
+ sign_up: A user signed up
30
+ login: A user logged in
31
+ logout: A user logged out
32
+
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This configuration file is for setting up the LoggableActivity gem in your Rails application.
4
+
5
+ # Specify the name of the model that represents the actor performing the activities.
6
+ # This should be a string that matches the class name of the model.
7
+ # Example: If your user model is named "User", set it to 'User'.
8
+ LoggableActivity.actor_model_name = 'User'
9
+
10
+ # Specify the attribute from which to fetch the actor's name.
11
+ # This should be eighter a string representing the attribute name of the actor model
12
+ # or a method that returns the actor's name.
13
+ # Example: If you want to use the user's email as their name, set it to 'email'.
14
+ LoggableActivity.fetch_actor_name_from = 'email'
15
+
16
+ # Specify the path to the configuration file for LoggableActivity.
17
+ # This file should be a YAML file that contains the necessary configuration for logging activities.
18
+ # The path is set relative to the Rails root directory.
19
+ # Example: The default configuration file path is 'config/loggable_activity.yaml'.
20
+ LoggableActivity.config_file_path = Rails.root.join('config/loggable_activity.yaml')
21
+
22
+ # Specify whether the sanitazion should be performed by a task or not
23
+ # If set to 'false' the sanitization is performed immediately.
24
+ # If set to 'true' the data is inaccessible, but can be restored.
25
+ # If set to 'true' you have to permanently delete
26
+ # the data by calling 'LoggableActivity::Sanitizer.run' from a task.
27
+ LoggableActivity.task_for_sanitization = false
@@ -0,0 +1,103 @@
1
+ Company:
2
+ fetch_record_name_from: name
3
+ route: /companies/:id
4
+ loggable_attrs:
5
+ - name
6
+ public_attrs:
7
+ - name
8
+ relations:
9
+ - has_many: :users
10
+ route: /users/:id
11
+ model: User
12
+ loggable_attrs:
13
+ - first_name
14
+ - last_name
15
+ auto_log:
16
+ - create
17
+ - update
18
+ - destroy
19
+ Hat:
20
+ fetch_record_name_from: color
21
+ route: /user/:id
22
+ loggable_attrs:
23
+ - color
24
+ auto_log:
25
+ - create
26
+ - destroy
27
+ relations:
28
+ - belongs_to: :user
29
+ route: /demo/users/:id
30
+ model: User
31
+ loggable_attrs:
32
+ - first_name
33
+ User:
34
+ fetch_record_name_from: full_name
35
+ route: /demo/users/:id
36
+ loggable_attrs:
37
+ - first_name
38
+ - last_name
39
+ - age
40
+ - email
41
+ - user_type
42
+ auto_log:
43
+ - create
44
+ - update
45
+ - destroy
46
+ public_attrs:
47
+ - age
48
+ - user_type
49
+ relations:
50
+ - has_one: :profile
51
+ route: /demo/users/:id/profile
52
+ model: Profile
53
+ loggable_attrs:
54
+ - bio
55
+ - phone_number
56
+ public_attrs:
57
+ - date_of_birth
58
+ - has_many: :hats
59
+ route: /users/:id
60
+ model: Hat
61
+ loggable_attrs:
62
+ - color
63
+ - belongs_to: :company
64
+ route: /companies/:id
65
+ model: Company
66
+ loggable_attrs:
67
+ - name
68
+ Profile:
69
+ fetch_record_name_from: phone_number
70
+ route: /demo/profiles/:id
71
+ loggable_attrs:
72
+ - bio
73
+ - profile_picture_url
74
+ - location
75
+ - date_of_birth
76
+ - phone_number
77
+ public_attrs:
78
+ - date_of_birth
79
+ # auto_log:
80
+ # - create
81
+ # - update
82
+ # - destroy
83
+ # relations:
84
+ # - belongs_to: :user
85
+ # route: /demo/users/:id
86
+ # model: User
87
+ # loggable_attrs:
88
+ # - first_name
89
+ # - last_name
90
+ # - age
91
+ # - email
92
+ # - user_type
93
+ # - belongs_to: :demo_address
94
+ # route: show_demo_address
95
+ # model: Demo::Address
96
+ # loggable_attrs:
97
+ # - street
98
+ # - postal_code
99
+ # - belongs_to: :demo_club
100
+ # route: show_demo_club
101
+ # model: Demo::Club
102
+ # loggable_attrs:
103
+ # - name
@@ -31,7 +31,8 @@ module LoggableActivity
31
31
  'has_many_update_payload' => 'has_many',
32
32
  'belongs_to_payload' => 'belongs_to',
33
33
  'belongs_to_destroy_payload' => 'belongs_to',
34
- 'belongs_to_update_payload' => 'belongs_to'
34
+ 'belongs_to_update_payload' => 'belongs_to',
35
+ 'custom_payload' => 'self'
35
36
  }.freeze
36
37
 
37
38
  # Returns an array of hashes, each representing an activity's attributes and its associated relations. The structure and relations to include are specified in 'config/loggable_activity.yaml'. This format is designed for UI display purposes.
@@ -106,7 +107,7 @@ module LoggableActivity
106
107
  # "David Bowie"
107
108
  #
108
109
  def record_display_name
109
- primary_payload.record_display_name
110
+ primary_payload&.record_display_name
110
111
  end
111
112
 
112
113
  # Returns the path for the activity.
@@ -135,7 +136,6 @@ module LoggableActivity
135
136
  return I18n.t('loggable.activity.deleted') if actor_deleted?
136
137
 
137
138
  ::LoggableActivity::Encryption.decrypt(encrypted_actor_name, actor_secret_key)
138
-
139
139
  end
140
140
 
141
141
  # Returns a list of activities for a given actor.
@@ -172,7 +172,7 @@ module LoggableActivity
172
172
  # payload.record_type # => 'SOMD_MODEL_NAME'
173
173
  #
174
174
  def primary_payload
175
- related_to_activity_as = %w[primary_payload primary_update_payload primary_destroy_payload]
175
+ related_to_activity_as = %w[primary_payload primary_update_payload primary_destroy_payload custom_payload]
176
176
  payloads.detect { |p| related_to_activity_as.include?(p.related_to_activity_as) }
177
177
  end
178
178
 
@@ -1,16 +1,8 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-06/schema#",
3
3
  "type": "object",
4
- "properties": {
5
- "fetch_actor_name_from": {
6
- "type": "string"
7
- },
8
- "task_for_sanitization": {
9
- "type": "boolean"
10
- }
11
- },
12
4
  "patternProperties": {
13
- "^(?!fetch_actor_name_from$|task_for_sanitization)[A-Za-z0-9_:]+$": {
5
+ "^[A-Za-z0-9_:]+$": {
14
6
  "type": "object",
15
7
  "properties": {
16
8
  "data_owner": {
@@ -82,8 +74,5 @@
82
74
  ]
83
75
  }
84
76
  },
85
- "additionalProperties": false,
86
- "required": [
87
- "fetch_actor_name_from"
88
- ]
77
+ "additionalProperties": false
89
78
  }
@@ -51,16 +51,6 @@ module LoggableActivity
51
51
  def for_class(class_name)
52
52
  config_data[class_name]
53
53
  end
54
-
55
- # Returns the name of the field or method to use for the actor's display name.
56
- def fetch_actor_name_from
57
- config_data['fetch_actor_name_from']
58
- end
59
-
60
- # Returns whatever models should be sanitized on delete.
61
- def task_for_sanitization
62
- config_data['task_for_sanitization']
63
- end
64
54
  end
65
55
  end
66
56
  end
@@ -16,13 +16,11 @@ module LoggableActivity
16
16
  # "SOME_ENCRYPTED_STRING"
17
17
  #
18
18
  def self.encrypt(data, secret_key)
19
- return nil if secret_key.nil?
20
- return nil if data.nil?
19
+ return nil if secret_key.nil? || data.nil?
21
20
 
22
21
  encryption_key = Base64.decode64(secret_key)
23
22
  unless encryption_key.bytesize == 32
24
- raise EncryptionError,
25
- "Encryption failed: Invalid encoded_key length #{encryption_key.bytesize}"
23
+ raise EncryptionError, "Encryption failed: Invalid encoded_key length #{encryption_key.bytesize}"
26
24
  end
27
25
 
28
26
  cipher = OpenSSL::Cipher.new('AES-256-CBC').encrypt
@@ -39,19 +37,47 @@ module LoggableActivity
39
37
  # Decrypts the given data using the given encryption key
40
38
  #
41
39
  # Example:
42
- # ::LoggableActivity::Encryption.decrypt('SOME_ENCRYPTED_STRING', 'SECRET_KEY')
40
+ # ::LoggableActivity::Encryption.decrypt('SOME_ENCRYPTED_DATA', 'SECRET_KEY')
43
41
  #
44
42
  # Returns:
45
43
  # "my secret data"
46
44
  #
47
45
  def self.decrypt(data, secret_key)
46
+ case data
47
+ when Hash
48
+ decrypt_hash(data, secret_key)
49
+ when Array
50
+ decrypt_array(data, secret_key)
51
+ else
52
+ decrypt_data(data, secret_key)
53
+ end
54
+ end
55
+
56
+ # Decrypts a hash's values using the given encryption key
57
+ def self.decrypt_hash(data, secret_key)
58
+ data
59
+ .transform_values { |value| decrypt(value, secret_key) }
60
+ .transform_keys(&:to_sym)
61
+ end
62
+
63
+ # Decrypts an array's values using the given encryption key
64
+ def self.decrypt_array(data, secret_key)
65
+ data.map { |value| decrypt(value, secret_key) }
66
+ end
67
+
68
+ # Checks if a value is blank
69
+ def self.blank?(value)
70
+ value.respond_to?(:empty?) ? value.empty? : !value
71
+ end
72
+
73
+ # Decrypts individual data using the given encryption key
74
+ def self.decrypt_data(data, secret_key)
48
75
  return I18n.t('loggable_activity.activity.deleted') if secret_key.nil?
49
76
  return '' if data.blank?
50
77
 
51
78
  encryption_key = Base64.decode64(secret_key)
52
79
  unless encryption_key.bytesize == 32
53
- raise EncryptionError,
54
- "Decryption failed: Invalid encoded_key length: #{encryption_key.bytesize}"
80
+ raise EncryptionError, "Decryption failed: Invalid encoded_key length: #{encryption_key.bytesize}"
55
81
  end
56
82
 
57
83
  cipher = OpenSSL::Cipher.new('AES-256-CBC').decrypt
@@ -73,10 +99,5 @@ module LoggableActivity
73
99
  Rails.logger.error "ArgumentError Decryption failed: #{e.message}"
74
100
  I18n.t('loggable_activity.decryption.failed')
75
101
  end
76
-
77
- # Returns true if the given value is blank
78
- def self.blank?(value)
79
- value.respond_to?(:empty?) ? value.empty? : !value
80
- end
81
102
  end
82
103
  end
@@ -14,7 +14,7 @@ module LoggableActivity
14
14
 
15
15
  # Prepare the record for deletion
16
16
  def mark_as_deleted!
17
- LoggableActivity::Configuration.task_for_sanitization ? update(delete_at: DateTime.now + 1.month) : delete
17
+ LoggableActivity.task_for_sanitization ? update(delete_at: DateTime.now + 1.month) : delete
18
18
  end
19
19
 
20
20
  # check if the encryption key is deleted or it is about to be deleted
@@ -4,21 +4,21 @@ module LoggableActivity
4
4
  # Error class for loggable activity.
5
5
  class Error < StandardError
6
6
  def initialize(msg = '')
7
- super(msg)
7
+ super
8
8
  end
9
9
  end
10
10
 
11
11
  # Error class for encryption.
12
12
  class EncryptionError < StandardError
13
13
  def initialize(msg = '')
14
- super(msg)
14
+ super
15
15
  end
16
16
  end
17
17
 
18
18
  # This class is used to load the configuration file located at config/loggable_activity.yml
19
19
  class ConfigurationError < StandardError
20
20
  def initialize(msg = '')
21
- super(msg)
21
+ super
22
22
  end
23
23
  end
24
24
  end
@@ -42,7 +42,7 @@ module LoggableActivity
42
42
  # @param params [Hash] Additional parameters for the activity.
43
43
  def log(action, actor: nil, params: {})
44
44
  @action = action
45
- @actor = actor || Thread.current[:current_user]
45
+ @actor = actor || Thread.current[:current_user] || created_by_self
46
46
  return nil if @actor.nil?
47
47
 
48
48
  @record = self
@@ -56,8 +56,14 @@ module LoggableActivity
56
56
  log_destroy
57
57
  when :update
58
58
  log_update
59
+ when :login
60
+ log_login
61
+ when :logout
62
+ log_logout
63
+ when :sign_up
64
+ log_sign_up
59
65
  else
60
- log_custom_activity(action)
66
+ log_custom_activity
61
67
  end
62
68
  end
63
69
 
@@ -67,6 +73,15 @@ module LoggableActivity
67
73
 
68
74
  private
69
75
 
76
+ # If the actor is the same as the record, it is created by the actor.
77
+ # This assumes it is a sign_up action.
78
+ def created_by_self
79
+ return unless LoggableActivity.actor_model_name == self.class.name
80
+
81
+ @action = :sign_up
82
+ self
83
+ end
84
+
70
85
  # Logs an activity for the current action.
71
86
  def log_activity
72
87
  create_activity(build_payloads)
@@ -95,6 +110,28 @@ module LoggableActivity
95
110
  )
96
111
  end
97
112
 
113
+ def log_login
114
+ create_activity(build_payloads)
115
+ end
116
+
117
+ def log_logout
118
+ create_activity(build_payloads)
119
+ end
120
+
121
+ def log_sign_up
122
+ create_activity(build_payloads)
123
+ end
124
+
125
+ # Logs a custom activity.
126
+ def log_custom_activity
127
+ create_activity(build_custom_payload)
128
+ end
129
+
130
+ def build_custom_payload
131
+ ::LoggableActivity::Services::CustomPayloadsBuilder
132
+ .new(self, @payloads, @params).build
133
+ end
134
+
98
135
  # Builds update payloads for the current action.
99
136
  def build_update_payloads
100
137
  ::LoggableActivity::Services::UpdatePayloadsBuilder
@@ -118,9 +155,6 @@ module LoggableActivity
118
155
  @payloads.empty?
119
156
  end
120
157
 
121
- # Logs a custom activity.
122
- def log_custom_activity(activity); end
123
-
124
158
  # Logs an update activity automatically if configured.
125
159
  def log_update_activity
126
160
  return unless hooks_enabled?
@@ -171,7 +205,7 @@ module LoggableActivity
171
205
 
172
206
  # Reads the field to feetch the record name from.
173
207
  def fetch_actor_name_from
174
- ::LoggableActivity::Configuration.fetch_actor_name_from
208
+ LoggableActivity.fetch_actor_name_from
175
209
  end
176
210
 
177
211
  # Returns the action key for the current action.
@@ -22,6 +22,7 @@ module LoggableActivity
22
22
  has_one_payload
23
23
  belongs_to_update_payload
24
24
  has_many_create_payload
25
+ custom_payload
25
26
  ].freeze
26
27
 
27
28
  # Enumeration for different updatepayload types
@@ -47,7 +48,8 @@ module LoggableActivity
47
48
  belongs_to_update_payload: 9,
48
49
  belongs_to_destroy_payload: 10,
49
50
  has_one_destroy_payload: 11,
50
- has_many_destroy_payload: 12
51
+ has_many_destroy_payload: 12,
52
+ custom_payload: 13
51
53
  }
52
54
 
53
55
  # Returns the decrypted attrs.
@@ -62,14 +64,14 @@ module LoggableActivity
62
64
  #
63
65
  def attrs
64
66
  return deleted_attrs if record.nil?
65
-
67
+
66
68
  case related_to_activity_as
67
69
  when *DECRYPT_ATTRS_TYPES
68
- decrypted_attrs.merge(public_attrs: public_attrs)
70
+ decrypted_attrs.merge(public_attrs:)
69
71
  when *DECRYPT_UPDATE_ATTRS_TYPES
70
72
  decrypted_update_attrs + public_attrs['changes']
71
73
  else
72
- { public_attrs: public_attrs }
74
+ { public_attrs: }
73
75
  end
74
76
  end
75
77
 
@@ -6,9 +6,10 @@ module LoggableActivity
6
6
  # Other service modules related to the loggable activity will inherit from this module.
7
7
  class BasePayloadsBuilder
8
8
  # Initializes the PayloadsBuilder with a record and an initial collection of payloads,
9
- def initialize(record, payloads)
9
+ def initialize(record, payloads, params = {})
10
10
  @record = record
11
11
  @payloads = payloads
12
+ @params = params
12
13
  @loggable_attrs = record.class.loggable_attrs
13
14
  @public_attrs = record.class.public_attrs
14
15
  # @relation_config = record.relation_config
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LoggableActivity
4
+ module Services
5
+ # This service class builds custom payloads.
6
+ class CustomPayloadsBuilder < BasePayloadsBuilder
7
+ # Builds a custom payloads for a ::LoggableActivity::Activity.
8
+ def build
9
+ encryption_key = encryption_key_for_record(@record)
10
+ @secret_key = encryption_key&.secret_key
11
+ encrypted_attrs = build_custom_payloads(@params)
12
+
13
+ payload = ::LoggableActivity::Payload.new(
14
+ encryption_key:,
15
+ record: @record,
16
+ encrypted_record_name:,
17
+ encrypted_attrs:,
18
+ related_to_activity_as: 'custom_payload',
19
+ route: @record.class.route,
20
+ current_payload: true,
21
+ data_owner: @record,
22
+ public_attrs: {}
23
+ )
24
+
25
+ unless payload.valid?
26
+ error_message = "Payload validation failed: #{payload.errors.full_messages.join(', ')}"
27
+ raise LoggableActivity::Error, error_message
28
+ end
29
+
30
+ @payloads << payload
31
+ end
32
+
33
+ private
34
+
35
+ def encrypted_record_name
36
+ return encrypt_attr(@params[:display_name], @secret_key) if @params[:display_name]
37
+
38
+ encrypt_record_name_for_record(@record, @secret_key)
39
+ end
40
+
41
+ def build_custom_payloads(params)
42
+ params.transform_values do |value|
43
+ case value
44
+ when Hash
45
+ build_custom_payloads(value)
46
+ when Array
47
+ value.map { |v| v.is_a?(Hash) ? build_custom_payloads(v) : encrypt_attr(v, @secret_key) }
48
+ else
49
+ encrypt_attr(value, @secret_key)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -3,5 +3,5 @@
3
3
  # Version of the gem
4
4
  module LoggableActivity
5
5
  # Version
6
- VERSION = '0.5.4'
6
+ VERSION = '0.5.6'
7
7
  end
@@ -12,6 +12,7 @@ require 'loggable_activity/payload'
12
12
  require 'loggable_activity/error'
13
13
  require 'loggable_activity/sanitizer'
14
14
  require 'loggable_activity/services/base_payloads_builder'
15
+ require 'loggable_activity/services/custom_payloads_builder'
15
16
  require 'loggable_activity/services/payloads_builder'
16
17
  require 'loggable_activity/services/update_payloads_builder'
17
18
  require 'loggable_activity/services/destroy_payloads_builder'
@@ -48,6 +49,8 @@ require 'kaminari'
48
49
  #
49
50
  # This module is designed to be both powerful and easy to integrate, providing a solid foundation for activity logging.
50
51
  module LoggableActivity
51
- mattr_accessor :actor_class
52
+ mattr_accessor :actor_model_name
53
+ mattr_accessor :fetch_actor_name_from
52
54
  mattr_accessor :config_file_path
55
+ mattr_accessor :task_for_sanitization
53
56
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: loggable_activity
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.4
4
+ version: 0.5.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Max Groenlund
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-07-14 00:00:00.000000000 Z
11
+ date: 2024-07-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -118,114 +118,6 @@ dependencies:
118
118
  - - ">="
119
119
  - !ruby/object:Gem::Version
120
120
  version: 3.6.3
121
- - !ruby/object:Gem::Dependency
122
- name: sqlite3
123
- requirement: !ruby/object:Gem::Requirement
124
- requirements:
125
- - - "~>"
126
- - !ruby/object:Gem::Version
127
- version: 1.4.2
128
- type: :development
129
- prerelease: false
130
- version_requirements: !ruby/object:Gem::Requirement
131
- requirements:
132
- - - "~>"
133
- - !ruby/object:Gem::Version
134
- version: 1.4.2
135
- - !ruby/object:Gem::Dependency
136
- name: awesome_print
137
- requirement: !ruby/object:Gem::Requirement
138
- requirements:
139
- - - "~>"
140
- - !ruby/object:Gem::Version
141
- version: '1.9'
142
- - - ">="
143
- - !ruby/object:Gem::Version
144
- version: 1.9.2
145
- type: :development
146
- prerelease: false
147
- version_requirements: !ruby/object:Gem::Requirement
148
- requirements:
149
- - - "~>"
150
- - !ruby/object:Gem::Version
151
- version: '1.9'
152
- - - ">="
153
- - !ruby/object:Gem::Version
154
- version: 1.9.2
155
- - !ruby/object:Gem::Dependency
156
- name: factory_bot_rails
157
- requirement: !ruby/object:Gem::Requirement
158
- requirements:
159
- - - "~>"
160
- - !ruby/object:Gem::Version
161
- version: '6.4'
162
- - - ">="
163
- - !ruby/object:Gem::Version
164
- version: 6.4.3
165
- type: :development
166
- prerelease: false
167
- version_requirements: !ruby/object:Gem::Requirement
168
- requirements:
169
- - - "~>"
170
- - !ruby/object:Gem::Version
171
- version: '6.4'
172
- - - ">="
173
- - !ruby/object:Gem::Version
174
- version: 6.4.3
175
- - !ruby/object:Gem::Dependency
176
- name: faker
177
- requirement: !ruby/object:Gem::Requirement
178
- requirements:
179
- - - "~>"
180
- - !ruby/object:Gem::Version
181
- version: '3.4'
182
- - - ">="
183
- - !ruby/object:Gem::Version
184
- version: 3.4.1
185
- type: :development
186
- prerelease: false
187
- version_requirements: !ruby/object:Gem::Requirement
188
- requirements:
189
- - - "~>"
190
- - !ruby/object:Gem::Version
191
- version: '3.4'
192
- - - ">="
193
- - !ruby/object:Gem::Version
194
- version: 3.4.1
195
- - !ruby/object:Gem::Dependency
196
- name: mocha
197
- requirement: !ruby/object:Gem::Requirement
198
- requirements:
199
- - - "~>"
200
- - !ruby/object:Gem::Version
201
- version: '2.4'
202
- type: :development
203
- prerelease: false
204
- version_requirements: !ruby/object:Gem::Requirement
205
- requirements:
206
- - - "~>"
207
- - !ruby/object:Gem::Version
208
- version: '2.4'
209
- - !ruby/object:Gem::Dependency
210
- name: rubocop
211
- requirement: !ruby/object:Gem::Requirement
212
- requirements:
213
- - - "~>"
214
- - !ruby/object:Gem::Version
215
- version: '1.60'
216
- - - ">="
217
- - !ruby/object:Gem::Version
218
- version: 1.60.2
219
- type: :development
220
- prerelease: false
221
- version_requirements: !ruby/object:Gem::Requirement
222
- requirements:
223
- - - "~>"
224
- - !ruby/object:Gem::Version
225
- version: '1.60'
226
- - - ">="
227
- - !ruby/object:Gem::Version
228
- version: 1.60.2
229
121
  description: |2
230
122
  LoggableActivity is a powerful gem for Ruby on Rails that provides seamless user activity logging
231
123
  prepared for GDPR compliance and supporting record relations. It allows you to effortlessly
@@ -245,6 +137,7 @@ files:
245
137
  - CHANGELOG.md
246
138
  - CHEAT_SHEET.md
247
139
  - CODE_OF_CONDUCT.md
140
+ - GETTING-STARTED.md
248
141
  - MIT-LICENSE
249
142
  - README.md
250
143
  - Rakefile
@@ -298,6 +191,11 @@ files:
298
191
  - git-org/hooks/push-to-checkout.sample
299
192
  - git-org/hooks/update.sample
300
193
  - git-org/info/exclude
194
+ - lib/generators/loggable_activity/install/install_generator.rb
195
+ - lib/generators/loggable_activity/install/templates/create_loggable_activities.rb
196
+ - lib/generators/loggable_activity/install/templates/loggable_activity.en.yml
197
+ - lib/generators/loggable_activity/install/templates/loggable_activity.rb
198
+ - lib/generators/loggable_activity/install/templates/loggable_activity.yml
301
199
  - lib/loggable_activity.rb
302
200
  - lib/loggable_activity/.DS_Store
303
201
  - lib/loggable_activity/activity.rb
@@ -313,6 +211,7 @@ files:
313
211
  - lib/loggable_activity/payload.rb
314
212
  - lib/loggable_activity/sanitizer.rb
315
213
  - lib/loggable_activity/services/base_payloads_builder.rb
214
+ - lib/loggable_activity/services/custom_payloads_builder.rb
316
215
  - lib/loggable_activity/services/destroy_payloads_builder.rb
317
216
  - lib/loggable_activity/services/payloads_builder.rb
318
217
  - lib/loggable_activity/services/update_payloads_builder.rb