sorcery 0.1.0 → 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.
Files changed (75) hide show
  1. data/Gemfile +4 -3
  2. data/Gemfile.lock +16 -13
  3. data/LICENSE.txt +1 -1
  4. data/README.rdoc +77 -63
  5. data/Rakefile +7 -12
  6. data/VERSION +1 -1
  7. data/lib/sorcery/controller/submodules/activity_logging.rb +58 -0
  8. data/lib/sorcery/controller/submodules/brute_force_protection.rb +15 -69
  9. data/lib/sorcery/controller/submodules/http_basic_auth.rb +65 -0
  10. data/lib/sorcery/controller/submodules/oauth/oauth1.rb +25 -0
  11. data/lib/sorcery/controller/submodules/oauth/oauth2.rb +23 -0
  12. data/lib/sorcery/controller/submodules/oauth/providers/facebook.rb +64 -0
  13. data/lib/sorcery/controller/submodules/oauth/providers/twitter.rb +61 -0
  14. data/lib/sorcery/controller/submodules/oauth.rb +95 -0
  15. data/lib/sorcery/controller/submodules/remember_me.rb +14 -5
  16. data/lib/sorcery/controller/submodules/session_timeout.rb +10 -2
  17. data/lib/sorcery/controller.rb +40 -22
  18. data/lib/sorcery/crypto_providers/bcrypt.rb +2 -6
  19. data/lib/sorcery/engine.rb +9 -2
  20. data/lib/sorcery/model/submodules/activity_logging.rb +40 -0
  21. data/lib/sorcery/model/submodules/brute_force_protection.rb +79 -0
  22. data/lib/sorcery/model/submodules/oauth.rb +53 -0
  23. data/lib/sorcery/model/submodules/remember_me.rb +6 -2
  24. data/lib/sorcery/model/submodules/reset_password.rb +96 -0
  25. data/lib/sorcery/model/submodules/user_activation.rb +44 -23
  26. data/lib/sorcery/model/temporary_token.rb +22 -0
  27. data/lib/sorcery/model.rb +22 -9
  28. data/lib/sorcery/test_helpers.rb +84 -0
  29. data/lib/sorcery.rb +17 -1
  30. data/sorcery.gemspec +250 -0
  31. data/spec/Gemfile +3 -2
  32. data/spec/Gemfile.lock +15 -2
  33. data/spec/rails3/app_root/.rspec +1 -0
  34. data/spec/rails3/{Gemfile → app_root/Gemfile} +6 -3
  35. data/spec/rails3/{Gemfile.lock → app_root/Gemfile.lock} +27 -2
  36. data/spec/rails3/app_root/app/controllers/application_controller.rb +48 -2
  37. data/spec/rails3/app_root/app/models/authentication.rb +3 -0
  38. data/spec/rails3/app_root/app/models/user.rb +4 -1
  39. data/spec/rails3/app_root/config/application.rb +1 -3
  40. data/spec/rails3/app_root/config/routes.rb +1 -9
  41. data/spec/rails3/app_root/db/migrate/activation/20101224223622_add_activation_to_users.rb +6 -4
  42. data/spec/rails3/app_root/db/migrate/activity_logging/20101224223624_add_activity_logging_to_users.rb +17 -0
  43. data/spec/rails3/app_root/db/migrate/brute_force_protection/20101224223626_add_brute_force_protection_to_users.rb +11 -0
  44. data/spec/rails3/app_root/db/migrate/core/20101224223620_create_users.rb +4 -4
  45. data/spec/rails3/app_root/db/migrate/oauth/20101224223628_create_authentications.rb +14 -0
  46. data/spec/rails3/app_root/db/migrate/reset_password/20101224223622_add_reset_password_to_users.rb +13 -0
  47. data/spec/rails3/app_root/spec/controller_activity_logging_spec.rb +84 -0
  48. data/spec/rails3/app_root/spec/controller_brute_force_protection_spec.rb +65 -0
  49. data/spec/rails3/app_root/spec/controller_http_basic_auth_spec.rb +50 -0
  50. data/spec/rails3/app_root/spec/controller_oauth2_spec.rb +117 -0
  51. data/spec/rails3/app_root/spec/controller_oauth_spec.rb +117 -0
  52. data/spec/rails3/{controller_remember_me_spec.rb → app_root/spec/controller_remember_me_spec.rb} +4 -4
  53. data/spec/rails3/{controller_session_timeout_spec.rb → app_root/spec/controller_session_timeout_spec.rb} +5 -4
  54. data/spec/rails3/{controller_spec.rb → app_root/spec/controller_spec.rb} +23 -16
  55. data/spec/rails3/app_root/spec/spec_helper.orig.rb +27 -0
  56. data/spec/rails3/app_root/spec/spec_helper.rb +61 -0
  57. data/spec/rails3/{user_activation_spec.rb → app_root/spec/user_activation_spec.rb} +62 -22
  58. data/spec/rails3/app_root/spec/user_activity_logging_spec.rb +36 -0
  59. data/spec/rails3/app_root/spec/user_brute_force_protection_spec.rb +76 -0
  60. data/spec/rails3/app_root/spec/user_oauth_spec.rb +39 -0
  61. data/spec/rails3/{user_remember_me_spec.rb → app_root/spec/user_remember_me_spec.rb} +4 -4
  62. data/spec/rails3/app_root/spec/user_reset_password_spec.rb +178 -0
  63. data/spec/rails3/{user_spec.rb → app_root/spec/user_spec.rb} +75 -41
  64. metadata +147 -102
  65. data/features/support/env.rb +0 -13
  66. data/lib/sorcery/model/submodules/password_reset.rb +0 -64
  67. data/spec/rails3/app_root/db/migrate/password_reset/20101224223622_add_password_reset_to_users.rb +0 -9
  68. data/spec/rails3/app_root/test/fixtures/users.yml +0 -9
  69. data/spec/rails3/app_root/test/performance/browsing_test.rb +0 -9
  70. data/spec/rails3/app_root/test/test_helper.rb +0 -13
  71. data/spec/rails3/app_root/test/unit/user_test.rb +0 -8
  72. data/spec/rails3/controller_brute_force_protection_spec.rb +0 -72
  73. data/spec/rails3/spec_helper.rb +0 -115
  74. data/spec/rails3/user_password_reset_spec.rb +0 -76
  75. /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
