foreverman-authlogic-connect 0.0.1

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 (62) 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/database.yml +3 -0
  55. data/test/libs/database.rb +47 -0
  56. data/test/libs/user.rb +7 -0
  57. data/test/libs/user_session.rb +2 -0
  58. data/test/test_helper.rb +178 -0
  59. data/test/test_oauth.rb +178 -0
  60. data/test/test_openid.rb +71 -0
  61. data/test/test_user.rb +85 -0
  62. metadata +244 -0
@@ -0,0 +1,46 @@
1
+ module AuthlogicConnect
2
+ KEY = "connect" unless defined?(KEY)
3
+ OAUTH = "oauth" unless defined?(OAUTH)
4
+ OPEN_ID = "open_id" unless defined?(OPEN_ID)
5
+
6
+ class << self
7
+
8
+ attr_accessor :config
9
+
10
+ def config=(value)
11
+ value.recursively_symbolize_keys!
12
+ @config = value
13
+ end
14
+
15
+ def key(path)
16
+ result = self.config
17
+ path.to_s.split(".").each { |node| result = result[node.to_sym] if result }
18
+ result
19
+ end
20
+
21
+ def credentials(service)
22
+ key("#{KEY}.#{service.to_s}")
23
+ end
24
+
25
+ def services
26
+ key(KEY)
27
+ end
28
+
29
+ def service_names
30
+ services.keys.collect(&:to_s)
31
+ end
32
+
33
+ def include?(service)
34
+ !credentials(service).nil?
35
+ end
36
+
37
+ def token(key)
38
+ raise "can't find key '#{key.to_s}' in AuthlogicConnect.config" unless AuthlogicConnect.include?(key) and !key.to_s.empty?
39
+ "#{key.to_s.camelcase}Token".constantize
40
+ end
41
+
42
+ def consumer(key)
43
+ token(key).consumer
44
+ end
45
+ end
46
+ end
@@ -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)