login_attack_report 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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