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.
- 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 +124 -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/database.yml +3 -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 +85 -0
- 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)
|