lsdr-authlogic-connect 0.0.3.9
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.
- data/MIT-LICENSE +20 -0
- data/README.markdown +240 -0
- data/Rakefile +71 -0
- data/init.rb +1 -0
- data/lib/authlogic-connect.rb +27 -0
- data/lib/authlogic_connect/authlogic_connect.rb +46 -0
- data/lib/authlogic_connect/callback_filter.rb +19 -0
- data/lib/authlogic_connect/common.rb +10 -0
- data/lib/authlogic_connect/common/session.rb +27 -0
- data/lib/authlogic_connect/common/state.rb +16 -0
- data/lib/authlogic_connect/common/user.rb +115 -0
- data/lib/authlogic_connect/common/variables.rb +77 -0
- data/lib/authlogic_connect/engine.rb +14 -0
- data/lib/authlogic_connect/ext.rb +56 -0
- data/lib/authlogic_connect/oauth.rb +14 -0
- data/lib/authlogic_connect/oauth/helper.rb +20 -0
- data/lib/authlogic_connect/oauth/process.rb +68 -0
- data/lib/authlogic_connect/oauth/session.rb +58 -0
- data/lib/authlogic_connect/oauth/state.rb +54 -0
- data/lib/authlogic_connect/oauth/tokens/facebook_token.rb +11 -0
- data/lib/authlogic_connect/oauth/tokens/get_satisfaction_token.rb +9 -0
- data/lib/authlogic_connect/oauth/tokens/google_token.rb +41 -0
- data/lib/authlogic_connect/oauth/tokens/linked_in_token.rb +19 -0
- data/lib/authlogic_connect/oauth/tokens/myspace_token.rb +26 -0
- data/lib/authlogic_connect/oauth/tokens/oauth_token.rb +131 -0
- data/lib/authlogic_connect/oauth/tokens/opensocial_token.rb +0 -0
- data/lib/authlogic_connect/oauth/tokens/twitter_token.rb +8 -0
- data/lib/authlogic_connect/oauth/tokens/vimeo_token.rb +18 -0
- data/lib/authlogic_connect/oauth/tokens/yahoo_token.rb +19 -0
- data/lib/authlogic_connect/oauth/user.rb +68 -0
- data/lib/authlogic_connect/oauth/variables.rb +55 -0
- data/lib/authlogic_connect/openid.rb +11 -0
- data/lib/authlogic_connect/openid/process.rb +30 -0
- data/lib/authlogic_connect/openid/session.rb +78 -0
- data/lib/authlogic_connect/openid/state.rb +47 -0
- data/lib/authlogic_connect/openid/tokens/aol_token.rb +0 -0
- data/lib/authlogic_connect/openid/tokens/blogger_token.rb +0 -0
- data/lib/authlogic_connect/openid/tokens/flickr_token.rb +0 -0
- data/lib/authlogic_connect/openid/tokens/my_openid_token.rb +3 -0
- data/lib/authlogic_connect/openid/tokens/openid_token.rb +9 -0
- data/lib/authlogic_connect/openid/user.rb +62 -0
- data/lib/authlogic_connect/openid/variables.rb +19 -0
- data/lib/authlogic_connect/token.rb +53 -0
- data/lib/open_id_authentication.rb +128 -0
- data/rails/init.rb +19 -0
- data/test/controllers/test_users_controller.rb +21 -0
- data/test/libs/database.rb +48 -0
- data/test/libs/user.rb +3 -0
- data/test/libs/user_session.rb +2 -0
- data/test/old.rb +53 -0
- data/test/test_authlogic_connect.rb +13 -0
- data/test/test_helper.rb +153 -0
- data/test/test_user.rb +255 -0
- metadata +247 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
# This class holds query/state variables common to oauth and openid
|
2
|
+
module AuthlogicConnect::Common::State
|
3
|
+
|
4
|
+
def auth_session?
|
5
|
+
!auth_session.blank?
|
6
|
+
end
|
7
|
+
|
8
|
+
def auth_params?
|
9
|
+
!auth_params.blank?
|
10
|
+
end
|
11
|
+
|
12
|
+
def is_auth_session?
|
13
|
+
self.is_a?(Authlogic::Session::Base)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# This class is the main api for the user.
|
2
|
+
# It is also required to properly sequence the save methods
|
3
|
+
# for the different authentication types (oauth and openid)
|
4
|
+
module AuthlogicConnect::Common::User
|
5
|
+
|
6
|
+
def self.included(base)
|
7
|
+
base.class_eval do
|
8
|
+
add_acts_as_authentic_module(InstanceMethods, :append)
|
9
|
+
add_acts_as_authentic_module(AuthlogicConnect::Common::Variables, :prepend)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module InstanceMethods
|
14
|
+
|
15
|
+
def self.included(base)
|
16
|
+
base.class_eval do
|
17
|
+
has_many :tokens, :class_name => "Token", :dependent => :destroy
|
18
|
+
belongs_to :active_token, :class_name => "Token", :dependent => :destroy
|
19
|
+
accepts_nested_attributes_for :tokens, :active_token
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def authenticated_with
|
24
|
+
@authenticated_with ||= self.tokens.collect{|t| t.service_name.to_s}
|
25
|
+
end
|
26
|
+
|
27
|
+
def authenticated_with?(service)
|
28
|
+
self.tokens.detect{|t| t.service_name.to_s == service.to_s}
|
29
|
+
end
|
30
|
+
|
31
|
+
def update_attributes(attributes, &block)
|
32
|
+
self.attributes = attributes
|
33
|
+
save(:validate => true, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
def has_token?(service_name)
|
37
|
+
!get_token(service_name).nil?
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_token(service_name)
|
41
|
+
self.tokens.detect {|i| i.service_name.to_s == service_name.to_s}
|
42
|
+
end
|
43
|
+
|
44
|
+
# core save method coordinating how to save the user.
|
45
|
+
# we dont' want to ru validations based on the
|
46
|
+
# authentication mission we are trying to accomplish.
|
47
|
+
# instead, we just return save as false.
|
48
|
+
# the next time around, when we recieve the callback,
|
49
|
+
# we will run the validations
|
50
|
+
def save(options = {}, &block)
|
51
|
+
# debug_user_save_pre(options, &block)
|
52
|
+
options = {} if options == false
|
53
|
+
unless options[:skip_redirect] == true
|
54
|
+
return false if remotely_authenticating?(&block)
|
55
|
+
end
|
56
|
+
# forces you to validate, maybe get rid of if needed,
|
57
|
+
# but everything depends on this
|
58
|
+
if ActiveRecord::VERSION::MAJOR < 3
|
59
|
+
result = super(true) # validate!
|
60
|
+
else
|
61
|
+
result = super(options.merge(:validate => true))
|
62
|
+
end
|
63
|
+
# debug_user_save_post
|
64
|
+
yield(result) if block_given? # give back to controller
|
65
|
+
|
66
|
+
cleanup_auth_session if result && !(options.has_key?(:keep_session) && options[:keep_session])
|
67
|
+
|
68
|
+
result
|
69
|
+
end
|
70
|
+
|
71
|
+
def remotely_authenticating?(&block)
|
72
|
+
return redirecting_to_oauth_server? if using_oauth? && block_given?
|
73
|
+
return redirecting_to_openid_server? if using_openid?
|
74
|
+
return false
|
75
|
+
end
|
76
|
+
|
77
|
+
# it only reaches this point once it has returned, or you
|
78
|
+
# have manually skipped the redirect and save was called directly.
|
79
|
+
def cleanup_auth_session
|
80
|
+
cleanup_oauth_session
|
81
|
+
cleanup_openid_session
|
82
|
+
end
|
83
|
+
|
84
|
+
def validate_password_with_oauth?
|
85
|
+
!using_openid? && super
|
86
|
+
end
|
87
|
+
|
88
|
+
def validate_password_with_openid?
|
89
|
+
!using_oauth? && super
|
90
|
+
end
|
91
|
+
|
92
|
+
# test methods for dev/debugging, commented out by default
|
93
|
+
def debug_user_save_pre(options = {}, &block)
|
94
|
+
puts "USER SAVE "
|
95
|
+
puts "block_given? #{block_given?.to_s}"
|
96
|
+
puts "using_oauth? #{using_oauth?.to_s}"
|
97
|
+
puts "using_openid? #{using_openid?.to_s}"
|
98
|
+
puts "authenticating_with_oauth? #{authenticating_with_oauth?.to_s}"
|
99
|
+
puts "authenticating_with_openid? #{authenticating_with_openid?.to_s}"
|
100
|
+
puts "validate_password_with_oauth? #{validate_password_with_oauth?.to_s}"
|
101
|
+
puts "validate_password_with_openid? #{validate_password_with_openid?.to_s}"
|
102
|
+
puts "!using_openid? && require_password? #{(!using_openid? && require_password?).to_s}"
|
103
|
+
end
|
104
|
+
|
105
|
+
def debug_user_save_post
|
106
|
+
puts "ERRORS: #{errors.full_messages}"
|
107
|
+
puts "using_oauth? #{using_oauth?.to_s}"
|
108
|
+
puts "using_openid? #{using_openid?.to_s}"
|
109
|
+
puts "validate_password_with_oauth? #{validate_password_with_oauth?.to_s}"
|
110
|
+
puts "validate_password_with_openid? #{validate_password_with_openid?.to_s}"
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module AuthlogicConnect::Common::Variables
|
2
|
+
include AuthlogicConnect::Common::State
|
3
|
+
|
4
|
+
def auth_controller
|
5
|
+
is_auth_session? ? controller : session_class.controller
|
6
|
+
end
|
7
|
+
|
8
|
+
def auth_session
|
9
|
+
return {} unless (auth_controller && auth_controller.session)
|
10
|
+
|
11
|
+
auth_controller.session.symbolize_keys!
|
12
|
+
auth_controller.session.keys.each do |key|
|
13
|
+
auth_controller.session[key.to_s] = auth_controller.session.delete(key) if key.to_s =~ /^OpenID/
|
14
|
+
end
|
15
|
+
auth_controller.session
|
16
|
+
end
|
17
|
+
|
18
|
+
def auth_params
|
19
|
+
return {} unless (auth_controller && auth_controller.params)
|
20
|
+
|
21
|
+
auth_controller.params.symbolize_keys!
|
22
|
+
auth_controller.params.keys.each do |key|
|
23
|
+
auth_controller.params[key.to_s] = auth_controller.params.delete(key) if key.to_s =~ /^OpenID/
|
24
|
+
end
|
25
|
+
auth_controller.params
|
26
|
+
end
|
27
|
+
|
28
|
+
def auth_callback_url(options = {})
|
29
|
+
auth_controller.url_for({:controller => auth_controller.controller_name, :action => auth_controller.action_name}.merge(options))
|
30
|
+
end
|
31
|
+
|
32
|
+
# if we've said it's a "user" (registration), or a "session" (login)
|
33
|
+
def auth_type
|
34
|
+
from_session_or_params(:authentication_type)
|
35
|
+
end
|
36
|
+
|
37
|
+
# auth_params and auth_session attributes are all String!
|
38
|
+
def from_session_or_params(by)
|
39
|
+
key = by.is_a?(Symbol) ? by : by.to_sym
|
40
|
+
result = auth_params[key] if (auth_params && auth_params[key])
|
41
|
+
result = auth_session[key] if (result.nil? || result.blank?)
|
42
|
+
result
|
43
|
+
end
|
44
|
+
|
45
|
+
# because user and session are so closely tied together, I am still
|
46
|
+
# uncertain as to how they are saved. So this makes sure if we are
|
47
|
+
# logging in, it must be saving the session, otherwise the user.
|
48
|
+
def correct_request_class?
|
49
|
+
if is_auth_session?
|
50
|
+
auth_type.to_s == "session"
|
51
|
+
else
|
52
|
+
auth_type.to_s == "user"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def add_session_key(key, value)
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
# because we may need to store 6+ session variables, all with pretty lengthy names,
|
61
|
+
# might as well just tinify them.
|
62
|
+
# just an idea
|
63
|
+
def optimized_session_key(key)
|
64
|
+
@optimized_session_keys ||= {
|
65
|
+
:auth_request_class => :authcl,
|
66
|
+
:authentication_method => :authme,
|
67
|
+
:authentication_type => :authty,
|
68
|
+
:oauth_provider => :authpr,
|
69
|
+
:auth_callback_method => :authcb,
|
70
|
+
:oauth_request_token => :authtk,
|
71
|
+
:oauth_request_token_secret => :authsc,
|
72
|
+
:auth_attributes => :authat
|
73
|
+
}
|
74
|
+
@optimized_session_keys[key]
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module AuthlogicConnect
|
2
|
+
class Engine < Rails::Engine
|
3
|
+
|
4
|
+
initializer "authlogic_connect.authentication_hook" do |app|
|
5
|
+
app.middleware.use AuthlogicConnect::CallbackFilter
|
6
|
+
app.middleware.use OpenIdAuthentication
|
7
|
+
end
|
8
|
+
|
9
|
+
initializer "authlogic_connect.finalize", :after => "authlogic_connect.authentication_hook" do |app|
|
10
|
+
OpenID::Util.logger = Rails.logger
|
11
|
+
ActionController::Base.send :include, OpenIdAuthentication
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# these are extensions I've found useful for this project
|
2
|
+
class String
|
3
|
+
# normalizes an OpenID according to http://openid.net/specs/openid-authentication-2_0.html#normalization
|
4
|
+
def normalize_identifier
|
5
|
+
# clean up whitespace
|
6
|
+
identifier = self.dup.strip
|
7
|
+
|
8
|
+
# if an XRI has a prefix, strip it.
|
9
|
+
identifier.gsub!(/xri:\/\//i, '')
|
10
|
+
|
11
|
+
# dodge XRIs -- TODO: validate, don't just skip.
|
12
|
+
unless ['=', '@', '+', '$', '!', '('].include?(identifier.at(0))
|
13
|
+
# does it begin with http? if not, add it.
|
14
|
+
identifier = "http://#{identifier}" unless identifier =~ /^http/i
|
15
|
+
|
16
|
+
# strip any fragments
|
17
|
+
identifier.gsub!(/\#(.*)$/, '')
|
18
|
+
|
19
|
+
begin
|
20
|
+
uri = URI.parse(identifier)
|
21
|
+
uri.scheme = uri.scheme.downcase # URI should do this
|
22
|
+
identifier = uri.normalize.to_s
|
23
|
+
rescue URI::InvalidURIError
|
24
|
+
raise InvalidOpenId.new("#{identifier} is not an OpenID identifier")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
return identifier
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Hash
|
33
|
+
def recursively_symbolize_keys!
|
34
|
+
self.symbolize_keys!
|
35
|
+
self.values.each do |v|
|
36
|
+
if v.is_a? Hash
|
37
|
+
v.recursively_symbolize_keys!
|
38
|
+
elsif v.is_a? Array
|
39
|
+
v.recursively_symbolize_keys!
|
40
|
+
end
|
41
|
+
end
|
42
|
+
self
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class Array
|
47
|
+
def recursively_symbolize_keys!
|
48
|
+
self.each do |item|
|
49
|
+
if item.is_a? Hash
|
50
|
+
item.recursively_symbolize_keys!
|
51
|
+
elsif item.is_a? Array
|
52
|
+
item.recursively_symbolize_keys!
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module AuthlogicConnect::Oauth
|
2
|
+
end
|
3
|
+
|
4
|
+
require File.dirname(__FILE__) + "/oauth/state"
|
5
|
+
require File.dirname(__FILE__) + "/oauth/variables"
|
6
|
+
require File.dirname(__FILE__) + "/oauth/process"
|
7
|
+
require File.dirname(__FILE__) + "/oauth/user"
|
8
|
+
require File.dirname(__FILE__) + "/oauth/session"
|
9
|
+
require File.dirname(__FILE__) + "/oauth/helper"
|
10
|
+
|
11
|
+
ActiveRecord::Base.send(:include, AuthlogicConnect::Oauth::User)
|
12
|
+
Authlogic::Session::Base.send(:include, AuthlogicConnect::Oauth::Session)
|
13
|
+
ActionController::Base.helper AuthlogicConnect::Oauth::Helper
|
14
|
+
ActionView::Helpers::FormBuilder.send(:include, AuthlogicConnect::Oauth::FormHelper)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module AuthlogicConnect::Oauth::Helper
|
2
|
+
|
3
|
+
# options include "name"
|
4
|
+
def oauth_register_hidden_input
|
5
|
+
oauth_input(:type => "user")
|
6
|
+
end
|
7
|
+
|
8
|
+
def oauth_login_hidden_input
|
9
|
+
oauth_input(:type => "session")
|
10
|
+
end
|
11
|
+
|
12
|
+
def oauth_input(options = {})
|
13
|
+
tag(:input, {:type => "hidden", :name => "authentication_type", :value => options[:type]})
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
module AuthlogicConnect::Oauth::FormHelper
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module AuthlogicConnect::Oauth::Process
|
2
|
+
|
3
|
+
include AuthlogicConnect::Oauth::Variables
|
4
|
+
|
5
|
+
# Step 2: after save is called, it runs this method for validation
|
6
|
+
def validate_by_oauth
|
7
|
+
validate_email_field = false
|
8
|
+
unless new_oauth_request? # shouldn't be validating if it's redirecting...
|
9
|
+
restore_attributes
|
10
|
+
complete_oauth_transaction
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Step 3: if new_oauth_request?, redirect to oauth provider
|
15
|
+
def redirect_to_oauth
|
16
|
+
save_oauth_session
|
17
|
+
authorize_url = token_class.authorize_url(auth_callback_url) do |request_token|
|
18
|
+
save_auth_session_token(request_token) # only for oauth version 1
|
19
|
+
end
|
20
|
+
auth_controller.redirect_to authorize_url
|
21
|
+
end
|
22
|
+
|
23
|
+
# Step 3a: save our passed-parameters into the session,
|
24
|
+
# so we can retrieve them after the redirect calls back
|
25
|
+
def save_oauth_session
|
26
|
+
# Store the class which is redirecting, so we can ensure other classes
|
27
|
+
# don't get confused and attempt to use the response
|
28
|
+
auth_session[:auth_request_class] = self.class.name
|
29
|
+
|
30
|
+
auth_session[:authentication_type] = auth_params[:authentication_type]
|
31
|
+
auth_session[:oauth_provider] = auth_params[:oauth_provider]
|
32
|
+
auth_session[:auth_method] = "oauth"
|
33
|
+
|
34
|
+
# Tell our rack callback filter what method the current request is using
|
35
|
+
auth_session[:auth_callback_method] = auth_controller.request.method
|
36
|
+
end
|
37
|
+
|
38
|
+
# Step 3b (if version 1.0 of oauth)
|
39
|
+
def save_auth_session_token(request)
|
40
|
+
# store token and secret
|
41
|
+
auth_session[:oauth_request_token] = request.token
|
42
|
+
auth_session[:oauth_request_token_secret] = request.secret
|
43
|
+
end
|
44
|
+
|
45
|
+
def restore_attributes
|
46
|
+
end
|
47
|
+
|
48
|
+
# Step 4: on callback, run this method
|
49
|
+
def authenticate_with_oauth
|
50
|
+
# implemented in User and Session Oauth modules
|
51
|
+
end
|
52
|
+
|
53
|
+
# Step last, after the response
|
54
|
+
# having lots of trouble testing logging and out multiple times,
|
55
|
+
# so there needs to be a solid way to know when a user has messed up loggin in.
|
56
|
+
def cleanup_oauth_session
|
57
|
+
[:auth_request_class,
|
58
|
+
:authentication_type,
|
59
|
+
:auth_method,
|
60
|
+
:auth_attributes,
|
61
|
+
:oauth_provider,
|
62
|
+
:auth_callback_method,
|
63
|
+
:oauth_request_token,
|
64
|
+
:oauth_request_token_secret
|
65
|
+
].each {|key| auth_session.delete(key)}
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module AuthlogicConnect::Oauth
|
2
|
+
# This module is responsible for adding oauth
|
3
|
+
# to the Authlogic::Session::Base class.
|
4
|
+
module Session
|
5
|
+
def self.included(base)
|
6
|
+
base.class_eval do
|
7
|
+
include InstanceMethods
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module InstanceMethods
|
12
|
+
include Process
|
13
|
+
|
14
|
+
def self.included(klass)
|
15
|
+
klass.class_eval do
|
16
|
+
validate :validate_by_oauth, :if => :authenticating_with_oauth?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Hooks into credentials so that you can pass a user who has already has an oauth access token.
|
21
|
+
def credentials=(value)
|
22
|
+
super
|
23
|
+
values = value.is_a?(Array) ? value : [value]
|
24
|
+
hash = values.first.is_a?(Hash) ? values.first.with_indifferent_access : nil
|
25
|
+
self.record = hash[:priority_record] if !hash.nil? && hash.key?(:priority_record)
|
26
|
+
end
|
27
|
+
|
28
|
+
def record=(record)
|
29
|
+
@record = record
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
# Clears out the block if we are authenticating with oauth,
|
34
|
+
# so that we can redirect without a DoubleRender error.
|
35
|
+
def save_with_oauth(&block)
|
36
|
+
block = nil if redirecting_to_oauth_server?
|
37
|
+
return block.nil?
|
38
|
+
end
|
39
|
+
|
40
|
+
def complete_oauth_transaction
|
41
|
+
if @record
|
42
|
+
self.attempted_record = record
|
43
|
+
else
|
44
|
+
# this generated token is always the same for a user!
|
45
|
+
# this is searching with User.find ...
|
46
|
+
# attempted_record is part of AuthLogic
|
47
|
+
hash = oauth_token_and_secret
|
48
|
+
token = token_class.find_by_key_or_token(hash[:key], hash[:token], :include => [:user]) # some weird error if I leave out the include)
|
49
|
+
self.attempted_record = token.user
|
50
|
+
end
|
51
|
+
|
52
|
+
if !attempted_record
|
53
|
+
errors.add_to_base("Could not find user in our database, have you registered with your oauth account?")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|