devise_signin_loggable 0.1.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/.gitignore +4 -0
- data/.rspec +1 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +51 -0
- data/Rakefile +2 -0
- data/devise_signin_loggable.gemspec +30 -0
- data/lib/devise_signin_loggable.rb +27 -0
- data/lib/devise_signin_loggable/logged_signin.rb +25 -0
- data/lib/devise_signin_loggable/model.rb +10 -0
- data/lib/devise_signin_loggable/version.rb +3 -0
- data/lib/generators/devise_signin_loggable/install_generator.rb +28 -0
- data/lib/generators/devise_signin_loggable/templates/20140319161305_create_logged_signins.rb +11 -0
- data/spec/lib/logged_signin_spec.rb +79 -0
- data/spec/spec_helper.rb +32 -0
- metadata +161 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
signin
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-1.9.3
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Aaron Klaassen
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# devise\_signin\_loggable
|
2
|
+
|
3
|
+
Every time a user signs in, log the time and IP.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this to your Gemfile:
|
8
|
+
|
9
|
+
gem 'devise_signin_loggable'
|
10
|
+
|
11
|
+
And run:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it with:
|
16
|
+
|
17
|
+
$ gem install devise_signin_loggable
|
18
|
+
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
After installation, run in the install generator and migrations:
|
23
|
+
|
24
|
+
rails generate devise_signin_loggable:install
|
25
|
+
rake db:migrate
|
26
|
+
|
27
|
+
The user models for which you want to log signins, add `:signin_loggable` to
|
28
|
+
the list of included devise modules:
|
29
|
+
|
30
|
+
devise :signin_loggable
|
31
|
+
|
32
|
+
## Configuration
|
33
|
+
|
34
|
+
Since a table of every signin by every user can obviously grow quickly, there
|
35
|
+
are two options to clear out old data: either by limiting the number of signins
|
36
|
+
stored per user, or by only keeping signins less than a given age.
|
37
|
+
|
38
|
+
In `devise.rb`, the generator adds the following lines:
|
39
|
+
|
40
|
+
# config.max_logged_signins_per_user = 50
|
41
|
+
# config.remove_logged_signins_older_than = 3.months
|
42
|
+
|
43
|
+
Uncomment and edit the values to suit. You can enable both options, but a given
|
44
|
+
log need only meet *one* of the conditions to be deleted.
|
45
|
+
|
46
|
+
## Author
|
47
|
+
|
48
|
+
Aaron Klaassen
|
49
|
+
aaron@outerspacehero.com
|
50
|
+
http://www.outerspacehero.com/
|
51
|
+
[@aaronklaassen](https://www.twitter.com/aaronklaassen/)
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "devise_signin_loggable/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "devise_signin_loggable"
|
7
|
+
s.version = DeviseSigninLoggable::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Aaron Klaassen"]
|
10
|
+
s.email = ["aaron@outerspacehero.com"]
|
11
|
+
s.homepage = "https://github.com/aaronklaassen/devise_signin_loggable"
|
12
|
+
s.summary = %q{Log your users' sign-ins.}
|
13
|
+
s.description = %q{Every time a user signs in, log the time and IP.}
|
14
|
+
s.license = "MIT"
|
15
|
+
|
16
|
+
s.rubyforge_project = "devise_signin_loggable"
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
|
23
|
+
s.add_dependency 'devise', '>= 3.0.0'
|
24
|
+
s.add_dependency 'activerecord', '>= 3.2.0'
|
25
|
+
|
26
|
+
s.add_development_dependency 'rspec', '~> 2.14.1'
|
27
|
+
s.add_development_dependency 'shoulda', '~> 3.5.0'
|
28
|
+
s.add_development_dependency 'sqlite3'
|
29
|
+
s.add_development_dependency 'pry'
|
30
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'devise'
|
2
|
+
require 'devise_signin_loggable/logged_signin'
|
3
|
+
|
4
|
+
module Devise
|
5
|
+
|
6
|
+
# By default, log everything.
|
7
|
+
mattr_accessor :max_logged_signins_per_user
|
8
|
+
@@max_signin_logs_per_user = nil
|
9
|
+
|
10
|
+
mattr_accessor :remove_logged_signins_older_than
|
11
|
+
@@remove_logged_signins_older_than = nil
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
Warden::Manager.after_authentication do |user,auth,opts|
|
16
|
+
|
17
|
+
# This can't be the right way to do this.
|
18
|
+
if user.class.ancestors.any? { |a| a.to_s.include?("SigninLoggable") }
|
19
|
+
|
20
|
+
log = Devise::Models::LoggedSignin.new
|
21
|
+
log.resource = user
|
22
|
+
log.ip_address = auth.request.remote_ip
|
23
|
+
log.save
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
Devise.add_module(:signin_loggable, :model => 'devise_signin_loggable/model')
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Devise
|
2
|
+
module Models
|
3
|
+
class LoggedSignin < ::ActiveRecord::Base
|
4
|
+
belongs_to :resource, polymorphic: true
|
5
|
+
|
6
|
+
validates :resource, :ip_address, presence: true
|
7
|
+
|
8
|
+
after_create :remove_old_signins
|
9
|
+
|
10
|
+
scope :by_resource, lambda { |res| where(resource_id: res.id, resource_type: res.class.to_s) }
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def remove_old_signins
|
15
|
+
by_user = Devise::Models::LoggedSignin.by_resource(resource)
|
16
|
+
|
17
|
+
min_date = resource.class.remove_logged_signins_older_than
|
18
|
+
max_signins = resource.class.max_logged_signins_per_user
|
19
|
+
|
20
|
+
by_user.where("created_at <= ?", min_date).destroy_all if min_date
|
21
|
+
by_user.reverse_order.offset(max_signins).destroy_all if max_signins
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module DeviseSigninLoggable
|
2
|
+
class InstallGenerator < Rails::Generators::Base
|
3
|
+
|
4
|
+
source_root File.expand_path("../templates", __FILE__)
|
5
|
+
|
6
|
+
def create_default_devise_settings
|
7
|
+
inject_into_file "config/initializers/devise.rb", default_devise_settings, :after => "Devise.setup do |config|\n"
|
8
|
+
end
|
9
|
+
|
10
|
+
def copy_migration_file
|
11
|
+
copy_file '20140319161305_create_logged_signins.rb', 'db/migrate/20140319161305_create_logged_signins.rb'
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def default_devise_settings
|
17
|
+
settings = <<-eof
|
18
|
+
# ==> Signin logging Configuration
|
19
|
+
# config.max_logged_signins_per_user = 50
|
20
|
+
# config.remove_logged_signins_older_than = 3.months
|
21
|
+
|
22
|
+
eof
|
23
|
+
|
24
|
+
settings
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Devise::Models::LoggedSignin do
|
4
|
+
|
5
|
+
def create_logged_signin(user, weeks_old = 0)
|
6
|
+
log = Devise::Models::LoggedSignin.new
|
7
|
+
log.resource = user
|
8
|
+
log.ip_address = "127.0.0.1"
|
9
|
+
log.created_at = weeks_old.weeks.ago
|
10
|
+
log.save!
|
11
|
+
log
|
12
|
+
end
|
13
|
+
|
14
|
+
before :each do
|
15
|
+
@user = User.create!
|
16
|
+
9.times { |i| create_logged_signin(@user, i) }
|
17
|
+
5.times { |i| create_logged_signin(User.create!, i) }
|
18
|
+
end
|
19
|
+
|
20
|
+
after :each do
|
21
|
+
User.destroy_all
|
22
|
+
Devise::Models::LoggedSignin.destroy_all
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "associations" do
|
26
|
+
it { should belong_to(:resource) }
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "removing previous signins on creation" do
|
30
|
+
|
31
|
+
context "devise is configured to remove old signins" do
|
32
|
+
it "deletes logged signins that are too old" do
|
33
|
+
User.stub(:remove_logged_signins_older_than).and_return(4.weeks.ago)
|
34
|
+
expect {
|
35
|
+
create_logged_signin(@user)
|
36
|
+
}.to change(Devise::Models::LoggedSignin, :count).by(-4)
|
37
|
+
|
38
|
+
cutoff = User.remove_logged_signins_older_than
|
39
|
+
old = Devise::Models::LoggedSignin.where("resource_id = ? AND created_at <= ?", @user.id, cutoff)
|
40
|
+
old.size.should be_zero
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "devise is not configured to remove old signins" do
|
45
|
+
it "does not delete any logged signins" do
|
46
|
+
User.stub(:remove_logged_signins_older_than).and_return(nil)
|
47
|
+
expect {
|
48
|
+
create_logged_signin(@user)
|
49
|
+
}.to change(Devise::Models::LoggedSignin, :count).by(1)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "devise is configured to limit logged signins per user" do
|
54
|
+
it "deletes logged signins when there are too many" do
|
55
|
+
User.stub(:max_logged_signins_per_user).and_return(7)
|
56
|
+
create_logged_signin(@user)
|
57
|
+
Devise::Models::LoggedSignin.by_resource(@user).count.should == 7
|
58
|
+
Devise::Models::LoggedSignin.count.should == 12
|
59
|
+
end
|
60
|
+
|
61
|
+
it "deletes the oldest signins and keeps the newest ones" do
|
62
|
+
User.stub(:max_logged_signins_per_user).and_return(7)
|
63
|
+
all_logins = Devise::Models::LoggedSignin.by_resource(@user).to_a
|
64
|
+
new_log = create_logged_signin(@user)
|
65
|
+
Devise::Models::LoggedSignin.by_resource(@user).should == all_logins.last(6) + [new_log]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "devise is not configured to limit logged signins" do
|
70
|
+
it "does not delete any logged signins" do
|
71
|
+
User.stub(:max_logged_signins_per_user).and_return(nil)
|
72
|
+
expect {
|
73
|
+
create_logged_signin(@user)
|
74
|
+
}.to change(Devise::Models::LoggedSignin, :count).by(1)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'shoulda'
|
3
|
+
require 'pry'
|
4
|
+
require 'devise_signin_loggable'
|
5
|
+
|
6
|
+
RSpec.configure do |config|
|
7
|
+
config.order = "random"
|
8
|
+
|
9
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
10
|
+
ActiveRecord::Schema.define do
|
11
|
+
self.verbose = false
|
12
|
+
|
13
|
+
# See the generator migration.
|
14
|
+
create_table :logged_signins do |t|
|
15
|
+
|
16
|
+
t.references :resource, polymorphic: true
|
17
|
+
t.string :ip_address
|
18
|
+
|
19
|
+
t.timestamps
|
20
|
+
end
|
21
|
+
|
22
|
+
# A stub for the specs.
|
23
|
+
create_table :users do |t|
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class User < ActiveRecord::Base;
|
30
|
+
def self.remove_logged_signins_older_than; end
|
31
|
+
def self.max_logged_signins_per_user; end
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: devise_signin_loggable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Aaron Klaassen
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-03-24 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: devise
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.0.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 3.0.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: activerecord
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 3.2.0
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 3.2.0
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 2.14.1
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.14.1
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: shoulda
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 3.5.0
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 3.5.0
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: sqlite3
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: pry
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
description: Every time a user signs in, log the time and IP.
|
111
|
+
email:
|
112
|
+
- aaron@outerspacehero.com
|
113
|
+
executables: []
|
114
|
+
extensions: []
|
115
|
+
extra_rdoc_files: []
|
116
|
+
files:
|
117
|
+
- .gitignore
|
118
|
+
- .rspec
|
119
|
+
- .ruby-gemset
|
120
|
+
- .ruby-version
|
121
|
+
- Gemfile
|
122
|
+
- LICENSE
|
123
|
+
- README.md
|
124
|
+
- Rakefile
|
125
|
+
- devise_signin_loggable.gemspec
|
126
|
+
- lib/devise_signin_loggable.rb
|
127
|
+
- lib/devise_signin_loggable/logged_signin.rb
|
128
|
+
- lib/devise_signin_loggable/model.rb
|
129
|
+
- lib/devise_signin_loggable/version.rb
|
130
|
+
- lib/generators/devise_signin_loggable/install_generator.rb
|
131
|
+
- lib/generators/devise_signin_loggable/templates/20140319161305_create_logged_signins.rb
|
132
|
+
- spec/lib/logged_signin_spec.rb
|
133
|
+
- spec/spec_helper.rb
|
134
|
+
homepage: https://github.com/aaronklaassen/devise_signin_loggable
|
135
|
+
licenses:
|
136
|
+
- MIT
|
137
|
+
post_install_message:
|
138
|
+
rdoc_options: []
|
139
|
+
require_paths:
|
140
|
+
- lib
|
141
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
142
|
+
none: false
|
143
|
+
requirements:
|
144
|
+
- - ! '>='
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '0'
|
147
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
148
|
+
none: false
|
149
|
+
requirements:
|
150
|
+
- - ! '>='
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
requirements: []
|
154
|
+
rubyforge_project: devise_signin_loggable
|
155
|
+
rubygems_version: 1.8.24
|
156
|
+
signing_key:
|
157
|
+
specification_version: 3
|
158
|
+
summary: Log your users' sign-ins.
|
159
|
+
test_files:
|
160
|
+
- spec/lib/logged_signin_spec.rb
|
161
|
+
- spec/spec_helper.rb
|