anideo-authlogic-connect 0.0.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 (61) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.markdown +234 -0
  3. data/Rakefile +85 -0
  4. data/init.rb +1 -0
  5. data/lib/authlogic-connect.rb +39 -0
  6. data/lib/authlogic_connect/access_token.rb +61 -0
  7. data/lib/authlogic_connect/authlogic_connect.rb +46 -0
  8. data/lib/authlogic_connect/callback_filter.rb +19 -0
  9. data/lib/authlogic_connect/common.rb +10 -0
  10. data/lib/authlogic_connect/common/session.rb +30 -0
  11. data/lib/authlogic_connect/common/state.rb +45 -0
  12. data/lib/authlogic_connect/common/user.rb +77 -0
  13. data/lib/authlogic_connect/common/variables.rb +124 -0
  14. data/lib/authlogic_connect/engine.rb +14 -0
  15. data/lib/authlogic_connect/ext.rb +56 -0
  16. data/lib/authlogic_connect/oauth.rb +14 -0
  17. data/lib/authlogic_connect/oauth/helper.rb +20 -0
  18. data/lib/authlogic_connect/oauth/process.rb +75 -0
  19. data/lib/authlogic_connect/oauth/session.rb +62 -0
  20. data/lib/authlogic_connect/oauth/state.rb +60 -0
  21. data/lib/authlogic_connect/oauth/tokens/aol_token.rb +2 -0
  22. data/lib/authlogic_connect/oauth/tokens/facebook_token.rb +11 -0
  23. data/lib/authlogic_connect/oauth/tokens/foursquare_token.rb +15 -0
  24. data/lib/authlogic_connect/oauth/tokens/get_satisfaction_token.rb +9 -0
  25. data/lib/authlogic_connect/oauth/tokens/github_token.rb +14 -0
  26. data/lib/authlogic_connect/oauth/tokens/google_token.rb +41 -0
  27. data/lib/authlogic_connect/oauth/tokens/linked_in_token.rb +19 -0
  28. data/lib/authlogic_connect/oauth/tokens/meetup_token.rb +12 -0
  29. data/lib/authlogic_connect/oauth/tokens/myspace_token.rb +26 -0
  30. data/lib/authlogic_connect/oauth/tokens/netflix_token.rb +10 -0
  31. data/lib/authlogic_connect/oauth/tokens/oauth_token.rb +164 -0
  32. data/lib/authlogic_connect/oauth/tokens/ohloh_token.rb +9 -0
  33. data/lib/authlogic_connect/oauth/tokens/opensocial_token.rb +0 -0
  34. data/lib/authlogic_connect/oauth/tokens/twitter_token.rb +8 -0
  35. data/lib/authlogic_connect/oauth/tokens/vimeo_token.rb +18 -0
  36. data/lib/authlogic_connect/oauth/tokens/yahoo_token.rb +19 -0
  37. data/lib/authlogic_connect/oauth/user.rb +64 -0
  38. data/lib/authlogic_connect/oauth/variables.rb +64 -0
  39. data/lib/authlogic_connect/openid.rb +11 -0
  40. data/lib/authlogic_connect/openid/process.rb +74 -0
  41. data/lib/authlogic_connect/openid/session.rb +56 -0
  42. data/lib/authlogic_connect/openid/state.rb +48 -0
  43. data/lib/authlogic_connect/openid/tokens/aol_token.rb +0 -0
  44. data/lib/authlogic_connect/openid/tokens/blogger_token.rb +0 -0
  45. data/lib/authlogic_connect/openid/tokens/flickr_token.rb +0 -0
  46. data/lib/authlogic_connect/openid/tokens/my_openid_token.rb +3 -0
  47. data/lib/authlogic_connect/openid/tokens/openid_token.rb +9 -0
  48. data/lib/authlogic_connect/openid/user.rb +38 -0
  49. data/lib/authlogic_connect/openid/variables.rb +19 -0
  50. data/lib/authlogic_connect/rack_state.rb +19 -0
  51. data/lib/open_id_authentication.rb +127 -0
  52. data/rails/init.rb +19 -0
  53. data/test/controllers/test_users_controller.rb +21 -0
  54. data/test/libs/database.rb +47 -0
  55. data/test/libs/user.rb +7 -0
  56. data/test/libs/user_session.rb +2 -0
  57. data/test/test_helper.rb +178 -0
  58. data/test/test_oauth.rb +178 -0
  59. data/test/test_openid.rb +71 -0
  60. data/test/test_user.rb +85 -0
  61. metadata +232 -0
