loggable_activity 0.5.4 → 0.5.6

Sign up to get free protection for your applications and to get access to all the features.
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