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