login_attack_report 0.0.1 → 0.0.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cfb4e080dba8769ccaeaf9ef1716703178a86445
4
- data.tar.gz: 1386ffc5e883023c61c5891d56ffcdd21444368d
3
+ metadata.gz: 83b80275b8c644e56b383ad22135a52e94e3188d
4
+ data.tar.gz: ae64da9fc95fb5131185f4add79e6ba46e16e860
5
5
  SHA512:
6
- metadata.gz: e1db48fc2ea2ced4a8b7bff8f195a0eead38de60a7267e1282cdc9eb2fcea5b882510a6e3434c7da3e2d3ab3afebdfa5c51a3e7ef5e9379e6fdae416c9f7d64e
7
- data.tar.gz: 34a9dab676b40a8ce061095310da08a2105755774b28c73c3a9a156cba955958b3289603adb8b962ba06e345d4e175c025584a24a8936731ac8e8ee10a0fb53c
6
+ metadata.gz: 9ade69377b319c33560880822e528a63aab873213a7d74e5ed12564cdc4a1b823989f6a91283404190889903ab7da66c90ba1c10209f50e3e37c8aff57212739
7
+ data.tar.gz: fa8c39bff1431eb75406f3bf9ad1aace546270f6d08dbfecc0c908f4898f10246c8706d8217cb231c55d3b337bc89496d795dbc21824dff4eeeaf96b72066f5c
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  *.gem
2
+ *.swp
2
3
  .bundle
3
4
  .config
4
5
  .rbenv-version
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in login_attack_report3.gemspec
3
+ # Specify your gem's dependencies in login_attack_report.gemspec
4
4
  gemspec
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # LoginAttackReport3
1
+ # LoginAttackReport
2
2
 
3
3
  TODO: Write a gem description
4
4
 
@@ -7,7 +7,7 @@ TODO: Write a gem description
7
7
  Add this line to your application's Gemfile:
8
8
 
9
9
  ```ruby
10
- gem 'login_attack_report3'
10
+ gem 'login_attack_report'
11
11
  ```
12
12
 
13
13
  And then execute:
@@ -16,7 +16,7 @@ And then execute:
16
16
 
17
17
  Or install it yourself as:
18
18
 
19
- $ gem install login_attack_report3
19
+ $ gem install login_attack_report
20
20
 
21
21
  ## Usage
22
22
 
@@ -24,7 +24,7 @@ TODO: Write usage instructions here
24
24
 
25
25
  ## Contributing
26
26
 
