Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move audit log to database #96

Merged
merged 11 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions app/interactors/application_interactor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ class ApplicationInteractor
include Interactor

def audit_log
return if context.identity.nil?
result = context.success? ? "success" : "failure"
level = context.success? ? :info : :error
payload = {
request_id: Thread.current[:request_id],
action: "#{self.class.name}",
result: result,
error: context.error&.message,
subject: context.identity&.subject,
cert_common_name: context.request&.try(:common_name),
kv_path: context.request&.try(:kv_path)
}.compact!
AuditLogger.new.send(level, payload)
AuditLog.create!(payload)
end
end
6 changes: 0 additions & 6 deletions app/lib/audit_logger.rb

This file was deleted.

17 changes: 0 additions & 17 deletions app/lib/json_log_formatter.rb

This file was deleted.

7 changes: 7 additions & 0 deletions app/models/audit_log.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class AuditLog < ApplicationRecord
validates :request_id, :action, :result, :subject, presence: true

if Config[:db_encryption]
encrypts :request_id, :action, :result, :error, :subject, :cert_common_name, :kv_path
end
end
3 changes: 0 additions & 3 deletions config/astral.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@ shared:
app_registry_client_cert:
app_registry_client_key:

# User activity logging
audit_log_file: <%= "#{Rails.root.join('log')}/astral-audit.log" %>

oidc_client_id:
oidc_client_secret:
oidc_redirect_uris: http://localhost:8250/oidc/callback
Expand Down
15 changes: 15 additions & 0 deletions db/migrate/20241120172746_create_audit_logs.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class CreateAuditLogs < ActiveRecord::Migration[7.2]
def change
create_table :audit_logs do |t|
t.string :request_id, null: false
t.string :action, null: false
t.string :result, null: false
t.string :error, null: true
t.string :subject, null: false
t.string :cert_common_name, null: true
t.string :kv_path, null: true
t.timestamps
end
add_index :audit_logs, [ :subject, :created_at ]
end
end
15 changes: 14 additions & 1 deletion db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 7 additions & 9 deletions test/interactors/application_interactor_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,7 @@ def setup
@domain = domains(:owner_match)
@identity = Identity.new(subject: @domain.users.first)
@cr = Requests::CertIssueRequest.new(common_name: @domain.fqdn)
@log = Tempfile.new("log-test")
Config[:audit_log_file] = @log.path
end

def teardown
@log.close
@log.unlink
Thread.current[:request_id] = "request_id"
end

test ".call will be logged as success" do
Expand All @@ -23,7 +17,9 @@ def call
end)
rslt = SuccessAction.call(identity: @identity, request: @cr)
assert rslt.success?
assert_match %Q("action":"SuccessAction","result":"success","subject":"[email protected]","cert_common_name":"example.com"), @log.readlines.last
log = AuditLog.last
expected = { "action"=>"SuccessAction", "result"=>"success", "subject"=>"[email protected]", "cert_common_name"=>"example.com" }
assert expected <= log.attributes
end

test ".call will be logged as failure" do
Expand All @@ -36,6 +32,8 @@ def call
end)
rslt = FailAction.call(identity: @identity, request: @cr)
assert_not rslt.success?
assert_match %Q("action":"FailAction","result":"failure","subject":"[email protected]","cert_common_name":"example.com"), @log.readlines.last
log = AuditLog.last
expected = { "action"=>"FailAction", "result"=>"failure", "subject"=>"[email protected]", "cert_common_name"=>"example.com" }
assert expected <= log.attributes
end
end
1 change: 1 addition & 0 deletions test/interactors/authenticate_identity_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ class AuthenticateIdentityTest < ActiveSupport::TestCase
def setup
@interactor = AuthenticateIdentity
@identity = Identity.new(subject: "[email protected]", groups: [ "admin_group" ])
Thread.current[:request_id] = "request_id"
end

test ".call success" do
Expand Down
1 change: 1 addition & 0 deletions test/interactors/authorize_cert_request_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ def setup
@identity = Identity.new(subject: @domain.users.first)
@cr = Requests::CertIssueRequest.new(common_name: @domain.fqdn)
@interactor = AuthorizeCertRequest
Thread.current[:request_id] = "request_id"
end

test ".call with matching owner" do
Expand Down
3 changes: 3 additions & 0 deletions test/interactors/obtain_cert_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ class ObtainCertTest < ActiveSupport::TestCase
def setup
@interactor = ObtainCert
@cert = OpenStruct.new(certificate: "certificate", ca_chain: "ca_chain")
Thread.current[:request_id] = "request_id"
end

test ".call success" do
request = Requests::CertIssueRequest.new
identity = Identity.new
identity.sub = SecureRandom.hex(4)
mock = Minitest::Mock.new
mock.expect :call, @cert, [ identity, request ]
Services::Certificate.stub :issue_cert, mock do
Expand All @@ -21,6 +23,7 @@ def setup
test ".call failure" do
request = Requests::CertIssueRequest.new
identity = Identity.new
identity.sub = SecureRandom.hex(4)
mock = Minitest::Mock.new
mock.expect :call, nil, [ identity, request ]
Services::Certificate.stub :issue_cert, mock do
Expand Down
28 changes: 0 additions & 28 deletions test/lib/json_log_formatter_test.rb

This file was deleted.

30 changes: 30 additions & 0 deletions test/models/audit_log_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
require "test_helper"

class AuditLogTest < ActiveSupport::TestCase
def setup
@attributes = {
request_id: "uuid1",
action: "string1",
result: "string2",
subject: "string3",
cert_common_name: "string4"
}
@audit_log = AuditLog.new(@attributes)
end

test "#new should set attributes from attributes argument" do
@attributes.each do |key, value|
assert_equal value, @audit_log.send(key), "Attribute #{key} was not set correctly"
end
end

test "#valid? should be valid with valid attributes" do
assert @audit_log.valid?
end

test "#valid? should require an result" do
@audit_log.result = nil
assert_not @audit_log.valid?
assert_includes @audit_log.errors[:result], "can't be blank"
end
end