oath 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +3 -0
  5. data/Gemfile +3 -0
  6. data/Gemfile.lock +165 -0
  7. data/LICENSE.txt +22 -0
  8. data/NEWS.rdoc +118 -0
  9. data/README.md +384 -0
  10. data/Rakefile +6 -0
  11. data/lib/oath.rb +132 -0
  12. data/lib/oath/back_door.rb +53 -0
  13. data/lib/oath/configuration.rb +149 -0
  14. data/lib/oath/constraints/signed_in.rb +14 -0
  15. data/lib/oath/constraints/signed_out.rb +14 -0
  16. data/lib/oath/controller_helpers.rb +161 -0
  17. data/lib/oath/failure_app.rb +48 -0
  18. data/lib/oath/field_map.rb +56 -0
  19. data/lib/oath/param_transformer.rb +38 -0
  20. data/lib/oath/railtie.rb +11 -0
  21. data/lib/oath/services.rb +5 -0
  22. data/lib/oath/services/authentication.rb +40 -0
  23. data/lib/oath/services/password_reset.rb +27 -0
  24. data/lib/oath/services/sign_in.rb +25 -0
  25. data/lib/oath/services/sign_out.rb +24 -0
  26. data/lib/oath/services/sign_up.rb +42 -0
  27. data/lib/oath/strategies/password_strategy.rb +42 -0
  28. data/lib/oath/test/controller_helpers.rb +43 -0
  29. data/lib/oath/test/helpers.rb +24 -0
  30. data/lib/oath/version.rb +4 -0
  31. data/lib/oath/warden_setup.rb +47 -0
  32. data/oath.gemspec +30 -0
  33. data/spec/features/user/user_signs_in_spec.rb +14 -0
  34. data/spec/features/user/user_signs_in_through_back_door_spec.rb +11 -0
  35. data/spec/features/user/user_tries_to_access_constrained_routes_spec.rb +18 -0
  36. data/spec/features/user/user_tries_to_access_http_auth_page_spec.rb +9 -0
  37. data/spec/features/visitor/visitor_fails_to_sign_up_spec.rb +10 -0
  38. data/spec/features/visitor/visitor_is_unauthorized_spec.rb +8 -0
  39. data/spec/features/visitor/visitor_signs_in_via_invalid_form_spec.rb +11 -0
  40. data/spec/features/visitor/visitor_signs_up_spec.rb +40 -0
  41. data/spec/features/visitor/visitor_tries_to_access_constrained_routes_spec.rb +14 -0
  42. data/spec/features/visitor/visitor_uses_remember_token_spec.rb +13 -0
  43. data/spec/oath/configuration_spec.rb +11 -0
  44. data/spec/oath/controller_helpers_spec.rb +180 -0
  45. data/spec/oath/field_map_spec.rb +19 -0
  46. data/spec/oath/services/authentication_spec.rb +25 -0
  47. data/spec/oath/services/password_reset_spec.rb +24 -0
  48. data/spec/oath/services/sign_in_spec.rb +13 -0
  49. data/spec/oath/services/sign_out_spec.rb +13 -0
  50. data/spec/oath/services/sign_up_spec.rb +49 -0
  51. data/spec/oath/strategies/password_strategy_spec.rb +23 -0
  52. data/spec/oath/test_controller_helpers_spec.rb +63 -0
  53. data/spec/oath/test_helpers_spec.rb +97 -0
  54. data/spec/oath_spec.rb +27 -0
  55. data/spec/rails_app/Rakefile +7 -0
  56. data/spec/rails_app/app/assets/images/rails.png +0 -0
  57. data/spec/rails_app/app/assets/javascripts/application.js +13 -0
  58. data/spec/rails_app/app/assets/stylesheets/application.css +13 -0
  59. data/spec/rails_app/app/controllers/application_controller.rb +4 -0
  60. data/spec/rails_app/app/controllers/basic_auth_controller.rb +7 -0
  61. data/spec/rails_app/app/controllers/constrained_to_users_controller.rb +5 -0
  62. data/spec/rails_app/app/controllers/constrained_to_visitors_controller.rb +5 -0
  63. data/spec/rails_app/app/controllers/failures_controller.rb +5 -0
  64. data/spec/rails_app/app/controllers/invalid_sessions_controller.rb +2 -0
  65. data/spec/rails_app/app/controllers/posts_controller.rb +6 -0
  66. data/spec/rails_app/app/controllers/sessions_controller.rb +26 -0
  67. data/spec/rails_app/app/controllers/users_controller.rb +23 -0
  68. data/spec/rails_app/app/helpers/application_helper.rb +2 -0
  69. data/spec/rails_app/app/models/user.rb +10 -0
  70. data/spec/rails_app/app/views/invalid_sessions/new.html.erb +4 -0
  71. data/spec/rails_app/app/views/layouts/application.html.erb +18 -0
  72. data/spec/rails_app/app/views/posts/index.html.erb +1 -0
  73. data/spec/rails_app/app/views/sessions/new.html.erb +5 -0
  74. data/spec/rails_app/app/views/users/new.html.erb +5 -0
  75. data/spec/rails_app/config.ru +4 -0
  76. data/spec/rails_app/config/application.rb +58 -0
  77. data/spec/rails_app/config/boot.rb +6 -0
  78. data/spec/rails_app/config/database.yml +25 -0
  79. data/spec/rails_app/config/environment.rb +5 -0
  80. data/spec/rails_app/config/environments/development.rb +29 -0
  81. data/spec/rails_app/config/environments/production.rb +54 -0
  82. data/spec/rails_app/config/environments/test.rb +29 -0
  83. data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  84. data/spec/rails_app/config/initializers/inflections.rb +15 -0
  85. data/spec/rails_app/config/initializers/secret_token.rb +7 -0
  86. data/spec/rails_app/config/routes.rb +24 -0
  87. data/spec/rails_app/db/seeds.rb +7 -0
  88. data/spec/rails_app/public/404.html +26 -0
  89. data/spec/rails_app/public/422.html +26 -0
  90. data/spec/rails_app/public/500.html +25 -0
  91. data/spec/rails_app/public/favicon.ico +0 -0
  92. data/spec/rails_app/script/rails +6 -0
  93. data/spec/spec_helper.rb +37 -0
  94. metadata +325 -0
