authlogic-connect 0.0.3.4 → 0.0.3.6

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