pg_audit_log 0.5.6 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.travis.yml +28 -0
- data/Gemfile +3 -5
- data/LICENSE +1 -1
- data/README.md +64 -0
- data/Rakefile +3 -8
- data/lib/pg_audit_log.rb +12 -11
- data/lib/pg_audit_log/entry.rb +3 -8
- data/lib/pg_audit_log/extensions/postgresql_adapter.rb +8 -15
- data/lib/pg_audit_log/function.rb +7 -7
- data/lib/pg_audit_log/triggers.rb +11 -13
- data/lib/pg_audit_log/version.rb +1 -1
- data/pg_audit_log.gemspec +22 -21
- data/spec/configuration_spec.rb +2 -3
- data/spec/function_spec.rb +9 -5
- data/spec/model_spec.rb +2 -3
- data/spec/pg_audit_log_spec.rb +74 -79
- data/spec/spec_helper.rb +17 -11
- data/spec/triggers_spec.rb +1 -3
- metadata +65 -50
- data/README.rdoc +0 -62
- data/init.rb +0 -1
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8f7c84ef450326cc585d0ffdf7963a07ba9e2341
|
4
|
+
data.tar.gz: a61e2f9ab3687d5e2afb14b6542adf56767c01af
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ce57db6aee49eb73c8b0e739a6be62281f361b79ddf2290108ceb7374d86b345c0342b81e527714b71b1ed0dda6dd1b6a047b23c643e91d2efd7221a31551ea1
|
7
|
+
data.tar.gz: a2e21fefdf61ea1ec1865d020c3c4f38382e8c4f1a664c2e28b5305e198656ae522fd82f528c410c00f03a6d6f069d4ea7fd1289beff0d8490c9157a960e89f2
|
data/.travis.yml
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
language: ruby
|
2
|
+
|
3
|
+
addons:
|
4
|
+
postgresql: '9.3'
|
5
|
+
|
6
|
+
before_script:
|
7
|
+
- psql -c 'CREATE DATABASE pg_audit_log_test;' -U postgres
|
8
|
+
|
9
|
+
rvm:
|
10
|
+
- 1.9.3
|
11
|
+
- 2.0.0
|
12
|
+
- 2.1.1
|
13
|
+
#- jruby-19mode
|
14
|
+
- rbx-2.2
|
15
|
+
|
16
|
+
env:
|
17
|
+
#- RAILS_BRANCH="master"
|
18
|
+
#- RAILS_BRANCH="4-1-stable"
|
19
|
+
#- RAILS_BRANCH="4-0-stable"
|
20
|
+
- RAILS_VERSION="~> 4.1.0"
|
21
|
+
- RAILS_VERSION="~> 4.0.0"
|
22
|
+
- RAILS_VERSION="~> 3.2.0"
|
23
|
+
|
24
|
+
matrix:
|
25
|
+
allow_failures:
|
26
|
+
- env: RAILS_BRANCH="master"
|
27
|
+
- env: RAILS_BRANCH="4-1-stable"
|
28
|
+
- env: RAILS_BRANCH="4-0-stable"
|
data/Gemfile
CHANGED
@@ -1,9 +1,7 @@
|
|
1
|
-
source
|
1
|
+
source 'https://rubygems.org'
|
2
2
|
|
3
3
|
# Specify your gem's dependencies the .gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
end
|
9
|
-
|
6
|
+
gem 'rails', :github => 'rails', :branch => ENV['RAILS_RECORD_BRANCH'] if ENV['ACTIVE_RECORD_BRANCH']
|
7
|
+
gem 'rails', ENV['RAILS_VERSION'] if ENV['RAILS_RECORD_VERSION']
|
data/LICENSE
CHANGED
data/README.md
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# pg_audit_log
|
2
|
+
|
3
|
+
[![Build Status](https://secure.travis-ci.org/Casecommons/pg_audit_log.png?branch=master)](https://travis-ci.org/Casecommons/pg_audit_log)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/Casecommons/pg_audit_log.png)](https://codeclimate.com/github/Casecommons/pg_audit_log)
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/pg_audit_log.png)](https://rubygems.org/gems/pg_audit_log)
|
6
|
+
|
7
|
+
## Description
|
8
|
+
|
9
|
+
PostgreSQL-only database-level audit logging of all databases changes using a completely transparent stored procedure and triggers.
|
10
|
+
Comes with specs for your project and a rake task to generate the reverse SQL to undo changes logged.
|
11
|
+
|
12
|
+
All SQL `INSERT`s, `UPDATE`s, and `DELETE`s will be captured. Record columns that do not change do not generate an audit log entry.
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
- First, enable plpgsql langauges in your postgresql instance. Execute the following as a superuser in postgres make sure your database has plpgsql enabled:
|
17
|
+
|
18
|
+
CREATE OR REPLACE PROCEDURAL LANGUAGE plpgsql;
|
19
|
+
|
20
|
+
- Generate the appropriate Rails files:
|
21
|
+
|
22
|
+
rails generate pg_audit_log:install
|
23
|
+
|
24
|
+
- Install the PostgreSQL function and triggers for your project:
|
25
|
+
|
26
|
+
rake pg_audit_log:install
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
The PgAuditLog::Entry ActiveRecord model represents a single entry in the audit log table. Each entry represents a single change to a single field of a record in a table. So if you change 3 columns of a record, that will generate 3 corresponding PgAuditLog::Entry records.
|
31
|
+
|
32
|
+
You can see the SQL it injects on every query by running with LOG_AUDIT_SQL
|
33
|
+
|
34
|
+
### Migrations
|
35
|
+
|
36
|
+
TODO
|
37
|
+
|
38
|
+
### schema.rb and development_structure.sql
|
39
|
+
|
40
|
+
Since schema.rb cannot represent TRIGGERs or FUNCTIONs you will need to set your environment to generate SQL instead of Ruby for your database schema and structure. In your application environment put the following:
|
41
|
+
|
42
|
+
config.active_record.schema_format = :sql
|
43
|
+
|
44
|
+
And you can generate this sql using:
|
45
|
+
|
46
|
+
rake db:structure:dump
|
47
|
+
|
48
|
+
## Uninstalling
|
49
|
+
|
50
|
+
rake pg_audit_log:uninstall
|
51
|
+
|
52
|
+
## Performance
|
53
|
+
|
54
|
+
On a 2.93GHz i7 with PostgreSQL 9.1 the audit log has an overhead of about 0.0035 seconds to each `INSERT`, `UPDATE`, or `DELETE`.
|
55
|
+
|
56
|
+
## Requirements
|
57
|
+
|
58
|
+
- ActiveRecord
|
59
|
+
- PostgreSQL
|
60
|
+
- Rails 3.2
|
61
|
+
|
62
|
+
## LICENSE
|
63
|
+
|
64
|
+
Copyright © 2010–2014 Case Commons, LLC. Licensed under the MIT license, available in the “LICENSE” file.
|
data/Rakefile
CHANGED
@@ -1,12 +1,7 @@
|
|
1
|
-
require '
|
2
|
-
require 'rspec/core'
|
1
|
+
require 'bundler/gem_tasks'
|
3
2
|
require 'rspec/core/rake_task'
|
4
3
|
|
5
|
-
|
6
|
-
|
4
|
+
desc 'Run specs'
|
5
|
+
RSpec::Core::RakeTask.new
|
7
6
|
|
8
7
|
task :default => :spec
|
9
|
-
|
10
|
-
desc "Run all specs in spec directory (excluding plugin specs)"
|
11
|
-
RSpec::Core::RakeTask.new(:spec)
|
12
|
-
|
data/lib/pg_audit_log.rb
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
module PgAuditLog
|
2
|
-
IGNORED_TABLES = [
|
2
|
+
IGNORED_TABLES = [
|
3
|
+
'plugin_schema_migrations'.freeze,
|
4
|
+
'sessions'.freeze,
|
5
|
+
'schema_migrations'.freeze,
|
6
|
+
]
|
3
7
|
end
|
4
8
|
|
5
|
-
require
|
6
|
-
require
|
7
|
-
|
8
|
-
raise "ActiveRecord #{::ActiveRecord::VERSION::MAJOR}.x unsupported!" unless ::ActiveRecord::VERSION::MAJOR == 3
|
9
|
-
|
10
|
-
require "pg_audit_log/extensions/postgresql_adapter.rb"
|
11
|
-
require "pg_audit_log/active_record"
|
12
|
-
require "pg_audit_log/entry"
|
13
|
-
require "pg_audit_log/function"
|
14
|
-
require "pg_audit_log/triggers"
|
9
|
+
require 'active_record'
|
10
|
+
require 'pg_audit_log/version'
|
15
11
|
|
12
|
+
require 'pg_audit_log/extensions/postgresql_adapter.rb'
|
13
|
+
require 'pg_audit_log/active_record'
|
14
|
+
require 'pg_audit_log/entry'
|
15
|
+
require 'pg_audit_log/function'
|
16
|
+
require 'pg_audit_log/triggers'
|
data/lib/pg_audit_log/entry.rb
CHANGED
@@ -1,14 +1,10 @@
|
|
1
1
|
class PgAuditLog::Entry < ActiveRecord::Base
|
2
|
-
TABLE_NAME =
|
3
|
-
|
4
|
-
self.table_name = TABLE_NAME
|
5
|
-
else
|
6
|
-
set_table_name TABLE_NAME
|
7
|
-
end
|
2
|
+
TABLE_NAME = 'audit_log'.freeze
|
3
|
+
self.table_name = TABLE_NAME
|
8
4
|
|
9
5
|
class CannotDeleteError < StandardError
|
10
6
|
def message
|
11
|
-
|
7
|
+
'Audit Logs cannot be deleted!'
|
12
8
|
end
|
13
9
|
end
|
14
10
|
|
@@ -90,5 +86,4 @@ class PgAuditLog::Entry < ActiveRecord::Base
|
|
90
86
|
raise CannotDeleteError
|
91
87
|
end
|
92
88
|
end
|
93
|
-
|
94
89
|
end
|
@@ -1,16 +1,12 @@
|
|
1
|
-
require
|
1
|
+
require 'active_record/connection_adapters/postgresql_adapter'
|
2
2
|
|
3
3
|
# Did not want to reopen the class but sending an include seemingly is not working.
|
4
4
|
class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
|
5
|
-
def drop_table_with_auditing(table_name
|
5
|
+
def drop_table_with_auditing(table_name)
|
6
6
|
if PgAuditLog::Triggers.tables_with_triggers.include?(table_name)
|
7
7
|
PgAuditLog::Triggers.drop_for_table(table_name)
|
8
8
|
end
|
9
|
-
|
10
|
-
drop_table_without_auditing(table_name)
|
11
|
-
else
|
12
|
-
drop_table_without_auditing(table_name, options)
|
13
|
-
end
|
9
|
+
drop_table_without_auditing(table_name)
|
14
10
|
end
|
15
11
|
alias_method_chain :drop_table, :auditing
|
16
12
|
|
@@ -69,14 +65,12 @@ class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
|
|
69
65
|
end
|
70
66
|
alias_method_chain :execute, :pg_audit_log
|
71
67
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
conn
|
77
|
-
end
|
78
|
-
alias_method_chain :exec_query, :pg_audit_log
|
68
|
+
def exec_query_with_pg_audit_log(sql, name = 'SQL', binds = [])
|
69
|
+
conn = exec_query_without_pg_audit_log(sql, name, binds)
|
70
|
+
set_audit_user_id_and_name
|
71
|
+
conn
|
79
72
|
end
|
73
|
+
alias_method_chain :exec_query, :pg_audit_log
|
80
74
|
|
81
75
|
private
|
82
76
|
|
@@ -86,5 +80,4 @@ class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
|
|
86
80
|
user_unique_name = current_user.try(:unique_name) || "UNKNOWN"
|
87
81
|
return [user_id, user_unique_name]
|
88
82
|
end
|
89
|
-
|
90
83
|
end
|
@@ -4,23 +4,23 @@ module PgAuditLog
|
|
4
4
|
|
5
5
|
class << self
|
6
6
|
def name
|
7
|
-
|
7
|
+
'audit_changes'
|
8
8
|
end
|
9
9
|
|
10
10
|
def users_table_name
|
11
|
-
|
11
|
+
'users'
|
12
12
|
end
|
13
13
|
|
14
14
|
def user_id_field
|
15
|
-
|
15
|
+
'user_id'
|
16
16
|
end
|
17
17
|
|
18
18
|
def user_name_field
|
19
|
-
|
19
|
+
'user_unique_name'
|
20
20
|
end
|
21
21
|
|
22
22
|
def users_access_column
|
23
|
-
|
23
|
+
'last_accessed_at'
|
24
24
|
end
|
25
25
|
|
26
26
|
def pg_audit_log_old_style_user_id
|
@@ -59,11 +59,11 @@ module PgAuditLog
|
|
59
59
|
unique_name varchar;
|
60
60
|
column_name varchar;
|
61
61
|
BEGIN
|
62
|
-
user_identifier := #{pg_audit_log_old_style_user_id ?
|
62
|
+
user_identifier := #{pg_audit_log_old_style_user_id ? %q(current_setting('audit.user_id')) : 'pg_temp.pg_audit_log_user_identifier()'};
|
63
63
|
IF user_identifier = #{DISABLED_USER} THEN
|
64
64
|
RETURN NULL;
|
65
65
|
END IF;
|
66
|
-
unique_name := #{pg_audit_log_old_style_user_id ?
|
66
|
+
unique_name := #{pg_audit_log_old_style_user_id ? %q(current_setting('audit.user_unique_name')) : 'pg_temp.pg_audit_log_user_unique_name()'};
|
67
67
|
primary_key_column := NULL;
|
68
68
|
EXECUTE 'SELECT pg_attribute.attname
|
69
69
|
FROM pg_index, pg_class, pg_attribute
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module PgAuditLog
|
2
2
|
class Triggers < PgAuditLog::ActiveRecord
|
3
|
-
class MissingTriggers <
|
3
|
+
class MissingTriggers < StandardError
|
4
4
|
def initialize(tables)
|
5
5
|
@tables = tables
|
6
6
|
end
|
@@ -57,12 +57,10 @@ module PgAuditLog
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def without_triggers
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
enable
|
65
|
-
end
|
60
|
+
disable
|
61
|
+
yield
|
62
|
+
ensure
|
63
|
+
enable
|
66
64
|
end
|
67
65
|
|
68
66
|
def create_for_table(table_name)
|
@@ -70,11 +68,11 @@ module PgAuditLog
|
|
70
68
|
PgAuditLog::Function.install unless PgAuditLog::Function.installed?
|
71
69
|
return if tables_with_triggers.include?(table_name)
|
72
70
|
execute <<-SQL
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
71
|
+
CREATE TRIGGER #{trigger_name_for_table(table_name)}
|
72
|
+
AFTER INSERT OR UPDATE OR DELETE
|
73
|
+
ON #{table_name}
|
74
|
+
FOR EACH ROW
|
75
|
+
EXECUTE PROCEDURE #{PgAuditLog::Function.name}()
|
78
76
|
SQL
|
79
77
|
end
|
80
78
|
|
@@ -92,7 +90,7 @@ module PgAuditLog
|
|
92
90
|
end
|
93
91
|
|
94
92
|
def trigger_prefix
|
95
|
-
|
93
|
+
'audit_'
|
96
94
|
end
|
97
95
|
|
98
96
|
def trigger_name_for_table(table_name)
|
data/lib/pg_audit_log/version.rb
CHANGED
data/pg_audit_log.gemspec
CHANGED
@@ -1,26 +1,27 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
$:.push File.expand_path(
|
3
|
-
require
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'pg_audit_log/version'
|
4
4
|
|
5
|
-
Gem::Specification.new do |
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
s.post_install_message = %q{Please run PgAuditLog::Function.install (in console/migration) to install the new versions of the database functions}
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'pg_audit_log'
|
7
|
+
spec.version = PgAuditLog::VERSION
|
8
|
+
spec.authors = ['Case Commons, LLC']
|
9
|
+
spec.email = ['casecommons-dev@googlegroups.com']
|
10
|
+
spec.homepage = 'https://github.com/Casecommons/pg_audit_log'
|
11
|
+
spec.summary = %q{PostgreSQL-only database-level audit logging of all databases changes.}
|
12
|
+
spec.description = %q{A completely transparent audit logging component for your application using a stored procedure and triggers. Comes with specs for your project and a rake task to generate the reverse SQL to undo changes logged.}
|
13
|
+
spec.license = 'MIT'
|
15
14
|
|
16
|
-
|
17
|
-
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
-
s.require_paths = ["lib"]
|
15
|
+
spec.post_install_message = %q{Please run PgAuditLog::Function.install (in console/migration) to install the new versions of the database functions}
|
20
16
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
17
|
+
spec.files = `git ls-files`.split($/)
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ['lib']
|
21
|
+
|
22
|
+
spec.add_dependency 'rails', '>= 3.2', '< 4.2'
|
23
|
+
spec.add_dependency 'pg', '>= 0.9.0'
|
24
|
+
spec.add_development_dependency 'rspec'
|
25
|
+
spec.add_development_dependency 'rspec-rails'
|
26
|
+
spec.add_development_dependency 'with_model', '>= 0.1.3'
|
26
27
|
end
|
data/spec/configuration_spec.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require 'spec_helper'
|
2
2
|
|
3
3
|
describe "the PostgreSQL database" do
|
4
4
|
after do
|
@@ -6,7 +6,6 @@ describe "the PostgreSQL database" do
|
|
6
6
|
end
|
7
7
|
|
8
8
|
it "has an audit log table" do
|
9
|
-
ActiveRecord::Base.connection.table_exists?(
|
9
|
+
ActiveRecord::Base.connection.table_exists?('audit_log').should be_true
|
10
10
|
end
|
11
|
-
|
12
11
|
end
|
data/spec/function_spec.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require 'spec_helper'
|
2
2
|
|
3
3
|
describe PgAuditLog::Function do
|
4
4
|
describe ".installed?" do
|
@@ -7,6 +7,7 @@ describe PgAuditLog::Function do
|
|
7
7
|
before do
|
8
8
|
PgAuditLog::Function.uninstall
|
9
9
|
end
|
10
|
+
|
10
11
|
it { should be_false }
|
11
12
|
end
|
12
13
|
|
@@ -14,6 +15,7 @@ describe PgAuditLog::Function do
|
|
14
15
|
before do
|
15
16
|
PgAuditLog::Function.install
|
16
17
|
end
|
18
|
+
|
17
19
|
it { should be_true }
|
18
20
|
end
|
19
21
|
end
|
@@ -24,9 +26,9 @@ describe PgAuditLog::Function do
|
|
24
26
|
|
25
27
|
context "new style" do
|
26
28
|
it "escapes the email" do
|
27
|
-
subject.should_not match(
|
29
|
+
subject.should_not match('SET')
|
28
30
|
|
29
|
-
subject.should match(
|
31
|
+
subject.should match('FUNCTION')
|
30
32
|
subject.should match("'o''connell@fred.com'::varchar")
|
31
33
|
end
|
32
34
|
end
|
@@ -36,12 +38,14 @@ describe PgAuditLog::Function do
|
|
36
38
|
Rails = double
|
37
39
|
Rails.stub_chain(:configuration, :pg_audit_log_old_style_user_id).and_return(true)
|
38
40
|
end
|
41
|
+
|
39
42
|
after { Object.send(:remove_const, :Rails) }
|
43
|
+
|
40
44
|
it "escapes the email" do
|
41
|
-
subject.should match(
|
45
|
+
subject.should match('SET')
|
42
46
|
subject.should match("'o''connell@fred.com'")
|
43
47
|
|
44
|
-
subject.should_not match(
|
48
|
+
subject.should_not match('FUNCTION')
|
45
49
|
subject.should_not match("'o''connell@fred.com'::varchar")
|
46
50
|
end
|
47
51
|
end
|
data/spec/model_spec.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
|
-
require
|
1
|
+
require 'spec_helper'
|
2
2
|
|
3
3
|
describe PgAuditLog::Entry do
|
4
|
-
|
5
4
|
subject { PgAuditLog::Entry.create! }
|
6
5
|
|
7
6
|
describe ".delete" do
|
@@ -21,4 +20,4 @@ describe PgAuditLog::Entry do
|
|
21
20
|
proc { subject.destroy }.should raise_error(PgAuditLog::Entry::CannotDeleteError)
|
22
21
|
end
|
23
22
|
end
|
24
|
-
end
|
23
|
+
end
|
data/spec/pg_audit_log_spec.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require 'spec_helper'
|
2
2
|
|
3
3
|
describe PgAuditLog do
|
4
4
|
let(:connection) { ActiveRecord::Base.connection }
|
@@ -30,52 +30,51 @@ describe PgAuditLog do
|
|
30
30
|
PgAuditLog::Entry.connection.execute("TRUNCATE #{PgAuditLog::Entry.quoted_table_name}")
|
31
31
|
end
|
32
32
|
|
33
|
-
let(:attributes) { { :str =>
|
33
|
+
let(:attributes) { { :str => 'foo', :txt => 'bar', :int => 5, :date => Date.current, :dt => Time.current.midnight } }
|
34
34
|
|
35
35
|
describe "on create" do
|
36
36
|
context "the audit log record with a primary key" do
|
37
|
-
|
38
37
|
before do
|
39
38
|
AuditedModel.create!(attributes)
|
40
39
|
end
|
41
40
|
|
42
|
-
subject { PgAuditLog::Entry.
|
41
|
+
subject { PgAuditLog::Entry.where(:field_name => 'str').last }
|
43
42
|
|
44
43
|
it { should be }
|
45
44
|
its(:occurred_at) { should be }
|
46
45
|
its(:table_name) { should == AuditedModel.table_name }
|
47
|
-
its(:field_name) { should ==
|
46
|
+
its(:field_name) { should == 'str' }
|
48
47
|
its(:primary_key) { should == AuditedModel.last.id.to_s }
|
49
|
-
its(:operation) { should ==
|
48
|
+
its(:operation) { should == 'INSERT' }
|
50
49
|
|
51
50
|
context "when a user is present" do
|
52
51
|
before do
|
53
|
-
Thread.current[:current_user] =
|
52
|
+
Thread.current[:current_user] = double('User', :id => 1, :unique_name => 'my current user')
|
54
53
|
AuditedModel.create!
|
55
54
|
end
|
56
55
|
|
57
56
|
after { Thread.current[:current_user] = nil }
|
58
57
|
|
59
58
|
its(:user_id) { should == 1 }
|
60
|
-
its(:user_unique_name) { should ==
|
59
|
+
its(:user_unique_name) { should == 'my current user' }
|
61
60
|
end
|
62
61
|
|
63
62
|
context "when no user is present" do
|
64
63
|
its(:user_id) { should == -1 }
|
65
|
-
its(:user_unique_name) { should ==
|
64
|
+
its(:user_unique_name) { should == 'UNKNOWN' }
|
66
65
|
end
|
67
66
|
|
68
67
|
it "captures all new values for all fields" do
|
69
68
|
attributes.each do |field_name, value|
|
69
|
+
entry = PgAuditLog::Entry.where(:field_name => field_name).last
|
70
70
|
if field_name == :dt
|
71
|
-
|
71
|
+
entry.field_value_new.should == value.strftime("%Y-%m-%d %H:%M:%S")
|
72
72
|
else
|
73
|
-
|
73
|
+
entry.field_value_new.should == value.to_s
|
74
74
|
end
|
75
|
-
|
75
|
+
entry.field_value_old.should be_nil
|
76
76
|
end
|
77
77
|
end
|
78
|
-
|
79
78
|
end
|
80
79
|
|
81
80
|
context "the audit log record without a primary key" do
|
@@ -83,10 +82,10 @@ describe PgAuditLog do
|
|
83
82
|
AuditedModelWithoutPrimaryKey.create!(attributes)
|
84
83
|
end
|
85
84
|
|
86
|
-
subject { PgAuditLog::Entry.
|
85
|
+
subject { PgAuditLog::Entry.where(:field_name => 'str').last }
|
87
86
|
|
88
87
|
it { should be }
|
89
|
-
its(:field_name) { should ==
|
88
|
+
its(:field_name) { should == 'str' }
|
90
89
|
its(:primary_key) { should be_nil }
|
91
90
|
end
|
92
91
|
end
|
@@ -98,34 +97,34 @@ describe PgAuditLog do
|
|
98
97
|
end
|
99
98
|
|
100
99
|
context "when going from a value to a another value" do
|
101
|
-
before { @model.update_attributes!(:str =>
|
102
|
-
subject { PgAuditLog::Entry.
|
100
|
+
before { @model.update_attributes!(:str => 'bar') }
|
101
|
+
subject { PgAuditLog::Entry.where(:field_name => 'str').last }
|
103
102
|
|
104
|
-
its(:operation) { should ==
|
105
|
-
its(:field_value_new) { should ==
|
106
|
-
its(:field_value_old) { should ==
|
103
|
+
its(:operation) { should == 'UPDATE' }
|
104
|
+
its(:field_value_new) { should == 'bar' }
|
105
|
+
its(:field_value_old) { should == 'foo' }
|
107
106
|
end
|
108
107
|
|
109
108
|
context "when going from nil to a value" do
|
110
109
|
let(:attributes) { {:txt => nil} }
|
111
|
-
before { @model.update_attributes!(:txt =>
|
112
|
-
subject { PgAuditLog::Entry.
|
110
|
+
before { @model.update_attributes!(:txt => 'baz') }
|
111
|
+
subject { PgAuditLog::Entry.where(:field_name => 'txt').last }
|
113
112
|
|
114
|
-
its(:field_value_new) { should ==
|
113
|
+
its(:field_value_new) { should == 'baz' }
|
115
114
|
its(:field_value_old) { should be_nil }
|
116
115
|
end
|
117
116
|
|
118
117
|
context "when going from a value to nil" do
|
119
118
|
before { @model.update_attributes!(:str => nil) }
|
120
|
-
subject { PgAuditLog::Entry.
|
119
|
+
subject { PgAuditLog::Entry.where(:field_name => 'str').last }
|
121
120
|
|
122
121
|
its(:field_value_new) { should be_nil }
|
123
|
-
its(:field_value_old) { should ==
|
122
|
+
its(:field_value_old) { should == 'foo' }
|
124
123
|
end
|
125
124
|
|
126
125
|
context "when the value does not change" do
|
127
|
-
before { @model.update_attributes!(:str =>
|
128
|
-
subject { PgAuditLog::Entry.
|
126
|
+
before { @model.update_attributes!(:str => 'foo') }
|
127
|
+
subject { PgAuditLog::Entry.where(:field_name => 'str', :operation => 'UPDATE').last }
|
129
128
|
|
130
129
|
it { should_not be }
|
131
130
|
end
|
@@ -133,18 +132,17 @@ describe PgAuditLog do
|
|
133
132
|
context "when the value is nil and does not change" do
|
134
133
|
let(:attributes) { {:txt => nil} }
|
135
134
|
before { @model.update_attributes!(:txt => nil) }
|
136
|
-
subject { PgAuditLog::Entry.
|
135
|
+
subject { PgAuditLog::Entry.where(:field_name => 'txt', :operation => 'UPDATE').last }
|
137
136
|
|
138
137
|
it { should_not be }
|
139
138
|
end
|
140
139
|
|
141
140
|
context "when the value is a boolean" do
|
142
|
-
|
143
141
|
context "going from nil -> true" do
|
144
142
|
before { @model.update_attributes!(:bool => true) }
|
145
|
-
subject { PgAuditLog::Entry.
|
143
|
+
subject { PgAuditLog::Entry.where(:field_name => 'bool', :operation => 'UPDATE').last }
|
146
144
|
|
147
|
-
its(:field_value_new) { should ==
|
145
|
+
its(:field_value_new) { should == 'true' }
|
148
146
|
its(:field_value_old) { should be_nil }
|
149
147
|
end
|
150
148
|
|
@@ -153,10 +151,10 @@ describe PgAuditLog do
|
|
153
151
|
before do
|
154
152
|
@model.update_attributes!(:bool => true)
|
155
153
|
end
|
156
|
-
subject { PgAuditLog::Entry.
|
154
|
+
subject { PgAuditLog::Entry.where(:field_name => 'bool', :operation => 'UPDATE').last }
|
157
155
|
|
158
|
-
its(:field_value_new) { should ==
|
159
|
-
its(:field_value_old) { should ==
|
156
|
+
its(:field_value_new) { should == 'true' }
|
157
|
+
its(:field_value_old) { should == 'false' }
|
160
158
|
end
|
161
159
|
|
162
160
|
context "going from true -> false" do
|
@@ -165,48 +163,46 @@ describe PgAuditLog do
|
|
165
163
|
before do
|
166
164
|
@model.update_attributes!(:bool => false)
|
167
165
|
end
|
168
|
-
subject { PgAuditLog::Entry.
|
166
|
+
subject { PgAuditLog::Entry.where(:field_name => 'bool', :operation => 'UPDATE').last }
|
169
167
|
|
170
|
-
its(:field_value_new) { should ==
|
171
|
-
its(:field_value_old) { should ==
|
168
|
+
its(:field_value_new) { should == 'false' }
|
169
|
+
its(:field_value_old) { should == 'true' }
|
172
170
|
end
|
173
|
-
|
174
171
|
end
|
175
172
|
end
|
176
173
|
|
177
174
|
context "the audit log record without a primary key" do
|
178
175
|
before do
|
179
176
|
AuditedModelWithoutPrimaryKey.create!(attributes)
|
180
|
-
AuditedModelWithoutPrimaryKey.update_all(:str =>
|
177
|
+
AuditedModelWithoutPrimaryKey.update_all(:str => 'bar')
|
181
178
|
end
|
182
179
|
|
183
|
-
subject { PgAuditLog::Entry.
|
180
|
+
subject { PgAuditLog::Entry.where(:field_name => 'str').last }
|
184
181
|
|
185
182
|
its(:primary_key) { should be_nil }
|
186
183
|
end
|
187
|
-
|
188
184
|
end
|
189
185
|
|
190
186
|
describe "on delete" do
|
191
|
-
|
192
187
|
context "the audit log record with a primary key" do
|
193
188
|
before do
|
194
189
|
model = AuditedModel.create!(attributes)
|
195
190
|
model.delete
|
196
191
|
end
|
197
192
|
|
198
|
-
subject { PgAuditLog::Entry.
|
193
|
+
subject { PgAuditLog::Entry.where(:field_name => 'str').last }
|
199
194
|
|
200
|
-
its(:operation) { should ==
|
195
|
+
its(:operation) { should == 'DELETE' }
|
201
196
|
|
202
197
|
it "captures all new values for all fields" do
|
203
198
|
attributes.each do |field_name, value|
|
199
|
+
entry = PgAuditLog::Entry.where(:field_name => field_name).last
|
204
200
|
if field_name == :dt
|
205
|
-
|
201
|
+
entry.field_value_old.should == value.strftime('%Y-%m-%d %H:%M:%S')
|
206
202
|
else
|
207
|
-
|
203
|
+
entry.field_value_old.should == value.to_s
|
208
204
|
end
|
209
|
-
|
205
|
+
entry.field_value_new.should be_nil
|
210
206
|
end
|
211
207
|
end
|
212
208
|
end
|
@@ -217,7 +213,7 @@ describe PgAuditLog do
|
|
217
213
|
AuditedModelWithoutPrimaryKey.delete_all
|
218
214
|
end
|
219
215
|
|
220
|
-
subject { PgAuditLog::Entry.
|
216
|
+
subject { PgAuditLog::Entry.where(:field_name => 'str').last }
|
221
217
|
|
222
218
|
its(:primary_key) { should be_nil }
|
223
219
|
end
|
@@ -225,7 +221,7 @@ describe PgAuditLog do
|
|
225
221
|
|
226
222
|
describe "performance" do
|
227
223
|
xit "should perform well" do
|
228
|
-
require
|
224
|
+
require 'benchmark'
|
229
225
|
results = Benchmark.measure do
|
230
226
|
1000.times do
|
231
227
|
AuditedModel.create!(attributes)
|
@@ -238,29 +234,28 @@ describe PgAuditLog do
|
|
238
234
|
end
|
239
235
|
|
240
236
|
describe "during migrations" do
|
241
|
-
|
242
237
|
before do
|
243
|
-
connection.drop_table(
|
244
|
-
connection.drop_table(
|
238
|
+
connection.drop_table('test_table') rescue nil
|
239
|
+
connection.drop_table('new_table') rescue nil
|
245
240
|
end
|
246
241
|
|
247
242
|
after do
|
248
|
-
connection.drop_table(
|
243
|
+
connection.drop_table('test_table') rescue nil
|
249
244
|
end
|
250
245
|
|
251
246
|
describe "when creating the table" do
|
252
247
|
it "should automatically create the trigger" do
|
253
|
-
PgAuditLog::Triggers.tables_with_triggers.should_not include(
|
254
|
-
connection.create_table(
|
255
|
-
PgAuditLog::Triggers.tables_with_triggers.should include(
|
248
|
+
PgAuditLog::Triggers.tables_with_triggers.should_not include('test_table')
|
249
|
+
connection.create_table('test_table')
|
250
|
+
PgAuditLog::Triggers.tables_with_triggers.should include('test_table')
|
256
251
|
end
|
257
252
|
end
|
258
253
|
|
259
254
|
describe "when dropping the table" do
|
260
255
|
it "should automatically drop the trigger" do
|
261
|
-
connection.create_table(
|
262
|
-
connection.drop_table(
|
263
|
-
PgAuditLog::Triggers.tables_with_triggers.should_not include(
|
256
|
+
connection.create_table('test_table')
|
257
|
+
connection.drop_table('test_table')
|
258
|
+
PgAuditLog::Triggers.tables_with_triggers.should_not include('test_table')
|
264
259
|
end
|
265
260
|
end
|
266
261
|
|
@@ -274,11 +269,11 @@ describe PgAuditLog do
|
|
274
269
|
end
|
275
270
|
|
276
271
|
it "should automatically drop and create the trigger" do
|
277
|
-
new_table_name = "new_table_#{Time.
|
278
|
-
connection.create_table(
|
279
|
-
connection.rename_table(
|
272
|
+
new_table_name = "new_table_#{Time.current.to_i}"
|
273
|
+
connection.create_table('test_table')
|
274
|
+
connection.rename_table('test_table', new_table_name)
|
280
275
|
|
281
|
-
trigger_names.should_not include(
|
276
|
+
trigger_names.should_not include('audit_test_table')
|
282
277
|
trigger_names.should include("audit_#{new_table_name}")
|
283
278
|
PgAuditLog::Triggers.tables_with_triggers.should include(new_table_name)
|
284
279
|
|
@@ -290,16 +285,17 @@ describe PgAuditLog do
|
|
290
285
|
describe "temporary tables" do
|
291
286
|
context "when creating them" do
|
292
287
|
it "should be ignored" do
|
293
|
-
connection.create_table(
|
294
|
-
PgAuditLog::Triggers.tables_with_triggers.should_not include(
|
295
|
-
connection.drop_table(
|
288
|
+
connection.create_table('some_temp_table', :temporary => true)
|
289
|
+
PgAuditLog::Triggers.tables_with_triggers.should_not include('some_temp_table')
|
290
|
+
connection.drop_table('some_temp_table')
|
296
291
|
end
|
297
292
|
end
|
293
|
+
|
298
294
|
context "when dropping them" do
|
299
295
|
it "should be ignored" do
|
300
|
-
connection.create_table(
|
301
|
-
connection.drop_table(
|
302
|
-
PgAuditLog::Triggers.tables_with_triggers.should_not include(
|
296
|
+
connection.create_table('some_temp_table', :temporary => true)
|
297
|
+
connection.drop_table('some_temp_table')
|
298
|
+
PgAuditLog::Triggers.tables_with_triggers.should_not include('some_temp_table')
|
303
299
|
end
|
304
300
|
end
|
305
301
|
end
|
@@ -311,9 +307,9 @@ describe PgAuditLog do
|
|
311
307
|
|
312
308
|
context "when creating a table" do
|
313
309
|
it "should install the function then enable the trigger on the table" do
|
314
|
-
connection.create_table(
|
315
|
-
PgAuditLog::Triggers.tables_with_triggers.should include(
|
316
|
-
connection.drop_table(
|
310
|
+
connection.create_table('some_more_new_table')
|
311
|
+
PgAuditLog::Triggers.tables_with_triggers.should include('some_more_new_table')
|
312
|
+
connection.drop_table('some_more_new_table')
|
317
313
|
end
|
318
314
|
end
|
319
315
|
end
|
@@ -326,9 +322,9 @@ describe PgAuditLog do
|
|
326
322
|
context "when creating a table" do
|
327
323
|
it "should install the entry table then enable the trigger on the table" do
|
328
324
|
PgAuditLog::Entry.installed?.should be_false
|
329
|
-
connection.create_table(
|
325
|
+
connection.create_table('another_table')
|
330
326
|
PgAuditLog::Entry.installed?.should be_true
|
331
|
-
connection.drop_table(
|
327
|
+
connection.drop_table('another_table')
|
332
328
|
end
|
333
329
|
end
|
334
330
|
end
|
@@ -336,12 +332,11 @@ describe PgAuditLog do
|
|
336
332
|
describe "ignored tables" do
|
337
333
|
context "when creating one of those tables" do
|
338
334
|
it "should not automatically create a trigger for it" do
|
339
|
-
PgAuditLog::IGNORED_TABLES <<
|
340
|
-
connection.create_table(
|
341
|
-
PgAuditLog::Triggers.tables_with_triggers.should_not include(
|
342
|
-
connection.drop_table(
|
335
|
+
PgAuditLog::IGNORED_TABLES << 'ignored_table'
|
336
|
+
connection.create_table('ignored_table')
|
337
|
+
PgAuditLog::Triggers.tables_with_triggers.should_not include('ignored_table')
|
338
|
+
connection.drop_table('ignored_table')
|
343
339
|
end
|
344
340
|
end
|
345
341
|
end
|
346
|
-
|
347
342
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,23 +1,29 @@
|
|
1
|
-
require
|
2
|
-
|
3
|
-
|
1
|
+
require 'bundler'
|
2
|
+
Bundler.setup
|
3
|
+
|
4
|
+
require 'pg_audit_log'
|
5
|
+
require 'with_model'
|
4
6
|
|
5
7
|
connection = nil
|
6
8
|
begin
|
7
|
-
ActiveRecord::Base.establish_connection(
|
8
|
-
|
9
|
-
|
9
|
+
ActiveRecord::Base.establish_connection({
|
10
|
+
:adapter => 'postgresql',
|
11
|
+
:database => 'pg_audit_log_test',
|
12
|
+
:min_messages => 'warning',
|
13
|
+
})
|
10
14
|
connection = ActiveRecord::Base.connection
|
11
|
-
connection.execute(
|
15
|
+
connection.execute('SELECT 1')
|
12
16
|
rescue PGError => e
|
13
|
-
puts
|
14
|
-
puts
|
17
|
+
puts '-' * 80
|
18
|
+
puts 'Unable to connect to database. Please run:'
|
15
19
|
puts
|
16
|
-
puts
|
17
|
-
puts
|
20
|
+
puts ' createdb pg_audit_log_test'
|
21
|
+
puts '-' * 80
|
18
22
|
raise e
|
19
23
|
end
|
20
24
|
|
25
|
+
ActiveRecord::Base.default_timezone = :local
|
26
|
+
|
21
27
|
RSpec.configure do |config|
|
22
28
|
config.mock_with :rspec
|
23
29
|
config.extend WithModel
|
data/spec/triggers_spec.rb
CHANGED
metadata
CHANGED
@@ -1,88 +1,108 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pg_audit_log
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.6.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Case Commons, LLC
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2014-05-16 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: rails
|
16
|
-
requirement:
|
17
|
-
none: false
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - ">="
|
20
18
|
- !ruby/object:Gem::Version
|
21
|
-
version: 3.
|
19
|
+
version: '3.2'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '4.2'
|
22
23
|
type: :runtime
|
23
24
|
prerelease: false
|
24
|
-
version_requirements:
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '3.2'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '4.2'
|
25
33
|
- !ruby/object:Gem::Dependency
|
26
34
|
name: pg
|
27
|
-
requirement:
|
28
|
-
none: false
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
29
36
|
requirements:
|
30
|
-
- -
|
37
|
+
- - ">="
|
31
38
|
- !ruby/object:Gem::Version
|
32
39
|
version: 0.9.0
|
33
40
|
type: :runtime
|
34
41
|
prerelease: false
|
35
|
-
version_requirements:
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 0.9.0
|
36
47
|
- !ruby/object:Gem::Dependency
|
37
|
-
name: rspec
|
38
|
-
requirement:
|
39
|
-
none: false
|
48
|
+
name: rspec
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
40
50
|
requirements:
|
41
|
-
- -
|
51
|
+
- - ">="
|
42
52
|
- !ruby/object:Gem::Version
|
43
|
-
version: '
|
53
|
+
version: '0'
|
44
54
|
type: :development
|
45
55
|
prerelease: false
|
46
|
-
version_requirements:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
47
61
|
- !ruby/object:Gem::Dependency
|
48
|
-
name:
|
49
|
-
requirement:
|
50
|
-
none: false
|
62
|
+
name: rspec-rails
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
51
64
|
requirements:
|
52
|
-
- -
|
65
|
+
- - ">="
|
53
66
|
- !ruby/object:Gem::Version
|
54
|
-
version: 0
|
67
|
+
version: '0'
|
55
68
|
type: :development
|
56
69
|
prerelease: false
|
57
|
-
version_requirements:
|
58
|
-
- !ruby/object:Gem::Dependency
|
59
|
-
name: autotest
|
60
|
-
requirement: &70199944307460 !ruby/object:Gem::Requirement
|
61
|
-
none: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
62
71
|
requirements:
|
63
|
-
- -
|
72
|
+
- - ">="
|
64
73
|
- !ruby/object:Gem::Version
|
65
74
|
version: '0'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: with_model
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: 0.1.3
|
66
82
|
type: :development
|
67
83
|
prerelease: false
|
68
|
-
version_requirements:
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 0.1.3
|
69
89
|
description: A completely transparent audit logging component for your application
|
70
90
|
using a stored procedure and triggers. Comes with specs for your project and a rake
|
71
|
-
task to generate the reverse SQL to undo changes logged
|
91
|
+
task to generate the reverse SQL to undo changes logged.
|
72
92
|
email:
|
73
93
|
- casecommons-dev@googlegroups.com
|
74
94
|
executables: []
|
75
95
|
extensions: []
|
76
96
|
extra_rdoc_files: []
|
77
97
|
files:
|
78
|
-
- .gitignore
|
79
|
-
- .rspec
|
80
|
-
- .rvmrc
|
98
|
+
- ".gitignore"
|
99
|
+
- ".rspec"
|
100
|
+
- ".rvmrc"
|
101
|
+
- ".travis.yml"
|
81
102
|
- Gemfile
|
82
103
|
- LICENSE
|
83
|
-
- README.
|
104
|
+
- README.md
|
84
105
|
- Rakefile
|
85
|
-
- init.rb
|
86
106
|
- lib/generators/pg_audit_log/install_generator.rb
|
87
107
|
- lib/generators/pg_audit_log/rspec_generator.rb
|
88
108
|
- lib/generators/pg_audit_log/templates/lib/tasks/pg_audit_log.rake
|
@@ -104,36 +124,30 @@ files:
|
|
104
124
|
- spec/spec_helper.rb
|
105
125
|
- spec/triggers_spec.rb
|
106
126
|
homepage: https://github.com/Casecommons/pg_audit_log
|
107
|
-
licenses:
|
127
|
+
licenses:
|
128
|
+
- MIT
|
129
|
+
metadata: {}
|
108
130
|
post_install_message: Please run PgAuditLog::Function.install (in console/migration)
|
109
131
|
to install the new versions of the database functions
|
110
132
|
rdoc_options: []
|
111
133
|
require_paths:
|
112
134
|
- lib
|
113
135
|
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
-
none: false
|
115
136
|
requirements:
|
116
|
-
- -
|
137
|
+
- - ">="
|
117
138
|
- !ruby/object:Gem::Version
|
118
139
|
version: '0'
|
119
|
-
segments:
|
120
|
-
- 0
|
121
|
-
hash: 1103642768265560851
|
122
140
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
123
|
-
none: false
|
124
141
|
requirements:
|
125
|
-
- -
|
142
|
+
- - ">="
|
126
143
|
- !ruby/object:Gem::Version
|
127
144
|
version: '0'
|
128
|
-
segments:
|
129
|
-
- 0
|
130
|
-
hash: 1103642768265560851
|
131
145
|
requirements: []
|
132
146
|
rubyforge_project:
|
133
|
-
rubygems_version:
|
147
|
+
rubygems_version: 2.2.2
|
134
148
|
signing_key:
|
135
|
-
specification_version:
|
136
|
-
summary:
|
149
|
+
specification_version: 4
|
150
|
+
summary: PostgreSQL-only database-level audit logging of all databases changes.
|
137
151
|
test_files:
|
138
152
|
- spec/configuration_spec.rb
|
139
153
|
- spec/function_spec.rb
|
@@ -141,3 +155,4 @@ test_files:
|
|
141
155
|
- spec/pg_audit_log_spec.rb
|
142
156
|
- spec/spec_helper.rb
|
143
157
|
- spec/triggers_spec.rb
|
158
|
+
has_rdoc:
|
data/README.rdoc
DELETED
@@ -1,62 +0,0 @@
|
|
1
|
-
= pg_audit_log
|
2
|
-
|
3
|
-
* http://github.com/casecommons/pg_audit_log/
|
4
|
-
|
5
|
-
== DESCRIPTION
|
6
|
-
|
7
|
-
PostgreSQL only database-level audit logging of all databases changes using a completely transparent stored procedure and triggers.
|
8
|
-
Comes with specs for your project and a rake task to generate the reverse SQL to undo changes logged}
|
9
|
-
|
10
|
-
All SQL INSERTs, UPDATEs, and DELETEs will be captured. Record columns that do not change do not generate an audit log entry.
|
11
|
-
|
12
|
-
Compatible with Rails 3.0.x, 3.1.x and 3.2.x
|
13
|
-
|
14
|
-
On a 2.93GHz i7 with postgresql 9.1 the audit log has an overhead of about 0.0035 seconds to each INSERT, UPDATE or DELETE
|
15
|
-
|
16
|
-
== INSTALL
|
17
|
-
|
18
|
-
=== Enable plpgsql langauges in your postgresql instance
|
19
|
-
|
20
|
-
As a superuser in postgres make sure your database has plpgsql enabled:
|
21
|
-
|
22
|
-
CREATE OR REPLACE PROCEDURAL LANGUAGE plpgsql;
|
23
|
-
|
24
|
-
=== Rails 3
|
25
|
-
|
26
|
-
$ rails generate pg_audit_log:install
|
27
|
-
|
28
|
-
=== Installing the PostgreSQL function and triggers for your project
|
29
|
-
|
30
|
-
$ rake pg_audit_log:install
|
31
|
-
|
32
|
-
== Using on your project
|
33
|
-
|
34
|
-
The PgAuditLog::Entry ActiveRecord model represents a single entry in the audit log table. Each entry represents a single change to a single field of a record in a table. So if you change 3 columns of a record, that will generate 3 corresponding PgAuditLog::Entry records.
|
35
|
-
|
36
|
-
You can see the SQL it injects on every query by running with LOG_AUDIT_SQL
|
37
|
-
|
38
|
-
=== Migrations
|
39
|
-
|
40
|
-
TODO
|
41
|
-
|
42
|
-
=== schema.rb and development_structure.sql
|
43
|
-
|
44
|
-
Since schema.rb cannot represent TRIGGERs or FUNCTIONs you will need to set your environment to generate SQL instead of ruby for your database schema and structure. In your application environment put the following:
|
45
|
-
|
46
|
-
config.active_record.schema_format = :sql
|
47
|
-
|
48
|
-
And you can generate this sql using:
|
49
|
-
|
50
|
-
$ rake db:structure:dump
|
51
|
-
|
52
|
-
=== Uninstalling
|
53
|
-
|
54
|
-
$ rake pg_audit_log:uninstall
|
55
|
-
|
56
|
-
== REQUIREMENTS
|
57
|
-
|
58
|
-
* ActiveRecord
|
59
|
-
|
60
|
-
== LICENSE
|
61
|
-
|
62
|
-
MIT
|
data/init.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
require "pg_audit_log"
|