@@ -0,0 +1,19 @@
1
+ class AuthlogicConnect::CallbackFilter
2
+ def initialize(app)
3
+ @app = app
4
+ end
5
+
6
+ # this intercepts how the browser interprets the url.
7
+ # so we override it and say,
8
+ # "if we've stored a variable in the session called :auth_callback_method,
9
+ # then convert that into a POST call so we re-call the original method"
10
+ def call(env)
11
+ if env["rack.session"].nil?
12
+ raise "Make sure you are setting the session in Rack too! Place this in config/application.rb"
13
+ end
14
+ unless env["rack.session"][:auth_callback_method].blank?
15
+ env["REQUEST_METHOD"] = env["rack.session"].delete(:auth_callback_method).to_s.upcase
16
+ end
17
+ @app.call(env)
18
+ end
19
+ end
@@ -0,0 +1,10 @@
1
+ module AuthlogicConnect::Common
2
+ end
3
+
4
+ require File.dirname(__FILE__) + "/common/state"
5
+ require File.dirname(__FILE__) + "/common/variables"
6
+ require File.dirname(__FILE__) + "/common/user"
7
+ require File.dirname(__FILE__) + "/common/session"
8
+
9
+ ActiveRecord::Base.send(:include, AuthlogicConnect::Common::User)
10
+ Authlogic::Session::Base.send(:include, AuthlogicConnect::Common::Session)
@@ -0,0 +1,30 @@
1
+ module AuthlogicConnect::Common
2
+ module Session
3
+
4
+ def self.included(base)
5
+ base.class_eval do
6
+ include Variables
7
+ include InstanceMethods
8
+ end
9
+ end
10
+
11
+ module InstanceMethods
12
+
13
+ # core save method coordinating how to save the session.
14
+ # want to destroy the block if we redirect to a remote service, that's it.
15
+ # otherwise the block contains the render methods we wan to use
16
+ def save(&block)
17
+ self.errors.clear
18
+ # log_state
19
+ authenticate_via_protocol(block_given?) do |redirecting|
20
+ block = nil if redirecting
21
+ result = super(&block)
22
+ cleanup_authentication_session unless block.nil?
23
+ result
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,45 @@
1
+ # This class holds query/state variables common to oauth and openid
2
+ module AuthlogicConnect::Common::State
3
+
4
+ def auth_controller?
5
+ !auth_controller.blank?
6
+ end
7
+
8
+ def auth_params?
9
+ auth_controller? && !auth_params.blank?
10
+ end
11
+
12
+ def auth_session?
13
+ !auth_session.blank?
14
+ end
15
+
16
+ def is_auth_session?
17
+ self.is_a?(Authlogic::Session::Base)
18
+ end
19
+
20
+ def start_authentication?
21
+ start_oauth? || start_openid?
22
+ end
23
+
24
+ def validate_password_with_oauth?
25
+ !using_openid? && super
26
+ end
27
+
28
+ def validate_password_with_openid?
29
+ !using_oauth? && super
30
+ end
31
+
32
+ # because user and session are so closely tied together, I am still
33
+ # uncertain as to how they are saved. So this makes sure if we are
34
+ # logging in, it must be saving the session, otherwise the user.
35
+ def correct_request_class?
36
+ return false unless auth_params?
37
+
38
+ if is_auth_session?
39
+ auth_type.to_s == "session"
40
+ else
41
+ auth_type.to_s == "user"
42
+ end
43
+ end
44
+
45
+ end
@@ -0,0 +1,77 @@
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 :access_tokens, :class_name => "AccessToken", :dependent => :destroy
18
+ belongs_to :active_token, :class_name => "AccessToken", :dependent => :destroy
19
+ accepts_nested_attributes_for :access_tokens, :active_token
20
+ end
21
+ end
22
+
23
+ def authenticated_with
24
+ @authenticated_with ||= self.access_tokens.collect{|t| t.service_name.to_s}
25
+ end
26
+
27
+ def authenticated_with?(service)
28
+ self.access_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.access_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
+ # when you call 'current_user_session' in ApplicationController,
51
+ # it leads to calling 'save' on this User object via "session.record.save",
52
+ # from the 'persisting?' method. So we don't want any of this to occur
53
+ # when that save is called, and the only way to check currently is
54
+ # to check if there is a block_given?
55
+ def save(options = {}, &block)
56
+ self.errors.clear
57
+ # log_state
58
+ options = {} if options == false
59
+ options[:validate] = true unless options.has_key?(:validate)
60
+ save_options = ActiveRecord::VERSION::MAJOR < 3 ? options[:validate] : options
61
+
62
+ # kill the block if we're starting authentication
63
+ authenticate_via_protocol(block_given?, options) do |start_authentication|
64
+ block = nil if start_authentication # redirecting
65
+ # forces you to validate, only if a block is given
66
+ result = super(save_options) # validate!
67
+ unless block.nil?
68
+ cleanup_authentication_session(options)
69
+ yield(result)
70
+ end
71
+ result
72
+ end
73
+ end
74
+
75
+ end
76
+
77
+ end
@@ -0,0 +1,124 @@
1
+ module AuthlogicConnect::Common::Variables
2
+ include AuthlogicConnect::Common::State
3
+
4
+ attr_reader :processing_authentication
5
+
6
+ def auth_class
7
+ is_auth_session? ? self.class : session_class
8
+ end
9
+
10
+ def auth_controller
11
+ is_auth_session? ? controller : session_class.controller
12
+ end
13
+
14
+ def auth_params
15
+ return nil unless auth_controller?
16
+ auth_controller.params.symbolize_keys!
17
+ auth_controller.params.keys.each do |key|
18
+ auth_controller.params[key.to_s] = auth_controller.params.delete(key) if key.to_s =~ /^OpenID/
19
+ end
20
+ auth_controller.params
21
+ end
22
+
23
+ def auth_session
24
+ return nil unless auth_controller?
25
+ auth_controller.session.symbolize_keys!
26
+ auth_controller.session.keys.each do |key|
27
+ auth_controller.session[key.to_s] = auth_controller.session.delete(key) if key.to_s =~ /^OpenID/
28
+ end
29
+ auth_controller.session
30
+ end
31
+
32
+ def auth_callback_url(options = {})
33
+ auth_controller.url_for({:controller => auth_controller.controller_name, :action => auth_controller.action_name}.merge(options))
34
+ end
35
+
36
+ # if we've said it's a "user" (registration), or a "session" (login)
37
+ def auth_type
38
+ from_session_or_params(:authentication_type)
39
+ end
40
+
41
+ # auth_params and auth_session attributes are all String!
42
+ def from_session_or_params(attribute)
43
+ return nil unless auth_controller?
44
+ key = attribute.is_a?(Symbol) ? attribute : attribute.to_sym
45
+ result = auth_params[key] if (auth_params && auth_params[key])
46
+ result = auth_session[key] if (result.nil? || result.blank?)
47
+ result
48
+ end
49
+
50
+ def add_session_key(key, value)
51
+
52
+ end
53
+
54
+ def remove_session_key(key)
55
+ keys = key.is_a?(Symbol) ? [key, key.to_s] : [key, key.to_sym]
56
+ keys.each {|k| auth_session.delete(k)}
57
+ end
58
+
59
+ # wraps the call to "save" (in yield).
60
+ # reason being, we need to somehow not allow oauth/openid validations to run
61
+ # when we don't have a block. We can't know that using class methods, so we create
62
+ # this property "processing_authentication", which is used in the validation method.
63
+ # it's value is set to "block_given", which is the value of block_given?
64
+ def authenticate_via_protocol(block_given = false, options = {}, &block)
65
+ @processing_authentication = auth_controller? && block_given
66
+ saved = yield start_authentication?
67
+ @processing_authentication = false
68
+ saved
69
+ end
70
+
71
+ # returns boolean
72
+ def authentication_protocol(with, phase)
73
+ returning(send("#{phase.to_s}_#{with.to_s}?")) do |ready|
74
+ send("#{phase.to_s}_#{with.to_s}") if ready
75
+ end if send("using_#{with.to_s}?")
76
+ end
77
+
78
+ # it only reaches this point once it has returned, or you
79
+ # have manually skipped the redirect and save was called directly.
80
+ def cleanup_authentication_session(options = {}, &block)
81
+ unless (options.has_key?(:keep_session) && options[:keep_session])
82
+ %w(oauth openid).each do |type|
83
+ send("cleanup_#{type.to_s}_session")
84
+ end
85
+ end
86
+ end
87
+
88
+ def log(*methods)
89
+ methods.each do |method|
90
+ puts "#{method.to_s}: #{send(method).inspect}"
91
+ end
92
+ end
93
+
94
+ def log_state
95
+ log(:correct_request_class?)
96
+ log(:using_oauth?, :start_oauth?, :complete_oauth?)
97
+ log(:oauth_request?, :oauth_response?, :stored_oauth_token_and_secret?)
98
+ log(:using_openid?, :start_openid?, :complete_openid?, :openid_request?, :openid_response?)
99
+ log(:authenticating_with_openid?)
100
+ log(:stored_oauth_token_and_secret)
101
+ end
102
+
103
+ # because we may need to store 6+ session variables, all with pretty lengthy names,
104
+ # might as well just tinify them.
105
+ # just an idea
106
+ def optimized_session_key(key)
107
+ @optimized_session_keys ||= {
108
+ :auth_request_class => :authcl,
109
+ :authentication_method => :authme,
110
+ :authentication_type => :authty,
111
+ :oauth_provider => :authpr,
112
+ :auth_callback_method => :authcb,
113
+ :oauth_request_token => :authtk,
114
+ :oauth_request_token_secret => :authsc,
115
+ :auth_attributes => :authat
116
+ }
117
+ @optimized_session_keys[key]
118
+ end
119
+
120
+ def auto_register?
121
+ true
122
+ end
123
+
124
+ 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,75 @@
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
+ if processing_authentication
8
+ authentication_protocol(:oauth, :start) || authentication_protocol(:oauth, :complete)
9
+ end
10
+ end
11
+
12
+ # Step 3: if new_oauth_request?, redirect to oauth provider
13
+ def start_oauth
14
+ save_oauth_session
15
+ authorize_url = token_class.authorize_url(auth_callback_url) do |request_token|
16
+ save_auth_session_token(request_token) # only for oauth version 1
17
+ end
18
+ auth_controller.redirect_to authorize_url
19
+ end
20
+
21
+ # Step 4: on callback, run this method
22
+ def complete_oauth
23
+ # implemented in User and Session Oauth modules
24
+ unless new_oauth_request? # shouldn't be validating if it's redirecting...
25
+ restore_attributes
26
+ complete_oauth_transaction
27
+ return true
28
+ end
29
+ return false
30
+ end
31
+
32
+ # Step 3a: save our passed-parameters into the session,
33
+ # so we can retrieve them after the redirect calls back
34
+ def save_oauth_session
35
+ # Store the class which is redirecting, so we can ensure other classes
36
+ # don't get confused and attempt to use the response
37
+ auth_session[:auth_request_class] = self.class.name
38
+
39
+ auth_session[:authentication_type] = auth_params[:authentication_type]
40
+ auth_session[:oauth_provider] = auth_params[:oauth_provider]
41
+ auth_session[:auth_method] = "oauth"
42
+
43
+ # Tell our rack callback filter what method the current request is using
44
+ auth_session[:auth_callback_method] = auth_controller.request.method
45
+ end
46
+
47
+ # Step 3b (if version 1.0 of oauth)
48
+ def save_auth_session_token(request)
49
+ # store token and secret
50
+ auth_session[:oauth_request_token] = request.token
51
+ auth_session[:oauth_request_token_secret] = request.secret
52
+ end
53
+
54
+ def restore_attributes
55
+ end
56
+
57
+ # Step last, after the response
58
+ # having lots of trouble testing logging and out multiple times,
59
+ # so there needs to be a solid way to know when a user has messed up loggin in.
60
+ def cleanup_oauth_session
61
+ [:auth_request_class,
62
+ :authentication_type,
63
+ :auth_method,
64
+ :auth_attributes,
65
+ :oauth_provider,
66
+ :auth_callback_method,
67
+ :oauth_request_token,
68
+ :oauth_request_token_secret,
69
+ :_key,
70
+ :_token,
71
+ :_secret,
72
+ ].each {|key| remove_session_key(key)}
73
+ end
74
+
75
+ end