cia 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile.lock +1 -1
- data/MIGRATION.rb +4 -9
- data/Readme.md +4 -7
- data/gemfiles/rails2.gemfile.lock +1 -1
- data/gemfiles/rails3.gemfile.lock +1 -1
- data/lib/cia.rb +17 -9
- data/lib/cia/auditable.rb +3 -3
- data/lib/cia/event.rb +2 -3
- data/lib/cia/version.rb +1 -1
- data/spec/cia_spec.rb +75 -11
- data/spec/spec_helper.rb +1 -11
- metadata +4 -10
- data/lib/cia/create_event.rb +0 -4
- data/lib/cia/delete_event.rb +0 -4
- data/lib/cia/null_transaction.rb +0 -6
- data/lib/cia/transaction.rb +0 -23
- data/lib/cia/update_event.rb +0 -4
- data/spec/cia/transaction_spec.rb +0 -75
data/Gemfile.lock
CHANGED
data/MIGRATION.rb
CHANGED
@@ -1,15 +1,11 @@
|
|
1
|
-
create_table :
|
1
|
+
create_table :cia_events do |t|
|
2
2
|
t.integer :actor_id
|
3
3
|
t.string :actor_type
|
4
|
+
t.string :source_type, :action, :null => false
|
5
|
+
t.integer :source_id, :null => false
|
4
6
|
t.string :ip_address
|
5
|
-
t.timestamp :created_at
|
6
|
-
end
|
7
|
-
|
8
|
-
create_table :cia_events do |t|
|
9
|
-
t.string :type, :source_type, :null => false
|
10
|
-
t.integer :cia_transaction_id, :source_id, :null => false
|
11
7
|
t.string :message
|
12
|
-
t.timestamp :created_at
|
8
|
+
t.timestamp :created_at#, :null => false
|
13
9
|
end
|
14
10
|
|
15
11
|
create_table :cia_attribute_changes do |t|
|
@@ -19,6 +15,5 @@ create_table :cia_attribute_changes do |t|
|
|
19
15
|
end
|
20
16
|
|
21
17
|
# DOWN
|
22
|
-
# drop_table :cia_transactions
|
23
18
|
# drop_table :cia_events
|
24
19
|
# drop_table :cia_attribute_changes
|
data/Readme.md
CHANGED
@@ -1,19 +1,16 @@
|
|
1
1
|
Central Internal Auditing
|
2
2
|
============================
|
3
3
|
|
4
|
-
Audit model
|
4
|
+
Audit model actions like update/create/destroy/<custom> + attribute changes.
|
5
5
|
|
6
|
-
-
|
6
|
+
- normalized and queryable through table layout
|
7
7
|
- actors and subjects are polymorphic
|
8
|
-
- events come in different types like `CIA::UpdateEvent`
|
9
|
-
- transactions wrap multiple events, a nice place to add debugging info like source/action/ip
|
10
8
|
- works on ActiveRecord 2 and 3
|
11
9
|
|
12
10
|
Table layout:
|
13
11
|
|
14
|
-
|
15
|
-
|
16
|
-
-> has many attribute changes (changed password from foo to bar on subject)
|
12
|
+
Event (actor/ip/time/updated subject + message)
|
13
|
+
-> has many attribute changes (changed password from foo to bar on subject)
|
17
14
|
|
18
15
|
|
19
16
|
Install
|
data/lib/cia.rb
CHANGED
@@ -1,12 +1,7 @@
|
|
1
1
|
require 'active_record'
|
2
2
|
require 'cia/version'
|
3
3
|
require 'cia/auditable'
|
4
|
-
require 'cia/null_transaction'
|
5
|
-
require 'cia/transaction'
|
6
4
|
require 'cia/event'
|
7
|
-
require 'cia/create_event'
|
8
|
-
require 'cia/update_event'
|
9
|
-
require 'cia/delete_event'
|
10
5
|
require 'cia/attribute_change'
|
11
6
|
|
12
7
|
module CIA
|
@@ -15,18 +10,31 @@ module CIA
|
|
15
10
|
end
|
16
11
|
|
17
12
|
def self.audit(options = {})
|
18
|
-
Thread.current[:cia_transaction] =
|
13
|
+
Thread.current[:cia_transaction] = options
|
19
14
|
yield
|
20
15
|
ensure
|
21
16
|
Thread.current[:cia_transaction] = nil
|
22
17
|
end
|
23
18
|
|
24
19
|
def self.current_transaction
|
25
|
-
Thread.current[:cia_transaction]
|
20
|
+
Thread.current[:cia_transaction]
|
26
21
|
end
|
27
22
|
|
28
|
-
def self.
|
29
|
-
|
23
|
+
def self.record(action, source)
|
24
|
+
return unless current_transaction
|
25
|
+
|
26
|
+
changes = source.changes.slice(*source.class.audited_attributes)
|
27
|
+
message = source.audit_message if source.respond_to?(:audit_message)
|
28
|
+
|
29
|
+
return if not message and changes.empty? and action.to_s == "update"
|
30
|
+
|
31
|
+
event = CIA::Event.create!(
|
32
|
+
:action => action.to_s,
|
33
|
+
:source => source,
|
34
|
+
:message => message
|
35
|
+
)
|
36
|
+
event.record_attribute_changes!(changes)
|
37
|
+
event
|
30
38
|
rescue Object => e
|
31
39
|
if exception_handler
|
32
40
|
exception_handler.call e
|
data/lib/cia/auditable.rb
CHANGED
@@ -3,9 +3,9 @@ module CIA
|
|
3
3
|
def self.included(base)
|
4
4
|
base.class_attribute :audited_attributes
|
5
5
|
base.send :extend, ClassMethods
|
6
|
-
base.after_create {|record| CIA.
|
7
|
-
base.after_update {|record| CIA.
|
8
|
-
base.after_destroy {|record| CIA.
|
6
|
+
base.after_create {|record| CIA.record(:create, record) }
|
7
|
+
base.after_update {|record| CIA.record(:update, record) }
|
8
|
+
base.after_destroy {|record| CIA.record(:destroy, record) }
|
9
9
|
end
|
10
10
|
|
11
11
|
module ClassMethods
|
data/lib/cia/event.rb
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
module CIA
|
2
2
|
class Event < ActiveRecord::Base
|
3
|
-
abstract_class
|
4
3
|
self.table_name = "cia_events"
|
5
4
|
|
5
|
+
belongs_to :actor, :polymorphic => true
|
6
6
|
belongs_to :source, :polymorphic => true
|
7
|
-
belongs_to :transaction, :foreign_key => :cia_transaction_id
|
8
7
|
has_many :attribute_changes, :foreign_key => :cia_event_id
|
9
8
|
|
10
|
-
validates_presence_of :
|
9
|
+
validates_presence_of :source, :action
|
11
10
|
|
12
11
|
def self.previous
|
13
12
|
scoped(:order => "id desc")
|
data/lib/cia/version.rb
CHANGED
data/spec/cia_spec.rb
CHANGED
@@ -7,20 +7,20 @@ describe CIA do
|
|
7
7
|
|
8
8
|
describe ".audit" do
|
9
9
|
it "has no transaction when it starts" do
|
10
|
-
CIA.current_transaction.should ==
|
10
|
+
CIA.current_transaction.should == nil
|
11
11
|
end
|
12
12
|
|
13
13
|
it "starts a new transaction" do
|
14
14
|
result = 1
|
15
|
-
CIA.audit({}) do
|
15
|
+
CIA.audit({:a => 1}) do
|
16
16
|
result = CIA.current_transaction
|
17
17
|
end
|
18
|
-
result.
|
18
|
+
result.should == {:a => 1}
|
19
19
|
end
|
20
20
|
|
21
21
|
it "stops the transaction after the block" do
|
22
22
|
CIA.audit({}){}
|
23
|
-
CIA.current_transaction.should ==
|
23
|
+
CIA.current_transaction.should == nil
|
24
24
|
end
|
25
25
|
|
26
26
|
it "returns the block content" do
|
@@ -34,14 +34,13 @@ describe CIA do
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
sleep 0.01
|
37
|
-
CIA.current_transaction.should ==
|
37
|
+
CIA.current_transaction.should == nil
|
38
38
|
sleep 0.04 # so next tests dont fail
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
describe ".
|
42
|
+
describe ".record" do
|
43
43
|
let(:object) { Car.new }
|
44
|
-
let(:transaction) { CIA.current_transaction }
|
45
44
|
|
46
45
|
around do |example|
|
47
46
|
CIA.audit :actor => User.create! do
|
@@ -53,7 +52,7 @@ describe CIA do
|
|
53
52
|
expect{
|
54
53
|
object.save!
|
55
54
|
}.to change{ CIA::Event.count }.by(+1)
|
56
|
-
CIA::Event.last.
|
55
|
+
CIA::Event.last.action.should == "create"
|
57
56
|
end
|
58
57
|
|
59
58
|
it "tracks delete" do
|
@@ -61,7 +60,7 @@ describe CIA do
|
|
61
60
|
expect{
|
62
61
|
object.destroy
|
63
62
|
}.to change{ CIA::Event.count }.by(+1)
|
64
|
-
CIA::Event.last.
|
63
|
+
CIA::Event.last.action.should == "destroy"
|
65
64
|
end
|
66
65
|
|
67
66
|
it "tracks update" do
|
@@ -69,13 +68,78 @@ describe CIA do
|
|
69
68
|
expect{
|
70
69
|
object.update_attributes(:wheels => 3)
|
71
70
|
}.to change{ CIA::Event.count }.by(+1)
|
72
|
-
CIA::Event.last.
|
71
|
+
CIA::Event.last.action.should == "update"
|
72
|
+
end
|
73
|
+
|
74
|
+
context "events" do
|
75
|
+
def parse_event_changes(event)
|
76
|
+
event.attribute_changes.map { |c| [c.attribute_name, c.old_value, c.new_value] }
|
77
|
+
end
|
78
|
+
|
79
|
+
it "records attribute creations" do
|
80
|
+
source = Car.create!
|
81
|
+
source.wheels = 4
|
82
|
+
event = CIA.record(:update, source)
|
83
|
+
|
84
|
+
parse_event_changes(event).should == [["wheels", nil, "4"]]
|
85
|
+
end
|
86
|
+
|
87
|
+
it "records multiple attributes" do
|
88
|
+
source = CarWith3Attributes.create!
|
89
|
+
source.wheels = 4
|
90
|
+
source.drivers = 2
|
91
|
+
source.color = "red"
|
92
|
+
event = CIA.record(:update, source)
|
93
|
+
|
94
|
+
parse_event_changes(event).should =~ [["wheels", nil, "4"], ["drivers", nil, "2"], ["color", nil, "red"]]
|
95
|
+
end
|
96
|
+
|
97
|
+
it "records attribute changes" do
|
98
|
+
source = Car.create!(:wheels => 2)
|
99
|
+
source.wheels = 4
|
100
|
+
event = CIA.record(:update, source)
|
101
|
+
parse_event_changes(event).should == [["wheels", "2", "4"]]
|
102
|
+
end
|
103
|
+
|
104
|
+
it "records attribute deletions" do
|
105
|
+
source = Car.create!(:wheels => 2)
|
106
|
+
source.wheels = nil
|
107
|
+
event = CIA.record(:update, source)
|
108
|
+
parse_event_changes(event).should == [["wheels", "2", nil]]
|
109
|
+
end
|
110
|
+
|
111
|
+
it "does not record unaudited attribute changes" do
|
112
|
+
source = Car.create!
|
113
|
+
source.drivers = 2
|
114
|
+
event = nil
|
115
|
+
expect{
|
116
|
+
event = CIA.record(:update, source)
|
117
|
+
}.to_not change{ CIA::Event.count }
|
118
|
+
|
119
|
+
event.should == nil
|
120
|
+
end
|
121
|
+
|
122
|
+
it "records audit_message as message even if there are no changes" do
|
123
|
+
source = CarWithAMessage.create!
|
124
|
+
source.audit_message = "Foo"
|
125
|
+
event = CIA.record(:update, source)
|
126
|
+
|
127
|
+
event.message.should == "Foo"
|
128
|
+
parse_event_changes(event).should == []
|
129
|
+
end
|
130
|
+
|
131
|
+
it "record non-updates even without changes" do
|
132
|
+
source = Car.create!
|
133
|
+
event = CIA.record(:create, source)
|
134
|
+
|
135
|
+
parse_event_changes(event).should == []
|
136
|
+
end
|
73
137
|
end
|
74
138
|
|
75
139
|
context "exception_handler" do
|
76
140
|
before do
|
77
141
|
$stderr.stub(:puts)
|
78
|
-
|
142
|
+
CIA.stub(:current_transaction).and_raise(StandardError.new("foo"))
|
79
143
|
end
|
80
144
|
|
81
145
|
def capture_exception
|
data/spec/spec_helper.rb
CHANGED
@@ -3,7 +3,6 @@ require 'cia'
|
|
3
3
|
|
4
4
|
RSpec.configure do |config|
|
5
5
|
config.before do
|
6
|
-
CIA::Transaction.delete_all
|
7
6
|
CIA::Event.delete_all
|
8
7
|
CIA::AttributeChange.delete_all
|
9
8
|
end
|
@@ -50,17 +49,8 @@ class CarWith3Attributes < ActiveRecord::Base
|
|
50
49
|
audit_attribute :drivers
|
51
50
|
end
|
52
51
|
|
53
|
-
class CarWithoutObservers < ActiveRecord::Base
|
54
|
-
self.table_name = "cars"
|
55
|
-
end
|
56
|
-
|
57
|
-
class CarWithoutObservers2 < ActiveRecord::Base
|
58
|
-
self.table_name = "cars"
|
59
|
-
end
|
60
|
-
|
61
52
|
def create_event
|
62
|
-
|
63
|
-
CIA::UpdateEvent.create!(:source => Car.create!, :transaction => transaction)
|
53
|
+
CIA::Event.create!(:source => Car.create!, :actor => User.create!, :action => "update")
|
64
54
|
end
|
65
55
|
|
66
56
|
def create_change(options={})
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cia
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-06-
|
12
|
+
date: 2012-06-25 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description:
|
15
15
|
email: michael@grosser.it
|
@@ -32,16 +32,10 @@ files:
|
|
32
32
|
- lib/cia.rb
|
33
33
|
- lib/cia/attribute_change.rb
|
34
34
|
- lib/cia/auditable.rb
|
35
|
-
- lib/cia/create_event.rb
|
36
|
-
- lib/cia/delete_event.rb
|
37
35
|
- lib/cia/event.rb
|
38
|
-
- lib/cia/null_transaction.rb
|
39
|
-
- lib/cia/transaction.rb
|
40
|
-
- lib/cia/update_event.rb
|
41
36
|
- lib/cia/version.rb
|
42
37
|
- spec/cia/attribute_change_spec.rb
|
43
38
|
- spec/cia/event_spec.rb
|
44
|
-
- spec/cia/transaction_spec.rb
|
45
39
|
- spec/cia_spec.rb
|
46
40
|
- spec/spec_helper.rb
|
47
41
|
homepage: http://github.com/grosser/cia
|
@@ -59,7 +53,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
59
53
|
version: '0'
|
60
54
|
segments:
|
61
55
|
- 0
|
62
|
-
hash: -
|
56
|
+
hash: -1846204307001126824
|
63
57
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
58
|
none: false
|
65
59
|
requirements:
|
@@ -68,7 +62,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
68
62
|
version: '0'
|
69
63
|
segments:
|
70
64
|
- 0
|
71
|
-
hash: -
|
65
|
+
hash: -1846204307001126824
|
72
66
|
requirements: []
|
73
67
|
rubyforge_project:
|
74
68
|
rubygems_version: 1.8.24
|
data/lib/cia/create_event.rb
DELETED
data/lib/cia/delete_event.rb
DELETED
data/lib/cia/null_transaction.rb
DELETED
data/lib/cia/transaction.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
module CIA
|
2
|
-
class Transaction < ActiveRecord::Base
|
3
|
-
self.table_name = "cia_transactions"
|
4
|
-
|
5
|
-
belongs_to :actor, :polymorphic => true
|
6
|
-
has_many :events, :foreign_key => :cia_transaction_id
|
7
|
-
|
8
|
-
def record(event_type, source)
|
9
|
-
changes = source.changes.slice(*source.class.audited_attributes)
|
10
|
-
message = source.audit_message if source.respond_to?(:audit_message)
|
11
|
-
|
12
|
-
return if not message and changes.empty? and event_type == CIA::UpdateEvent
|
13
|
-
|
14
|
-
event = event_type.create!(
|
15
|
-
:source => source,
|
16
|
-
:transaction => self,
|
17
|
-
:message => message
|
18
|
-
)
|
19
|
-
event.record_attribute_changes!(changes)
|
20
|
-
event
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
data/lib/cia/update_event.rb
DELETED
@@ -1,75 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe CIA::Transaction do
|
4
|
-
it "has many events" do
|
5
|
-
event = create_event
|
6
|
-
event.transaction.events.should == [event]
|
7
|
-
end
|
8
|
-
|
9
|
-
context "#record" do
|
10
|
-
def parse_event_changes(event)
|
11
|
-
event.attribute_changes.map { |c| [c.attribute_name, c.old_value, c.new_value] }
|
12
|
-
end
|
13
|
-
|
14
|
-
let(:transaction){ CIA::Transaction.new(:actor => User.create!) }
|
15
|
-
|
16
|
-
it "records attribute creations" do
|
17
|
-
source = Car.create!
|
18
|
-
source.wheels = 4
|
19
|
-
event = transaction.record(CIA::UpdateEvent, source)
|
20
|
-
|
21
|
-
parse_event_changes(event).should == [["wheels", nil, "4"]]
|
22
|
-
end
|
23
|
-
|
24
|
-
it "records multiple attributes" do
|
25
|
-
source = CarWith3Attributes.create!
|
26
|
-
source.wheels = 4
|
27
|
-
source.drivers = 2
|
28
|
-
source.color = "red"
|
29
|
-
event = transaction.record(CIA::UpdateEvent, source)
|
30
|
-
|
31
|
-
parse_event_changes(event).should =~ [["wheels", nil, "4"], ["drivers", nil, "2"], ["color", nil, "red"]]
|
32
|
-
end
|
33
|
-
|
34
|
-
it "records attribute changes" do
|
35
|
-
source = Car.create!(:wheels => 2)
|
36
|
-
source.wheels = 4
|
37
|
-
event = transaction.record(CIA::UpdateEvent, source)
|
38
|
-
parse_event_changes(event).should == [["wheels", "2", "4"]]
|
39
|
-
end
|
40
|
-
|
41
|
-
it "records attribute deletions" do
|
42
|
-
source = Car.create!(:wheels => 2)
|
43
|
-
source.wheels = nil
|
44
|
-
event = transaction.record(CIA::UpdateEvent, source)
|
45
|
-
parse_event_changes(event).should == [["wheels", "2", nil]]
|
46
|
-
end
|
47
|
-
|
48
|
-
it "does not record unaudited attribute changes" do
|
49
|
-
source = Car.create!
|
50
|
-
source.drivers = 2
|
51
|
-
event = nil
|
52
|
-
expect{
|
53
|
-
event = transaction.record(CIA::UpdateEvent, source)
|
54
|
-
}.to_not change{ CIA::Event.count }
|
55
|
-
|
56
|
-
event.should == nil
|
57
|
-
end
|
58
|
-
|
59
|
-
it "records audit_message as message even if there are no changes" do
|
60
|
-
source = CarWithAMessage.create!
|
61
|
-
source.audit_message = "Foo"
|
62
|
-
event = transaction.record(CIA::UpdateEvent, source)
|
63
|
-
|
64
|
-
event.message.should == "Foo"
|
65
|
-
parse_event_changes(event).should == []
|
66
|
-
end
|
67
|
-
|
68
|
-
it "record non-updates even without changes" do
|
69
|
-
source = Car.create!
|
70
|
-
event = transaction.record(CIA::CreateEvent, source)
|
71
|
-
|
72
|
-
parse_event_changes(event).should == []
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|