authlogic-connect 0.0.3.4 → 0.0.3.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/README.markdown +156 -43
  2. data/Rakefile +1 -1
  3. data/lib/authlogic-connect.rb +2 -71
  4. data/lib/authlogic_connect/authlogic_connect.rb +46 -0
  5. data/lib/authlogic_connect/callback_filter.rb +1 -1
  6. data/lib/authlogic_connect/common.rb +1 -1
  7. data/lib/authlogic_connect/common/state.rb +16 -0
  8. data/lib/authlogic_connect/common/user.rb +102 -34
  9. data/lib/authlogic_connect/common/variables.rb +68 -16
  10. data/lib/authlogic_connect/engine.rb +0 -1
  11. data/lib/authlogic_connect/{common/ext.rb → ext.rb} +1 -0
  12. data/lib/authlogic_connect/oauth.rb +3 -1
  13. data/lib/authlogic_connect/oauth/helper.rb +17 -13
  14. data/lib/authlogic_connect/oauth/process.rb +61 -76
  15. data/lib/authlogic_connect/oauth/session.rb +3 -14
  16. data/lib/authlogic_connect/oauth/state.rb +54 -0
  17. data/lib/authlogic_connect/oauth/tokens/google_token.rb +9 -1
  18. data/lib/authlogic_connect/oauth/tokens/oauth_token.rb +67 -2
  19. data/lib/authlogic_connect/oauth/tokens/twitter_token.rb +2 -0
  20. data/lib/authlogic_connect/oauth/user.rb +57 -74
  21. data/lib/authlogic_connect/oauth/variables.rb +52 -27
  22. data/lib/authlogic_connect/openid.rb +3 -0
  23. data/lib/authlogic_connect/openid/process.rb +30 -0
  24. data/lib/authlogic_connect/openid/session.rb +6 -53
  25. data/lib/authlogic_connect/openid/state.rb +47 -0
  26. data/lib/authlogic_connect/openid/tokens/my_openid_token.rb +3 -0
  27. data/lib/authlogic_connect/openid/tokens/openid_token.rb +6 -0
  28. data/lib/authlogic_connect/openid/user.rb +38 -68
  29. data/lib/authlogic_connect/openid/variables.rb +17 -3
  30. data/lib/authlogic_connect/token.rb +0 -1
  31. data/lib/open_id_authentication.rb +0 -1
  32. data/rails/init.rb +1 -1
  33. data/test/controllers/test_users_controller.rb +21 -0
  34. data/test/libs/database.rb +48 -0
  35. data/test/libs/user.rb +3 -0
  36. data/test/libs/user_session.rb +2 -0
  37. data/test/old.rb +53 -0
  38. data/test/test_authlogic_connect.rb +1 -1
  39. data/test/test_helper.rb +142 -42
  40. data/test/test_user.rb +255 -0
  41. metadata +15 -4
@@ -1,5 +1,7 @@
1
1
  class TwitterToken < OauthToken
2
2
 
3
+ key :user_id
4
+
3
5
  settings "http://twitter.com",
4
6
  :authorize_url => "http://twitter.com/oauth/authenticate"
5
7
 
@@ -1,85 +1,68 @@
1
- module AuthlogicConnect::Oauth
2
- module User
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
3
15
  def self.included(base)
4
16
  base.class_eval do
5
- add_acts_as_authentic_module(InstanceMethods, :prepend)
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?)
6
27
  end
28
+
29
+ # email needs to be optional for oauth
30
+ base.validate_email_field = false
31
+ end
32
+
33
+ # user adds a few extra things to this method from Process
34
+ # modules work like inheritance
35
+ def save_oauth_session
36
+ super
37
+ auth_session[:auth_attributes] = attributes.reject!{|k, v| v.blank?} unless is_auth_session?
7
38
  end
8
39
 
