oath 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/.rspec +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +165 -0
- data/LICENSE.txt +22 -0
- data/NEWS.rdoc +118 -0
- data/README.md +384 -0
- data/Rakefile +6 -0
- data/lib/oath.rb +132 -0
- data/lib/oath/back_door.rb +53 -0
- data/lib/oath/configuration.rb +149 -0
- data/lib/oath/constraints/signed_in.rb +14 -0
- data/lib/oath/constraints/signed_out.rb +14 -0
- data/lib/oath/controller_helpers.rb +161 -0
- data/lib/oath/failure_app.rb +48 -0
- data/lib/oath/field_map.rb +56 -0
- data/lib/oath/param_transformer.rb +38 -0
- data/lib/oath/railtie.rb +11 -0
- data/lib/oath/services.rb +5 -0
- data/lib/oath/services/authentication.rb +40 -0
- data/lib/oath/services/password_reset.rb +27 -0
- data/lib/oath/services/sign_in.rb +25 -0
- data/lib/oath/services/sign_out.rb +24 -0
- data/lib/oath/services/sign_up.rb +42 -0
- data/lib/oath/strategies/password_strategy.rb +42 -0
- data/lib/oath/test/controller_helpers.rb +43 -0
- data/lib/oath/test/helpers.rb +24 -0
- data/lib/oath/version.rb +4 -0
- data/lib/oath/warden_setup.rb +47 -0
- data/oath.gemspec +30 -0
- data/spec/features/user/user_signs_in_spec.rb +14 -0
- data/spec/features/user/user_signs_in_through_back_door_spec.rb +11 -0
- data/spec/features/user/user_tries_to_access_constrained_routes_spec.rb +18 -0
- data/spec/features/user/user_tries_to_access_http_auth_page_spec.rb +9 -0
- data/spec/features/visitor/visitor_fails_to_sign_up_spec.rb +10 -0
- data/spec/features/visitor/visitor_is_unauthorized_spec.rb +8 -0
- data/spec/features/visitor/visitor_signs_in_via_invalid_form_spec.rb +11 -0
- data/spec/features/visitor/visitor_signs_up_spec.rb +40 -0
- data/spec/features/visitor/visitor_tries_to_access_constrained_routes_spec.rb +14 -0
- data/spec/features/visitor/visitor_uses_remember_token_spec.rb +13 -0
- data/spec/oath/configuration_spec.rb +11 -0
- data/spec/oath/controller_helpers_spec.rb +180 -0
- data/spec/oath/field_map_spec.rb +19 -0
- data/spec/oath/services/authentication_spec.rb +25 -0
- data/spec/oath/services/password_reset_spec.rb +24 -0
- data/spec/oath/services/sign_in_spec.rb +13 -0
- data/spec/oath/services/sign_out_spec.rb +13 -0
- data/spec/oath/services/sign_up_spec.rb +49 -0
- data/spec/oath/strategies/password_strategy_spec.rb +23 -0
- data/spec/oath/test_controller_helpers_spec.rb +63 -0
- data/spec/oath/test_helpers_spec.rb +97 -0
- data/spec/oath_spec.rb +27 -0
- data/spec/rails_app/Rakefile +7 -0
- data/spec/rails_app/app/assets/images/rails.png +0 -0
- data/spec/rails_app/app/assets/javascripts/application.js +13 -0
- data/spec/rails_app/app/assets/stylesheets/application.css +13 -0
- data/spec/rails_app/app/controllers/application_controller.rb +4 -0
- data/spec/rails_app/app/controllers/basic_auth_controller.rb +7 -0
- data/spec/rails_app/app/controllers/constrained_to_users_controller.rb +5 -0
- data/spec/rails_app/app/controllers/constrained_to_visitors_controller.rb +5 -0
- data/spec/rails_app/app/controllers/failures_controller.rb +5 -0
- data/spec/rails_app/app/controllers/invalid_sessions_controller.rb +2 -0
- data/spec/rails_app/app/controllers/posts_controller.rb +6 -0
- data/spec/rails_app/app/controllers/sessions_controller.rb +26 -0
- data/spec/rails_app/app/controllers/users_controller.rb +23 -0
- data/spec/rails_app/app/helpers/application_helper.rb +2 -0
- data/spec/rails_app/app/models/user.rb +10 -0
- data/spec/rails_app/app/views/invalid_sessions/new.html.erb +4 -0
- data/spec/rails_app/app/views/layouts/application.html.erb +18 -0
- data/spec/rails_app/app/views/posts/index.html.erb +1 -0
- data/spec/rails_app/app/views/sessions/new.html.erb +5 -0
- data/spec/rails_app/app/views/users/new.html.erb +5 -0
- data/spec/rails_app/config.ru +4 -0
- data/spec/rails_app/config/application.rb +58 -0
- data/spec/rails_app/config/boot.rb +6 -0
- data/spec/rails_app/config/database.yml +25 -0
- data/spec/rails_app/config/environment.rb +5 -0
- data/spec/rails_app/config/environments/development.rb +29 -0
- data/spec/rails_app/config/environments/production.rb +54 -0
- data/spec/rails_app/config/environments/test.rb +29 -0
- data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails_app/config/initializers/inflections.rb +15 -0
- data/spec/rails_app/config/initializers/secret_token.rb +7 -0
- data/spec/rails_app/config/routes.rb +24 -0
- data/spec/rails_app/db/seeds.rb +7 -0
- data/spec/rails_app/public/404.html +26 -0
- data/spec/rails_app/public/422.html +26 -0
- data/spec/rails_app/public/500.html +25 -0
- data/spec/rails_app/public/favicon.ico +0 -0
- data/spec/rails_app/script/rails +6 -0
- data/spec/spec_helper.rb +37 -0
- metadata +325 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
module Oath
|
2
|
+
class FailureApp
|
3
|
+
def self.call(env)
|
4
|
+
request = Rack::Request.new(env)
|
5
|
+
new(request).response
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(request)
|
9
|
+
@request = request
|
10
|
+
end
|
11
|
+
|
12
|
+
def response
|
13
|
+
[401, headers, body]
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
attr_reader :request
|
19
|
+
|
20
|
+
def headers
|
21
|
+
if http_auth_header?
|
22
|
+
basic_headers.merge(auth_headers)
|
23
|
+
else
|
24
|
+
basic_headers
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def basic_headers
|
29
|
+
{
|
30
|
+
"Content-Type" => request.content_type.to_s
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
def auth_headers
|
35
|
+
{
|
36
|
+
"WWW-Authenticate" => 'Basic realm="Application"'
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def body
|
41
|
+
["Authorization Failed"]
|
42
|
+
end
|
43
|
+
|
44
|
+
def http_auth_header?
|
45
|
+
!request.xhr?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Oath
|
2
|
+
# FieldMap is used to allow multiple lookup fields. For instance if you
|
3
|
+
# wanted to allow a user to sign in via email or username. This is used
|
4
|
+
# internally by the authenticate_session controller helper
|
5
|
+
# @since 0.0.15
|
6
|
+
class FieldMap
|
7
|
+
# @param params [Hash] hash of parameters
|
8
|
+
# @param field_map [Hash] hash of values to map
|
9
|
+
def initialize params, field_map
|
10
|
+
@params = params
|
11
|
+
@field_map = field_map
|
12
|
+
end
|
13
|
+
|
14
|
+
# converts params into values that can be passed into a where clause
|
15
|
+
#
|
16
|
+
# @return [Array] if initialized with field_map
|
17
|
+
# @return [Hash] if not initialized with field_map
|
18
|
+
def to_fields
|
19
|
+
if @field_map
|
20
|
+
params_from_field_map
|
21
|
+
else
|
22
|
+
params_with_symbolized_keys
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def params_with_symbolized_keys
|
29
|
+
@params.inject(default_fields){|hash,(key,value)| hash.merge(key.to_sym => value) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def default_fields
|
33
|
+
{ Oath.config.user_lookup_field => nil }
|
34
|
+
end
|
35
|
+
|
36
|
+
def params_from_field_map
|
37
|
+
[query_string, *([value] * lookup_keys.length)]
|
38
|
+
end
|
39
|
+
|
40
|
+
def query_string
|
41
|
+
lookup_keys.map { |key| "#{key} = ?" }.join(" OR ")
|
42
|
+
end
|
43
|
+
|
44
|
+
def session_key
|
45
|
+
@field_map.keys.first
|
46
|
+
end
|
47
|
+
|
48
|
+
def lookup_keys
|
49
|
+
@field_map.values.first
|
50
|
+
end
|
51
|
+
|
52
|
+
def value
|
53
|
+
@params[session_key]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Oath
|
2
|
+
# Parameter transformer. Sanitizes and transforms parameter values
|
3
|
+
# @since 1.0.0
|
4
|
+
class ParamTransformer
|
5
|
+
# Initialize parameter transformer
|
6
|
+
#
|
7
|
+
# @param params [ActionController::Parameters] parameters to be altered
|
8
|
+
def initialize(params, transformations)
|
9
|
+
@params = params
|
10
|
+
@transformations = transformations
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns the transformed parameters
|
14
|
+
def to_h
|
15
|
+
sanitized_params.each_with_object({}) do |(key, value), hash|
|
16
|
+
hash[key] = transform(key, value)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :params, :transformations
|
23
|
+
|
24
|
+
def sanitized_params
|
25
|
+
params.to_h
|
26
|
+
end
|
27
|
+
|
28
|
+
def transform(key, value)
|
29
|
+
return value unless value.is_a? String
|
30
|
+
|
31
|
+
if transformations.key?(key)
|
32
|
+
transformations[key].call(value)
|
33
|
+
else
|
34
|
+
value
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/oath/railtie.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'warden'
|
2
|
+
|
3
|
+
module Oath
|
4
|
+
# Railtie for Oath. Injects the Warden middleware and initializes Oath.
|
5
|
+
# @since 0.0.15
|
6
|
+
class Railtie < Rails::Railtie
|
7
|
+
config.app_middleware.use Warden::Manager do |config|
|
8
|
+
Oath.initialize(config)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Oath
|
2
|
+
module Services
|
3
|
+
# Authentication service. Checks to see if the credentials provided are valid
|
4
|
+
# @since 0.0.15
|
5
|
+
class Authentication
|
6
|
+
# Initialize service
|
7
|
+
#
|
8
|
+
# @param user [User] A user object
|
9
|
+
# @param undigested_token [String] An undigested password
|
10
|
+
def initialize user, undigested_token
|
11
|
+
@user = user
|
12
|
+
@undigested_token = undigested_token
|
13
|
+
end
|
14
|
+
|
15
|
+
# Perform the service
|
16
|
+
#
|
17
|
+
# @return [User] if authentication succeeds
|
18
|
+
# @return [false] if authentication fails
|
19
|
+
def perform
|
20
|
+
if authenticated?
|
21
|
+
user
|
22
|
+
else
|
23
|
+
false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_reader :user, :undigested_token
|
30
|
+
|
31
|
+
def authenticated?
|
32
|
+
user && Oath.compare_token(user.send(token_store_field), undigested_token)
|
33
|
+
end
|
34
|
+
|
35
|
+
def token_store_field
|
36
|
+
Oath.config.user_token_store_field
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Oath
|
2
|
+
module Services
|
3
|
+
# Password reset service. Updates the password on a User
|
4
|
+
# @since 0.0.15
|
5
|
+
class PasswordReset
|
6
|
+
# Initialize service
|
7
|
+
#
|
8
|
+
# @param user [User] A user object
|
9
|
+
# @param new_password [String] The new undigested password for a user
|
10
|
+
def initialize user, new_password
|
11
|
+
@user = user
|
12
|
+
@new_password = new_password
|
13
|
+
end
|
14
|
+
|
15
|
+
# Perform the service.
|
16
|
+
def perform
|
17
|
+
field = Oath.config.user_token_store_field
|
18
|
+
digested_password = Oath.hash_token(new_password)
|
19
|
+
user[field] = digested_password
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_reader :user, :new_password
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Oath
|
2
|
+
module Services
|
3
|
+
# Sign in service. Signs the user in via warden
|
4
|
+
# @since 0.0.15
|
5
|
+
class SignIn
|
6
|
+
# Initialize service
|
7
|
+
#
|
8
|
+
# @param user [User] A user object
|
9
|
+
# @param warden [Warden] warden
|
10
|
+
def initialize user, warden
|
11
|
+
@user = user
|
12
|
+
@warden = warden
|
13
|
+
end
|
14
|
+
|
15
|
+
# Perform the service
|
16
|
+
def perform
|
17
|
+
warden.set_user(user)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :warden, :user
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Oath
|
2
|
+
module Services
|
3
|
+
# Sign out service. Signs the user out via warden
|
4
|
+
# @since 0.0.15
|
5
|
+
class SignOut
|
6
|
+
# Initialize service
|
7
|
+
#
|
8
|
+
# @param warden [Warden] warden
|
9
|
+
def initialize warden
|
10
|
+
@warden = warden
|
11
|
+
@user = warden.user
|
12
|
+
end
|
13
|
+
|
14
|
+
# Perform the service
|
15
|
+
def perform
|
16
|
+
warden.logout
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
attr_reader :warden, :user
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Oath
|
2
|
+
module Services
|
3
|
+
# Sign up service. Signs the user up
|
4
|
+
# @since 0.0.15
|
5
|
+
class SignUp
|
6
|
+
# Initialize service
|
7
|
+
#
|
8
|
+
# @param user_params [Hash] A hash of user credentials. Should contain the lookup and token fields
|
9
|
+
def initialize user_params
|
10
|
+
digested_token = token_digest(user_params)
|
11
|
+
@user_params = user_params.
|
12
|
+
except(token_field).
|
13
|
+
merge(token_store_field.to_sym => digested_token)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Performs the service
|
17
|
+
# @see Oath::Configuration.default_creation_method
|
18
|
+
def perform
|
19
|
+
Oath.config.creation_method.call(user_params)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_reader :user_params
|
25
|
+
|
26
|
+
def token_digest(user_params)
|
27
|
+
undigested_token = user_params[token_field]
|
28
|
+
unless undigested_token.blank?
|
29
|
+
Oath.hash_token(undigested_token)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def token_store_field
|
34
|
+
Oath.config.user_token_store_field
|
35
|
+
end
|
36
|
+
|
37
|
+
def token_field
|
38
|
+
Oath.config.user_token_field
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'warden'
|
2
|
+
|
3
|
+
module Oath
|
4
|
+
module Strategies
|
5
|
+
# Password strategy for warden
|
6
|
+
# @since 0.0.15
|
7
|
+
class PasswordStrategy < ::Warden::Strategies::Base
|
8
|
+
|
9
|
+
# Checks if strategy should be executed
|
10
|
+
# @return [Boolean]
|
11
|
+
def valid?
|
12
|
+
lookup_field_value || token_field_value
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
# Authenticates for warden
|
17
|
+
def authenticate!
|
18
|
+
user = Oath.config.user_class.find_by(lookup_field => lookup_field_value)
|
19
|
+
auth = Oath.config.authentication_service.new(user, token_field_value)
|
20
|
+
auth.authenticated? ? success!(user) : fail!("Could not log in")
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def lookup_field_value
|
26
|
+
params[lookup_field]
|
27
|
+
end
|
28
|
+
|
29
|
+
def token_field_value
|
30
|
+
params[token_field]
|
31
|
+
end
|
32
|
+
|
33
|
+
def lookup_field
|
34
|
+
Oath.config.user_lookup_field
|
35
|
+
end
|
36
|
+
|
37
|
+
def token_field
|
38
|
+
Oath.config.user_token_field
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'warden'
|
2
|
+
|
3
|
+
module Oath
|
4
|
+
module Test
|
5
|
+
# These are test helpers for controller specs
|
6
|
+
# @note these have only been tested with rspec controller specs
|
7
|
+
# @since 0.0.15
|
8
|
+
module ControllerHelpers
|
9
|
+
def self.included(base)
|
10
|
+
base.class_eval do
|
11
|
+
setup :store_controller_for_warden, :warden if respond_to?(:setup)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Signs a user in for tests
|
16
|
+
# @param user [User] the user to sign in
|
17
|
+
def sign_in(user)
|
18
|
+
@controller.sign_in(user)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Signs the user out in tests
|
22
|
+
def sign_out
|
23
|
+
@controller.sign_out
|
24
|
+
end
|
25
|
+
|
26
|
+
# A mock of warden for tests
|
27
|
+
def warden
|
28
|
+
@warden ||= begin
|
29
|
+
manager = Warden::Manager.new(nil) do |config|
|
30
|
+
config.merge! Oath.warden_config
|
31
|
+
end
|
32
|
+
@request.env['warden'] = Warden::Proxy.new(@request.env, manager)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def store_controller_for_warden
|
38
|
+
@request.env['action_controller.instance'] = @controller
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Oath
|
2
|
+
module Test
|
3
|
+
# Helpers for integration or feature specs
|
4
|
+
# @note these have only been tested with rspec integration and feature specs
|
5
|
+
# @since 0.0.15
|
6
|
+
module Helpers
|
7
|
+
include Warden::Test::Helpers
|
8
|
+
|
9
|
+
# Sign a user in
|
10
|
+
# @param user [User] user to sign in
|
11
|
+
# @returns user [User] signed in user
|
12
|
+
def sign_in user
|
13
|
+
login_as user
|
14
|
+
|
15
|
+
user
|
16
|
+
end
|
17
|
+
|
18
|
+
# Sign a user out
|
19
|
+
def sign_out
|
20
|
+
logout
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/oath/version.rb
ADDED