- gherkin (2.3.2)
45
- json (~> 1.4.6)
46
- term-ansicolor (~> 1.0.5)
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.4.6)
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
- rails (= 3.0.3)
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
@@ -1,4 +1,4 @@
1
- Copyright (c) 2010 Noam Ben-Ari
1
+ Copyright (c) 2010 Noam Ben-Ari <mailto:nbenari@gmail.com>
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.rdoc CHANGED
@@ -3,56 +3,88 @@ 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
- == Example app using sorcery:
8
+ == Summary
8
9
 
9
- https://github.com/NoamB/sorcery-example-app
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:
10
12
 
11
- == Current Features:
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
12
24
 
13
- * Basic Login/Logout.
14
- * Password encryption with configurable algorithm.
25
+ Documentation: http://rubydoc.info/gems/sorcery/0.1.4/frames
26
+
27
+ == Full Features List by module:
28
+
29
+ Core (see lib/sorcery/model/model.rb and lib/sorcery/controller/controller.rb):
30
+ * login/logout, optional return user to requested url on login, configurable redirect for non-logged-in users.
31
+ * password encryption, algorithms: bcrypt(default), md5, sha1, sha256, sha512, aes256, custom(yours!), none. Configurable stretches and salt.
32
+ * configurable attribute names for username, password and email.
33
+
34
+ User Activation (see lib/sorcery/model/submodules/user_activation.rb):
15
35
  * User activation by email with optional success email.
36
+ * configurable attribute names.
37
+ * configurable mailer, method name, and attribute name.
38
+ * configurable temporary token expiration.
39
+ * Optionally prevent non-active users to login.
40
+
41
+ Reset Password (see lib/sorcery/model/submodules/reset_password.rb):
16
42
  * Reset password with email verification.
