oath 1.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.
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