@@ -0,0 +1,47 @@
1
+ require 'warden'
2
+ require "oath/strategies/password_strategy"
3
+
4
+ module Oath
5
+ # Sets up warden specifics for working with oath
6
+ class WardenSetup
7
+ def initialize(warden_config)
8
+ @warden_config = warden_config
9
+ end
10
+
11
+ # Sets up warden specifics for working with oath:
12
+ # * Session serialization
13
+ # * Strategy
14
+ # * Failure app
15
+ def call
16
+ setup_warden_manager
17
+ setup_warden_strategies
18
+ setup_warden_config
19
+ end
20
+
21
+ private
22
+ attr_reader :warden_config
23
+
24
+ def setup_warden_manager
25
+ Warden::Manager.serialize_into_session(&serialize_into_session_method)
26
+ Warden::Manager.serialize_from_session(&serialize_from_session_method)
27
+ end
28
+
29
+ def setup_warden_strategies
30
+ Warden::Strategies.add(:password_strategy, Oath.config.authentication_strategy)
31
+ end
32
+
33
+ def setup_warden_config
34
+ warden_config.tap do |config|
35
+ config.failure_app = Oath.config.failure_app
36
+ end
37
+ end
38
+
39
+ def serialize_into_session_method
40
+ Oath.config.warden_serialize_into_session
41
+ end
42
+
43
+ def serialize_from_session_method
44
+ Oath.config.warden_serialize_from_session
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'oath/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "oath"
8
+ gem.version = Oath::VERSION
9
+ gem.authors = ["halogenandtoast", "calebthompson"]
10
+ gem.email = ["halogenandtoast@gmail.com"]
11
+ gem.description = %q{simple rails authentication}
12
+ gem.summary = %q{Making rails authentication as simple as possible}
13
+ gem.homepage = "https://github.com/halogenandtoast/oath"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency 'rails'
21
+ gem.add_dependency 'bcrypt'
22
+ gem.add_dependency 'warden'
23
+
24
+ gem.add_development_dependency 'rake'
25
+ gem.add_development_dependency 'rspec'
26
+ gem.add_development_dependency 'rspec-rails'
27
+ gem.add_development_dependency 'capybara'
28
+ gem.add_development_dependency 'sqlite3'
29
+ gem.add_development_dependency 'active_hash'
30
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'User signs in' do
4
+ scenario 'with mismatched email case' do
5
+ user = User.create!(email: "example@example.com", password_digest: "password")
6
+
7
+ visit sign_in_path
8
+ fill_in "session[email]", with: "Example@example.com"
9
+ fill_in "session[password]", with: "password"
10
+ click_button "go"
11
+
12
+ expect(current_path).to eq posts_path
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'User signs in through the back-door' do
4
+ scenario 'with the configured lookup field' do
5
+ user = User.create!
6
+
7
+ visit constrained_to_users_path(as: user)
8
+
9
+ expect(current_path).to eq constrained_to_users_path
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'User tries to access constrained routes' do
4
+ scenario 'they can access a route constrained to users' do
5
+ sign_in User.new
6
+
7
+ visit constrained_to_users_path
8
+ expect(page.status_code).to eq(200)
9
+ end
10
+
11
+ scenario 'they cannot access a route constrained to visitors' do
12
+ sign_in User.new
13
+
14
+ expect {
15
+ visit constrained_to_visitors_path
16
+ }.to raise_error ActionController::RoutingError
17
+ end
18
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'User tries to access constrained routes' do
4
+ scenario 'they can access a route constrained to users' do
5
+ page.driver.browser.basic_authorize("admin", "password")
6
+ visit basic_auth_path
7
+ expect(page.status_code).to eq(200)
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'Visitor signs up' do
4
+ scenario 'with an email and password' do
5
+ visit sign_up_path
6
+ click_on 'go'
7
+
8
+ expect(page).not_to have_content("Sign out")
9
+ end
10
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'Visitor is unauthorzed' do
4
+ scenario 'when visiting a resource' do
5
+ visit failure_path
6
+ expect(page.status_code).to eq(401)
7
+ end
8
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'Visitor signs in with invalid form' do
4
+ scenario 'is not signed in' do
5
+ Oath::Services::SignUp.new(email: 'email@example.com', password: 'password').perform
6
+ visit invalid_sign_in_path
7
+ fill_in "session_password", with: 'password'
8
+ click_button 'go'
9
+ expect(page).to have_content("Sign in")
10
+ end
11
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'Visitor signs up' do
4
+ scenario 'with an email and password' do
5
+ visit sign_up_path
6
+ fill_in 'user_email', with: 'email@example.com'
7
+ fill_in 'user_password', with: 'password'
8
+ click_on 'go'
9
+
10
+ expect(page.current_path).to eq(posts_path)
11
+ end
12
+
13
+ scenario 'with uppercase email' do
14
+ visit sign_up_path
15
+ fill_in 'user_email', with: 'Email@example.com'
16
+ fill_in 'user_password', with: 'password'
17
+ click_on 'go'
18
+
19
+ expect(User.last.email).to eq('email@example.com')
20
+ end
21
+
22
+ scenario 'multiple users' do
23
+ visit sign_up_path
24
+ fill_in 'user_email', with: 'email@example.com'
25
+ fill_in 'user_password', with: 'password'
26
+ click_on 'go'
27
+ click_on 'Sign out'
28
+ visit sign_up_path
29
+ fill_in 'user_email', with: 'email2@example.com'
30
+ fill_in 'user_password', with: 'password2'
31
+ click_on 'go'
32
+ click_on 'Sign out'
33
+ visit sign_in_path
34
+ fill_in 'session_email', with: 'email@example.com'
35
+ fill_in 'session_password', with: 'password'
36
+ click_on 'go'
37
+
38
+ expect(page.current_path).to eq(posts_path)
39
+ end
40
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'Visitor tries to access constrained routes' do
4
+ scenario 'they can access a route constrained to visitors' do
5
+ visit constrained_to_visitors_path
6
+ expect(page.status_code).to eq(200)
7
+ end
8
+
9
+ scenario 'they cannot access a route constrained to users' do
10
+ expect {
11
+ visit constrained_to_users_path
12
+ }.to raise_error ActionController::RoutingError
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'Visitor signs in' do
4
+ scenario 'with remember token' do
5
+ pending
6
+ Oath::SignUp.new(email: "email@example.com", password: "password").perform
7
+ visit sign_in_path
8
+ fill_in 'session_email', with: 'email@example.com'
9
+ fill_in 'session_password', with: 'password'
10
+ check 'Remember me'
11
+ click_on 'go'
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+ require 'warden'
3
+
4
+ module Oath
5
+ describe Configuration do
6
+ it 'sets the no login redirect to a resonable default' do
7
+ configuration = Configuration.new
8
+ expect(configuration.no_login_redirect).to eq({ controller: "/sessions", action: "new" })
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,180 @@
1
+ require 'spec_helper'
2
+ require 'warden'
3
+
4
+ module Oath
5
+ describe ControllerHelpers do
6
+ class WardenMock
7
+ def user; end
8
+ end
9
+ class Flash < Struct.new(:notice)
10
+ end
11
+ class FakeRequest
12
+ attr_reader :env
13
+ def initialize(env)
14
+ @env = env
15
+ end
16
+ end
17
+
18
+ class Dummy
19
+ attr_reader :redirected, :redirected_to, :flash, :request
20
+ def initialize warden
21
+ @warden = warden
22
+ @flash = Flash.new
23
+ @redirected = false
24
+ @request = FakeRequest.new(env)
25
+ end
26
+ def redirect_to path
27
+ @redirected = true
28
+ @redirected_to = path
29
+ end
30
+ def env
31
+ { "warden" => @warden }
32
+ end
33
+ end
34
+
35
+ before(:each) do
36
+ @warden = WardenMock.new
37
+ @dummy = Dummy.new(@warden)
38
+ @dummy.extend(ControllerHelpers)
39
+ end
40
+
41
+ it 'performs a sign in' do
42
+ user = stub_sign_in
43
+ @dummy.sign_in user
44
+ end
45
+
46
+ it 'runs the block when user is signed in' do
47
+ user = stub_sign_in
48
+ expectation = double()
49
+ allow(expectation).to receive(:success)
50
+ @dummy.sign_in(user) { expectation.success }
51
+ expect(expectation).to have_received(:success)
52
+ end
53
+
54
+ it 'does not run the block when user can not be signed in' do
55
+ user = stub_sign_in(false)
56
+ expectation = double()
57
+ allow(expectation).to receive(:failure)
58
+ @dummy.sign_in(user) { expectation.failure }
59
+ expect(expectation).not_to have_received(:failure)
60
+ end
61
+
62
+ it 'performs a sign out' do
63
+ sign_out = double()
64
+ allow(sign_out).to receive(:perform)
65
+ allow(Services::SignOut).to receive(:new).with(@warden).and_return(sign_out)
66
+ @dummy.sign_out
67
+ expect(sign_out).to have_received(:perform)
68
+ end
69
+
70
+ it 'performs a sign_up' do
71
+ user_params = stub_sign_up
72
+ @dummy.sign_up user_params
73
+ end
74
+
75
+ it 'runs the block when user is signed up' do
76
+ user_params = stub_sign_up
77
+ expectation = double()
78
+ allow(expectation).to receive(:success)
79
+ @dummy.sign_up(user_params) { expectation.success }
80
+ expect(expectation).to have_received(:success)
81
+ end
82
+
83
+ it 'does not run the block when user can not be signed up' do
84
+ user_params = stub_sign_up(false)
85
+ expectation = double()
86
+ allow(expectation).to receive(:failure)
87
+ @dummy.sign_up(user_params) { expectation.failure }
88
+ expect(expectation).not_to have_received(:failure)
89
+ end
90
+
91
+ it 'authenticates a session' do
92
+ session_params = { password: 'password', email: 'a@b.com' }
93
+ user = double()
94
+ authentication = double()
95
+ allow(authentication).to receive(:perform).and_return(user)
96
+ allow(Oath).to receive(:lookup).with({email: 'a@b.com'}, nil).and_return(user)
97
+ allow(Services::Authentication).to receive(:new).with(user, 'password').and_return(authentication)
98
+ expect(@dummy.authenticate_session(session_params)).to eq user
99
+ end
100
+
101
+ it 'authenticates a session against multiple fields' do
102
+ session_params = { email_or_username: 'foo', password: 'password' }
103
+ field_map = { email_or_username: [:email, :username] }
104
+ user = double()
105
+ authentication = double()
106
+ allow(authentication).to receive(:perform).and_return(user)
107
+ allow(Oath).to receive(:lookup).with(session_params.except(:password), field_map).and_return(user)
108
+ allow(Services::Authentication).to receive(:new).with(user, 'password').and_return(authentication)
109
+ expect(@dummy.authenticate_session(session_params, field_map)).to eq user
110
+ end
111
+
112
+ it 'returns false when it could not authenticate the user' do
113
+ session_params = { password: "password", lookup_key: "lookup_key" }
114
+ user = double()
115
+ authentication = double()
116
+ allow(authentication).to receive(:perform).and_return(false)
117
+ allow(Oath).to receive(:lookup).with({ lookup_key: "lookup_key" }, nil).and_return(user)
118
+ allow(Services::Authentication).to receive(:new).with(user, 'password').and_return(authentication)
119
+ expect(@dummy.authenticate_session(session_params)).to be_falsey
120
+ end
121
+
122
+ it 'performs an authenticate' do
123
+ user = double()
124
+ password = double()
125
+ authentication = double()
126
+ allow(authentication).to receive(:perform)
127
+ allow(Services::Authentication).to receive(:new).with(user, password).and_return(authentication)
128
+ @dummy.authenticate user, password
129
+ expect(authentication).to have_received(:perform)
130
+ end
131
+
132
+ it 'returns the current user' do
133
+ current_user = double()
134
+ allow(@warden).to receive(:user).and_return(current_user)
135
+ expect(@dummy.current_user).to eq current_user
136
+ end
137
+
138
+ it 'returns signed_in?' do
139
+ allow(@warden).to receive(:user)
140
+ allow(@dummy).to receive(:current_user)
141
+ @dummy.signed_in?
142
+ expect(@warden).to have_received(:user)
143
+ expect(@dummy).not_to have_received(:current_user)
144
+ end
145
+
146
+ it 'redirects when not signed_in' do
147
+ allow(@warden).to receive(:user).and_return(false)
148
+ @dummy.require_login
149
+ expect(@dummy.redirected).to eq(true)
150
+ expect(@dummy.redirected_to).to eq(Oath.config.no_login_redirect)
151
+ expect(@dummy.flash.notice).to eq(Oath.config.sign_in_notice.call)
152
+ end
153
+
154
+ it 'does not redirect when signed_in' do
155
+ allow(@warden).to receive(:user).and_return(true)
156
+ @dummy.require_login
157
+ expect(@dummy.redirected).to eq(false)
158
+ end
159
+
160
+ it 'returns warden' do
161
+ expect(@dummy.warden).to eq @warden
162
+ end
163
+
164
+ def stub_sign_in(success = true)
165
+ user = double()
166
+ sign_in = double()
167
+ allow(sign_in).to receive(:perform).and_return(success)
168
+ allow(Services::SignIn).to receive(:new).with(user, @warden).and_return(sign_in)
169
+ user
170
+ end
171
+
172
+ def stub_sign_up(success = true)
173
+ user_params = double()
174
+ sign_up = double()
175
+ allow(sign_up).to receive(:perform).and_return(success)
176
+ allow(Services::SignUp).to receive(:new).with(user_params).and_return(sign_up)
177
+ user_params
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ module Oath
4
+ describe FieldMap do
5
+ it 'returns the params with symbolized keys without a field map' do
6
+ params = double()
7
+ allow(params).to receive(:inject).and_return(params)
8
+ field_map = FieldMap.new(params, nil)
9
+ expect(field_map.to_fields).to eq(params)
10
+ end
11
+
12
+ it 'returns mapped params with a field map' do
13
+ params = { email_or_username: 'foo' }
14
+ map = { email_or_username: [:email, :username] }
15
+ field_map = FieldMap.new(params, map)
16
+ expect(field_map.to_fields).to eq(["email = ? OR username = ?", 'foo', 'foo'])
17
+ end
18
+ end
19
+ end