lsdr-authlogic-connect 0.0.3.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|