9
- module InstanceMethods
10
- include Process
11
- # Set up some simple validations
12
- def self.included(base)
13
- base.class_eval do
14
- has_many :tokens, :class_name => "Token", :dependent => :destroy
15
- belongs_to :active_token, :class_name => "Token", :dependent => :destroy
16
- accepts_nested_attributes_for :tokens, :active_token
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
- end
28
-
29
- # email needs to be optional for oauth
30
- base.validate_email_field = false
31
- end
32
-
33
- def update_attributes(attributes, &block)
34
- self.attributes = attributes
35
- save(true, &block)
36
- end
37
-
38
- # NEED TO GIVE A BLOCK
39
- def save_with_oauth(perform_validation = true, &block)
40
- if perform_validation && block_given? && redirecting_to_oauth_server?
41
- # Save attributes so they aren't lost during the authentication with the oauth server
42
- auth_session[:authlogic_oauth_attributes] = attributes.reject!{|k, v| v.blank?}
43
- redirect_to_oauth
44
- return false
45
- end
46
- return true
47
- end
48
-
49
- protected
40
+ def restore_attributes
41
+ # Restore any attributes which were saved before redirecting to the auth server
42
+ self.attributes = auth_session[:auth_attributes]
43
+ end
50
44
 
51
- def using_oauth?
52
- !oauth_token.blank?
53
- end
54
-
55
- def validate_password_with_oauth?
56
- !using_oauth? && require_password?
45
+ # single implementation method for oauth.
46
+ # this is called after we get the callback url and we are saving the user
47
+ # to the database.
48
+ # it is called by the validation chain.
49
+ def complete_oauth_transaction
50
+ unless create_oauth_token
51
+ self.errors.add(:tokens, "you have already created an account using your #{oauth_token.service_name} account, so it")
57
52
  end
53
+ end
54
+
55
+ def create_oauth_token
56
+ token = token_class.new(oauth_token_and_secret)
58
57
 
59
- def authenticating_with_oauth?
60
- return false unless oauth_provider
61
- # Initial request when user presses one of the button helpers
62
- initial_request = (auth_params && !auth_params[:register_with_oauth].blank?)
63
- # When the oauth provider responds and we made the initial request
64
- initial_response = (oauth_response && auth_session && auth_session[:oauth_request_class] == self.class.name)
65
-
66
- return initial_request || initial_response
67
- end
68
-
69
- def authenticate_with_oauth
70
- # Restore any attributes which were saved before redirecting to the oauth server
71
- self.attributes = auth_session.delete(:authlogic_oauth_attributes)
72
- token = AuthlogicConnect.token(oauth_provider).new(oauth_key_and_secret)
73
- puts "NEW TOKEN: #{token.inspect}"
74
- if old_token = Token.find_by_key(token.key)
75
- puts "OLD TOKEN? #{old_token.inspect}"
76
- self.errors.add("you have already created an account using your #{oauth_token.service_name} account, so it")
77
- else
78
- self.tokens << token
79
- self.active_token = token
80
- end
58
+ if has_token?(oauth_provider) || Token.find_by_key(token.key) || Token.find_by_token(token.token)
59
+ return false
60
+ else
61
+ self.tokens << token
62
+ self.active_token = token
63
+ return true
81
64
  end
82
-
83
65
  end
66
+
84
67
  end
85
- end
68
+ end
@@ -1,30 +1,55 @@
1
- module AuthlogicConnect::Oauth
2
- module Variables
3
-
4
- # These are just helper variables
5
- def oauth_response
6
- auth_params && oauth_key
7
- end
8
-
9
- def oauth_key
10
- return nil unless auth_controller
11
- oauth_version == 1.0 ? auth_params[:oauth_token] : auth_params[:code]
12
- end
13
-
14
- def oauth_version
15
- oauth_token.oauth_version
16
- end
17
-
18
- def oauth_provider
19
- auth_session[:oauth_provider] || "facebook"
20
- end
21
-
22
- def oauth_consumer
23
- oauth_token.consumer
24
- end
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
25
9
 
26
- def oauth_token
27
- AuthlogicConnect.token(oauth_provider)
28
- end
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
+ # this is a thick method.
45
+ # it gives you the final key and secret that we will store in the database
46
+ def oauth_token_and_secret
47
+ token_class.get_token_and_secret(
48
+ :token => auth_session[:oauth_request_token],
49
+ :secret => oauth_version == 1.0 ? auth_session[:oauth_request_token_secret] : oauth_token,
50
+ :oauth_verifier => auth_params[:oauth_verifier],
51
+ :redirect_uri => auth_callback_url
52
+ )
29
53
  end
54
+
30
55
  end
@@ -1,6 +1,9 @@
1
1
  module AuthlogicConnect::Openid
2
2
  end
3
3
 
