sorcery 0.1.3 → 0.2.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 +4 -3
- data/Gemfile.lock +16 -13
- data/LICENSE.txt +1 -1
- data/README.rdoc +53 -36
- data/Rakefile +7 -13
- data/VERSION +1 -1
- data/lib/sorcery/controller/submodules/activity_logging.rb +58 -0
- data/lib/sorcery/controller/submodules/brute_force_protection.rb +15 -69
- data/lib/sorcery/controller/submodules/http_basic_auth.rb +13 -5
- data/lib/sorcery/controller/submodules/oauth/oauth1.rb +25 -0
- data/lib/sorcery/controller/submodules/oauth/oauth2.rb +23 -0
- data/lib/sorcery/controller/submodules/oauth/providers/facebook.rb +64 -0
- data/lib/sorcery/controller/submodules/oauth/providers/twitter.rb +61 -0
- data/lib/sorcery/controller/submodules/oauth.rb +95 -0
- data/lib/sorcery/controller/submodules/remember_me.rb +14 -5
- data/lib/sorcery/controller/submodules/session_timeout.rb +10 -2
- data/lib/sorcery/controller.rb +40 -22
- data/lib/sorcery/crypto_providers/bcrypt.rb +1 -5
- data/lib/sorcery/engine.rb +9 -2
- data/lib/sorcery/model/submodules/activity_logging.rb +40 -0
- data/lib/sorcery/model/submodules/brute_force_protection.rb +79 -0
- data/lib/sorcery/model/submodules/oauth.rb +53 -0
- data/lib/sorcery/model/submodules/remember_me.rb +6 -2
- data/lib/sorcery/model/submodules/reset_password.rb +96 -0
- data/lib/sorcery/model/submodules/user_activation.rb +40 -19
- data/lib/sorcery/model/temporary_token.rb +22 -0
- data/lib/sorcery/model.rb +10 -3
- data/lib/sorcery/test_helpers.rb +84 -0
- data/lib/sorcery.rb +16 -1
- data/sorcery.gemspec +84 -43
- data/spec/Gemfile +3 -2
- data/spec/Gemfile.lock +15 -2
- data/spec/rails3/app_root/.rspec +1 -0
- data/spec/rails3/{Gemfile → app_root/Gemfile} +5 -3
- data/spec/rails3/{Gemfile.lock → app_root/Gemfile.lock} +25 -2
- data/spec/rails3/app_root/app/controllers/application_controller.rb +44 -3
- data/spec/rails3/app_root/app/models/authentication.rb +3 -0
- data/spec/rails3/app_root/app/models/user.rb +4 -1
- data/spec/rails3/app_root/config/application.rb +1 -3
- data/spec/rails3/app_root/config/routes.rb +1 -10
- data/spec/rails3/app_root/db/migrate/activation/20101224223622_add_activation_to_users.rb +6 -4
- data/spec/rails3/app_root/db/migrate/activity_logging/20101224223624_add_activity_logging_to_users.rb +17 -0
- data/spec/rails3/app_root/db/migrate/brute_force_protection/20101224223626_add_brute_force_protection_to_users.rb +11 -0
- data/spec/rails3/app_root/db/migrate/core/20101224223620_create_users.rb +4 -4
- data/spec/rails3/app_root/db/migrate/oauth/20101224223628_create_authentications.rb +14 -0
- data/spec/rails3/app_root/db/migrate/reset_password/20101224223622_add_reset_password_to_users.rb +13 -0
- data/spec/rails3/app_root/spec/controller_activity_logging_spec.rb +84 -0
- data/spec/rails3/app_root/spec/controller_brute_force_protection_spec.rb +65 -0
- data/spec/rails3/{controller_http_basic_auth_spec.rb → app_root/spec/controller_http_basic_auth_spec.rb} +13 -3
- data/spec/rails3/app_root/spec/controller_oauth2_spec.rb +117 -0
- data/spec/rails3/app_root/spec/controller_oauth_spec.rb +117 -0
- data/spec/rails3/{controller_remember_me_spec.rb → app_root/spec/controller_remember_me_spec.rb} +4 -4
- data/spec/rails3/{controller_session_timeout_spec.rb → app_root/spec/controller_session_timeout_spec.rb} +5 -4
- data/spec/rails3/{controller_spec.rb → app_root/spec/controller_spec.rb} +23 -16
- data/spec/rails3/app_root/spec/spec_helper.orig.rb +27 -0
- data/spec/rails3/app_root/spec/spec_helper.rb +61 -0
- data/spec/rails3/{user_activation_spec.rb → app_root/spec/user_activation_spec.rb} +60 -20
- data/spec/rails3/app_root/spec/user_activity_logging_spec.rb +36 -0
- data/spec/rails3/app_root/spec/user_brute_force_protection_spec.rb +76 -0
- data/spec/rails3/app_root/spec/user_oauth_spec.rb +39 -0
- data/spec/rails3/{user_remember_me_spec.rb → app_root/spec/user_remember_me_spec.rb} +4 -4
- data/spec/rails3/app_root/spec/user_reset_password_spec.rb +178 -0
- data/spec/rails3/{user_spec.rb → app_root/spec/user_spec.rb} +68 -38
- metadata +144 -63
- data/features/support/env.rb +0 -13
- data/lib/sorcery/model/submodules/password_reset.rb +0 -64
- data/spec/rails3/app_root/db/migrate/password_reset/20101224223622_add_password_reset_to_users.rb +0 -9
- data/spec/rails3/app_root/test/fixtures/users.yml +0 -9
- data/spec/rails3/app_root/test/performance/browsing_test.rb +0 -9
- data/spec/rails3/app_root/test/test_helper.rb +0 -13
- data/spec/rails3/app_root/test/unit/user_test.rb +0 -8
- data/spec/rails3/controller_brute_force_protection_spec.rb +0 -72
- data/spec/rails3/spec_helper.rb +0 -115
- data/spec/rails3/user_password_reset_spec.rb +0 -76
- /data/spec/rails3/{Rakefile → app_root/Rakefile} +0 -0
data/Gemfile
CHANGED
|
@@ -2,17 +2,18 @@ source "http://rubygems.org"
|
|
|
2
2
|
# Add dependencies required to use your gem here.
|
|
3
3
|
# Example:
|
|
4
4
|
# gem "activesupport", ">= 2.3.5"
|
|
5
|
-
|
|
5
|
+
gem "rails", ">= 3.0.0"
|
|
6
|
+
gem 'json', ">= 1.5.1"
|
|
7
|
+
gem 'oauth', ">= 0.4.4"
|
|
8
|
+
gem 'oauth2', ">= 0.1.1"
|
|
6
9
|
# Add dependencies to develop your gem here.
|
|
7
10
|
# Include everything needed to run rake, tests, features, etc.
|
|
8
11
|
group :development do
|
|
9
|
-
gem "rails", "3.0.3"
|
|
10
12
|
gem "rspec", "~> 2.3.0"
|
|
11
13
|
gem 'rspec-rails'
|
|
12
14
|
gem 'ruby-debug19'
|
|
13
15
|
gem 'sqlite3-ruby', :require => 'sqlite3'
|
|
14
16
|
gem "yard", "~> 0.6.0"
|
|
15
|
-
gem "cucumber", ">= 0"
|
|
16
17
|
gem "bundler", "~> 1.0.0"
|
|
17
18
|
gem "jeweler", "~> 1.5.2"
|
|
18
19
|
gem 'simplecov', '>= 0.3.8', :require => false # Will install simplecov-html as a dependency
|
data/Gemfile.lock
CHANGED
|
@@ -28,29 +28,25 @@ GEM
|
|
|
28
28
|
activemodel (= 3.0.3)
|
|
29
29
|
activesupport (= 3.0.3)
|
|
30
30
|
activesupport (3.0.3)
|
|
31
|
+
addressable (2.2.4)
|
|
31
32
|
archive-tar-minitar (0.5.2)
|
|
32
33
|
arel (2.0.6)
|
|
33
34
|
builder (2.1.2)
|
|
34
35
|
columnize (0.3.2)
|
|
35
|
-
cucumber (0.10.0)
|
|
36
|
-
builder (>= 2.1.2)
|
|
37
|
-
diff-lcs (~> 1.1.2)
|
|
38
|
-
gherkin (~> 2.3.2)
|
|
39
|
-
json (~> 1.4.6)
|
|
40
|
-
term-ansicolor (~> 1.0.5)
|
|
41
36
|
diff-lcs (1.1.2)
|
|
42
37
|
erubis (2.6.6)
|
|
43
38
|
abstract (>= 1.0.0)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
39
|
+
faraday (0.5.5)
|
|
40
|
+
addressable (~> 2.2.4)
|
|
41
|
+
multipart-post (~> 1.1.0)
|
|
42
|
+
rack (>= 1.1.0, < 2)
|
|
47
43
|
git (1.2.5)
|
|
48
44
|
i18n (0.5.0)
|
|
49
45
|
jeweler (1.5.2)
|
|
50
46
|
bundler (~> 1.0.0)
|
|
51
47
|
git (>= 1.2.5)
|
|
52
48
|
rake
|
|
53
|
-
json (1.
|
|
49
|
+
json (1.5.1)
|
|
54
50
|
linecache19 (0.5.11)
|
|
55
51
|
ruby_core_source (>= 0.1.4)
|
|
56
52
|
mail (2.2.13)
|
|
@@ -59,6 +55,12 @@ GEM
|
|
|
59
55
|
mime-types (~> 1.16)
|
|
60
56
|
treetop (~> 1.4.8)
|
|
61
57
|
mime-types (1.16)
|
|
58
|
+
multi_json (0.0.5)
|
|
59
|
+
multipart-post (1.1.0)
|
|
60
|
+
oauth (0.4.4)
|
|
61
|
+
oauth2 (0.1.1)
|
|
62
|
+
faraday (~> 0.5.0)
|
|
63
|
+
multi_json (~> 0.0.4)
|
|
62
64
|
polyglot (0.3.1)
|
|
63
65
|
rack (1.2.1)
|
|
64
66
|
rack-mount (0.6.13)
|
|
@@ -106,7 +108,6 @@ GEM
|
|
|
106
108
|
simplecov-html (>= 0.3.7)
|
|
107
109
|
simplecov-html (0.3.9)
|
|
108
110
|
sqlite3-ruby (1.3.2)
|
|
109
|
-
term-ansicolor (1.0.5)
|
|
110
111
|
thor (0.14.6)
|
|
111
112
|
treetop (1.4.9)
|
|
112
113
|
polyglot (>= 0.3.1)
|
|
@@ -118,9 +119,11 @@ PLATFORMS
|
|
|
118
119
|
|
|
119
120
|
DEPENDENCIES
|
|
120
121
|
bundler (~> 1.0.0)
|
|
121
|
-
cucumber
|
|
122
122
|
jeweler (~> 1.5.2)
|
|
123
|
-
|
|
123
|
+
json (>= 1.5.1)
|
|
124
|
+
oauth (>= 0.4.4)
|
|
125
|
+
oauth2 (>= 0.1.1)
|
|
126
|
+
rails (>= 3.0.0)
|
|
124
127
|
rspec (~> 2.3.0)
|
|
125
128
|
rspec-rails
|
|
126
129
|
ruby-debug19
|
data/LICENSE.txt
CHANGED
data/README.rdoc
CHANGED
|
@@ -3,27 +3,46 @@ Magical Authentication for Rails 3.
|
|
|
3
3
|
|
|
4
4
|
Inspired by restful_authentication, Authlogic and Devise.
|
|
5
5
|
Crypto code taken almost unchanged from Authlogic.
|
|
6
|
+
OAuth code inspired by OmniAuth.
|
|
6
7
|
|
|
7
|
-
==
|
|
8
|
+
== Summary
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
Sorcery aims to make your life easier by giving you an easy API to write your own user authentication flow with.
|
|
11
|
+
It does this with a few goals in mind:
|
|
12
|
+
|
|
13
|
+
* Less is more - less than 20 simple methods to remember for the entire feature-set make the lib easy to 'get'.
|
|
14
|
+
* No built-in or generated code - use the API inside *your own* MVC structures, and don't fight to fix someone else's.
|
|
15
|
+
* Magic yes, Voodoo no - the lib should be easy to hack for most developers.
|
|
16
|
+
* Configuration over Confusion - Simple & short configuration as possible, not drowning in syntactic sugar.
|
|
17
|
+
* Keep MVC cleanly separated - DB is for models, sessions are for controllers. Models stay unaware of sessions.
|
|
18
|
+
|
|
19
|
+
Hopefully, I've achieved this. If not, let me know.
|
|
20
|
+
|
|
21
|
+
== Useful Links:
|
|
22
|
+
|
|
23
|
+
Example app using sorcery: https://github.com/NoamB/sorcery-example-app
|
|
24
|
+
|
|
25
|
+
Documentation: http://rubydoc.info/gems/sorcery/0.1.4/frames
|
|
10
26
|
|
|
11
27
|
== Full Features List by module:
|
|
12
28
|
|
|
13
29
|
Core (see lib/sorcery/model/model.rb and lib/sorcery/controller/controller.rb):
|
|
14
|
-
* login/logout, optional
|
|
30
|
+
* login/logout, optional return user to requested url on login, configurable redirect for non-logged-in users.
|
|
15
31
|
* password encryption, algorithms: bcrypt(default), md5, sha1, sha256, sha512, aes256, custom(yours!), none. Configurable stretches and salt.
|
|
16
32
|
* configurable attribute names for username, password and email.
|
|
17
33
|
|
|
18
34
|
User Activation (see lib/sorcery/model/submodules/user_activation.rb):
|
|
19
35
|
* User activation by email with optional success email.
|
|
20
36
|
* configurable attribute names.
|
|
21
|
-
* configurable mailer.
|
|
22
|
-
*
|
|
37
|
+
* configurable mailer, method name, and attribute name.
|
|
38
|
+
* configurable temporary token expiration.
|
|
39
|
+
* Optionally prevent non-active users to login.
|
|
23
40
|
|
|
24
|
-
Password
|
|
41
|
+
Reset Password (see lib/sorcery/model/submodules/reset_password.rb):
|
|
25
42
|
* Reset password with email verification.
|
|
26
43
|
* configurable mailer, method name, and attribute name.
|
|
44
|
+
* configurable temporary token expiration.
|
|
45
|
+
* configurable time between emails (hammering protection).
|
|
27
46
|
|
|
28
47
|
Remember Me (see lib/sorcery/model/submodules/remember_me.rb):
|
|
29
48
|
* Remember me with configurable expiration.
|
|
@@ -33,51 +52,39 @@ Session Timeout (see lib/sorcery/controller/submodules/session_timeout.rb):
|
|
|
33
52
|
* Configurable session timeout.
|
|
34
53
|
* Optionally session timeout will be calculated from last user action.
|
|
35
54
|
|
|
36
|
-
Brute Force Protection (see lib/sorcery/
|
|
55
|
+
Brute Force Protection (see lib/sorcery/model/submodules/brute_force_protection.rb):
|
|
37
56
|
* Brute force login hammering protection.
|
|
38
|
-
* configurable logins before
|
|
57
|
+
* configurable logins before lock and lock duration.
|
|
39
58
|
|
|
40
59
|
Basic HTTP Authentication (see lib/sorcery/controller/submodules/http_basic_auth.rb):
|
|
41
60
|
* A before filter for requesting authentication with HTTP Basic.
|
|
42
61
|
* automatic login from HTTP Basic.
|
|
43
62
|
* automatic login is disabled if session key changed.
|
|
44
63
|
|
|
45
|
-
|
|
46
|
-
*
|
|
47
|
-
*
|
|
64
|
+
Activity Logging (see lib/sorcery/model/submodules/activity_logging.rb):
|
|
65
|
+
* automatic logging of last login, last logout and last activity time.
|
|
66
|
+
* an easy method of collecting the list of currently logged in users.
|
|
67
|
+
* configurable timeout by which to decide whether to include a user in the list of logged in users.
|
|
68
|
+
|
|
69
|
+
Oauth (see lib/sorcery/controller/submodules/oauth.rb):
|
|
70
|
+
* OAuth1 and OAuth2 support (currently twitter & facebook)
|
|
71
|
+
* configurable db field names and authentications table.
|
|
48
72
|
|
|
49
73
|
== Next Planned Features:
|
|
50
74
|
|
|
51
|
-
I've got many plans which include:
|
|
52
|
-
* Hammering reset password protection
|
|
53
|
-
* Configurable Auto login on registration/activation
|
|
54
|
-
* Other reset password strategies (security questions?)
|
|
75
|
+
I've got many plans which include (by priority):
|
|
55
76
|
* Sinatra support
|
|
56
77
|
* Mongoid support
|
|
57
|
-
*
|
|
58
|
-
*
|
|
78
|
+
* Configurable Auto login on registration/activation
|
|
79
|
+
* Other reset password strategies (security questions?)
|
|
80
|
+
* Other brute force protection strategies (captcha)
|
|
59
81
|
* Have an idea? Let me know, and it might get into the gem!
|
|
60
82
|
|
|
61
|
-
== Project Goals:
|
|
62
|
-
|
|
63
|
-
This gem plugin was started out of a few personal goals which are not related to the problem solved by it at all:
|
|
64
|
-
* I wanted to write something 100% TDD from start to finish.
|
|
65
|
-
* I wanted to learn how to write an engine for Rails 3.
|
|
66
|
-
|
|
67
|
-
In addition to the above goals, when I decided this will be an authentication plugin, and while looking at existing solutions, these goals came up:
|
|
68
|
-
* Simple & short configuration as possible, not drowning in syntactic sugar.
|
|
69
|
-
* Keep MVC cleanly separated - DB is for models, sessions are for controllers. Models stay unaware of sessions.
|
|
70
|
-
* Magic yes, Voodoo no.
|
|
71
|
-
* No generated code polluting the application's code.
|
|
72
|
-
* No built-in controllers, models, mailers, migrations or templates; Real apps will need all of these custom made.
|
|
73
|
-
|
|
74
|
-
Hopefully, I've achieved this. If not, let me know.
|
|
75
|
-
|
|
76
83
|
== Installation:
|
|
77
84
|
|
|
78
|
-
You can either git clone and then 'rake install',
|
|
85
|
+
You can either git clone and then 'rake install' to live on the edge (unstable),
|
|
79
86
|
|
|
80
|
-
|
|
87
|
+
Or simply (stable):
|
|
81
88
|
|
|
82
89
|
gem install sorcery
|
|
83
90
|
|
|
@@ -122,11 +129,21 @@ The configuration options vary with the modules you've chosen to use.
|
|
|
122
129
|
|
|
123
130
|
== Contributing to sorcery
|
|
124
131
|
|
|
125
|
-
|
|
132
|
+
Your feedback is very welcome and will make this gem much much better for you, me and everyone else.
|
|
133
|
+
Besides feedback on code, features, suggestions and bug reports, you may want to actually make an impact on the code.
|
|
134
|
+
For this:
|
|
135
|
+
|
|
136
|
+
* Fork the project.
|
|
137
|
+
* Make your feature addition or bug fix.
|
|
138
|
+
* Add tests for it. This is important so I don’t break it in a future version unintentionally.
|
|
139
|
+
* Commit, do not mess with Rakefiles, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
|
140
|
+
* Send me a pull request. Bonus points for topic branches.
|
|
141
|
+
|
|
142
|
+
If you feel my work has made your life easier, and you would like to thank me through a donation, my paypal email is in the contact details.
|
|
126
143
|
|
|
127
144
|
== Contact
|
|
128
145
|
|
|
129
|
-
email: nbenari@gmail.com
|
|
146
|
+
email: nbenari@gmail.com ( also for paypal )
|
|
130
147
|
twitter: @nbenari
|
|
131
148
|
|
|
132
149
|
== Copyright
|
data/Rakefile
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# require 'bundler'
|
|
2
|
+
# -- Commented because it's slow
|
|
2
3
|
# begin
|
|
3
4
|
# Bundler.setup(:default, :development)
|
|
4
5
|
# rescue Bundler::BundlerError => e
|
|
@@ -6,6 +7,8 @@
|
|
|
6
7
|
# $stderr.puts "Run `bundle install` to install missing gems"
|
|
7
8
|
# exit e.status_code
|
|
8
9
|
# end
|
|
10
|
+
# --
|
|
11
|
+
|
|
9
12
|
require 'rake'
|
|
10
13
|
|
|
11
14
|
require 'jeweler'
|
|
@@ -22,31 +25,22 @@ Jeweler::Tasks.new do |gem|
|
|
|
22
25
|
# and development dependencies are only needed for development (ie running rake tasks, tests, etc)
|
|
23
26
|
# gem.add_runtime_dependency 'jabber4r', '> 0.1'
|
|
24
27
|
# gem.add_development_dependency 'rspec', '> 1.2.3'
|
|
25
|
-
|
|
28
|
+
gem.add_runtime_dependency 'bcrypt-ruby', '~> 2.1.4'
|
|
29
|
+
gem.add_runtime_dependency 'oauth', '>= 0.4.4'
|
|
30
|
+
gem.add_runtime_dependency 'oauth2', '>= 0.1.1'
|
|
26
31
|
end
|
|
27
32
|
Jeweler::RubygemsDotOrgTasks.new
|
|
28
33
|
|
|
29
34
|
require 'rspec/core'
|
|
30
35
|
require 'rspec/core/rake_task'
|
|
36
|
+
|
|
31
37
|
RSpec::Core::RakeTask.new(:spec) do |spec|
|
|
32
38
|
spec.pattern = FileList['spec/**/*_spec.rb']
|
|
33
39
|
end
|
|
34
40
|
|
|
35
|
-
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
|
36
|
-
spec.pattern = 'spec/**/*_spec.rb'
|
|
37
|
-
spec.rcov = true
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
require 'cucumber/rake/task'
|
|
41
|
-
Cucumber::Rake::Task.new(:features)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
41
|
require 'yard'
|
|
46
42
|
YARD::Rake::YardocTask.new
|
|
47
43
|
|
|
48
|
-
|
|
49
|
-
#task :default => :spec
|
|
50
44
|
desc 'Default: Run all specs.'
|
|
51
45
|
task :default => :all_specs
|
|
52
46
|
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.2.0
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
module Sorcery
|
|
2
|
+
module Controller
|
|
3
|
+
module Submodules
|
|
4
|
+
# This submodule keeps track of events such as login, logout, and last activity time, per user.
|
|
5
|
+
# It helps in estimating which users are active now in the site.
|
|
6
|
+
# This cannot be determined absolutely because a user might be reading a page without clicking anything for a while.
|
|
7
|
+
|
|
8
|
+
# This is the controller part of the submodule, which adds hooks to register user events,
|
|
9
|
+
# and methods to collect active users data for use in the app.
|
|
10
|
+
# see Socery::Model::Submodules::ActivityLogging for configuration options.
|
|
11
|
+
module ActivityLogging
|
|
12
|
+
def self.included(base)
|
|
13
|
+
base.send(:include, InstanceMethods)
|
|
14
|
+
Config.after_login << :register_login_time_to_db
|
|
15
|
+
Config.before_logout << :register_logout_time_to_db
|
|
16
|
+
base.after_filter :register_last_activity_time_to_db
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
module InstanceMethods
|
|
20
|
+
# Returns an array of the active users.
|
|
21
|
+
def current_users
|
|
22
|
+
Config.user_class.current_users
|
|
23
|
+
# A possible patch here:
|
|
24
|
+
# we'll add the current_user to the users list if he's not in it (can happen when he was inactive for more than activity timeout):
|
|
25
|
+
#
|
|
26
|
+
# users.unshift!(current_user) if logged_in? && users.find {|u| u.id == current_user.id}.nil?
|
|
27
|
+
#
|
|
28
|
+
# disadvantages: can hurt performance.
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
protected
|
|
32
|
+
|
|
33
|
+
# registers last login time on every login.
|
|
34
|
+
# This runs as a hook just after a successful login.
|
|
35
|
+
def register_login_time_to_db(user, credentials)
|
|
36
|
+
user.send(:"#{user.sorcery_config.last_login_at_attribute_name}=", Time.now.utc.to_s(:db))
|
|
37
|
+
user.save!(:validate => false)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# registers last logout time on every logout.
|
|
41
|
+
# This runs as a hook just before a logout.
|
|
42
|
+
def register_logout_time_to_db(user)
|
|
43
|
+
user.send(:"#{user.sorcery_config.last_logout_at_attribute_name}=", Time.now.utc.to_s(:db))
|
|
44
|
+
user.save!(:validate => false)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Updates last activity time on every request.
|
|
48
|
+
# The only exception is logout - we do not update activity on logout
|
|
49
|
+
def register_last_activity_time_to_db
|
|
50
|
+
return if !logged_in?
|
|
51
|
+
current_user.send(:"#{current_user.sorcery_config.last_activity_at_attribute_name}=", Time.now.utc.to_s(:db))
|
|
52
|
+
current_user.save!(:validate => false)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -1,86 +1,32 @@
|
|
|
1
1
|
module Sorcery
|
|
2
2
|
module Controller
|
|
3
3
|
module Submodules
|
|
4
|
+
# This module helps protect user accounts by locking them down after too many failed attemps to login were detected.
|
|
5
|
+
# This is the controller part of the submodule which takes care of updating the failed logins and resetting them.
|
|
6
|
+
# See Sorcery::Model::Submodules::BruteForceProtection for configuration options.
|
|
4
7
|
module BruteForceProtection
|
|
5
8
|
def self.included(base)
|
|
6
9
|
base.send(:include, InstanceMethods)
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
:login_retries_time_period, # the time after which the failed logins counter is reset.
|
|
11
|
-
:login_ban_time_period, # how long the user should be banned. in seconds.
|
|
12
|
-
:banned_action # what controller action should be called when a banned user tries.
|
|
13
|
-
|
|
14
|
-
def merge_brute_force_protection_defaults!
|
|
15
|
-
@defaults.merge!(:@login_retries_amount_allowed => 50,
|
|
16
|
-
:@login_retries_time_period => 30,
|
|
17
|
-
:@login_ban_time_period => 3600,
|
|
18
|
-
:@banned_action => :default_banned_action)
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
merge_brute_force_protection_defaults!
|
|
22
|
-
end
|
|
23
|
-
Config.after_failed_login << :check_failed_logins_limit_reached
|
|
24
|
-
base.prepend_before_filter :deny_banned_user
|
|
10
|
+
|
|
11
|
+
Config.after_login << :reset_failed_logins_count!
|
|
12
|
+
Config.after_failed_login << :update_failed_logins_count!
|
|
25
13
|
end
|
|
26
14
|
|
|
27
15
|
module InstanceMethods
|
|
28
|
-
def check_failed_logins_limit_reached(user, credentials)
|
|
29
|
-
now = Time.now.utc
|
|
30
|
-
|
|
31
|
-
# not banned
|
|
32
|
-
if session[:first_failed_login_time]
|
|
33
|
-
reset_failed_logins_if_time_passed(now)
|
|
34
|
-
else
|
|
35
|
-
session[:first_failed_login_time] = now
|
|
36
|
-
end
|
|
37
|
-
increment_failed_logins
|
|
38
|
-
# ban
|
|
39
|
-
ban_if_above_limit(now)
|
|
40
|
-
end
|
|
41
16
|
|
|
42
17
|
protected
|
|
43
18
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
end
|
|
50
|
-
false
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def increment_failed_logins
|
|
54
|
-
session[:failed_logins] ||= 0
|
|
55
|
-
session[:failed_logins] += 1
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def reset_failed_logins_if_time_passed(now)
|
|
59
|
-
if now - session[:first_failed_login_time] > Config.login_retries_time_period
|
|
60
|
-
session[:failed_logins] = 0
|
|
61
|
-
session[:first_failed_login_time] = now
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def ban_if_above_limit(now)
|
|
66
|
-
if session[:failed_logins] > Config.login_retries_amount_allowed
|
|
67
|
-
session[:banned] = true
|
|
68
|
-
session[:ban_start_time] = now
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def deny_banned_user
|
|
73
|
-
if session[:banned]
|
|
74
|
-
now = Time.now.utc
|
|
75
|
-
release_ban_if_time_passed(now)
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
# if still banned
|
|
79
|
-
send(Config.banned_action) if session[:banned]
|
|
19
|
+
# Increments the failed logins counter on every failed login.
|
|
20
|
+
# Runs as a hook after a failed login.
|
|
21
|
+
def update_failed_logins_count!(credentials)
|
|
22
|
+
user = Config.user_class.where("#{Config.user_class.sorcery_config.username_attribute_name} = ?", credentials[0]).first
|
|
23
|
+
user.register_failed_login! if user
|
|
80
24
|
end
|
|
81
25
|
|
|
82
|
-
|
|
83
|
-
|
|
26
|
+
# Resets the failed logins counter.
|
|
27
|
+
# Runs as a hook after a successful login.
|
|
28
|
+
def reset_failed_logins_count!(user, credentials)
|
|
29
|
+
user.update_attributes!(Config.user_class.sorcery_config.failed_logins_count_attribute_name => 0)
|
|
84
30
|
end
|
|
85
31
|
end
|
|
86
32
|
end
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
module Sorcery
|
|
2
2
|
module Controller
|
|
3
3
|
module Submodules
|
|
4
|
+
# This submodule integrates HTTP Basic authentication into sorcery.
|
|
5
|
+
# You are provided with a before filter, require_login_from_http_basic, which requests the browser for authentication.
|
|
6
|
+
# Then the rest of the submodule takes care of logging the user in into the session, so that the next requests will keep him logged in.
|
|
4
7
|
module HttpBasicAuth
|
|
5
8
|
def self.included(base)
|
|
6
9
|
base.send(:include, InstanceMethods)
|
|
@@ -23,22 +26,27 @@ module Sorcery
|
|
|
23
26
|
|
|
24
27
|
# to be used as a before_filter.
|
|
25
28
|
# The method sets a session when requesting the user's credentials.
|
|
26
|
-
# This is a trick to overcome the way HTTP authentication
|
|
29
|
+
# This is a trick to overcome the way HTTP authentication works (explained below):
|
|
27
30
|
#
|
|
28
31
|
# Once the user fills the credentials once, the browser will always send it to the server when visiting the website, until the browser is closed.
|
|
29
32
|
# This causes wierd behaviour if the user logs out. The session is reset, yet the user is re-logged in by the before_filter calling 'login_from_basic_auth'.
|
|
30
33
|
# To overcome this, we set a session when requesting the password, which logout will reset, and that's how we know if we need to request for HTTP auth again.
|
|
31
|
-
def
|
|
32
|
-
request_http_basic_authentication(realm_name_by_controller) and (session[:http_authentication_used] = true) and return if request.authorization.nil? || session[:http_authentication_used].nil?
|
|
33
|
-
|
|
34
|
+
def require_login_from_http_basic
|
|
35
|
+
(request_http_basic_authentication(realm_name_by_controller) and (session[:http_authentication_used] = true) and return) if (request.authorization.nil? || session[:http_authentication_used].nil?)
|
|
36
|
+
require_login
|
|
37
|
+
session[:http_authentication_used] = nil unless logged_in?
|
|
34
38
|
end
|
|
35
39
|
|
|
40
|
+
# given to main controller module as a login source callback
|
|
36
41
|
def login_from_basic_auth
|
|
37
42
|
authenticate_with_http_basic do |username, password|
|
|
38
|
-
@
|
|
43
|
+
@current_user = (Config.user_class.authenticate(username, password) if session[:http_authentication_used]) || false
|
|
44
|
+
login_user(@current_user) if @current_user
|
|
45
|
+
@current_user
|
|
39
46
|
end
|
|
40
47
|
end
|
|
41
48
|
|
|
49
|
+
# Sets the realm name by searching the controller name in the hash given at configuration time.
|
|
42
50
|
def realm_name_by_controller
|
|
43
51
|
current_controller = self.class
|
|
44
52
|
while current_controller != ActionController::Base
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Sorcery
|
|
2
|
+
module Controller
|
|
3
|
+
module Submodules
|
|
4
|
+
module Oauth
|
|
5
|
+
module Oauth1
|
|
6
|
+
def oauth_version
|
|
7
|
+
"1.0"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def get_request_token
|
|
11
|
+
::OAuth::Consumer.new(@key, @secret, :site => @site).get_request_token(:oauth_callback => @callback_url)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def authorize_url(args)
|
|
15
|
+
args[:request_token].authorize_url(:oauth_callback => @callback_url)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def get_access_token(args)
|
|
19
|
+
args[:request_token].get_access_token(:oauth_verifier => args[:oauth_verifier])
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Sorcery
|
|
2
|
+
module Controller
|
|
3
|
+
module Submodules
|
|
4
|
+
module Oauth
|
|
5
|
+
module Oauth2
|
|
6
|
+
def oauth_version
|
|
7
|
+
"2.0"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def authorize_url(args)
|
|
11
|
+
client = ::OAuth2::Client.new(@key, @secret, :site => @site)
|
|
12
|
+
client.web_server.authorize_url(:redirect_uri => @callback_url, :scope => @scope)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def get_access_token(args)
|
|
16
|
+
client = ::OAuth2::Client.new(@key, @secret, :site => @site)
|
|
17
|
+
client.web_server.get_access_token(args[:code], :redirect_uri => @callback_url)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module Sorcery
|
|
2
|
+
module Controller
|
|
3
|
+
module Submodules
|
|
4
|
+
module Oauth
|
|
5
|
+
module Providers
|
|
6
|
+
# This module adds support for OAuth with facebook.com.
|
|
7
|
+
# When included in the 'config.providers' option, it adds a new option, 'config.facebook'.
|
|
8
|
+
# Via this new option you can configure Facebook specific settings like your app's key and secret.
|
|
9
|
+
#
|
|
10
|
+
# config.facebook.key = <key>
|
|
11
|
+
# config.facebook.secret = <secret>
|
|
12
|
+
# ...
|
|
13
|
+
#
|
|
14
|
+
module Facebook
|
|
15
|
+
def self.included(base)
|
|
16
|
+
base.module_eval do
|
|
17
|
+
class << self
|
|
18
|
+
attr_reader :facebook # access to facebook_client.
|
|
19
|
+
|
|
20
|
+
def merge_facebook_defaults!
|
|
21
|
+
@defaults.merge!(:@facebook => FacebookClient)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
merge_facebook_defaults!
|
|
25
|
+
update!
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
module FacebookClient
|
|
30
|
+
class << self
|
|
31
|
+
attr_accessor :key,
|
|
32
|
+
:secret,
|
|
33
|
+
:callback_url,
|
|
34
|
+
:site,
|
|
35
|
+
:user_info_path,
|
|
36
|
+
:scope,
|
|
37
|
+
:user_info_mapping
|
|
38
|
+
|
|
39
|
+
include Oauth2
|
|
40
|
+
|
|
41
|
+
def init
|
|
42
|
+
@site = "https://graph.facebook.com"
|
|
43
|
+
@user_info_path = "/me"
|
|
44
|
+
@scope = "email,offline_access"
|
|
45
|
+
@user_info_mapping = {}
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def get_user_hash(access_token)
|
|
49
|
+
user_hash = {}
|
|
50
|
+
response = access_token.get(@user_info_path)
|
|
51
|
+
user_hash[:user_info] = JSON.parse(response)
|
|
52
|
+
user_hash[:uid] = user_hash[:user_info]['id'].to_i
|
|
53
|
+
user_hash
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
init
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|