feat: Audit log APIs (#6434)
- Adds the appropriate APIs for Audit Logs. ref: #6015
This commit is contained in:
2
Gemfile
2
Gemfile
@@ -205,6 +205,8 @@ end
|
||||
# worked with microsoft refresh token
|
||||
gem 'omniauth-oauth2'
|
||||
|
||||
gem 'audited', '~> 5.2'
|
||||
|
||||
# need for google auth
|
||||
gem 'omniauth'
|
||||
gem 'omniauth-google-oauth2'
|
||||
|
||||
@@ -90,6 +90,8 @@ GEM
|
||||
rake (>= 10.4, < 14.0)
|
||||
ast (2.4.2)
|
||||
attr_extras (6.2.5)
|
||||
audited (5.2.0)
|
||||
activerecord (>= 5.0, < 7.1)
|
||||
aws-eventstream (1.2.0)
|
||||
aws-partitions (1.605.0)
|
||||
aws-sdk-core (3.131.2)
|
||||
@@ -763,6 +765,7 @@ DEPENDENCIES
|
||||
administrate
|
||||
annotate
|
||||
attr_extras
|
||||
audited (~> 5.2)
|
||||
aws-sdk-s3
|
||||
azure-storage-blob
|
||||
barnes
|
||||
|
||||
@@ -155,3 +155,4 @@ class Account < ApplicationRecord
|
||||
end
|
||||
|
||||
Account.prepend_mod_with('Account')
|
||||
Account.include_mod_with('Audit::Account')
|
||||
|
||||
@@ -76,3 +76,5 @@ class AutomationRule < ApplicationRecord
|
||||
errors.add(:conditions, 'Automation conditions should have query operator.') if operators.length > 1
|
||||
end
|
||||
end
|
||||
|
||||
AutomationRule.include_mod_with('Audit::Inbox')
|
||||
|
||||
@@ -158,3 +158,4 @@ class Inbox < ApplicationRecord
|
||||
end
|
||||
|
||||
Inbox.prepend_mod_with('Inbox')
|
||||
Inbox.include_mod_with('Audit::Inbox')
|
||||
|
||||
@@ -37,3 +37,5 @@ class Webhook < ApplicationRecord
|
||||
errors.add(:subscriptions, I18n.t('errors.webhook.invalid')) if invalid_subscriptions
|
||||
end
|
||||
end
|
||||
|
||||
Webhook.include_mod_with('Audit::Inbox')
|
||||
|
||||
5
config/initializers/audited.rb
Normal file
5
config/initializers/audited.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
# configuration related audited gem : https://github.com/collectiveidea/audited
|
||||
|
||||
Audited.config do |config|
|
||||
config.audit_class = 'Enterprise::AuditLog'
|
||||
end
|
||||
@@ -45,6 +45,7 @@ Rails.application.routes.draw do
|
||||
resources :agents, only: [:index, :create, :update, :destroy]
|
||||
resources :agent_bots, only: [:index, :create, :show, :update, :destroy]
|
||||
resources :assignable_agents, only: [:index]
|
||||
resource :audit_logs, only: [:show]
|
||||
resources :callbacks, only: [] do
|
||||
collection do
|
||||
post :register_facebook_page
|
||||
|
||||
34
db/migrate/20230202132107_install_audited.rb
Normal file
34
db/migrate/20230202132107_install_audited.rb
Normal file
@@ -0,0 +1,34 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class InstallAudited < ActiveRecord::Migration[6.1]
|
||||
# rubocop:disable RMetrics/MethodLength
|
||||
def self.up
|
||||
create_table :audits, :force => true do |t|
|
||||
t.bigint :auditable_id
|
||||
t.string :auditable_type
|
||||
t.bigint :associated_id
|
||||
t.string :associated_type
|
||||
t.bigint :user_id
|
||||
t.string :user_type
|
||||
t.string :username
|
||||
t.string :action
|
||||
t.jsonb :audited_changes
|
||||
t.integer :version, :integer, :default => 0
|
||||
t.string :comment
|
||||
t.string :remote_address
|
||||
t.string :request_uuid
|
||||
t.datetime :created_at
|
||||
end
|
||||
# rubocop:enable RMetrics/MethodLength
|
||||
|
||||
add_index :audits, [:auditable_type, :auditable_id, :version], :name => 'auditable_index'
|
||||
add_index :audits, [:associated_type, :associated_id], :name => 'associated_index'
|
||||
add_index :audits, [:user_id, :user_type], :name => 'user_index'
|
||||
add_index :audits, :request_uuid
|
||||
add_index :audits, :created_at
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :audits
|
||||
end
|
||||
end
|
||||
22
db/schema.rb
22
db/schema.rb
@@ -153,6 +153,28 @@ ActiveRecord::Schema.define(version: 2023_02_24_124632) do
|
||||
t.index ["message_id"], name: "index_attachments_on_message_id"
|
||||
end
|
||||
|
||||
create_table 'audits', force: :cascade do |t|
|
||||
t.bigint 'auditable_id'
|
||||
t.string 'auditable_type'
|
||||
t.bigint 'associated_id'
|
||||
t.string 'associated_type'
|
||||
t.bigint 'user_id'
|
||||
t.string 'user_type'
|
||||
t.string 'username'
|
||||
t.string 'action'
|
||||
t.jsonb 'audited_changes'
|
||||
t.integer 'version', default: 0
|
||||
t.string 'comment'
|
||||
t.string 'remote_address'
|
||||
t.string 'request_uuid'
|
||||
t.datetime 'created_at'
|
||||
t.index %w[associated_type associated_id], name: 'associated_index'
|
||||
t.index %w[auditable_type auditable_id version], name: 'auditable_index'
|
||||
t.index ['created_at'], name: 'index_audits_on_created_at'
|
||||
t.index ['request_uuid'], name: 'index_audits_on_request_uuid'
|
||||
t.index %w[user_id user_type], name: 'user_index'
|
||||
end
|
||||
|
||||
create_table "automation_rules", force: :cascade do |t|
|
||||
t.bigint "account_id", null: false
|
||||
t.string "name", null: false
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# module Enterprise::Api::V1::Accounts::AuditLogsController < Api::V1::Accounts::BaseController
|
||||
class Api::V1::Accounts::AuditLogsController < Api::V1::Accounts::BaseController
|
||||
before_action :check_admin_authorization?
|
||||
before_action :fetch_audit
|
||||
|
||||
def show
|
||||
render json: @audit_logs
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def fetch_audit
|
||||
@audit_logs = Current.account.associated_audits
|
||||
end
|
||||
end
|
||||
7
enterprise/app/models/enterprise/audit/account.rb
Normal file
7
enterprise/app/models/enterprise/audit/account.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
module Enterprise::Audit::Account
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
has_associated_audits
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,7 @@
|
||||
module Enterprise::Audit::AutomationRule
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
audited associated_with: :account
|
||||
end
|
||||
end
|
||||
7
enterprise/app/models/enterprise/audit/inbox.rb
Normal file
7
enterprise/app/models/enterprise/audit/inbox.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
module Enterprise::Audit::Inbox
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
audited associated_with: :account
|
||||
end
|
||||
end
|
||||
7
enterprise/app/models/enterprise/audit/webhook.rb
Normal file
7
enterprise/app/models/enterprise/audit/webhook.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
module Enterprise::Audit::Webhook
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
audited associated_with: :account
|
||||
end
|
||||
end
|
||||
11
enterprise/app/models/enterprise/audit_log.rb
Normal file
11
enterprise/app/models/enterprise/audit_log.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
class Enterprise::AuditLog < Audited::Audit
|
||||
after_save :log_additional_information
|
||||
|
||||
private
|
||||
|
||||
def log_additional_information
|
||||
# rubocop:disable Rails/SkipsModelValidations
|
||||
update_columns(username: user&.email)
|
||||
# rubocop:enable Rails/SkipsModelValidations
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,44 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Enterprise Audit API', type: :request do
|
||||
let!(:account) { create(:account) }
|
||||
let!(:inbox) { create(:inbox, account: account) }
|
||||
let!(:admin) { create(:user, account: account, role: :administrator) }
|
||||
|
||||
describe 'GET /api/v1/accounts/{account.id}/audit_logs' do
|
||||
context 'when it is an un-authenticated user' do
|
||||
it 'does not fetch audit logs associated with the account' do
|
||||
get "/api/v1/accounts/#{account.id}/audit_logs",
|
||||
as: :json
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it is an authenticated normal user' do
|
||||
let(:user) { create(:user, account: account) }
|
||||
|
||||
it 'fetches audit logs associated with the account' do
|
||||
get "/api/v1/accounts/#{account.id}/audit_logs",
|
||||
headers: user.create_new_auth_token,
|
||||
as: :json
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
|
||||
# check for response in parse
|
||||
context 'when it is an authenticated admin user' do
|
||||
it 'fetches audit logs associated with the account' do
|
||||
get "/api/v1/accounts/#{account.id}/audit_logs",
|
||||
headers: admin.create_new_auth_token,
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
json_response = JSON.parse(response.body)
|
||||
expect(json_response[0]['auditable_type']).to eql('Inbox')
|
||||
expect(json_response[0]['action']).to eql('create')
|
||||
expect(json_response[0]['audited_changes']['name']).to eql(inbox.name)
|
||||
expect(json_response[0]['associated_id']).to eql(account.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -10,6 +10,13 @@ RSpec.describe Account do
|
||||
|
||||
let!(:account) { create(:account) }
|
||||
|
||||
describe 'audit logs' do
|
||||
it 'returns audit logs' do
|
||||
# checking whether associated_audits method is present
|
||||
expect(account.associated_audits.present?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns max limits from global config when enterprise version' do
|
||||
expect(account.usage_limits).to eq(
|
||||
{
|
||||
|
||||
29
spec/enterprise/models/automation_rule_spec.rb
Normal file
29
spec/enterprise/models/automation_rule_spec.rb
Normal file
@@ -0,0 +1,29 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe AutomationRule do
|
||||
let!(:automation_rule) { create(:automation_rule, name: 'automation rule 1') }
|
||||
|
||||
describe 'audit log' do
|
||||
context 'when automation rule is created' do
|
||||
it 'has associated audit log created' do
|
||||
expect(Audited::Audit.where(auditable_type: 'AutomationRule', action: 'create').count).to eq 1
|
||||
end
|
||||
end
|
||||
|
||||
context 'when automation rule is updated' do
|
||||
it 'has associated audit log created' do
|
||||
automation_rule.update(name: 'automation rule 2')
|
||||
expect(Audited::Audit.where(auditable_type: 'AutomationRule', action: 'update').count).to eq 1
|
||||
end
|
||||
end
|
||||
|
||||
context 'when automation rule is deleted' do
|
||||
it 'has associated audit log created' do
|
||||
automation_rule.destroy!
|
||||
expect(Audited::Audit.where(auditable_type: 'AutomationRule', action: 'destroy').count).to eq 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -3,8 +3,9 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Inbox do
|
||||
let!(:inbox) { create(:inbox) }
|
||||
|
||||
describe 'member_ids_with_assignment_capacity' do
|
||||
let!(:inbox) { create(:inbox) }
|
||||
let!(:inbox_member_1) { create(:inbox_member, inbox: inbox) }
|
||||
let!(:inbox_member_2) { create(:inbox_member, inbox: inbox) }
|
||||
let!(:inbox_member_3) { create(:inbox_member, inbox: inbox) }
|
||||
@@ -35,4 +36,26 @@ RSpec.describe Inbox do
|
||||
expect(inbox.member_ids_with_assignment_capacity).to eq(inbox.members.ids)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'audit log' do
|
||||
context 'when inbox is created' do
|
||||
it 'has associated audit log created' do
|
||||
expect(Audited::Audit.where(auditable_type: 'Inbox', action: 'create').count).to eq 1
|
||||
end
|
||||
end
|
||||
|
||||
context 'when inbox is updated' do
|
||||
it 'has associated audit log created' do
|
||||
inbox.update(auto_assignment_config: { max_assignment_limit: 2 })
|
||||
expect(Audited::Audit.where(auditable_type: 'Inbox', action: 'update').count).to eq 1
|
||||
end
|
||||
end
|
||||
|
||||
context 'when inbox is deleted' do
|
||||
it 'has associated audit log created' do
|
||||
inbox.destroy!
|
||||
expect(Audited::Audit.where(auditable_type: 'Inbox', action: 'destroy').count).to eq 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
30
spec/enterprise/models/webhook_spec.rb
Normal file
30
spec/enterprise/models/webhook_spec.rb
Normal file
@@ -0,0 +1,30 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Webhook do
|
||||
let(:account) { create(:account) }
|
||||
let!(:webhook) { create(:webhook, account: account) }
|
||||
|
||||
describe 'audit log' do
|
||||
context 'when webhook is created' do
|
||||
it 'has associated audit log created' do
|
||||
expect(Audited::Audit.where(auditable_type: 'Webhook', action: 'create').count).to eq 1
|
||||
end
|
||||
end
|
||||
|
||||
context 'when webhook is updated' do
|
||||
it 'has associated audit log created' do
|
||||
webhook.update(url: 'https://example.com')
|
||||
expect(Audited::Audit.where(auditable_type: 'Webhook', action: 'update').count).to eq 1
|
||||
end
|
||||
end
|
||||
|
||||
context 'when webhook is deleted' do
|
||||
it 'has associated audit log created' do
|
||||
webhook.destroy!
|
||||
expect(Audited::Audit.where(auditable_type: 'Webhook', action: 'destroy').count).to eq 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user