4
+ require File.dirname(__FILE__) + "/openid/state"
5
+ require File.dirname(__FILE__) + "/openid/variables"
6
+ require File.dirname(__FILE__) + "/openid/process"
4
7
  require File.dirname(__FILE__) + "/openid/user"
5
8
  require File.dirname(__FILE__) + "/openid/session"
6
9
 
@@ -0,0 +1,30 @@
1
+ module AuthlogicConnect::Openid::Process
2
+
3
+ include AuthlogicConnect::Openid::Variables
4
+
5
+ # want to do this after the final save
6
+ def cleanup_openid_session
7
+ [:auth_attributes, :authentication_type, :auth_callback_method].each {|key| auth_session.delete(key)}
8
+ auth_session.each_key do |key|
9
+ auth_session.delete(key) if key.to_s =~ /^OpenID/
10
+ end
11
+ end
12
+
13
+ def validate_by_openid
14
+ errors.add(:tokens, "had the following error: #{@openid_error}") if @openid_error
15
+ end
16
+
17
+ def save_openid_session
18
+ # Tell our rack callback filter what method the current request is using
19
+ auth_session[:auth_callback_method] = auth_controller.request.method
20
+ auth_session[:auth_attributes] = attributes_to_save
21
+ auth_session[:authentication_type] = auth_params[:authentication_type]
22
+ auth_session[:auth_method] = "openid"
23
+ end
24
+
25
+ def restore_attributes
26
+ # Restore any attributes which were saved before redirecting to the auth server
27
+ self.attributes = auth_session[:auth_attributes]
28
+ end
29
+
30
+ end
@@ -3,48 +3,16 @@ module AuthlogicConnect::Openid
3
3
  module Session
4
4
  # Add a simple openid_identifier attribute and some validations for the field.
5
5
  def self.included(klass)
6
- klass.extend ClassMethods
7
6
  klass.class_eval do
8
7
  include InstanceMethods
9
8
  end
10
9
  end
11
10
 
12
- module ClassMethods
13
- # What method should we call to find a record by the openid_identifier?
14
- # This is useful if you want to store multiple openid_identifiers for a single record.
15
- # You could do something like:
16
- #
17
- # class User < ActiveRecord::Base
18
- # def self.find_by_openid_identifier(identifier)
19
- # user.first(:conditions => {:openid_identifiers => {:identifier => identifier}})
20
- # end
21
- # end
22
- #
23
- # Obviously the above depends on what you are calling your assocition, etc. But you get the point.
24
- #
25
- # * <tt>Default:</tt> :find_by_openid_identifier
26
- # * <tt>Accepts:</tt> Symbol
27
- def find_by_openid_identifier_method(value = nil)
28
- rw_config(:find_by_openid_identifier_method, value, :find_by_openid_identifier)
29
- end
30
- alias_method :find_by_openid_identifier_method=, :find_by_openid_identifier_method
31
-
32
- # Add this in your Session object to Auto Register a new user using openid via sreg
33
- def auto_register(value=true)
34
- auto_register_value(value)
35
- end
36
-
37
- def auto_register_value(value=nil)
38
- rw_config(:auto_register,value,false)
39
- end
40
-
41
- alias_method :auto_register=,:auto_register
42
- end
43
-
44
11
  module InstanceMethods
12
+ include AuthlogicConnect::Openid::Process
13
+
45
14
  def self.included(klass)
46
15
  klass.class_eval do
47
- attr_reader :openid_identifier
48
16
  validate :validate_openid_error
49
17
  validate :validate_by_openid, :if => :authenticating_with_openid?
50
18
  end
@@ -58,18 +26,10 @@ module AuthlogicConnect::Openid
58
26
  self.openid_identifier = hash[:openid_identifier] if !hash.nil? && hash.key?(:openid_identifier)
59
27
  end
60
28
 
61
- def openid_identifier=(value)
62
- @openid_identifier = value.blank? ? nil : OpenIdAuthentication.normalize_identifier(value)
63
- @openid_error = nil
64
- rescue OpenIdAuthentication::InvalidOpenId => e
65
- @openid_identifier = nil
66
- @openid_error = e.message
67
- end
68
-
69
29
  # Cleaers out the block if we are authenticating with OpenID, so that we can redirect without a DoubleRender
70
30
  # error.
71
31
  def save_with_openid(&block)
