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,25 @@
1
+ require 'spec_helper'
2
+ require 'oath/services/authentication'
3
+
4
+ describe Oath::Services::Authentication, '#authentication' do
5
+ it 'is authenticated for a valid password' do
6
+ password_digest = BCrypt::Password.create('password')
7
+ user = double(password_digest: password_digest)
8
+ auth = Oath::Services::Authentication.new(user, 'password')
9
+
10
+ expect(auth.perform).to eq(user)
11
+ end
12
+
13
+ it 'is not authenticated for the wrong password' do
14
+ password_digest = BCrypt::Password.create('password')
15
+ user = double(password_digest: password_digest)
16
+ auth = Oath::Services::Authentication.new(user, 'drowssap')
17
+
18
+ expect(auth.perform).to eq(false)
19
+ end
20
+
21
+ it 'is not authenticated without a user' do
22
+ auth = Oath::Services::Authentication.new(nil, 'password')
23
+ expect(auth.perform).to eq(false)
24
+ end
25
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+ require 'oath/services/password_reset'
3
+ require 'ostruct'
4
+
5
+ describe Oath::Services::PasswordReset do
6
+ before do
7
+ Oath.config.hashing_method = ->(password) { password + "secret" }
8
+ end
9
+
10
+ it 'updates the password with the hashing strategy' do
11
+ password_digest = Oath.hash_token('password')
12
+ user = double()
13
+ field = Oath.config.user_token_store_field
14
+ allow(user).to receive(:[]=)
15
+ password_reset = Oath::Services::PasswordReset.new(user, 'password')
16
+
17
+ password_reset.perform
18
+ expect(user).to have_received(:[]=).with(field, 'passwordsecret')
19
+ end
20
+
21
+ after do
22
+ Oath.config.hashing_method = Oath.config.default_hashing_method
23
+ end
24
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+ require 'oath/services/sign_in'
3
+
4
+ describe Oath::Services::SignIn, '#perform' do
5
+ it 'signs the user in' do
6
+ user = double()
7
+ warden = double()
8
+ allow(warden).to receive(:set_user)
9
+
10
+ Oath::Services::SignIn.new(user, warden).perform
11
+ expect(warden).to have_received(:set_user).with(user)
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+ require 'oath/services/sign_out'
3
+
4
+ describe Oath::Services::SignOut, '#perform' do
5
+ it 'signs out the user' do
6
+ warden = double()
7
+ allow(warden).to receive(:logout)
8
+ allow(warden).to receive(:user).and_return(double())
9
+
10
+ Oath::Services::SignOut.new(warden).perform
11
+ expect(warden).to have_received(:logout)
12
+ end
13
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+ require 'oath/services/sign_up'
3
+
4
+ describe Oath::Services::SignUp, '#perform' do
5
+ it 'creates a user with the right parameters' do
6
+ allow(User).to receive(:create)
7
+ user_params = { email: 'email@example.com', password: 'password' }
8
+
9
+ Oath::Services::SignUp.new(user_params).perform
10
+ expect(User).to have_received(:create) do |args|
11
+ expect(args[:email]).to eq(user_params[:email])
12
+ expect(Oath.compare_token(args[:password_digest], 'password')).to be_truthy
13
+ end
14
+ end
15
+
16
+ it 'creates a user from the configured user creation method' do
17
+ user_create_double = double(Proc, call: true)
18
+
19
+ user_params = { email: 'email@example.com', password: 'password' }
20
+
21
+ with_oath_config(creation_method: user_create_double) do
22
+ Oath::Services::SignUp.new(user_params).perform
23
+ end
24
+
25
+ expect(user_create_double).to have_received(:call) do |args|
26
+ expect(Oath.compare_token(args[:password_digest], 'password')).to be_truthy
27
+ end
28
+ end
29
+
30
+ it 'does not create a user with an empty password' do
31
+ allow(User).to receive(:create)
32
+ user_params = { email: 'email@example.com', password: '' }
33
+
34
+ Oath::Services::SignUp.new(user_params).perform
35
+ expect(User).to have_received(:create) do |args|
36
+ expect(args[:password_digest]).to be_nil
37
+ end
38
+ end
39
+
40
+ it 'does not create a user with a nil password' do
41
+ allow(User).to receive(:create)
42
+ user_params = { email: nil, password: nil }
43
+
44
+ Oath::Services::SignUp.new(user_params).perform
45
+ expect(User).to have_received(:create) do |args|
46
+ expect(args[:password_digest]).to be_nil
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ module Oath
4
+ module Strategies
5
+ describe PasswordStrategy do
6
+ it "bases lookup and token on config values" do
7
+ params = HashWithIndifferentAccess.new(username: 'test', the_password: 'password')
8
+
9
+ with_oath_config(user_lookup_field: "username", user_token_field: "the_password") do
10
+ env = Rack::MockRequest.env_for("/", params: params)
11
+ strategy = PasswordStrategy.new(env)
12
+ expect(strategy).to be_valid
13
+ end
14
+ end
15
+
16
+ it "it doesn't trigger if params are not provided" do
17
+ env = Rack::MockRequest.env_for("/")
18
+ strategy = PasswordStrategy.new(env)
19
+ expect(strategy).not_to be_valid
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ class FakeRequest
4
+ attr_reader :env
5
+ def initialize
6
+ @env = {}
7
+ end
8
+ end
9
+
10
+ class FakeController
11
+ attr_reader :user
12
+ def sign_in(user)
13
+ @user = user
14
+ end
15
+
16
+ def sign_out
17
+ @user = nil
18
+ end
19
+ end
20
+
21
+ class FakeSpec
22
+ cattr_reader :setup_methods
23
+ attr_reader :request, :controller
24
+ def initialize(controller = FakeController.new)
25
+ @request = FakeRequest.new
26
+ @controller = controller
27
+ end
28
+
29
+ def self.setup(*methods)
30
+ @@setup_methods = methods
31
+ end
32
+ include Oath::Test::ControllerHelpers
33
+ end
34
+
35
+ module Oath
36
+ module Test
37
+ describe ControllerHelpers do
38
+ it 'sets up warden' do
39
+ expect(FakeSpec.setup_methods).to eq([:store_controller_for_warden, :warden])
40
+ end
41
+
42
+ it 'creates a warden manager' do
43
+ fake_spec = FakeSpec.new
44
+ expect(fake_spec.warden).to be_a(Warden::Proxy)
45
+ end
46
+
47
+ it 'calls the sign in method on the controller' do
48
+ controller = FakeController.new
49
+ fake_spec = FakeSpec.new(controller)
50
+ fake_spec.sign_in("user")
51
+ expect(controller.user).to eq("user")
52
+ end
53
+
54
+ it 'calls the sign out method on the controller' do
55
+ controller = FakeController.new
56
+ fake_spec = FakeSpec.new(controller)
57
+ fake_spec.sign_in("user")
58
+ fake_spec.sign_out
59
+ expect(controller.user).to eq(nil)
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,97 @@
1
+ require 'spec_helper'
2
+ require 'warden'
3
+ require 'oath/test/helpers'
4
+
5
+ module Warden::Spec
6
+ module Helpers
7
+ FAILURE_APP = lambda{|e|[401, {"Content-Type" => "text/plain"}, ["You Fail!"]] }
8
+
9
+ def env_with_params(path = "/", params = {}, env = {})
10
+ method = params.delete(:method) || "GET"
11
+ env = { 'HTTP_VERSION' => '1.1', 'REQUEST_METHOD' => "#{method}" }.merge(env)
12
+ Rack::MockRequest.env_for("#{path}?#{Rack::Utils.build_query(params)}", env)
13
+ end
14
+
15
+ def setup_rack(app = nil, opts = {}, &block)
16
+ app ||= block if block_given?
17
+
18
+ opts[:failure_app] ||= failure_app
19
+ opts[:default_strategies] ||= [:password]
20
+ opts[:default_serializers] ||= [:session]
21
+ blk = opts[:configurator] || proc{}
22
+
23
+ Rack::Builder.new do
24
+ use opts[:session] || Warden::Spec::Helpers::Session unless opts[:nil_session]
25
+ use Warden::Manager, opts, &blk
26
+ run app
27
+ end
28
+ end
29
+
30
+ def valid_response
31
+ Rack::Response.new("OK").finish
32
+ end
33
+
34
+ def failure_app
35
+ Warden::Spec::Helpers::FAILURE_APP
36
+ end
37
+
38
+ def success_app
39
+ lambda{|e| [200, {"Content-Type" => "text/plain"}, ["You Win"]]}
40
+ end
41
+
42
+ class Session
43
+ attr_accessor :app
44
+ def initialize(app,configs = {})
45
+ @app = app
46
+ end
47
+
48
+ def call(e)
49
+ e['rack.session'] ||= {}
50
+ @app.call(e)
51
+ end
52
+ end # session
53
+ end
54
+ end
55
+
56
+ module Oath
57
+ module Test
58
+ describe Helpers do
59
+ include Oath::Test::Helpers
60
+ include Warden::Spec::Helpers
61
+
62
+ before { $captures = [] }
63
+ after { Oath.test_reset! }
64
+
65
+ it 'performs a sign in' do
66
+ user = double(id: 1)
67
+ return_value = sign_in(user)
68
+ app = lambda do |env|
69
+ $captures << :run
70
+ expect(env['warden']).to be_authenticated
71
+ expect(env['warden'].user).to eq(user)
72
+ valid_response
73
+ end
74
+ setup_rack(app).call(env_with_params)
75
+
76
+ expect(return_value).to eq(user)
77
+ expect($captures).to eq([:run])
78
+ end
79
+
80
+ it 'performs a sign out' do
81
+ user = double(id: 1)
82
+ sign_in(user)
83
+ sign_out
84
+
85
+ app = lambda do |env|
86
+ $captures << :run
87
+ warden = env['warden']
88
+ expect(warden.user).to be_nil
89
+ expect(warden).not_to be_authenticated
90
+ end
91
+
92
+ setup_rack(app).call(env_with_params)
93
+ expect($captures).to eq([:run])
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+ require 'warden'
3
+
4
+ describe 'Oath' do
5
+ it "stores the warden config" do
6
+ expect(Oath.warden_config).to be_a Warden::Config
7
+ end
8
+
9
+ it "provides a .test_mode!" do
10
+ Oath.test_mode!
11
+ expect(Oath.hash_token('password')).to eql('password')
12
+ expect(Oath.compare_token('password', 'password')).to be_truthy
13
+ end
14
+
15
+ it "does not lookup with empty params" do
16
+ allow(Oath::FieldMap).to receive(:new).and_return(fake_field_map)
17
+ with_oath_config(find_method: -> (conditions) { raise }) do
18
+ expect(-> { Oath.lookup({}, {}) }).not_to raise_exception
19
+ end
20
+ end
21
+
22
+ def fake_field_map
23
+ double(Oath::FieldMap).tap do |field_map|
24
+ allow(field_map).to receive(:to_fields).and_return(["foo=1 OR bar=1"])
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env rake
2
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
3
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
4
+
5
+ require File.expand_path('../config/application', __FILE__)
6
+
7
+ RailsApp::Application.load_tasks
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // the compiled file.
9
+ //
10
+ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11
+ // GO AFTER THE REQUIRES BELOW.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,13 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ *= require_tree .
13
+ */
@@ -0,0 +1,4 @@
1
+ class ApplicationController < ActionController::Base
2
+ include Oath::ControllerHelpers
3
+ protect_from_forgery
4
+ end
@@ -0,0 +1,7 @@
1
+ class BasicAuthController < ApplicationController
2
+ http_basic_authenticate_with name: "admin", password: "password"
3
+
4
+ def show
5
+ render plain: "Hello"
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ class ConstrainedToUsersController < ApplicationController
2
+ def show
3
+ head :ok
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class ConstrainedToVisitorsController < ApplicationController
2
+ def show
3
+ head :ok
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class FailuresController < ApplicationController
2
+ def show
3
+ render status: :unauthorized, plain: "Unauthorized"
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ class InvalidSessionsController < SessionsController
2
+ end
@@ -0,0 +1,6 @@
1
+ class PostsController < ApplicationController
2
+ before_filter :require_login
3
+
4
+ def index
5
+ end
6
+ end
@@ -0,0 +1,26 @@
1
+ class SessionsController < ApplicationController
2
+ def new
3
+ end
4
+
5
+ def create
6
+ user = authenticate_session(session_params)
7
+
8
+ if sign_in(user)
9
+ redirect_to posts_path
10
+ else
11
+ redirect_to root_path, notice: "Invalid email or password"
12
+ end
13
+ end
14
+
15
+ def destroy
16
+ sign_out
17
+ redirect_to root_path
18
+ end
19
+
20
+ private
21
+
22
+ def session_params
23
+ params.require(:session).permit(:email, :password)
24
+ end
25
+ end
26
+