robdimarco-authlogic-connect 0.0.6
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 +234 -0
- data/Rakefile +85 -0
- data/init.rb +1 -0
- data/lib/authlogic-connect.rb +39 -0
- data/lib/authlogic_connect/access_token.rb +61 -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 +30 -0
- data/lib/authlogic_connect/common/state.rb +45 -0
- data/lib/authlogic_connect/common/user.rb +77 -0
- data/lib/authlogic_connect/common/variables.rb +121 -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 +75 -0
- data/lib/authlogic_connect/oauth/session.rb +62 -0
- data/lib/authlogic_connect/oauth/state.rb +60 -0
- data/lib/authlogic_connect/oauth/tokens/aol_token.rb +2 -0
- data/lib/authlogic_connect/oauth/tokens/facebook_token.rb +11 -0
- data/lib/authlogic_connect/oauth/tokens/foursquare_token.rb +15 -0
- data/lib/authlogic_connect/oauth/tokens/get_satisfaction_token.rb +9 -0
- data/lib/authlogic_connect/oauth/tokens/github_token.rb +14 -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/meetup_token.rb +12 -0
- data/lib/authlogic_connect/oauth/tokens/myspace_token.rb +26 -0
- data/lib/authlogic_connect/oauth/tokens/netflix_token.rb +10 -0
- data/lib/authlogic_connect/oauth/tokens/oauth_token.rb +164 -0
- data/lib/authlogic_connect/oauth/tokens/ohloh_token.rb +9 -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 +64 -0
- data/lib/authlogic_connect/oauth/variables.rb +64 -0
- data/lib/authlogic_connect/openid.rb +11 -0
- data/lib/authlogic_connect/openid/process.rb +74 -0
- data/lib/authlogic_connect/openid/session.rb +56 -0
- data/lib/authlogic_connect/openid/state.rb +48 -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 +38 -0
- data/lib/authlogic_connect/openid/variables.rb +19 -0
- data/lib/authlogic_connect/rack_state.rb +19 -0
- data/lib/open_id_authentication.rb +127 -0
- data/rails/init.rb +19 -0
- data/test/controllers/test_users_controller.rb +21 -0
- data/test/libs/database.rb +47 -0
- data/test/libs/user.rb +7 -0
- data/test/libs/user_session.rb +2 -0
- data/test/test_helper.rb +178 -0
- data/test/test_oauth.rb +178 -0
- data/test/test_openid.rb +71 -0
- data/test/test_user.rb +98 -0
- metadata +243 -0
File without changes
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# http://www.vimeo.com/api/docs/oauth
|
2
|
+
# http://www.vimeo.com/api/applications/new
|
3
|
+
# http://vimeo.com/api/applications
|
4
|
+
class VimeoToken < OauthToken
|
5
|
+
|
6
|
+
key do |access_token|
|
7
|
+
body = JSON.parse(access_token.get("http://vimeo.com/api/v2/#{access_token.token}/info.json"))
|
8
|
+
user_id = body.first["id"]
|
9
|
+
end
|
10
|
+
|
11
|
+
settings "http://vimeo.com",
|
12
|
+
:request_token_path => "/oauth/request_token",
|
13
|
+
:authorize_path => "/oauth/authorize",
|
14
|
+
:access_token_path => "/oauth/access_token",
|
15
|
+
:http_method => "get",
|
16
|
+
:scheme => :query_string
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# https://developer.apps.yahoo.com/dashboard/createKey.html
|
2
|
+
# https://developer.apps.yahoo.com/projects
|
3
|
+
# http://developer.yahoo.com/oauth/guide/oauth-accesstoken.html
|
4
|
+
# http://developer.yahoo.com/oauth/guide/oauth-auth-flow.html
|
5
|
+
# http://code.google.com/apis/gadgets/docs/oauth.html
|
6
|
+
# http://developer.yahoo.com/social/rest_api_guide/web-services-guids.html
|
7
|
+
# A GUID identifies a person
|
8
|
+
# http://social.yahooapis.com/v1/me/guid
|
9
|
+
class YahooToken < OauthToken
|
10
|
+
|
11
|
+
# http://social.yahooapis.com/v1/me/guid
|
12
|
+
key :xoauth_yahoo_guid
|
13
|
+
|
14
|
+
settings "https://api.login.yahoo.com",
|
15
|
+
:request_token_path => '/oauth/v2/get_request_token',
|
16
|
+
:access_token_path => '/oauth/v2/get_token',
|
17
|
+
:authorize_path => '/oauth/v2/request_auth'
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module AuthlogicConnect::Oauth::User
|
2
|
+
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval do
|
5
|
+
# add_acts_as_authentic_module makes sure it is
|
6
|
+
# only added to the user model, not all activerecord models.
|
7
|
+
add_acts_as_authentic_module(InstanceMethods, :prepend)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module InstanceMethods
|
12
|
+
include AuthlogicConnect::Oauth::Process
|
13
|
+
|
14
|
+
# Set up some simple validations
|
15
|
+
def self.included(base)
|
16
|
+
base.class_eval do
|
17
|
+
|
18
|
+
validate :validate_by_oauth, :if => :authenticating_with_oauth?
|
19
|
+
|
20
|
+
# need these validation options if you don't want it to choke
|
21
|
+
# on password length, which you don't need if you're using oauth
|
22
|
+
validates_length_of_password_field_options validates_length_of_password_field_options.merge(:if => :validate_password_with_oauth?)
|
23
|
+
validates_confirmation_of_password_field_options validates_confirmation_of_password_field_options.merge(:if => :validate_password_with_oauth?)
|
24
|
+
validates_length_of_password_confirmation_field_options validates_length_of_password_confirmation_field_options.merge(:if => :validate_password_with_oauth?)
|
25
|
+
validates_length_of_login_field_options validates_length_of_login_field_options.merge(:if => :validate_password_with_oauth?)
|
26
|
+
validates_format_of_login_field_options validates_format_of_login_field_options.merge(:if => :validate_password_with_oauth?)
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# user adds a few extra things to this method from Process
|
32
|
+
# modules work like inheritance
|
33
|
+
def save_oauth_session
|
34
|
+
super
|
35
|
+
auth_session[:auth_attributes] = attributes.reject!{|k, v| v.blank? || !self.respond_to?(k)} unless is_auth_session?
|
36
|
+
end
|
37
|
+
|
38
|
+
def redirect_to_oauth
|
39
|
+
return has_token?(oauth_provider) ? false : super
|
40
|
+
end
|
41
|
+
|
42
|
+
def restore_attributes
|
43
|
+
# Restore any attributes which were saved before redirecting to the auth server
|
44
|
+
self.attributes = auth_session[:auth_attributes]
|
45
|
+
end
|
46
|
+
|
47
|
+
# single implementation method for oauth.
|
48
|
+
# this is called after we get the callback url and we are saving the user
|
49
|
+
# to the database.
|
50
|
+
# it is called by the validation chain.
|
51
|
+
def complete_oauth_transaction
|
52
|
+
token = token_class.new(oauth_token_and_secret)
|
53
|
+
old_token = token_class.find_by_key_or_token(token.key, token.token)
|
54
|
+
token = old_token if old_token
|
55
|
+
|
56
|
+
if has_token?(oauth_provider)
|
57
|
+
self.errors.add(:tokens, "you have already created an account using your #{token_class.service_name} account, so it")
|
58
|
+
else
|
59
|
+
self.access_tokens << token
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module AuthlogicConnect::Oauth::Variables
|
2
|
+
include AuthlogicConnect::Oauth::State
|
3
|
+
|
4
|
+
# this doesn't do anything yet, just to show what variables
|
5
|
+
# we need from the form
|
6
|
+
def oauth_variables
|
7
|
+
[:oauth_provider]
|
8
|
+
end
|
9
|
+
|
10
|
+
# this comes straight from either the params or session.
|
11
|
+
# it is required for most of the other accessors in here
|
12
|
+
def oauth_provider
|
13
|
+
from_session_or_params(:oauth_provider)
|
14
|
+
end
|
15
|
+
|
16
|
+
# next is "token_class", which is found from the oauth_provider key.
|
17
|
+
# it is the OauthToken subclass, such as TwitterToken, which we
|
18
|
+
# use as the api for accessing oauth and saving the response to the database for a user.
|
19
|
+
def token_class
|
20
|
+
AuthlogicConnect.token(oauth_provider) unless oauth_provider.blank?
|
21
|
+
end
|
22
|
+
|
23
|
+
# This should go...
|
24
|
+
def oauth_response
|
25
|
+
auth_params && oauth_token
|
26
|
+
end
|
27
|
+
|
28
|
+
# the token from the response parameters
|
29
|
+
def oauth_token
|
30
|
+
return nil unless token_class
|
31
|
+
oauth_version == 1.0 ? auth_params[:oauth_token] : auth_params[:code]
|
32
|
+
end
|
33
|
+
|
34
|
+
# the version of oauth we're using. Accessed from the OauthToken subclass
|
35
|
+
def oauth_version
|
36
|
+
token_class.oauth_version
|
37
|
+
end
|
38
|
+
|
39
|
+
# the Oauth gem consumer, whereby we can make requests to the server
|
40
|
+
def oauth_consumer
|
41
|
+
token_class.consumer
|
42
|
+
end
|
43
|
+
|
44
|
+
def stored_oauth_token_and_secret
|
45
|
+
if auth_controller?
|
46
|
+
{:key => auth_params[:_key], :token => auth_params[:_token], :secret => auth_params[:_secret]}
|
47
|
+
else
|
48
|
+
{:key => nil, :token => nil, :secret => nil}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# this is a thick method.
|
53
|
+
# it gives you the final key and secret that we will store in the database
|
54
|
+
def oauth_token_and_secret
|
55
|
+
return stored_oauth_token_and_secret if stored_oauth_token_and_secret?
|
56
|
+
token_class.get_token_and_secret(
|
57
|
+
:token => auth_session[:oauth_request_token],
|
58
|
+
:secret => oauth_version == 1.0 ? auth_session[:oauth_request_token_secret] : oauth_token,
|
59
|
+
:oauth_verifier => auth_params[:oauth_verifier],
|
60
|
+
:redirect_uri => auth_callback_url
|
61
|
+
)
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module AuthlogicConnect::Openid
|
2
|
+
end
|
3
|
+
|
4
|
+
require File.dirname(__FILE__) + "/openid/state"
|
5
|
+
require File.dirname(__FILE__) + "/openid/variables"
|
6
|
+
require File.dirname(__FILE__) + "/openid/process"
|
7
|
+
require File.dirname(__FILE__) + "/openid/user"
|
8
|
+
require File.dirname(__FILE__) + "/openid/session"
|
9
|
+
|
10
|
+
ActiveRecord::Base.send(:include, AuthlogicConnect::Openid::User)
|
11
|
+
Authlogic::Session::Base.send(:include, AuthlogicConnect::Openid::Session)
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module AuthlogicConnect::Openid::Process
|
2
|
+
|
3
|
+
include AuthlogicConnect::Openid::Variables
|
4
|
+
|
5
|
+
def start_openid
|
6
|
+
save_openid_session
|
7
|
+
call_openid
|
8
|
+
end
|
9
|
+
|
10
|
+
def complete_openid
|
11
|
+
restore_attributes
|
12
|
+
call_openid
|
13
|
+
end
|
14
|
+
|
15
|
+
def call_openid
|
16
|
+
options = {}
|
17
|
+
options[:return_to] = auth_callback_url
|
18
|
+
# this is called both on start and complete.
|
19
|
+
# reason being, in the open_id_authentication library (where "authenticate_with_open_id" is defined),
|
20
|
+
# it checks the rack session to find openid pareters, and knows whether we're at
|
21
|
+
# start or complete
|
22
|
+
auth_controller.send(:authenticate_with_open_id, openid_identifier, options) do |result, openid_identifier|
|
23
|
+
complete_openid_transaction(result, openid_identifier)
|
24
|
+
return true
|
25
|
+
end
|
26
|
+
return false
|
27
|
+
end
|
28
|
+
|
29
|
+
def complete_openid_transaction(result, openid_identifier)
|
30
|
+
if result.unsuccessful?
|
31
|
+
errors.add_to_base(result.message)
|
32
|
+
end
|
33
|
+
|
34
|
+
if AccessToken.find_by_key(openid_identifier.normalize_identifier)
|
35
|
+
else
|
36
|
+
token = OpenidToken.new(:key => openid_identifier)
|
37
|
+
self.access_tokens << token
|
38
|
+
self.active_token = token
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# want to do this after the final save
|
43
|
+
def cleanup_openid_session
|
44
|
+
[:auth_attributes, :authentication_type, :auth_callback_method].each {|key| remove_session_key(key)}
|
45
|
+
auth_session.each_key do |key|
|
46
|
+
remove_session_key(key) if key.to_s =~ /^OpenID/
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def validate_by_openid
|
51
|
+
if processing_authentication
|
52
|
+
authentication_protocol(:openid, :start) || authentication_protocol(:openid, :complete)
|
53
|
+
errors.add(:access_tokens, "had the following error: #{@openid_error}") if @openid_error
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def save_openid_session
|
58
|
+
# Tell our rack callback filter what method the current request is using
|
59
|
+
auth_session[:auth_callback_method] = auth_controller.request.method
|
60
|
+
auth_session[:auth_attributes] = attributes_to_save
|
61
|
+
auth_session[:authentication_type] = auth_params[:authentication_type]
|
62
|
+
auth_session[:auth_method] = "openid"
|
63
|
+
end
|
64
|
+
|
65
|
+
def attributes_to_save
|
66
|
+
{}
|
67
|
+
end
|
68
|
+
|
69
|
+
def restore_attributes
|
70
|
+
# Restore any attributes which were saved before redirecting to the auth server
|
71
|
+
self.attributes = auth_session[:auth_attributes] unless is_auth_session?
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module AuthlogicConnect::Openid
|
2
|
+
# This module is responsible for adding all of the OpenID goodness to the Authlogic::Session::Base class.
|
3
|
+
module Session
|
4
|
+
# Add a simple openid_identifier attribute and some validations for the field.
|
5
|
+
def self.included(klass)
|
6
|
+
klass.class_eval do
|
7
|
+
include InstanceMethods
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module InstanceMethods
|
12
|
+
include AuthlogicConnect::Openid::Process
|
13
|
+
|
14
|
+
def self.included(klass)
|
15
|
+
klass.class_eval do
|
16
|
+
validate :validate_by_openid, :if => :authenticating_with_openid?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Hooks into credentials so that you can pass an :openid_identifier key.
|
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
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def auto_register?
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
def complete_openid_transaction(result, openid_identifier)
|
34
|
+
if result.unsuccessful?
|
35
|
+
errors.add_to_base(result.message)
|
36
|
+
end
|
37
|
+
|
38
|
+
token = AccessToken.find_by_key(openid_identifier.normalize_identifier, :include => [:user])
|
39
|
+
|
40
|
+
self.attempted_record = token.user if token
|
41
|
+
|
42
|
+
if !attempted_record
|
43
|
+
if auto_register?
|
44
|
+
self.attempted_record = klass.new
|
45
|
+
self.attempted_record.access_tokens << OpenidToken.new(:key => openid_identifier.normalize_identifier)
|
46
|
+
self.attempted_record.save
|
47
|
+
else
|
48
|
+
auth_session[:openid_identifier] = openid_identifier
|
49
|
+
errors.add(:user, "Could not find user in our database, have you registered with your openid account?")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# all these methods must return true or false
|
2
|
+
module AuthlogicConnect::Openid::State
|
3
|
+
|
4
|
+
# 1. to call
|
5
|
+
def openid_request?
|
6
|
+
!openid_identifier.blank? && auth_session[:auth_attributes].nil?
|
7
|
+
end
|
8
|
+
|
9
|
+
def openid_identifier?
|
10
|
+
openid_request?
|
11
|
+
end
|
12
|
+
|
13
|
+
def openid_provider?
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
# 2. from call
|
18
|
+
# better check needed
|
19
|
+
def openid_response?
|
20
|
+
auth_controller? && !auth_session[:auth_attributes].nil? && auth_session[:auth_method] == "openid"
|
21
|
+
end
|
22
|
+
alias_method :openid_complete?, :openid_response?
|
23
|
+
|
24
|
+
# 3. either to or from call
|
25
|
+
# this should include more!
|
26
|
+
# we know we are using open id if:
|
27
|
+
# the params passed in have "openid_identifier"
|
28
|
+
def using_openid?
|
29
|
+
auth_controller? && (openid_request? || openid_response?)
|
30
|
+
end
|
31
|
+
|
32
|
+
def authenticating_with_openid?
|
33
|
+
auth_controller? && auth_class.activated? && using_openid?
|
34
|
+
end
|
35
|
+
|
36
|
+
def start_openid?
|
37
|
+
authenticating_with_openid? && !openid_response?
|
38
|
+
end
|
39
|
+
|
40
|
+
def complete_openid?
|
41
|
+
openid_complete?
|
42
|
+
end
|
43
|
+
|
44
|
+
def validate_password_with_openid?
|
45
|
+
!using_openid? && require_password?
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module AuthlogicConnect::Openid
|
2
|
+
module User
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval do
|
5
|
+
add_acts_as_authentic_module(AuthlogicConnect::Openid::Process, :prepend)
|
6
|
+
add_acts_as_authentic_module(InstanceMethods, :append)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module InstanceMethods
|
11
|
+
|
12
|
+
def self.included(base)
|
13
|
+
base.class_eval do
|
14
|
+
validate :validate_by_openid, :if => :authenticating_with_openid?
|
15
|
+
|
16
|
+
validates_length_of_password_field_options validates_length_of_password_field_options.merge(:if => :validate_password_with_openid?)
|
17
|
+
validates_confirmation_of_password_field_options validates_confirmation_of_password_field_options.merge(:if => :validate_password_with_openid?)
|
18
|
+
validates_length_of_password_confirmation_field_options validates_length_of_password_confirmation_field_options.merge(:if => :validate_password_with_openid?)
|
19
|
+
validates_length_of_login_field_options validates_length_of_login_field_options.merge(:if => :validate_password_with_openid?)
|
20
|
+
validates_format_of_login_field_options validates_format_of_login_field_options.merge(:if => :validate_password_with_openid?)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def attributes_to_save
|
25
|
+
attr_list = [:id, :password, crypted_password_field, password_salt_field, :persistence_token, :perishable_token, :single_access_token, :login_count,
|
26
|
+
:failed_login_count, :last_request_at, :current_login_at, :last_login_at, :current_login_ip, :last_login_ip, :created_at,
|
27
|
+
:updated_at, :lock_version]
|
28
|
+
attrs_to_save = attributes.clone.delete_if do |k, v|
|
29
|
+
attr_list.include?(k.to_sym)
|
30
|
+
end
|
31
|
+
if self.respond_to?(:password) && self.respond_to?(:password_confirmation)
|
32
|
+
attrs_to_save.merge!(:password => password, :password_confirmation => password_confirmation)
|
33
|
+
end
|
34
|
+
attrs_to_save.reject!{|k, v| v.blank? || !self.respond_to?(k)}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|