72
- block = nil if !openid_identifier.blank?
32
+ block = nil if Token.find_by_key(openid_identifier.normalize_identifier)
73
33
  return block.nil?
74
34
  end
75
35
 
@@ -78,21 +38,14 @@ module AuthlogicConnect::Openid
78
38
  attempted_record.nil? && errors.empty? && (!openid_identifier.blank? || (controller.params[:open_id_complete] && controller.params[:for_session]))
79
39
  end
80
40
 
81
- def find_by_openid_identifier_method
82
- self.class.find_by_openid_identifier_method
83
- end
84
-
85
- def find_by_openid_identifier_method
86
- self.class.find_by_openid_identifier_method
87
- end
88
-
89
41
  def auto_register?
90
- self.class.auto_register_value
42
+ false
91
43
  end
92
44
 
93
45
  def validate_by_openid
94
46
  self.remember_me = auth_params[:remember_me] == "true" if auth_params.key?(:remember_me)
95
- self.attempted_record = klass.send(find_by_openid_identifier_method, openid_identifier)
47
+ token = Token.find_by_key(openid_identifier.normalize_identifier, :include => [:user])
48
+ self.attempted_record = token.user if token
96
49
  if !attempted_record
97
50
  if auto_register?
98
51
  self.attempted_record = klass.new :openid_identifier => openid_identifier
@@ -0,0 +1,47 @@
1
+ # all these methods must return true or false
2
+ module AuthlogicConnect::Openid::State
3
+ # 1. to call
4
+ def openid_request?
5
+ !openid_identifier.blank?
6
+ end
7
+
8
+ def openid_identifier?
9
+ openid_request?
10
+ end
11
+
12
+ def openid_provider?
13
+
14
+ end
15
+
16
+ # 2. from call
17
+ # better check needed
18
+ def openid_response?
19
+ !auth_session[:auth_attributes].nil? && auth_session[:auth_method] == "openid"
20
+ end
21
+ alias_method :openid_complete?, :openid_response?
22
+
23
+ # 3. either to or from call
24
+ # this should include more!
25
+ # we know we are using open id if:
26
+ # the params passed in have "openid_identifier"
27
+ def using_openid?
28
+ openid_request? || openid_response?
29
+ end
30
+
31
+ def authenticating_with_openid?
32
+ session_class.activated? && using_openid?
33
+ end
34
+
35
+ def allow_openid_redirect?
36
+ authenticating_with_openid?
37
+ end
38
+
39
+ def redirecting_to_openid_server?
40
+ allow_openid_redirect? && !authenticate_with_openid
41
+ end
42
+
43
+ def validate_password_with_openid?
44
+ !using_openid? && require_password?
45
+ end
46
+
47
+ end
@@ -0,0 +1,3 @@
1
+ class MyOpenidToken < OpenidToken
2
+
3
+ end
@@ -1,3 +1,9 @@
1
1
  class OpenidToken < Token
2
2
 
3
+ before_save :format_identifier
4
+
5
+ def format_identifier
6
+ self.key = self.key.to_s.normalize_identifier unless self.key.blank?
7
+ end
8
+
3
9
  end
@@ -2,18 +2,17 @@ module AuthlogicConnect::Openid
2
2
  module User
3
3
  def self.included(base)
4
4
  base.class_eval do
5
- add_acts_as_authentic_module(InstanceMethods, :prepend)
5
+ add_acts_as_authentic_module(AuthlogicConnect::Openid::Process, :prepend)
6
+ add_acts_as_authentic_module(InstanceMethods, :append)
6
7
  end
7
8
  end
8
9
 
9
10
  module InstanceMethods
10
-
11
- def self.included(base)
12
- return if !base.column_names.include?("openid_identifier")
13
-
11
+
12
+ def self.included(base)
14
13
  base.class_eval do
15
- validates_uniqueness_of :openid_identifier, :scope => validations_scope, :if => :using_openid?
16
- validate :validate_openid
14
+ validate :validate_by_openid, :if => :authenticating_with_openid?
15
+
17
16
  validates_length_of_password_field_options validates_length_of_password_field_options.merge(:if => :validate_password_with_openid?)
18
17
  validates_confirmation_of_password_field_options validates_confirmation_of_password_field_options.merge(:if => :validate_password_with_openid?)