43
+ * configurable mailer, method name, and attribute name.
44
+ * configurable temporary token expiration.
45
+ * configurable time between emails (hammering protection).
46
+
47
+ Remember Me (see lib/sorcery/model/submodules/remember_me.rb):
17
48
  * Remember me with configurable expiration.
49
+ * configurable attribute names.
50
+
51
+ Session Timeout (see lib/sorcery/controller/submodules/session_timeout.rb):
18
52
  * Configurable session timeout.
19
- * Brute force login hammering protection.
20
- * Modular design, load only the modules you need.
21
- * 100% TDD'd code, 100% test coverage.
53
+ * Optionally session timeout will be calculated from last user action.
22
54
 
23
- == Planned Features:
55
+ Brute Force Protection (see lib/sorcery/model/submodules/brute_force_protection.rb):
56
+ * Brute force login hammering protection.
57
+ * configurable logins before lock and lock duration.
24
58
 
25
- I've got many plans which include:
26
- * Basic HTTP Authentication
27
- * Auto login
28
- * Hammering reset password protection
29
- * Other reset password strategies (security questions?)
30
- * Sinatra support
31
- * Mongoid support
32
- * OmniAuth integration
33
- * Activity logging
34
- * Have an idea? Let me know, and it might get into the gem!
59
+ Basic HTTP Authentication (see lib/sorcery/controller/submodules/http_basic_auth.rb):
60
+ * A before filter for requesting authentication with HTTP Basic.
61
+ * automatic login from HTTP Basic.
62
+ * automatic login is disabled if session key changed.
35
63
 
36
- == Project Goals:
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.
37
68
 
38
- This gem plugin was started out of a few personal goals which are not related to the problem solved by it at all:
39
- * I wanted to write something 100% TDD from start to finish.
40
- * I wanted to learn how to write an engine for Rails 3.
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.
41
72
 
42
- 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:
43
- * Simple & short configuration as possible, not drowning in syntactic sugar.
44
- * Keep MVC cleanly separated - DB is for models, sessions are for controllers. Models stay unaware of sessions.
45
- * Magic yes, Voodoo no.
46
- * No generated code polluting the application's code.
47
- * No built-in controllers, models, mailers, migrations or templates; Real apps will need all of these custom made.
73
+ == Next Planned Features:
48
74
 
49
- Hopefully, I've achieved this. If not, let me know.
75
+ I've got many plans which include (by priority):
76
+ * Sinatra support
77
+ * Mongoid support
78
+ * Configurable Auto login on registration/activation
79
+ * Other reset password strategies (security questions?)
80
+ * Other brute force protection strategies (captcha)
81
+ * Have an idea? Let me know, and it might get into the gem!
50
82
 
51
83
  == Installation:
52
84
 
53
- 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),
54
86
 
55
- In the future will be available:
87
+ Or simply (stable):
56
88
 
57
89
  gem install sorcery
58
90
 
@@ -80,7 +112,7 @@ For example:
80
112
  2. app/models/user.rb (or another model of your choice)
81
113
 
82
114
  activate_sorcery! do |config|
83
- config.sorcery_mailer = MyMailer
115
+ config.user_activation_mailer = MyMailer
84
116
  config.username_attribute_name = :email
85
117
  end
86
118
 
@@ -92,45 +124,27 @@ For example:
92
124
 
93
125
  Also check the migrations in the example app to see what database fields are expected.
94
126
 
95
-
96
-
97
127
  The configuration options vary with the modules you've chosen to use.
98
128
 