27
- 1. Fork it ( https://github.com/[my-github-username]/login_attack_report3/fork )
27
+ 1. Fork it ( https://github.com/[my-github-username]/login_attack_report/fork )
28
28
  2. Create your feature branch (`git checkout -b my-new-feature`)
29
29
  3. Commit your changes (`git commit -am 'Add some feature'`)
30
30
  4. Push to the branch (`git push origin my-new-feature`)
@@ -0,0 +1,7 @@
1
+ require 'login_attack_report/login_attack_report_version_concern'
2
+
3
+ module LoginAttackReport
4
+ class LoginAttackReportVersion < ::ActiveRecord::Base
5
+ include LoginAttackReport::LoginAttackReportVersionConcern
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ #Dir[File.join(File.dirname(__FILE__), 'active_record', 'models', 'login_attack_report', '*.rb')].each do |file|
2
+ # require "login_attack_report/frameworks/active_record/models/#{File.basename(file, '.rb')}"
3
+ #end
4
+ require "login_attack_report/frameworks/active_record/models/login_attack_report_version.rb"
@@ -0,0 +1,7 @@
1
+ module LoginAttackReport
2
+ module Rails
3
+ class Engine < ::Rails::Engine
4
+ paths['app/models'] << 'lib/login_attack_report/frameworks/active_record/models'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ require 'login_attack_report/frameworks/rails/engine'
2
+
3
+ module LoginAttackReport
4
+ module Rails
5
+ end
6
+ end
@@ -0,0 +1,504 @@
1
+ require 'active_support/core_ext/object' # provides the `try` method
2
+
3
+ module LoginAttackReport
4
+ module Model
5
+
6
+ def self.included(base)
7
+ base.send :extend, ClassMethods
8
+ end
9
+
10
+ module ClassMethods
11
+ # Declare this in your model to track every create, update, and destroy. Each version of
12
+ # the model is available in the `versions` association.
13
+ #
14
+ # Options:
15
+ # :on the events to track (optional; defaults to all of them). Set to an array of
16
+ # `:create`, `:update`, `:destroy` as desired.
17
+ # :class_name the name of a custom Version class. This class should inherit from `PaperTrail::Version`.
18
+ # :ignore an array of attributes for which a new `Version` will not be created if only they change.
19
+ # it can also aceept a Hash as an argument where the key is the attribute to ignore (a `String` or `Symbol`),
20
+ # which will only be ignored if the value is a `Proc` which returns truthily.
21
+ # :if, :unless Procs that allow to specify conditions when to save versions for an object
22
+ # :only inverse of `ignore` - a new `Version` will be created only for these attributes if supplied
23
+ # it can also aceept a Hash as an argument where the key is the attribute to track (a `String` or `Symbol`),
24
+ # which will only be counted if the value is a `Proc` which returns truthily.
25
+ # :skip fields to ignore completely. As with `ignore`, updates to these fields will not create
26
+ # a new `Version`. In addition, these fields will not be included in the serialized versions
27
+ # of the object whenever a new `Version` is created.
28
+ # :meta a hash of extra data to store. You must add a column to the `versions` table for each key.
29
+ # Values are objects or procs (which are called with `self`, i.e. the model with the paper
30
+ # trail). See `PaperTrail::Controller.info_for_paper_trail` for how to store data from
31
+ # the controller.
32
+ # :versions the name to use for the versions association. Default is `:versions`.
33
+ # :version the name to use for the method which returns the version the instance was reified from.
34
+ # Default is `:version`.
35
+ # :save_changes whether or not to save changes to the object_changes column if it exists. Default is true
36
+ #
37
+ def has_paper_trail(options = {})
38
+ # Lazily include the instance methods so we don't clutter up
39
+ # any more ActiveRecord models than we have to.
40
+ send :include, InstanceMethods
41
+
42
+ class_attribute :version_association_name
43
+ self.version_association_name = options[:version] || :version
44
+
45
+ # The version this instance was reified from.
46
+ attr_accessor self.version_association_name
47
+
48
+ class_attribute :version_class_name
49
+ self.version_class_name = options[:class_name] || 'PaperTrail::Version'
50
+
51
+ class_attribute :paper_trail_options
52
+ self.paper_trail_options = options.dup
53
+
54
+ [:ignore, :skip, :only].each do |k|
55
+ paper_trail_options[k] =
56
+ [paper_trail_options[k]].flatten.compact.map { |attr| attr.is_a?(Hash) ? attr.stringify_keys : attr.to_s }
57
+ end
58
+
59
+ paper_trail_options[:meta] ||= {}
60
+ paper_trail_options[:save_changes] = true if paper_trail_options[:save_changes].nil?
61
+
62
+ class_attribute :versions_association_name
63
+ self.versions_association_name = options[:versions] || :versions
64
+
65
+ attr_accessor :paper_trail_event
66
+
67
+ if ::ActiveRecord::VERSION::MAJOR >= 4 # `has_many` syntax for specifying order uses a lambda in Rails 4
68
+ has_many self.versions_association_name,
69
+ lambda { order(model.timestamp_sort_order) },
70
+ :class_name => self.version_class_name, :as => :item
71
+ else
72
+ has_many self.versions_association_name,
73
+ :class_name => self.version_class_name,
74
+ :as => :item,
75
+ :order => self.paper_trail_version_class.timestamp_sort_order
76
+ end
77
+
78
+ options[:on] ||= [:create, :update, :destroy]
79
+ options_on = Array(options[:on]) # so that a single symbol can be passed in without wrapping it in an `Array`
80
+ after_create :record_create, :if => :save_version? if options_on.include?(:create)
81
+ if options_on.include?(:update)
82
+ before_save :reset_timestamp_attrs_for_update_if_needed!, :on => :update
83
+ after_update :record_update, :if => :save_version?
84
+ after_update :clear_version_instance!
85
+ end
86
+ after_destroy :record_destroy, :if => :save_version? if options_on.include?(:destroy)
87
+
88
+ # Reset the transaction id when the transaction is closed
89
+ after_commit :reset_transaction_id
90
+ after_rollback :reset_transaction_id
91
+ after_rollback :clear_rolled_back_versions
92
+ end
93
+
94
+ # Switches PaperTrail off for this class.
95
+ def paper_trail_off!
96
+ PaperTrail.enabled_for_model(self, false)
97
+ end
98
+
99
+ def paper_trail_off
100
+ warn "DEPRECATED: use `paper_trail_off!` instead of `paper_trail_off`. Support for `paper_trail_off` will be removed in PaperTrail 4.0"
101
+ self.paper_trail_off!
102
+ end
103
+
104
+ # Switches PaperTrail on for this class.
105
+ def paper_trail_on!
106
+ PaperTrail.enabled_for_model(self, true)
107
+ end
108
+
109
+ def paper_trail_on
110
+ warn "DEPRECATED: use `paper_trail_on!` instead of `paper_trail_on`. Support for `paper_trail_on` will be removed in PaperTrail 4.0"
111
+ self.paper_trail_on!
112
+ end
113
+
114
+ def paper_trail_enabled_for_model?
115
+ return false unless self.include?(PaperTrail::Model::InstanceMethods)
116
+ PaperTrail.enabled_for_model?(self)
117
+ end
118
+
119
+ def paper_trail_version_class
120
+ @paper_trail_version_class ||= version_class_name.constantize
121
+ end
122
+
123
+ # Used for Version#object attribute
124
+ def serialize_attributes_for_paper_trail!(attributes)
125
+ # don't serialize before values before inserting into columns of type `JSON` on `PostgreSQL` databases
126
+ return attributes if self.paper_trail_version_class.object_col_is_json?
127
+
128
+ serialized_attributes.each do |key, coder|
129
+ if attributes.key?(key)
130
+ # Fall back to current serializer if `coder` has no `dump` method
131
+ coder = PaperTrail.serializer unless coder.respond_to?(:dump)
132
+ attributes[key] = coder.dump(attributes[key])
133
+ end
134
+ end
135
+ end
136
+
137
+ def unserialize_attributes_for_paper_trail!(attributes)
138
+ # don't serialize before values before inserting into columns of type `JSON` on `PostgreSQL` databases
139
+ return attributes if self.paper_trail_version_class.object_col_is_json?
140
+
141
+ serialized_attributes.each do |key, coder|
142
+ if attributes.key?(key)
143
+ coder = PaperTrail.serializer unless coder.respond_to?(:dump)
144
+ attributes[key] = coder.load(attributes[key])
145
+ end
146
+ end
147
+ end
148
+
149
+ # Used for Version#object_changes attribute
150
+ def serialize_attribute_changes_for_paper_trail!(changes)
151
+ # don't serialize before values before inserting into columns of type `JSON` on `PostgreSQL` databases
152
+ return changes if self.paper_trail_version_class.object_changes_col_is_json?
153
+
154
+ serialized_attributes.each do |key, coder|
155
+ if changes.key?(key)
156
+ # Fall back to current serializer if `coder` has no `dump` method
157
+ coder = PaperTrail.serializer unless coder.respond_to?(:dump)
158
+ old_value, new_value = changes[key]
159
+ changes[key] = [coder.dump(old_value),
160
+ coder.dump(new_value)]
161
+ end
162
+ end
163
+ end
164
+
165
+ def unserialize_attribute_changes_for_paper_trail!(changes)
166
+ # don't serialize before values before inserting into columns of type `JSON` on `PostgreSQL` databases
167
+ return changes if self.paper_trail_version_class.object_changes_col_is_json?
168
+
169
+ serialized_attributes.each do |key, coder|
170
+ if changes.key?(key)
171
+ coder = PaperTrail.serializer unless coder.respond_to?(:dump)
172
+ old_value, new_value = changes[key]
173
+ changes[key] = [coder.load(old_value),
174
+ coder.load(new_value)]
175
+ end
176
+ end
177
+ end
178
+ end
179
+
180
+ # Wrap the following methods in a module so we can include them only in the
181
+ # ActiveRecord models that declare `has_paper_trail`.
182
+ module InstanceMethods
183
+ # Returns true if this instance is the current, live one;
184
+ # returns false if this instance came from a previous version.
185
+ def live?
186
+ source_version.nil?
187
+ end
188
+
189
+ # Returns who put the object into its current state.
190
+ def paper_trail_originator
191
+ (source_version || send(self.class.versions_association_name).last).try(:whodunnit)
192
+ end
193
+
194
+ def originator
195
+ warn "DEPRECATED: use `paper_trail_originator` instead of `originator`. Support for `originator` will be removed in PaperTrail 4.0"
196
+ self.paper_trail_originator
197
+ end
198
+
199
+ # Invoked after rollbacks to ensure versions records are not created
200
+ # for changes that never actually took place
201
+ def clear_rolled_back_versions
202
+ send(self.class.versions_association_name).reload
203
+ end
204
+
205
+ # Returns the object (not a Version) as it was at the given timestamp.
206
+ def version_at(timestamp, reify_options={})
207
+ # Because a version stores how its object looked *before* the change,
208
+ # we need to look for the first version created *after* the timestamp.
209
+ v = send(self.class.versions_association_name).subsequent(timestamp, true).first
210
+ return v.reify(reify_options) if v
211
+ self unless self.destroyed?
212
+ end
213
+
214
+ # Returns the objects (not Versions) as they were between the given times.
215
+ def versions_between(start_time, end_time, reify_options={})
216
+ versions = send(self.class.versions_association_name).between(start_time, end_time)
217
+ versions.collect { |version| version_at(version.send PaperTrail.timestamp_field) }
218
+ end
219
+
220
+ # Returns the object (not a Version) as it was most recently.
221
+ def previous_version
222
+ preceding_version = source_version ? source_version.previous : send(self.class.versions_association_name).last
223
+ preceding_version.reify if preceding_version
224
+ end
225
+
226
+ # Returns the object (not a Version) as it became next.
227
+ # NOTE: if self (the item) was not reified from a version, i.e. it is the
228
+ # "live" item, we return nil. Perhaps we should return self instead?
229
+ def next_version
230
+ subsequent_version = source_version.next
231
+ subsequent_version ? subsequent_version.reify : self.class.find(self.id)
232
+ rescue
233
+ nil
234
+ end
235
+
236
+ def paper_trail_enabled_for_model?
237
+ self.class.paper_trail_enabled_for_model?
238
+ end
239
+
240
+ # Executes the given method or block without creating a new version.
241
+ def without_versioning(method = nil)
242
+ paper_trail_was_enabled = self.paper_trail_enabled_for_model?
243
+ self.class.paper_trail_off!
244
+ method ? method.to_proc.call(self) : yield(self)
245
+ ensure
246
+ self.class.paper_trail_on! if paper_trail_was_enabled
247
+ end
248
+
249
+ # Utility method for reifying. Anything executed inside the block will appear like a new record
250
+ def appear_as_new_record
251
+ instance_eval {
252
+ alias :old_new_record? :new_record?
253
+ alias :new_record? :present?
254
+ }
255
+ yield
256
+ instance_eval { alias :new_record? :old_new_record? }
257
+ end
258
+
259
+ # Temporarily overwrites the value of whodunnit and then executes the provided block.
260
+ def whodunnit(value)
261
+ raise ArgumentError, 'expected to receive a block' unless block_given?
262
+ current_whodunnit = PaperTrail.whodunnit
263
+ PaperTrail.whodunnit = value
264
+ yield self
265
+ ensure
266
+ PaperTrail.whodunnit = current_whodunnit
267
+ end
268
+
269
+ # Mimicks behavior of `touch` method from `ActiveRecord::Persistence`, but generates a version
270
+ #
271
+ # TODO: lookinto leveraging the `after_touch` callback from `ActiveRecord` to allow the
272
+ # regular `touch` method go generate a version as normal. May make sense to switch the `record_update`
273
+ # method to leverage an `after_update` callback anyways (likely for v4.0.0)
274
+ def touch_with_version(name = nil)
275
+ raise ActiveRecordError, "can not touch on a new record object" unless persisted?
276
+
277
+ attributes = timestamp_attributes_for_update_in_model
278
+ attributes << name if name
279
+ current_time = current_time_from_proper_timezone
280
+
281
+ attributes.each { |column| write_attribute(column, current_time) }
282
+ # ensure a version is written even if the `:on` collection is empty
283
+ record_update(true) if paper_trail_options[:on] == []
284
+ save!(:validate => false)
285
+ end
286
+
287
+ private
288
+
289
+ def source_version
290
+ send self.class.version_association_name
291
+ end
292
+
293
+ def record_create
294
+ if paper_trail_switched_on?
295
+ data = {
296
+ :event => paper_trail_event || 'create',
297
+ :whodunnit => PaperTrail.whodunnit
298
+ }
299
+ if respond_to?(:created_at)
300
+ data[PaperTrail.timestamp_field] = created_at
301
+ end
302
+ if paper_trail_options[:save_changes] && changed_notably? && self.class.paper_trail_version_class.column_names.include?('object_changes')
303
+ data[:object_changes] = self.class.paper_trail_version_class.object_changes_col_is_json? ? changes_for_paper_trail :
304
+ PaperTrail.serializer.dump(changes_for_paper_trail)
305
+ end
306
+ if self.class.paper_trail_version_class.column_names.include?('transaction_id')
307
+ data[:transaction_id] = PaperTrail.transaction_id
308
+ end
309
+ version = send(self.class.versions_association_name).create! merge_metadata(data)
310
+ set_transaction_id(version)
311
+ save_associations(version)
312
+ end
313
+ end
314
+
315
+ def record_update(force = nil)
316
+ if paper_trail_switched_on? && (force || changed_notably?)
317
+ object_attrs = object_attrs_for_paper_trail(attributes_before_change)
318
+ data = {
319
+ :event => paper_trail_event || 'update',
320
+ :object => self.class.paper_trail_version_class.object_col_is_json? ? object_attrs : PaperTrail.serializer.dump(object_attrs),
321
+ :whodunnit => PaperTrail.whodunnit
322
+ }
323
+ if respond_to?(:updated_at)
324
+ data[PaperTrail.timestamp_field] = updated_at
325
+ end
326
+ if paper_trail_options[:save_changes] && self.class.paper_trail_version_class.column_names.include?('object_changes')
327
+ data[:object_changes] = self.class.paper_trail_version_class.object_changes_col_is_json? ? changes_for_paper_trail :
328
+ PaperTrail.serializer.dump(changes_for_paper_trail)
329
+ end
330
+ if self.class.paper_trail_version_class.column_names.include?('transaction_id')
331
+ data[:transaction_id] = PaperTrail.transaction_id
332
+ end
333
+ version = send(self.class.versions_association_name).create merge_metadata(data)
334
+ set_transaction_id(version)
335
+ save_associations(version)
336
+ end
337
+ end
338
+
339
+ def changes_for_paper_trail
340
+ _changes = changes.delete_if { |k,v| !notably_changed.include?(k) }
341
+ if PaperTrail.serialized_attributes?
342
+ self.class.serialize_attribute_changes_for_paper_trail!(_changes)
343
+ end
344
+ _changes.to_hash
345
+ end
346
+
347
+ # Invoked via`after_update` callback for when a previous version is reified and then saved
348
+ def clear_version_instance!
349
+ send("#{self.class.version_association_name}=", nil)
350
+ end
351
+
352
+ def reset_timestamp_attrs_for_update_if_needed!
353
+ return if self.live? # invoked via callback when a user attempts to persist a reified `Version`
354
+ timestamp_attributes_for_update_in_model.each do |column|
355
+ # ActiveRecord 4.2 deprecated `reset_column!` in favor of `restore_column!`
356
+ if respond_to?("restore_#{column}!")
357
+ send("restore_#{column}!")
358
+ else
359
+ send("reset_#{column}!")
360
+ end
361
+ end
362
+ end
363
+
364
+ def record_destroy
365
+ if paper_trail_switched_on? and not new_record?
366
+ object_attrs = object_attrs_for_paper_trail(attributes_before_change)
367
+ data = {
368
+ :item_id => self.id,
369
+ :item_type => self.class.base_class.name,
370
+ :event => paper_trail_event || 'destroy',
371
+ :object => self.class.paper_trail_version_class.object_col_is_json? ? object_attrs : PaperTrail.serializer.dump(object_attrs),
372
+ :whodunnit => PaperTrail.whodunnit
373
+ }
374
+ if self.class.paper_trail_version_class.column_names.include?('transaction_id')
375
+ data[:transaction_id] = PaperTrail.transaction_id
376
+ end
377
+ version = self.class.paper_trail_version_class.create(merge_metadata(data))
378
+ send("#{self.class.version_association_name}=", version)
379
+ send(self.class.versions_association_name).send :load_target
380
+ set_transaction_id(version)
381
+ save_associations(version)
382
+ end
383
+ end
384
+
385
+ # saves associations if the join table for `VersionAssociation` exists
386
+ def save_associations(version)
387
+ return unless PaperTrail.config.track_associations?
388
+ self.class.reflect_on_all_associations(:belongs_to).each do |assoc|
389
+ assoc_version_args = {
390
+ :version_id => version.id,
391
+ :foreign_key_name => assoc.foreign_key
392
+ }
393
+
394
+ if assoc.options[:polymorphic]
395
+ associated_record = send(assoc.name) if send(assoc.foreign_type)
396
+ if associated_record && associated_record.class.paper_trail_enabled_for_model?
397
+ assoc_version_args.merge!(:foreign_key_id => associated_record.id)
398
+ end
399
+ elsif assoc.klass.paper_trail_enabled_for_model?
400
+ assoc_version_args.merge!(:foreign_key_id => send(assoc.foreign_key))
401
+ end
402
+
403
+ PaperTrail::VersionAssociation.create(assoc_version_args) if assoc_version_args.has_key?(:foreign_key_id)
404
+ end
405
+ end
406
+
407
+ def set_transaction_id(version)
408
+ return unless self.class.paper_trail_version_class.column_names.include?('transaction_id')
409
+ if PaperTrail.transaction? && PaperTrail.transaction_id.nil?
410
+ PaperTrail.transaction_id = version.id
411
+ version.transaction_id = version.id
412
+ version.save
413
+ end
414
+ end
415
+
416
+ def reset_transaction_id
417
+ PaperTrail.transaction_id = nil
418
+ end
419
+
420
+ def merge_metadata(data)
421
+ # First we merge the model-level metadata in `meta`.
422
+ paper_trail_options[:meta].each do |k,v|
423
+ data[k] =
424
+ if v.respond_to?(:call)
425
+ v.call(self)
426
+ elsif v.is_a?(Symbol) && respond_to?(v)
427
+ # if it is an attribute that is changing in an existing object,
428
+ # be sure to grab the current version
429
+ if has_attribute?(v) && send("#{v}_changed?".to_sym) && data[:event] != 'create'
430
+ send("#{v}_was".to_sym)
431
+ else
432
+ send(v)
433
+ end
434
+ else
435
+ v
436
+ end
437
+ end
438
+ # Second we merge any extra data from the controller (if available).
439
+ data.merge(PaperTrail.controller_info || {})
440
+ end
441
+
442
+ def attributes_before_change
443
+ attributes.tap do |prev|
444
+ enums = self.respond_to?(:defined_enums) ? self.defined_enums : {}
445
+ changed_attributes.select { |k,v| self.class.column_names.include?(k) }.each do |attr, before|
446
+ before = enums[attr][before] if enums[attr]
447
+ prev[attr] = before
448
+ end
449
+ end
450
+ end
451
+
452
+ # returns hash of attributes (with appropriate attributes serialized),
453
+ # ommitting attributes to be skipped
454
+ def object_attrs_for_paper_trail(attributes_hash)
455
+ attrs = attributes_hash.except(*self.paper_trail_options[:skip])
456
+ if PaperTrail.serialized_attributes?
457
+ self.class.serialize_attributes_for_paper_trail!(attrs)
458
+ end
459
+ attrs
460
+ end
461
+
462
+ # This method is invoked in order to determine whether it is appropriate to generate a new version instance.
463
+ # Because we are now using `after_(create/update/etc)` callbacks, we need to go out of our way to
464
+ # ensure that during updates timestamp attributes are not acknowledged as a notable changes
465
+ # to raise false positives when attributes are ignored.
466
+ def changed_notably?
467
+ if self.paper_trail_options[:ignore].any? && (changed & self.paper_trail_options[:ignore]).any?
468
+ (notably_changed - timestamp_attributes_for_update_in_model.map(&:to_s)).any?
469
+ else
470
+ notably_changed.any?
471
+ end
472
+ end
473
+
474
+ def notably_changed
475
+ only = self.paper_trail_options[:only].dup
476
+ # remove Hash arguments and then evaluate whether the attributes (the keys of the hash) should also get pushed into the collection
477
+ only.delete_if do |obj|
478
+ obj.is_a?(Hash) && obj.each { |attr, condition| only << attr if condition.respond_to?(:call) && condition.call(self) }
479
+ end
480
+ only.empty? ? changed_and_not_ignored : (changed_and_not_ignored & only)
481
+ end
482
+
483
+ def changed_and_not_ignored
484
+ ignore = self.paper_trail_options[:ignore].dup
485
+ # remove Hash arguments and then evaluate whether the attributes (the keys of the hash) should also get pushed into the collection
486
+ ignore.delete_if do |obj|
487
+ obj.is_a?(Hash) && obj.each { |attr, condition| ignore << attr if condition.respond_to?(:call) && condition.call(self) }
488
+ end
489
+ skip = self.paper_trail_options[:skip]
490
+ changed - ignore - skip
491
+ end
492
+
493
+ def paper_trail_switched_on?
494
+ PaperTrail.enabled? && PaperTrail.enabled_for_controller? && self.paper_trail_enabled_for_model?
495
+ end
496
+
497
+ def save_version?
498
+ if_condition = self.paper_trail_options[:if]
499
+ unless_condition = self.paper_trail_options[:unless]
500
+ (if_condition.blank? || if_condition.call(self)) && !unless_condition.try(:call, self)
501
+ end
502
+ end
503
+ end
504
+ end
@@ -0,0 +1,85 @@
1
+ require 'active_support/concern'
2
+
3
+ module LoginAttackReport
4
+ module LoginAttackReportVersionConcern
5
+ extend ::ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ def login_ok_limit_over
9
+ # TODO config
10
+ model = :User
11
+ login_limit = 200
12
+ login_err_limit = 50
13
+
14
+ PaperTrail::Version
15
+ .where(item_type: model)
16
+ .where(
17
+ 'created_at >= ? and created_at <= ? and ' \
18
+ 'object_changes like \'%sign_in_count:%\'',
19
+ Time.now.prev_month.beginning_of_month,
20
+ Time.now.prev_month.end_of_month
21
+ ).group(:item_id).having("count(item_id) > #{login_limit}")
22
+ end
23
+
24
+ def alert_login_ng_limit_over
25
+ # TODO config
26
+ model = :User
27
+ login_limit = 200
28
+ login_err_limit = 50
29
+
30
+ PaperTrail::Version
31
+ .where(item_type: model)
32
+ .where(
33
+ 'created_at >= ? and created_at <= ? and ' \
34
+ 'object_changes like \'%sign_in_count:%\'',
35
+ Time.now.prev_month.beginning_of_month,
36
+ Time.now.prev_month.end_of_month
37
+ ).group(:item_id).having("count(item_id) > #{login_limit}")
38
+ end
39
+ def alert_ip_limit_over
40
+ # TODO config
41
+ model = :User
42
+ login_limit = 200
43
+ login_err_limit = 50
44
+ alert_ip_limit_over = PaperTrail::Version
45
+ .where(item_type: model)
46
+ .where(
47
+ 'created_at >= ? and created_at <= ? and ' \
48
+ '(object_changes like \'%sign_in_count:%\' or ' \
49
+ 'object_changes like \'--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\nfailed_attempts:%\'' \
50
+ ')',
51
+ Time.now.prev_month.beginning_of_month,
52
+ Time.now.prev_month.end_of_month
53
+ )
54
+
55
+ if alert_ip_limit_over.present?
56
+ ok_hash = Hash.new({})
57
+ ng_hash = Hash.new({})
58
+ alert_ip_limit_over.find_each do |version|
59
+ # アクセス元ipアドレス取得
60
+ if /current_sign_in_ip/ =~ version.object_changes
61
+ current_sign_in_ip = YAML.load(version.object_changes)['current_sign_in_ip'][1]
62
+ else
63
+ current_sign_in_ip = YAML.load(version.object)['current_sign_in_ip']
64
+ end
65
+ # ログイン成功回数取得
66
+ if /sign_in_count/ =~ version.object_changes
67
+ if ok_hash[current_sign_in_ip].present?
68
+ ok_hash[current_sign_in_ip] += 1
69
+ else
70
+ ok_hash[current_sign_in_ip] = 1
71
+ end
72
+ # ログイン失敗回数取得
73
+ else
74
+ if ng_hash[current_sign_in_ip].present?
75
+ ng_hash[current_sign_in_ip] += 1
76
+ else
77
+ ng_hash[current_sign_in_ip] = 1
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -1,3 +1,3 @@
1
1
  module LoginAttackReport
2
- VERSION = "0.0.1"
2
+ VERSION = '0.0.2'
3
3
  end
@@ -1,4 +1,14 @@
1
- require "login_attack_report/version"
1
+ require 'login_attack_report/version'
2
+ require 'active_support'
3
+ require 'active_record'
4
+ require 'paper_trail'
5
+ require 'rails'
6
+
7
+ Dir[File.join(File.dirname(__FILE__), 'login_attack_report', '*.rb')].each do |file|
8
+ require File.join('login_attack_report', File.basename(file, '.rb'))
9
+ end
10
+ require 'login_attack_report/frameworks/active_record'
11
+ require 'login_attack_report/frameworks/rails'
2
12
 
3
13
  module LoginAttackReport
4
14
  # Your code goes here...
@@ -6,3 +16,7 @@ module LoginAttackReport
6
16
  "call new_method"
7
17
  end
8
18
  end
19
+
20
+ ActiveSupport.on_load(:active_record) do
21
+ include LoginAttackReport::Model
22
+ end
@@ -19,13 +19,16 @@ Gem::Specification.new do |spec|
19
19
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
20
  spec.require_paths = ['lib']
21
21
 
22
- spec.required_rubygems_version = '>= 1.3.6'
22
+ spec.required_rubygems_version = '>= 1.9.0'
23
23
 
24
- spec.add_dependency 'rails', '>= 4.0.0'
25
- spec.add_dependency 'paper_trail', '>= 3.0.0'
24
+ spec.add_dependency 'rails', ['>= 3.0', '< 6.0']
25
+ spec.add_dependency 'activerecord', ['>= 3.0', '< 6.0']
26
+ spec.add_dependency 'activesupport', ['>= 3.0', '< 6.0']
27
+ spec.add_dependency 'paper_trail', ['>= 3.0', '< 6.0']
26
28
  spec.add_dependency 'devise', '>= 3.2.2'
27
29
 
28
30
  spec.add_development_dependency 'bundler', '~> 1.7'
29
31
  spec.add_development_dependency 'rake', '~> 10.0'
30
32
  spec.add_development_dependency 'rspec'
33
+ spec.add_development_dependency 'pry'
31
34
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: login_attack_report
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - taru m
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-18 00:00:00.000000000 Z
11
+ date: 2015-06-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -16,28 +16,80 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 4.0.0
19
+ version: '3.0'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '6.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '3.0'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '6.0'
33
+ - !ruby/object:Gem::Dependency
34
+ name: activerecord
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '3.0'
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: '6.0'
20
43
  type: :runtime
21
44
  prerelease: false
22
45
  version_requirements: !ruby/object:Gem::Requirement
23
46
  requirements:
24
47
  - - ">="
25
48
  - !ruby/object:Gem::Version
26
- version: 4.0.0
49
+ version: '3.0'
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: '6.0'
53
+ - !ruby/object:Gem::Dependency
54
+ name: activesupport
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '3.0'
60
+ - - "<"
61
+ - !ruby/object:Gem::Version
62
+ version: '6.0'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '3.0'
70
+ - - "<"
71
+ - !ruby/object:Gem::Version
72
+ version: '6.0'
27
73
  - !ruby/object:Gem::Dependency
28
74
  name: paper_trail
29
75
  requirement: !ruby/object:Gem::Requirement
30
76
  requirements:
31
77
  - - ">="
32
78
  - !ruby/object:Gem::Version
33
- version: 3.0.0
79
+ version: '3.0'
80
+ - - "<"
81
+ - !ruby/object:Gem::Version
82
+ version: '6.0'
34
83
  type: :runtime
35
84
  prerelease: false
36
85
  version_requirements: !ruby/object:Gem::Requirement
37
86
  requirements:
38
87
  - - ">="
39
88
  - !ruby/object:Gem::Version
40
- version: 3.0.0
89
+ version: '3.0'
90
+ - - "<"
91
+ - !ruby/object:Gem::Version
92
+ version: '6.0'
41
93
  - !ruby/object:Gem::Dependency
42
94
  name: devise
43
95
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +146,20 @@ dependencies:
94
146
  - - ">="
95
147
  - !ruby/object:Gem::Version
96
148
  version: '0'
149
+ - !ruby/object:Gem::Dependency
150
+ name: pry
151
+ requirement: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ version: '0'
156
+ type: :development
157
+ prerelease: false
158
+ version_requirements: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - ">="
161
+ - !ruby/object:Gem::Version
162
+ version: '0'
97
163
  description: login attack report in Rails.
98
164
  email:
99
165
  - Write your email address
@@ -109,6 +175,12 @@ files:
109
175
  - README.md
110
176
  - Rakefile
111
177
  - lib/login_attack_report.rb
178
+ - lib/login_attack_report/frameworks/active_record.rb
179
+ - lib/login_attack_report/frameworks/active_record/models/login_attack_report_version.rb
180
+ - lib/login_attack_report/frameworks/rails.rb
181
+ - lib/login_attack_report/frameworks/rails/engine.rb
182
+ - lib/login_attack_report/has_login_attack_report.rb
183
+ - lib/login_attack_report/login_attack_report_version_concern.rb
112
184
  - lib/login_attack_report/version.rb
113
185
  - login_attack_report.gemspec
114
186
  - spec/login_attack_report_spec.rb
@@ -130,7 +202,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
130
202
  requirements:
131
203
  - - ">="
132
204
  - !ruby/object:Gem::Version
133
- version: 1.3.6
205
+ version: 1.9.0
134
206
  requirements: []
135
207
  rubyforge_project:
136
208
  rubygems_version: 2.2.0