19
18
  validates_length_of_password_confirmation_field_options validates_length_of_password_confirmation_field_options.merge(:if => :validate_password_with_openid?)
@@ -22,71 +21,42 @@ module AuthlogicConnect::Openid
22
21
  end
23
22
  end
24
23
 
25
- def openid_identifier=(value)
26
- write_attribute(:openid_identifier, value.blank? ? nil : value.to_s.normalize_identifier)
27
- reset_persistence_token if openid_identifier_changed?
28
- rescue Exception => e
29
- @openid_error = e.message
24
+ def authenticate_with_openid
25
+ @openid_error = nil
26
+ if !openid_response?
27
+ save_openid_session
28
+ else
29
+ restore_attributes
30
+ end
31
+ options = {}
32
+ options[:return_to] = auth_callback_url(:for_model => "1", :action => "create")
33
+ auth_controller.send(:authenticate_with_open_id, openid_identifier, options) do |result, openid_identifier|
34
+ create_openid_token(result, openid_identifier)
35
+ return true
36
+ end
37
+ return false
30
38
  end
31
39
 
32
- def save_with_openid(perform_validation = true, &block)
33
- return false if perform_validation && block_given? && authenticating_with_openid? && !authenticating_with_openid
34
- return true
40
+ def create_openid_token(result, openid_identifier)
41
+ if result.unsuccessful?
42
+ @openid_error = result.message
43
+ elsif Token.find_by_key(openid_identifier.normalize_identifier)
44
+ else
45
+ token = OpenidToken.new(:key => openid_identifier)
46
+ self.tokens << token
47
+ self.active_token = token
48
+ end
35
49
  end
36
50
 
37
- protected
38
-
39
- def validate_openid
40
- errors.add(:openid_identifier, "had the following error: #{@openid_error}") if @openid_error
41
- end
42
-
43
- def using_openid?
44
- respond_to?(:openid_identifier) && !auth_params[:openid_identifier].blank?
45
- end
46
-
47
- def openid_complete?
48
- auth_session[:openid_attributes]
49
- end
50
-
51
- def authenticating_with_openid?
52
- session_class.activated? && ((using_openid?) || openid_complete?)
53
- end
54
-
55
- def validate_password_with_openid?
56
- !using_openid? && require_password?
57
- end
58
-
59
- def authenticating_with_openid
60
- @openid_error = nil
61
- if !openid_complete?
62
- # Tell our rack callback filter what method the current request is using
63
- auth_session[:auth_callback_method] = auth_controller.request.method
64
- auth_session[:openid_attributes] = attributes_to_save
65
- else
66
- self.attributes = auth_session.delete(:openid_attributes)
67
- end
68
-
69
- options = {}
70
- options[:return_to] = auth_controller.url_for(:for_model => "1", :controller => "users", :action => "create")
71
- auth_controller.send(:authenticate_with_open_id, openid_identifier, options) do |result, openid_identifier, registration|
72
- if result.unsuccessful?
73
- @openid_error = result.message
74
- else
75
- self.openid_identifier = openid_identifier
76
- end
77
- return true
78
- end
79
- return false
80
- end
81
-
82
- def attributes_to_save
83
- attrs_to_save = attributes.clone.delete_if do |k, v|
84
- [:id, :password, crypted_password_field, password_salt_field, :persistence_token, :perishable_token, :single_access_token, :login_count,
85
- :failed_login_count, :last_request_at, :current_login_at, :last_login_at, :current_login_ip, :last_login_ip, :created_at,
86
- :updated_at, :lock_version].include?(k.to_sym)
87
- end
88
- attrs_to_save.merge!(:password => password, :password_confirmation => password_confirmation)
89
- end
51
+ def attributes_to_save
52
+ attr_list = [:id, :password, crypted_password_field, password_salt_field, :persistence_token, :perishable_token, :single_access_token, :login_count,
53
+ :failed_login_count, :last_request_at, :current_login_at, :last_login_at, :current_login_ip, :last_login_ip, :created_at,
54
+ :updated_at, :lock_version]
55
+ attrs_to_save = attributes.clone.delete_if do |k, v|
56
+ attr_list.include?(k.to_sym)
57
+ end
58
+ attrs_to_save.merge!(:password => password, :password_confirmation => password_confirmation)
59
+ end
90
60
  end
91
61
  end
92
62
  end