99
- == Basic Configuration (in Model):
100
-
101
- see lib/sorcery/model.rb
102
-
103
- == User Activation Configuration (in Model):
104
-
105
- see lib/sorcery/model/submodules/user_activation.rb
106
-
107
- == Remember Me Configuration (in Model):
108
-
109
- see lib/sorcery/model/submodules/remember_me.rb
110
-
111
- == Password Reset Configuration (in Model):
112
-
113
- see lib/sorcery/model/submodules/password_reset.rb
114
-
115
-
116
-
117
- == Session Timeout Configuration (in Controller or config/application.rb):
118
-
119
- see lib/sorcery/controller/submodules/session_timeout.rb
120
-
121
- == Brute Force Protection Configuration (in Controller or config/application.rb):
122
-
123
- see lib/sorcery/controller/submodules/brute_force_protection.rb
124
129
 
130
+ == Contributing to sorcery
125
131
 
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:
126
135
 
127
- == Contributing to sorcery
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.
128
141
 
129
- I can use help of any kind, be it comments on code (code review), suggestions, features, bug reports, bug fixes and even a donation.
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.
130
143
 
131
144
  == Contact
132
145
 
133
- email: nbenari@gmail.com
146
+ email: nbenari@gmail.com ( also for paypal )
147
+ twitter: @nbenari
134
148
 
135
149
  == Copyright
136
150
 
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,30 +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'
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'
25
31
  end
26
32
  Jeweler::RubygemsDotOrgTasks.new
27
33
 
28
34
  require 'rspec/core'
29
35
  require 'rspec/core/rake_task'
36
+
30
37
  RSpec::Core::RakeTask.new(:spec) do |spec|
31
38
  spec.pattern = FileList['spec/**/*_spec.rb']
32
39
  end
33
40
 
34
- RSpec::Core::RakeTask.new(:rcov) do |spec|
35
- spec.pattern = 'spec/**/*_spec.rb'
36
- spec.rcov = true
37
- end
38
-
39
- require 'cucumber/rake/task'
40
- Cucumber::Rake::Task.new(:features)
41
-
42
-
43
-
44
41
  require 'yard'
45
42
  YARD::Rake::YardocTask.new
46
43
 
47
-
48
- #task :default => :spec
49
44
  desc 'Default: Run all specs.'
50
45
  task :default => :all_specs
51
46
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.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
- Config.module_eval do
8
- class << self
9
- attr_accessor :login_retries_amount_allowed, # how many failed logins allowed.
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
- def release_ban_if_time_passed(now)
45
- if now - session[:ban_start_time] > Config.login_ban_time_period
46
- session[:banned] = nil
47
- session[:failed_logins] = 0
48
- return true
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
- def default_banned_action
83
- render :nothing => true
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
@@ -0,0 +1,65 @@
1
+ module Sorcery
2
+ module Controller
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.
7
+ module HttpBasicAuth
8
+ def self.included(base)
9
+ base.send(:include, InstanceMethods)
10
+ Config.module_eval do
11
+ class << self
12
+ attr_accessor :controller_to_realm_map # how many failed logins allowed.
13
+
14
+ def merge_http_basic_auth_defaults!
15
+ @defaults.merge!(:@controller_to_realm_map => {"application" => "Application"})
16
+ end
17
+ end
18
+ merge_http_basic_auth_defaults!
19
+ end
20
+ Config.login_sources << :login_from_basic_auth
21
+ end
22
+
23
+ module InstanceMethods
24
+
25
+ protected
26
+
27
+ # to be used as a before_filter.
28
+ # The method sets a session when requesting the user's credentials.
29
+ # This is a trick to overcome the way HTTP authentication works (explained below):
30
+ #
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.
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'.
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.
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?
38
+ end
39
+
40
+ # given to main controller module as a login source callback
41
+ def login_from_basic_auth
42
+ authenticate_with_http_basic do |username, password|
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
46
+ end
47
+ end
48
+
49
+ # Sets the realm name by searching the controller name in the hash given at configuration time.
50
+ def realm_name_by_controller
51
+ current_controller = self.class
52
+ while current_controller != ActionController::Base
53
+ result = Config.controller_to_realm_map[current_controller.controller_name]
54
+ return result if result
55
+ current_controller = self.class.superclass
56
+ end
57
+ nil
58
+ end
59
+
60
+ end
61
+
62
+ end
63
+ end
64
+ end
65
+ end
@@ -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