Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

made a lot of changes to the structure of the code, and added quite a…

… bit of shoulda tests. still figuring out a good workflow for testing controllers. made a solid distinction between oauth and openid, its all in the readme. version 0.0.36
  • Loading branch information...
commit 9bab11dcf7f643ff105ac402d8914e5bd9d3bb20 1 parent 8522597
@lancejpollard authored
Showing with 1,410 additions and 555 deletions.
  1. +156 −43 README.markdown
  2. +1 −1  Rakefile
  3. +2 −71 lib/authlogic-connect.rb
  4. +46 −0 lib/authlogic_connect/authlogic_connect.rb
  5. +1 −1  lib/authlogic_connect/callback_filter.rb
  6. +1 −1  lib/authlogic_connect/common.rb
  7. +16 −0 lib/authlogic_connect/common/state.rb
  8. +102 −34 lib/authlogic_connect/common/user.rb
  9. +68 −16 lib/authlogic_connect/common/variables.rb
  10. +0 −1  lib/authlogic_connect/engine.rb
  11. +1 −0  lib/authlogic_connect/{common → }/ext.rb
  12. +3 −1 lib/authlogic_connect/oauth.rb
  13. +17 −13 lib/authlogic_connect/oauth/helper.rb
  14. +61 −76 lib/authlogic_connect/oauth/process.rb
  15. +3 −14 lib/authlogic_connect/oauth/session.rb
  16. +54 −0 lib/authlogic_connect/oauth/state.rb
  17. +9 −1 lib/authlogic_connect/oauth/tokens/google_token.rb
  18. +67 −2 lib/authlogic_connect/oauth/tokens/oauth_token.rb
  19. +2 −0  lib/authlogic_connect/oauth/tokens/twitter_token.rb
  20. +57 −74 lib/authlogic_connect/oauth/user.rb
  21. +52 −27 lib/authlogic_connect/oauth/variables.rb
  22. +3 −0  lib/authlogic_connect/openid.rb
  23. +30 −0 lib/authlogic_connect/openid/process.rb
  24. +6 −53 lib/authlogic_connect/openid/session.rb
  25. +47 −0 lib/authlogic_connect/openid/state.rb
  26. +3 −0  lib/authlogic_connect/openid/tokens/my_openid_token.rb
  27. +6 −0 lib/authlogic_connect/openid/tokens/openid_token.rb
  28. +38 −68 lib/authlogic_connect/openid/user.rb
  29. +17 −3 lib/authlogic_connect/openid/variables.rb
  30. +0 −1  lib/authlogic_connect/token.rb
  31. +0 −1  lib/open_id_authentication.rb
  32. +15 −9 pkg/authlogic-connect.gemspec
  33. +1 −1  rails/init.rb
  34. +21 −0 test/controllers/test_users_controller.rb
  35. +48 −0 test/libs/database.rb
  36. +3 −0  test/libs/user.rb
  37. +2 −0  test/libs/user_session.rb
  38. +53 −0 test/old.rb
  39. +1 −1  test/test_authlogic_connect.rb
  40. +142 −42 test/test_helper.rb
  41. +255 −0 test/test_user.rb
View
199 README.markdown
@@ -10,31 +10,6 @@ There are 3 ways you can allow your users to login with Authlogic Connect:
All of that is easier than creating a new account and password.
-## Helpful links
-
-* <b>Authlogic:</b> [http://github.com/binarylogic/authlogic](http://github.com/binarylogic/authlogic)
-* <b>Authlogic Connect Example Project:</b> [http://github.com/viatropos/authlogic-connect-example](http://github.com/viatropos/authlogic-connect-example)
-* <b>Live example with Twitter and Facebook using Rails 3:</b> [http://authlogic-connect.heroku.com](http://authlogic-connect.heroku.com)
-* <b>Rails 2.3.5 Example:</b> [http://github.com/viatropos/authlogic-connect-example-rails2](http://github.com/viatropos/authlogic-connect-example-rails2)
-* **Rubygems Repository:** [http://rubygems.org/gems/authlogic-connect](http://rubygems.org/gems/authlogic-connect)
-
-## Supported Providers
-
-### Oauth
-
-- Twitter
-- Facebook
-- Google
-
-### OpenID
-
-- MyOpenID
-
-Lists of all known providers here:
-
-- [Oauth Providers](http://wiki.oauth.net/ServiceProviders)
-- [OpenID Providers](http://en.wikipedia.org/wiki/List_of_OpenID_providers)
-
## Install and use
### 1. Install Authlogic Connect
@@ -59,6 +34,20 @@ Rails 3: `Gemfile`
gem "oauth"
gem "oauth2"
gem "authlogic-connect"
+
+### 2b. Add the `OpenIdAuthentication.store`
+
+Do to "some strange problem":http://github.com/openid/ruby-openid/issues#issue/1 I have yet to really understand, Rails 2.3.5 doesn't like when `OpenIdAuthentication.store` is null, which means it uses the "in memory" store and for some reason fails.
+
+So as a fix, add these at the end of your `config/environment.rb` files:
+
+In development mode:
+
+ OpenIdAuthentication.store = :file
+
+In production (on Heroku primarily)
+
+ OpenIdAuthentication.store = :memcache
### 3. Add the Migrations
@@ -77,7 +66,8 @@ Files needed are:
In `config/initializers/authlogic_connect_config.rb`, write your keys and secrets for each service you would like to support. You have to manually go to the websites and register with the service provider (list of those links coming soon, in token classes for now).
AuthlogicConnect.config = {
- :services => {
+ :default => "facebook",
+ :connect => {
:twitter => {
:key => "my_key",
:secret => "my_secret",
@@ -120,14 +110,21 @@ Because of the redirects involved in Oauth and OpenID, you MUST pass a block to
You should save your `@user` objects this way as well, because you also want the user to authenticate with OAuth.
If we don't use the block, we will get a DoubleRender error. This lets us skip that entire block and send the user along their way without any problems.
+
+### 6. Add Parameters to Forms in your Views
-### 6. Create Custom Tokens (if they don't already exist)
+ <%# oauth_register_button :value => "Register with Twitter" %>
+ <%# oauth_login_button :value => "Login with Twitter" %>
+
+Check out the example projects to see exactly what's required. These aren't totally useful yet. Their job is to just send the right parameters to authlogic-connect.
+
+### 7. Create Custom Tokens (if they don't already exist)
Here's an example of the FacebookToken for Oauth
class FacebookToken < OauthToken
- version 2.0 # oauth 2.0
+ version 2.0
settings "https://graph.facebook.com",
:authorize_url => "https://graph.facebook.com/oauth/authorize",
@@ -137,16 +134,103 @@ Here's an example of the FacebookToken for Oauth
If there is an Oauth/OpenID service you need, let me know, or fork/add/push and I will integrate it into the project and add you to the list.
-Currently Implemented (some fully, some partially):
+## Examples
+
+These are examples of what you can get from a User. Code is placed in controller for demo purposes, it should be abstracted into the model.
+
+### API
+
+User model has the following public accessors and methods. This example assumes:
+
+# You've associated your Google, OpenID, and Twitter accounts with this app.
+# You're currently logged in via Google.
+
+ def show
+ @user = @current_user
+ puts @user.tokens #=> [
+ #<OpenidToken id: 12, user_id: 9, type: "OpenidToken", key: "http://my-openid-login.myopenid.com/", token: nil, secret: nil, active: nil, created_at: "2010-05-24 14:52:19", updated_at: "2010-05-24 14:52:19">,
+ #<TwitterToken id: 13, user_id: 9, type: "TwitterToken", key: "my-twitter-id-123", token: "twitter-token", secret: "twitter-secret", active: nil, created_at: "2010-05-24 15:03:05", updated_at: "2010-05-24 15:03:05">,
+ #<GoogleToken id: 14, user_id: 9, type: "GoogleToken", key: "my-email@gmail.com", token: "google-token", secret: "google-secret", active: nil, created_at: "2010-05-24 15:09:04", updated_at: "2010-05-24 15:09:04">]
+ puts @user.tokens.length #=> 3
+ # currently logged in with...
+ puts @user.active_token #=> #<GoogleToken id: 14, user_id: 9, type: "GoogleToken", key: "my-email@gmail.com", token: "google-token", secret: "google-secret", active: nil, created_at: "2010-05-24 15:09:04", updated_at: "2010-05-24 15:09:04">
+ puts @user.authenticated_with #=> ["twitter", "openid", "google"]
+ puts @user.authenticated_with?(:twitter) #=> true
+ puts @user.authenticated_with?(:facebook) #=> false
+ puts @user.has_token?(:google) #=> true
+ puts @user.get_token(:google) #=> #<GoogleToken id: 14, user_id: 9, type: "GoogleToken", key: "my-email@gmail.com", token: "google-token", secret: "google-secret", active: nil, created_at: "2010-05-24 15:09:04", updated_at: "2010-05-24 15:09:04">
+ # change active_token
+ @user.active_token = @user.get_token(:twitter)
+ puts @user.active_token #=> #<TwitterToken id: 13, user_id: 9, type: "TwitterToken", key: "my-twitter-id-123", token: "twitter-token", secret: "twitter-secret", active: nil, created_at: "2010-05-24 15:03:05", updated_at: "2010-05-24 15:03:05">
+ @twitter = @user.active_token
+ @twitter_profile = JSON.parse(@twitter.get("/account/verify_credentials.json").body) #=> twitter api stuff
+ # ...
+ end
-- [Oauth Tokens](http://github.com/viatropos/authlogic-connect/tree/master/lib/authlogic_connect/oauth/tokens/)
-
-### 7. Add login and register buttons to your views
+### Get Facebook Data
- <%# oauth_register_button :value => "Register with Twitter" %>
- <%# oauth_login_button :value => "Login with Twitter" %>
+If they've associated their Facebook account with your site, you can access Facebook data.
-Check out the example projects to see exactly what's required. These aren't totally useful yet. Their job is to just send the right parameters to authlogic-connect.
+ def show
+ @user = @current_user
+ token = @user.active_token
+ facebook = JSON.parse(token.get("/me"))
+ @profile = {
+ :id => facebook["id"],
+ :name => facebook["name"],
+ :photo => "https://graph.facebook.com/#{facebook["id"]}/picture",
+ :link => facebook["link"],
+ :title => "Facebook"
+ }
+ @profile = @user.profile
+ end
+
+## Supported Providers
+
+### Oauth
+
+- Twitter
+- Facebook
+- Google
+
+### OpenID
+
+- MyOpenID
+
+Lists of all known providers here:
+
+- [Oauth Providers](http://wiki.oauth.net/ServiceProviders)
+- [OpenID Providers](http://en.wikipedia.org/wiki/List_of_OpenID_providers)
+- [More OpenID](http://openid.net/get-an-openid/)
+
+## Oauth vs. OpenID
+
+There is a big but subtle difference between Oauth and OpenID: Oauth is NOT a login protocol. OpenID IS.
+
+You should use Oauth when you want to be able to access and/or manipulate data on behalf of the user. If all you want is authentication, OpenID is best. However, if you want to login through Twitter or Facebook, you _have_ to use Oauth (forget Facebook Connect, too complicated).
+
+An example would be using Google and Oauth. With Google and Oauth, the user can grant you rights to "access the gmail contacts" for example, and you can get a list of their contacts. That requires that your app is authorized, which requires they grant access via Oauth. This is kinda strange though, because why does a login app need to access your google contacts? It doesn't. That's why we should use OpenID in this case. But we can still use Oauth... continuing...
+
+The problem with the Google Contacts Oauth example is that when you get the Oauth Access Token, Google doesn't give you any data, so you can't say "the 'guy who just logged in's email is abc@gmail.com, save that to the database". That's where OpenID would shine.
+
+If you want to use Oauth for logging in, you must get back some unique key to identify the user by. The best options are something like email, username, or some unique id. This is accomplished in the `GoogleToken` oauth class by passing a block to the `key` class method:
+
+ key do |access_token|
+ body = JSON.parse(access_token.get("https://www.google.com/m8/feeds/contacts/default/full?alt=json&max-results=0").body)
+ email = body["feed"]["author"].first["email"]["$t"] # $t is some weird google json thing
+ end
+
+That hack lets us use Oauth to get the email address of the user, which we need if we want to somehow find the account for a user who has logged out
+
+The confusing thing is that Twitter allows you to login with Oauth, it's one of the few it seems. This is because Twitter sends back the `user_id` and `screen_name`, allowing you to pretend the user logged in. Google doesn't send that. Which means you have to make an _additional_ call to the service, if you're using Oauth. If you're using OpenID, that's specifically for login so you're going to get back email/name/etc.
+
+## Helpful links
+
+* <b>Authlogic:</b> [http://github.com/binarylogic/authlogic](http://github.com/binarylogic/authlogic)
+* <b>Authlogic Connect Example Project:</b> [http://github.com/viatropos/authlogic-connect-example](http://github.com/viatropos/authlogic-connect-example)
+* <b>Live example with Twitter and Facebook using Rails 3:</b> [http://authlogic-connect.heroku.com](http://authlogic-connect.heroku.com)
+* <b>Rails 2.3.5 Example:</b> [http://github.com/viatropos/authlogic-connect-example-rails2](http://github.com/viatropos/authlogic-connect-example-rails2)
+* **Rubygems Repository:** [http://rubygems.org/gems/authlogic-connect](http://rubygems.org/gems/authlogic-connect)
## The Flow
@@ -192,7 +276,9 @@ Then a Rack Middleware filter converts the GET return request from the authentic
### Tests
-This has no tests! I had to build this in a day and am not fluent with Shoulda, which I'd like to use. It should have lots of tests to make sure all permutations of login and account association work perfectly.
+This only has a few unit tests. Enough to make sure the methods are returning what we are expecting.
+
+It should have Functional and Integration tests, using the Authlogic Connect example projects. If any of you guys know of an easy way to set that up, I'd love to know. Send me a github message :).
Goal:
@@ -201,14 +287,16 @@ Goal:
- Testing style like [Paperclip Tests](http://github.com/thoughtbot/paperclip/tree/master/test/)
- Rails 2.3+ and Rails 3 Compatability
-I have no idea how to get up and running with Autotest and Shoulda right now. If you know, I'd love to get the answer on Stack Overflow:
-
-[http://stackoverflow.com/questions/2823224/what-test-environment-setup-do-committers-use-in-the-ruby-community](http://stackoverflow.com/questions/2823224/what-test-environment-setup-do-committers-use-in-the-ruby-community)
+### TODO
-## TODO
+- If the user bails out in the middle of a login session, there needs to be a mechanism that knows how to reset their session.
+- If the openid is filled out, and then the user clicks Twitter oauth, it should know that it should log them in via twitter. This can only really be done by javascript. But what should take precedence? The thing that requires no typing: oauth. So oauth should be checked first on save.
+- Add rememberme functionality correctly. Right now I think it remembers you by default.
+- Login should work without having to access the remote service again.
+- If I create new user with Twitter or Google, then logout, I can login through twitter no problem. However, I cannot login through Google. This is because google returns new tokens, so I can't find it in the database. How do I find it? Also, if you go and revoke access to twitter (go to your twitter profile on twitter.com, click "settings", and revoke access to app) after you've created an account, and you try to login, same problem. This is because tokens are regenerated. NEED CONFIRMATION SCREEN
+- If the user has only created an account with say Twitter, then logs out, if they try to login with google, it should ask if they have a different account. How should this work?
-- Change `register_with_oauth` and related to `register_method` and `login_method`: oauth, openid, traditional
-- Build view helpers
+OAuth is for accessing remote information. It doesn't always give you data about the user. OpenID on the other hand gives you all the info you need for login.
## Helpful References for Rails 3
@@ -216,4 +304,29 @@ I have no idea how to get up and running with Autotest and Shoulda right now. I
- [Rails 3 Plugins - Part 1, Big Picture](http://www.themodestrubyist.com/2010/03/01/rails-3-plugins---part-1---the-big-picture/)
- [Rails 3 Plugins - Part 2, Writing an Engine](http://www.themodestrubyist.com/2010/03/05/rails-3-plugins---part-2---writing-an-engine/)
- [Rails 3 Plugins - Part 3, Initializers](http://www.themodestrubyist.com/2010/03/16/rails-3-plugins---part-3---rake-tasks-generators-initializers-oh-my/)
-- [Using Gemspecs as Intended](http://yehudakatz.com/2010/04/02/using-gemspecs-as-intended/)
+- [Using Gemspecs as Intended](http://yehudakatz.com/2010/04/02/using-gemspecs-as-intended/)
+
+## Parameters
+
+should look like this:
+
+Params from form:
+
+ {"authentication_type"=>"user", "submit"=>"Register", "openid_identifier"=>"", "oauth_provider"=>"twitter"}
+
+Session just before redirect:
+
+ {"authentication_type"=>"user", "oauth_request_token"=>"token_key", "session_id"=>"session_hash", "auth_callback_method"=>"POST", "auth_attributes"=>{"login_count"=>0}, "oauth_request_token_secret"=>"token_secret", "auth_request_class"=>"User", "auth_method"=>"oauth", "oauth_provider"=>"twitter"}
+
+## Details
+
+The regular OAuth process is a four-step sequence:
+
+1. ask for a "request" token.
+2. ask for the token to be authorized, which triggers user approval.
+3. exchange the authorized request token for an "access" token.
+4. use the access token to interact with the user's Google service data.
+
+## OpenID Process
+
+If they logout and log back into OpenID, we can find their token solely from the data they pass in (`openid_identifier`). This is unlike Oauth, where we have to run through the whole process again because we don't know anything about them.
View
2  Rakefile
@@ -6,7 +6,7 @@ require 'rake/gempackagetask'
spec = Gem::Specification.new do |s|
s.name = "authlogic-connect"
s.author = "Lance Pollard"
- s.version = "0.0.3.4"
+ s.version = "0.0.3.6"
s.summary = "Authlogic Connect: Let your app use all of Oauth and OpenID"
s.homepage = "http://github.com/viatropos/authlogic-connect"
s.email = "lancejpollard@gmail.com"
View
73 lib/authlogic-connect.rb
@@ -7,78 +7,9 @@
this = File.dirname(__FILE__)
library = "#{this}/authlogic_connect"
-class Hash
- def recursively_symbolize_keys!
- self.symbolize_keys!
- self.values.each do |v|
- if v.is_a? Hash
- v.recursively_symbolize_keys!
- elsif v.is_a? Array
- v.recursively_symbolize_keys!
- end
- end
- self
- end
-end
-
-class Array
- def recursively_symbolize_keys!
- self.each do |item|
- if item.is_a? Hash
- item.recursively_symbolize_keys!
- elsif item.is_a? Array
- item.recursively_symbolize_keys!
- end
- end
- end
-end
-
-module AuthlogicConnect
- KEY = "connect"
-
- class << self
-
- attr_accessor :config
-
- def config=(value)
- value.recursively_symbolize_keys!
- @config = value
- end
-
- def key(path)
- result = self.config
- path.to_s.split(".").each { |node| result = result[node.to_sym] if result }
- result
- end
-
- def credentials(service)
- key("#{KEY}.#{service.to_s}")
- end
-
- def services
- key(KEY)
- end
-
- def service_names
- services.keys.collect(&:to_s)
- end
-
- def include?(service)
- !credentials(service).nil?
- end
-
- def token(key)
- raise "can't find key '#{key.to_s}' in AuthlogicConnect.config" unless AuthlogicConnect.include?(key) and !key.to_s.empty?
- "#{key.to_s.camelcase}Token".constantize
- end
-
- def consumer(key)
- token(key).consumer
- end
- end
-end
-
require "#{this}/open_id_authentication"
+require "#{library}/ext"
+require "#{library}/authlogic_connect"
require "#{library}/callback_filter"
require "#{library}/token"
require "#{library}/openid"
View
46 lib/authlogic_connect/authlogic_connect.rb
@@ -0,0 +1,46 @@
+module AuthlogicConnect
+ KEY = "connect" unless defined?(KEY)
+ OAUTH = "oauth" unless defined?(OAUTH)
+ OPEN_ID = "open_id" unless defined?(OPEN_ID)
+
+ class << self
+
+ attr_accessor :config
+
+ def config=(value)
+ value.recursively_symbolize_keys!
+ @config = value
+ end
+
+ def key(path)
+ result = self.config
+ path.to_s.split(".").each { |node| result = result[node.to_sym] if result }
+ result
+ end
+
+ def credentials(service)
+ key("#{KEY}.#{service.to_s}")
+ end
+
+ def services
+ key(KEY)
+ end
+
+ def service_names
+ services.keys.collect(&:to_s)
+ end
+
+ def include?(service)
+ !credentials(service).nil?
+ end
+
+ def token(key)
+ raise "can't find key '#{key.to_s}' in AuthlogicConnect.config" unless AuthlogicConnect.include?(key) and !key.to_s.empty?
+ "#{key.to_s.camelcase}Token".constantize
+ end
+
+ def consumer(key)
+ token(key).consumer
+ end
+ end
+end
View
2  lib/authlogic_connect/callback_filter.rb
@@ -16,4 +16,4 @@ def call(env)
end
@app.call(env)
end
-end
+end
View
2  lib/authlogic_connect/common.rb
@@ -1,7 +1,7 @@
module AuthlogicConnect::Common
end
-require File.dirname(__FILE__) + "/common/ext"
+require File.dirname(__FILE__) + "/common/state"
require File.dirname(__FILE__) + "/common/variables"
require File.dirname(__FILE__) + "/common/user"
require File.dirname(__FILE__) + "/common/session"
View
16 lib/authlogic_connect/common/state.rb
@@ -0,0 +1,16 @@
+# This class holds query/state variables common to oauth and openid
+module AuthlogicConnect::Common::State
+
+ def auth_session?
+ !auth_session.blank?
+ end
+
+ def auth_params?
+ !auth_params.blank?
+ end
+
+ def is_auth_session?
+ self.is_a?(Authlogic::Session::Base)
+ end
+
+end
View
136 lib/authlogic_connect/common/user.rb
@@ -1,47 +1,115 @@
-module AuthlogicConnect::Common
- module User
+# This class is the main api for the user.
+# It is also required to properly sequence the save methods
+# for the different authentication types (oauth and openid)
+module AuthlogicConnect::Common::User
+
+ def self.included(base)
+ base.class_eval do
+ add_acts_as_authentic_module(InstanceMethods, :append)
+ add_acts_as_authentic_module(AuthlogicConnect::Common::Variables, :prepend)
+ end
+ end
+
+ module InstanceMethods
def self.included(base)
base.class_eval do
- add_acts_as_authentic_module(Variables, :prepend)
- add_acts_as_authentic_module(InstanceMethods, :append)
+ has_many :tokens, :class_name => "Token", :dependent => :destroy
+ belongs_to :active_token, :class_name => "Token", :dependent => :destroy
+ accepts_nested_attributes_for :tokens, :active_token
end
end
- module InstanceMethods
-
- def authenticated_with
- @authenticated_with ||= self.tokens.collect{|t| t.service_name.to_s}
- end
-
- def authenticated_with?(service)
- self.tokens.detect{|t| t.service_name.to_s == service.to_s}
- end
-
- # core save method coordinating how to save the user
- def save(perform_validation = true, &block)
- status = true
- if authenticating_with_openid?
- status = save_with_openid(perform_validation, &block)
- elsif authenticating_with_oauth?
- status = save_with_oauth(perform_validation, &block)
- end
- if status
- result = super(:validate => true)
- yield(result) if block_given?
- end
- result
+ def authenticated_with
+ @authenticated_with ||= self.tokens.collect{|t| t.service_name.to_s}
+ end
+
+ def authenticated_with?(service)
+ self.tokens.detect{|t| t.service_name.to_s == service.to_s}
+ end
+
+ def update_attributes(attributes, &block)
+ self.attributes = attributes
+ save(:validate => true, &block)
+ end
+
+ def has_token?(service_name)
+ !get_token(service_name).nil?
+ end
+
+ def get_token(service_name)
+ self.tokens.detect {|i| i.service_name.to_s == service_name.to_s}
+ end
+
+ # core save method coordinating how to save the user.
+ # we dont' want to ru validations based on the
+ # authentication mission we are trying to accomplish.
+ # instead, we just return save as false.
+ # the next time around, when we recieve the callback,
+ # we will run the validations
+ def save(options = {}, &block)
+ # debug_user_save_pre(options, &block)
+ options = {} if options == false
+ unless options[:skip_redirect] == true
+ return false if remotely_authenticating?(&block)
end
-
- def validate_password_with_oauth?
- !using_openid? && super
+ # forces you to validate, maybe get rid of if needed,
+ # but everything depends on this
+ if ActiveRecord::VERSION::MAJOR < 3
+ result = super(true) # validate!
+ else
+ result = super(options.merge(:validate => true))
end
+ # debug_user_save_post
+ yield(result) if block_given? # give back to controller
- def validate_password_with_openid?
- !using_oauth? && super
- end
+ cleanup_auth_session if result && !(options.has_key?(:keep_session) && options[:keep_session])
+ result
+ end
+
+ def remotely_authenticating?(&block)
+ return redirecting_to_oauth_server? if using_oauth? && block_given?
+ return redirecting_to_openid_server? if using_openid?
+ return false
+ end
+
+ # it only reaches this point once it has returned, or you
+ # have manually skipped the redirect and save was called directly.
+ def cleanup_auth_session
+ cleanup_oauth_session
+ cleanup_openid_session
+ end
+
+ def validate_password_with_oauth?
+ !using_openid? && super
+ end
+
+ def validate_password_with_openid?
+ !using_oauth? && super
+ end
+
+ # test methods for dev/debugging, commented out by default
+ def debug_user_save_pre(options = {}, &block)
+ puts "USER SAVE "
+ puts "block_given? #{block_given?.to_s}"
+ puts "using_oauth? #{using_oauth?.to_s}"
+ puts "using_openid? #{using_openid?.to_s}"
+ puts "authenticating_with_oauth? #{authenticating_with_oauth?.to_s}"
+ puts "authenticating_with_openid? #{authenticating_with_openid?.to_s}"
+ puts "validate_password_with_oauth? #{validate_password_with_oauth?.to_s}"
+ puts "validate_password_with_openid? #{validate_password_with_openid?.to_s}"
+ puts "!using_openid? && require_password? #{(!using_openid? && require_password?).to_s}"
+ end
+
+ def debug_user_save_post
+ puts "ERRORS: #{errors.full_messages}"
+ puts "using_oauth? #{using_oauth?.to_s}"
+ puts "using_openid? #{using_openid?.to_s}"
+ puts "validate_password_with_oauth? #{validate_password_with_oauth?.to_s}"
+ puts "validate_password_with_openid? #{validate_password_with_openid?.to_s}"
end
end
-end
+
+end
View
84 lib/authlogic_connect/common/variables.rb
@@ -1,21 +1,73 @@
-module AuthlogicConnect::Common
- module Variables
-
- def auth_controller
- is_auth_session? ? controller : session_class.controller
- end
-
- def auth_session
- auth_controller.session
+module AuthlogicConnect::Common::Variables
+ include AuthlogicConnect::Common::State
+
+ def auth_controller
+ is_auth_session? ? controller : session_class.controller
+ end
+
+ def auth_session
+ auth_controller.session.symbolize_keys!
+ auth_controller.session.keys.each do |key|
+ auth_controller.session[key.to_s] = auth_controller.session.delete(key) if key.to_s =~ /^OpenID/
end
-
- def auth_params
- auth_controller.params
+ auth_controller.session
+ end
+
+ def auth_params
+ auth_controller.params.symbolize_keys!
+ auth_controller.params.keys.each do |key|
+ auth_controller.params[key.to_s] = auth_controller.params.delete(key) if key.to_s =~ /^OpenID/
end
-
- def is_auth_session?
- self.is_a?(Authlogic::Session::Base)
+ auth_controller.params
+ end
+
+ def auth_callback_url(options = {})
+ auth_controller.url_for({:controller => auth_controller.controller_name, :action => auth_controller.action_name}.merge(options))
+ end
+
+ # if we've said it's a "user" (registration), or a "session" (login)
+ def auth_type
+ from_session_or_params(:authentication_type)
+ end
+
+ # auth_params and auth_session attributes are all String!
+ def from_session_or_params(by)
+ key = by.is_a?(Symbol) ? by : by.to_sym
+ result = auth_params[key] if (auth_params && auth_params[key])
+ result = auth_session[key] if result.blank? # might be null here too
+ result
+ end
+
+ # because user and session are so closely tied together, I am still
+ # uncertain as to how they are saved. So this makes sure if we are
+ # logging in, it must be saving the session, otherwise the user.
+ def correct_request_class?
+ if is_auth_session?
+ auth_type.to_s == "session"
+ else
+ auth_type.to_s == "user"
end
+ end
+
+ def add_session_key(key, value)
end
-end
+
+ # because we may need to store 6+ session variables, all with pretty lengthy names,
+ # might as well just tinify them.
+ # just an idea
+ def optimized_session_key(key)
+ @optimized_session_keys ||= {
+ :auth_request_class => :authcl,
+ :authentication_method => :authme,
+ :authentication_type => :authty,
+ :oauth_provider => :authpr,
+ :auth_callback_method => :authcb,
+ :oauth_request_token => :authtk,
+ :oauth_request_token_secret => :authsc,
+ :auth_attributes => :authat
+ }
+ @optimized_session_keys[key]
+ end
+
+end
View
1  lib/authlogic_connect/engine.rb
@@ -1,6 +1,5 @@
module AuthlogicConnect
class Engine < Rails::Engine
- engine_name :authlogic_connect
initializer "authlogic_connect.authentication_hook" do |app|
app.middleware.use AuthlogicConnect::CallbackFilter
View
1  lib/authlogic_connect/common/ext.rb → lib/authlogic_connect/ext.rb
@@ -1,3 +1,4 @@
+# these are extensions I've found useful for this project
class String
# normalizes an OpenID according to http://openid.net/specs/openid-authentication-2_0.html#normalization
def normalize_identifier
View
4 lib/authlogic_connect/oauth.rb
@@ -1,6 +1,7 @@
module AuthlogicConnect::Oauth
end
+require File.dirname(__FILE__) + "/oauth/state"
require File.dirname(__FILE__) + "/oauth/variables"
require File.dirname(__FILE__) + "/oauth/process"
require File.dirname(__FILE__) + "/oauth/user"
@@ -9,4 +10,5 @@ module AuthlogicConnect::Oauth
ActiveRecord::Base.send(:include, AuthlogicConnect::Oauth::User)
Authlogic::Session::Base.send(:include, AuthlogicConnect::Oauth::Session)
-ActionController::Base.helper AuthlogicConnect::Oauth::Helper
+ActionController::Base.helper AuthlogicConnect::Oauth::Helper
+ActionView::Helpers::FormBuilder.send(:include, AuthlogicConnect::Oauth::FormHelper)
View
30 lib/authlogic_connect/oauth/helper.rb 100755 → 100644
@@ -1,16 +1,20 @@
-module AuthlogicConnect::Oauth
- module Helper
- def oauth_register_button(options = {})
- oauth_button('register_with_oauth', options)
- end
-
- def oauth_login_button(options = {})
- oauth_button('login_with_oauth', options)
- end
+module AuthlogicConnect::Oauth::Helper
- private
- def oauth_button(name, options = {})
- "<input type='submit' value='#{options[:value]}' name='#{name}' id='user_submit' class='#{options[:class]}'/>"
- end
+ # options include "name"
+ def oauth_register_hidden_input
+ oauth_input(:type => "user")
end
+
+ def oauth_login_hidden_input
+ oauth_input(:type => "session")
+ end
+
+ def oauth_input(options = {})
+ tag(:input, {:type => "hidden", :name => "authentication_type", :value => options[:type]})
+ end
+
+end
+
+module AuthlogicConnect::Oauth::FormHelper
+
end
View
137 lib/authlogic_connect/oauth/process.rb
@@ -1,83 +1,68 @@
-module AuthlogicConnect::Oauth
- module Process
+module AuthlogicConnect::Oauth::Process
- private
- include AuthlogicConnect::Oauth::Variables
+ include AuthlogicConnect::Oauth::Variables
- def validate_by_oauth
- validate_email_field = false
-
- if oauth_response.blank?
- redirect_to_oauth
- else
- authenticate_with_oauth
- end
- end
-
- def redirecting_to_oauth_server?
- authenticating_with_oauth? && oauth_response.blank?
- end
-
- def redirect_to_oauth
- save_oauth_callback
-
- if oauth_version == 1.0
- request = oauth_token.get_request_token(oauth_callback_url)
- save_auth_session(request)
- auth_controller.redirect_to request.authorize_url
- else
- auth_controller.redirect_to oauth_consumer.web_server.authorize_url(
- :redirect_uri => oauth_callback_url,
- :scope => oauth_token.config[:scope]
- )
- end
- end
-
- def save_oauth_callback
- puts "save_oauth_callback"
- # Store the class which is redirecting, so we can ensure other classes
- # don't get confused and attempt to use the response
- auth_session[:oauth_request_class] = self.class.name
- auth_session[:oauth_provider] = auth_params[:oauth_provider]
-
- # Tell our rack callback filter what method the current request is using
- auth_session[:auth_callback_method] = auth_controller.request.method
- end
-
- def save_auth_session(request)
- # store token and secret
- auth_session[:oauth_request_token] = request.token
- auth_session[:oauth_request_token_secret] = request.secret
- end
-
- def oauth_callback_url
- auth_controller.url_for :controller => auth_controller.controller_name, :action => auth_controller.action_name
+ # Step 2: after save is called, it runs this method for validation
+ def validate_by_oauth
+ validate_email_field = false
+ unless new_oauth_request? # shouldn't be validating if it's redirecting...
+ restore_attributes
+ complete_oauth_transaction
end
-
- def request_token
- oauth_token.request_token(auth_session[:oauth_request_token], auth_session[:oauth_request_token_secret])
- end
-
- # in oauth 1.0, key = oauth_token, secret = oauth_secret
- # in oauth 2.0, key = code, secret = access_token
- def oauth_key_and_secret
- if oauth_version == 1.0
- result = request_token.get_access_token(:oauth_verifier => auth_params[:oauth_verifier])
- result = {:key => result.token, :secret => result.secret}
- else
- result = oauth_consumer.web_server.get_access_token(oauth_key, :redirect_uri => oauth_callback_url)
- result = {:key => result.token, :secret => oauth_key}
- end
- result
- end
-
- def generate_access_token
- if oauth_version == 1.0
- request_token.get_access_token(:oauth_verifier => auth_params[:oauth_verifier])
- else
- oauth_consumer.web_server.get_access_token(oauth_key, :redirect_uri => oauth_callback_url)
- end
+ end
+
+ # Step 3: if new_oauth_request?, redirect to oauth provider
+ def redirect_to_oauth
+ save_oauth_session
+ authorize_url = token_class.authorize_url(auth_callback_url) do |request_token|
+ save_auth_session_token(request_token) # only for oauth version 1
end
+ auth_controller.redirect_to authorize_url
+ end
+
+ # Step 3a: save our passed-parameters into the session,
+ # so we can retrieve them after the redirect calls back
+ def save_oauth_session
+ # Store the class which is redirecting, so we can ensure other classes
+ # don't get confused and attempt to use the response
+ auth_session[:auth_request_class] = self.class.name
+
+ auth_session[:authentication_type] = auth_params[:authentication_type]
+ auth_session[:oauth_provider] = auth_params[:oauth_provider]
+ auth_session[:auth_method] = "oauth"
+ # Tell our rack callback filter what method the current request is using
+ auth_session[:auth_callback_method] = auth_controller.request.method
+ end
+
+ # Step 3b (if version 1.0 of oauth)
+ def save_auth_session_token(request)
+ # store token and secret
+ auth_session[:oauth_request_token] = request.token
+ auth_session[:oauth_request_token_secret] = request.secret
+ end
+
+ def restore_attributes
end
+
+ # Step 4: on callback, run this method
+ def authenticate_with_oauth
+ # implemented in User and Session Oauth modules
+ end
+
+ # Step last, after the response
+ # having lots of trouble testing logging and out multiple times,
+ # so there needs to be a solid way to know when a user has messed up loggin in.
+ def cleanup_oauth_session
+ [:auth_request_class,
+ :authentication_type,
+ :auth_method,
+ :auth_attributes,
+ :oauth_provider,
+ :auth_callback_method,
+ :oauth_request_token,
+ :oauth_request_token_secret
+ ].each {|key| auth_session.delete(key)}
+ end
+
end
View
17 lib/authlogic_connect/oauth/session.rb
@@ -37,26 +37,15 @@ def save_with_oauth(&block)
return block.nil?
end
- def authenticating_with_oauth?
- return false unless oauth_provider
-
- # Initial request when user presses one of the button helpers
- initial_request = (controller.params && !controller.params[:login_with_oauth].blank?)
- # When the oauth provider responds and we made the initial request
- initial_response = (oauth_response && auth_session && auth_session[:oauth_request_class] == self.class.name)
-
- return initial_request || initial_response
- end
-
- def authenticate_with_oauth
+ def complete_oauth_transaction
if @record
self.attempted_record = record
else
# this generated token is always the same for a user!
# this is searching with User.find ...
# attempted_record is part of AuthLogic
- key = oauth_key_and_secret[:key]
- token = oauth_token.find_by_key(key, :include => [:user]) # some weird error if I leave out the include
+ hash = oauth_token_and_secret
+ token = token_class.find_by_key_or_token(hash[:key], hash[:token], :include => [:user]) # some weird error if I leave out the include)
self.attempted_record = token.user
end
View
54 lib/authlogic_connect/oauth/state.rb
@@ -0,0 +1,54 @@
+# all these methods must return true or false
+module AuthlogicConnect::Oauth::State
+
+ # 1. to call
+ # checks that we just passed parameters to it,
+ # and that the parameters say 'authentication_method' == 'oauth'
+ def oauth_request?
+ !auth_params.nil? && oauth_provider?
+ end
+
+ # 2. from call
+ # checks that the correct session variables are there
+ def oauth_response?
+ !oauth_response.nil? && !auth_session.nil? && auth_session[:auth_request_class] == self.class.name && auth_session[:auth_method] == "oauth"
+ end
+ alias_method :oauth_complete?, :oauth_response?
+
+ # 3. either to or from call
+ def using_oauth?
+ oauth_request? || oauth_response?
+ end
+
+ def new_oauth_request?
+ oauth_response.blank?
+ end
+
+ def oauth_provider?
+ !oauth_provider.nil? && !oauth_provider.empty?
+ end
+
+ # main method we call on validation
+ def authenticating_with_oauth?
+ correct_request_class? && using_oauth?
+ end
+
+ def allow_oauth_redirect?
+ authenticating_with_oauth? && !oauth_complete?
+ end
+
+ # both checks if it can redirect, and does the redirect.
+ # is there a more concise way to do this?
+ def redirecting_to_oauth_server?
+ if allow_oauth_redirect?
+ redirect_to_oauth
+ return true
+ end
+ return false
+ end
+
+ def validate_password_with_oauth?
+ !using_oauth? && require_password?
+ end
+
+end
View
10 lib/authlogic_connect/oauth/tokens/google_token.rb
@@ -1,5 +1,8 @@
+# http://code.google.com/apis/accounts/docs/OAuth_ref.html
+# http://code.google.com/apis/accounts/docs/OpenID.html#settingup
# http://code.google.com/apis/accounts/docs/OAuth.html
# http://code.google.com/apis/accounts/docs/RegistrationForWebAppsAuto.html
+# http://www.manu-j.com/blog/add-google-oauth-ruby-on-rails-sites/214/
# http://googlecodesamples.com/oauth_playground/
# Scopes:
# Analytics https://www.google.com/analytics/feeds/
@@ -30,4 +33,9 @@ class GoogleToken < OauthToken
:access_token_path => "/accounts/OAuthGetAccessToken",
:scope => "https://www.google.com/m8/feeds/"
-end
+ key do |access_token|
+ body = JSON.parse(access_token.get("https://www.google.com/m8/feeds/contacts/default/full?alt=json&max-results=0").body)
+ email = body["feed"]["author"].first["email"]["$t"] # $t is some weird google json thing
+ end
+
+end
View
69 lib/authlogic_connect/oauth/tokens/oauth_token.rb
@@ -3,9 +3,9 @@ class OauthToken < Token
def client
unless @client
if oauth_version == 1.0
- @client = OAuth::AccessToken.new(self.consumer, self.key, self.secret)
+ @client = OAuth::AccessToken.new(self.consumer, self.token, self.secret)
else
- @client = OAuth2::AccessToken.new(self.consumer, self.key)
+ @client = OAuth2::AccessToken.new(self.consumer, self.token)
end
end
@@ -22,6 +22,7 @@ def get(path, options = {})
class << self
+ # oauth version, 1.0 or 2.0
def version(value)
@oauth_version = value
end
@@ -30,6 +31,22 @@ def oauth_version
@oauth_version ||= 1.0
end
+ # unique key that we will use from the AccessToken response
+ # to identify the user by.
+ # in Twitter, its "user_id". Twitter has "screen_name", but that's
+ # more subject to change than user_id. Pick whatever is least likely to change
+ def key(value = nil, &block)
+ if block_given?
+ @oauth_key = block
+ else
+ @oauth_key = value.is_a?(Symbol) ? value : value.to_sym
+ end
+ end
+
+ def oauth_key
+ @oauth_key
+ end
+
def consumer
unless @consumer
if oauth_version == 1.0
@@ -42,6 +59,54 @@ def consumer
@consumer
end
+ # if we're lucky we can find it by the token.
+ def find_by_key_or_token(key, token, options = {})
+ result = self.find_by_key(key, options) unless key.nil?
+ unless result
+ result = self.find_by_token(token, options) unless token.nil?
+ end
+ result
+ end
+
+ # this is a wrapper around oauth 1 and 2.
+ # it looks obscure, but from the api point of view
+ # you won't have to worry about it's implementation.
+ # in oauth 1.0, key = oauth_token, secret = oauth_secret
+ # in oauth 2.0, key = code, secret = access_token
+ def get_token_and_secret(options = {})
+ oauth_verifier = options[:oauth_verifier]
+ redirect_uri = options[:redirect_uri]
+ token = options[:token]
+ secret = options[:secret]
+ if oauth_version == 1.0
+ access = request_token(token, secret).get_access_token(:oauth_verifier => oauth_verifier)
+ result = {:token => access.token, :secret => access.secret, :key => nil}
+ if self.oauth_key
+ if oauth_key.is_a?(Proc)
+ result[:key] = oauth_key.call(access)
+ else
+ result[:key] = access.params[self.oauth_key] || access.params[self.oauth_key.to_s] # try both
+ end
+ end
+ else
+ access = consumer.web_server.get_access_token(secret, :redirect_uri => redirect_uri)
+ result = {:token => access.token, :secret => secret, :key => nil}
+ end
+ result
+ end
+
+ # this is a cleaner method so we can access the authorize_url
+ # from oauth 1 or 2
+ def authorize_url(callback_url, &block)
+ if oauth_version == 1.0
+ request = get_request_token(callback_url)
+ yield request if block_given?
+ return request.authorize_url
+ else
+ return consumer.web_server.authorize_url(:redirect_uri => callback_url, :scope => self.config[:scope])
+ end
+ end
+
def request_token(token, secret)
OAuth::RequestToken.new(consumer, token, secret)
end
View
2  lib/authlogic_connect/oauth/tokens/twitter_token.rb
@@ -1,5 +1,7 @@
class TwitterToken < OauthToken
+ key :user_id
+
settings "http://twitter.com",
:authorize_url => "http://twitter.com/oauth/authenticate"
View
131 lib/authlogic_connect/oauth/user.rb
@@ -1,85 +1,68 @@
-module AuthlogicConnect::Oauth
- module User
+module AuthlogicConnect::Oauth::User
+
+ def self.included(base)
+ base.class_eval do
+ # add_acts_as_authentic_module makes sure it is
+ # only added to the user model, not all activerecord models.
+ add_acts_as_authentic_module(InstanceMethods, :prepend)
+ end
+ end
+
+ module InstanceMethods
+ include AuthlogicConnect::Oauth::Process
+
+ # Set up some simple validations
def self.included(base)
base.class_eval do
- add_acts_as_authentic_module(InstanceMethods, :prepend)
+
+ validate :validate_by_oauth, :if => :authenticating_with_oauth?
+
+ # need these validation options if you don't want it to choke
+ # on password length, which you don't need if you're using oauth
+ validates_length_of_password_field_options validates_length_of_password_field_options.merge(:if => :validate_password_with_oauth?)
+ validates_confirmation_of_password_field_options validates_confirmation_of_password_field_options.merge(:if => :validate_password_with_oauth?)
+ validates_length_of_password_confirmation_field_options validates_length_of_password_confirmation_field_options.merge(:if => :validate_password_with_oauth?)
+ validates_length_of_login_field_options validates_length_of_login_field_options.merge(:if => :validate_password_with_oauth?)
+ validates_format_of_login_field_options validates_format_of_login_field_options.merge(:if => :validate_password_with_oauth?)
end
+
+ # email needs to be optional for oauth
+ base.validate_email_field = false
+ end
+
+ # user adds a few extra things to this method from Process
+ # modules work like inheritance
+ def save_oauth_session
+ super
+ auth_session[:auth_attributes] = attributes.reject!{|k, v| v.blank?} unless is_auth_session?
end
- module InstanceMethods
- include Process
- # Set up some simple validations
- def self.included(base)
- base.class_eval do
- has_many :tokens, :class_name => "Token", :dependent => :destroy
- belongs_to :active_token, :class_name => "Token", :dependent => :destroy
- accepts_nested_attributes_for :tokens, :active_token
-
- validate :validate_by_oauth, :if => :authenticating_with_oauth?
-
- # need these validation options if you don't want it to choke
- # on password length, which you don't need if you're using oauth
- validates_length_of_password_field_options validates_length_of_password_field_options.merge(:if => :validate_password_with_oauth?)
- validates_confirmation_of_password_field_options validates_confirmation_of_password_field_options.merge(:if => :validate_password_with_oauth?)
- validates_length_of_password_confirmation_field_options validates_length_of_password_confirmation_field_options.merge(:if => :validate_password_with_oauth?)
- validates_length_of_login_field_options validates_length_of_login_field_options.merge(:if => :validate_password_with_oauth?)
- validates_format_of_login_field_options validates_format_of_login_field_options.merge(:if => :validate_password_with_oauth?)
- end
-
- # email needs to be optional for oauth
- base.validate_email_field = false
- end
-
- def update_attributes(attributes, &block)
- self.attributes = attributes
- save(true, &block)
- end
-
- # NEED TO GIVE A BLOCK
- def save_with_oauth(perform_validation = true, &block)
- if perform_validation && block_given? && redirecting_to_oauth_server?
- # Save attributes so they aren't lost during the authentication with the oauth server
- auth_session[:authlogic_oauth_attributes] = attributes.reject!{|k, v| v.blank?}
- redirect_to_oauth
- return false
- end
- return true
- end
-
- protected
+ def restore_attributes
+ # Restore any attributes which were saved before redirecting to the auth server
+ self.attributes = auth_session[:auth_attributes]
+ end
- def using_oauth?
- !oauth_token.blank?
- end
-
- def validate_password_with_oauth?
- !using_oauth? && require_password?
+ # single implementation method for oauth.
+ # this is called after we get the callback url and we are saving the user
+ # to the database.
+ # it is called by the validation chain.
+ def complete_oauth_transaction
+ unless create_oauth_token
+ self.errors.add(:tokens, "you have already created an account using your #{oauth_token.service_name} account, so it")
end
+ end
+
+ def create_oauth_token
+ token = token_class.new(oauth_token_and_secret)
- def authenticating_with_oauth?
- return false unless oauth_provider
- # Initial request when user presses one of the button helpers
- initial_request = (auth_params && !auth_params[:register_with_oauth].blank?)
- # When the oauth provider responds and we made the initial request
- initial_response = (oauth_response && auth_session && auth_session[:oauth_request_class] == self.class.name)
-
- return initial_request || initial_response
- end
-
- def authenticate_with_oauth
- # Restore any attributes which were saved before redirecting to the oauth server
- self.attributes = auth_session.delete(:authlogic_oauth_attributes)
- token = AuthlogicConnect.token(oauth_provider).new(oauth_key_and_secret)
- puts "NEW TOKEN: #{token.inspect}"
- if old_token = Token.find_by_key(token.key)
- puts "OLD TOKEN? #{old_token.inspect}"
- self.errors.add("you have already created an account using your #{oauth_token.service_name} account, so it")
- else
- self.tokens << token
- self.active_token = token
- end
+ if has_token?(oauth_provider) || Token.find_by_key(token.key) || Token.find_by_token(token.token)
+ return false
+ else
+ self.tokens << token
+ self.active_token = token
+ return true
end
-
end
+
end
-end
+end
View
79 lib/authlogic_connect/oauth/variables.rb
@@ -1,30 +1,55 @@
-module AuthlogicConnect::Oauth
- module Variables
-
- # These are just helper variables
- def oauth_response
- auth_params && oauth_key
- end
-
- def oauth_key
- return nil unless auth_controller
- oauth_version == 1.0 ? auth_params[:oauth_token] : auth_params[:code]
- end
-
- def oauth_version
- oauth_token.oauth_version
- end
-
- def oauth_provider
- auth_session[:oauth_provider] || "facebook"
- end
-
- def oauth_consumer
- oauth_token.consumer
- end
+module AuthlogicConnect::Oauth::Variables
+ include AuthlogicConnect::Oauth::State
+
+ # this doesn't do anything yet, just to show what variables
+ # we need from the form
+ def oauth_variables
+ [:oauth_provider]
+ end
- def oauth_token
- AuthlogicConnect.token(oauth_provider)
- end
+ # this comes straight from either the params or session.
+ # it is required for most of the other accessors in here
+ def oauth_provider
+ from_session_or_params(:oauth_provider)
+ end
+
+ # next is "token_class", which is found from the oauth_provider key.
+ # it is the OauthToken subclass, such as TwitterToken, which we
+ # use as the api for accessing oauth and saving the response to the database for a user.
+ def token_class
+ AuthlogicConnect.token(oauth_provider) unless oauth_provider.blank?
+ end
+
+ # This should go...
+ def oauth_response
+ auth_params && oauth_token
+ end
+
+ # the token from the response parameters
+ def oauth_token
+ return nil unless token_class
+ oauth_version == 1.0 ? auth_params[:oauth_token] : auth_params[:code]
+ end
+
+ # the version of oauth we're using. Accessed from the OauthToken subclass
+ def oauth_version
+ token_class.oauth_version
+ end
+
+ # the Oauth gem consumer, whereby we can make requests to the server
+ def oauth_consumer
+ token_class.consumer
+ end
+
+ # this is a thick method.
+ # it gives you the final key and secret that we will store in the database
+ def oauth_token_and_secret
+ token_class.get_token_and_secret(
+ :token => auth_session[:oauth_request_token],
+ :secret => oauth_version == 1.0 ? auth_session[:oauth_request_token_secret] : oauth_token,
+ :oauth_verifier => auth_params[:oauth_verifier],
+ :redirect_uri => auth_callback_url
+ )
end
+
end
View
3  lib/authlogic_connect/openid.rb
@@ -1,6 +1,9 @@
module AuthlogicConnect::Openid
end
+require File.dirname(__FILE__) + "/openid/state"
+require File.dirname(__FILE__) + "/openid/variables"
+require File.dirname(__FILE__) + "/openid/process"
require File.dirname(__FILE__) + "/openid/user"
require File.dirname(__FILE__) + "/openid/session"
View
30 lib/authlogic_connect/openid/process.rb
@@ -0,0 +1,30 @@
+module AuthlogicConnect::Openid::Process
+
+ include AuthlogicConnect::Openid::Variables
+
+ # want to do this after the final save
+ def cleanup_openid_session
+ [:auth_attributes, :authentication_type, :auth_callback_method].each {|key| auth_session.delete(key)}
+ auth_session.each_key do |key|
+ auth_session.delete(key) if key.to_s =~ /^OpenID/
+ end
+ end
+
+ def validate_by_openid
+ errors.add(:tokens, "had the following error: #{@openid_error}") if @openid_error
+ end
+
+ def save_openid_session
+ # Tell our rack callback filter what method the current request is using
+ auth_session[:auth_callback_method] = auth_controller.request.method
+ auth_session[:auth_attributes] = attributes_to_save
+ auth_session[:authentication_type] = auth_params[:authentication_type]
+ auth_session[:auth_method] = "openid"
+ end
+
+ def restore_attributes
+ # Restore any attributes which were saved before redirecting to the auth server
+ self.attributes = auth_session[:auth_attributes]
+ end
+
+end
View
59 lib/authlogic_connect/openid/session.rb
@@ -3,48 +3,16 @@ module AuthlogicConnect::Openid
module Session
# Add a simple openid_identifier attribute and some validations for the field.
def self.included(klass)
- klass.extend ClassMethods
klass.class_eval do
include InstanceMethods
end
end
- module ClassMethods
- # What method should we call to find a record by the openid_identifier?
- # This is useful if you want to store multiple openid_identifiers for a single record.
- # You could do something like:
- #
- # class User < ActiveRecord::Base
- # def self.find_by_openid_identifier(identifier)
- # user.first(:conditions => {:openid_identifiers => {:identifier => identifier}})
- # end
- # end
- #
- # Obviously the above depends on what you are calling your assocition, etc. But you get the point.
- #
- # * <tt>Default:</tt> :find_by_openid_identifier
- # * <tt>Accepts:</tt> Symbol
- def find_by_openid_identifier_method(value = nil)
- rw_config(:find_by_openid_identifier_method, value, :find_by_openid_identifier)
- end
- alias_method :find_by_openid_identifier_method=, :find_by_openid_identifier_method
-
- # Add this in your Session object to Auto Register a new user using openid via sreg
- def auto_register(value=true)
- auto_register_value(value)
- end
-
- def auto_register_value(value=nil)
- rw_config(:auto_register,value,false)
- end
-
- alias_method :auto_register=,:auto_register
- end
-
module InstanceMethods
+ include AuthlogicConnect::Openid::Process
+
def self.included(klass)
klass.class_eval do
- attr_reader :openid_identifier
validate :validate_openid_error
validate :validate_by_openid, :if => :authenticating_with_openid?
end
@@ -58,18 +26,10 @@ def credentials=(value)
self.openid_identifier = hash[:openid_identifier] if !hash.nil? && hash.key?(:openid_identifier)
end
- def openid_identifier=(value)
- @openid_identifier = value.blank? ? nil : OpenIdAuthentication.normalize_identifier(value)
- @openid_error = nil
- rescue OpenIdAuthentication::InvalidOpenId => e
- @openid_identifier = nil
- @openid_error = e.message
- end
-
# Cleaers out the block if we are authenticating with OpenID, so that we can redirect without a DoubleRender
# error.
def save_with_openid(&block)
- block = nil if !openid_identifier.blank?
+ block = nil if Token.find_by_key(openid_identifier.normalize_identifier)
return block.nil?
end
@@ -78,21 +38,14 @@ def authenticating_with_openid?
attempted_record.nil? && errors.empty? && (!openid_identifier.blank? || (controller.params[:open_id_complete] && controller.params[:for_session]))
end
- def find_by_openid_identifier_method
- self.class.find_by_openid_identifier_method
- end
-
- def find_by_openid_identifier_method
- self.class.find_by_openid_identifier_method
- end
-
def auto_register?
- self.class.auto_register_value
+ false
end
def validate_by_openid
self.remember_me = auth_params[:remember_me] == "true" if auth_params.key?(:remember_me)
- self.attempted_record = klass.send(find_by_openid_identifier_method, openid_identifier)
+ token = Token.find_by_key(openid_identifier.normalize_identifier, :include => [:user])
+ self.attempted_record = token.user if token
if !attempted_record
if auto_register?
self.attempted_record = klass.new :openid_identifier => openid_identifier
View
47 lib/authlogic_connect/openid/state.rb
@@ -0,0 +1,47 @@
+# all these methods must return true or false
+module AuthlogicConnect::Openid::State
+ # 1. to call
+ def openid_request?
+ !openid_identifier.blank?
+ end
+
+ def openid_identifier?
+ openid_request?
+ end
+
+ def openid_provider?
+
+ end
+
+ # 2. from call
+ # better check needed
+ def openid_response?
+ !auth_session[:auth_attributes].nil? && auth_session[:auth_method] == "openid"
+ end
+ alias_method :openid_complete?, :openid_response?
+
+ # 3. either to or from call
+ # this should include more!
+ # we know we are using open id if:
+ # the params passed in have "openid_identifier"
+ def using_openid?
+ openid_request? || openid_response?
+ end
+
+ def authenticating_with_openid?
+ session_class.activated? && using_openid?
+ end
+
+ def allow_openid_redirect?
+ authenticating_with_openid?
+ end
+
+ def redirecting_to_openid_server?
+ allow_openid_redirect? && !authenticate_with_openid
+ end
+
+ def validate_password_with_openid?
+ !using_openid? && require_password?
+ end
+
+end
View
3  lib/authlogic_connect/openid/tokens/my_openid_token.rb
@@ -0,0 +1,3 @@
+class MyOpenidToken < OpenidToken
+
+end
View
6 lib/authlogic_connect/openid/tokens/openid_token.rb
@@ -1,3 +1,9 @@
class OpenidToken < Token
+ before_save :format_identifier
+
+ def format_identifier
+ self.key = self.key.to_s.normalize_identifier unless self.key.blank?
+ end
+
end
View
106 lib/authlogic_connect/openid/user.rb
@@ -2,18 +2,17 @@ module AuthlogicConnect::Openid
module User
def self.included(base)
base.class_eval do
- add_acts_as_authentic_module(InstanceMethods, :prepend)
+ add_acts_as_authentic_module(AuthlogicConnect::Openid::Process, :prepend)
+ add_acts_as_authentic_module(InstanceMethods, :append)
end
end
module InstanceMethods
-
- def self.included(base)
- return if !base.column_names.include?("openid_identifier")
-
+
+ def self.included(base)
base.class_eval do
- validates_uniqueness_of :openid_identifier, :scope => validations_scope, :if => :using_openid?
- validate :validate_openid
+ validate :validate_by_openid, :if => :authenticating_with_openid?
+
validates_length_of_password_field_options validates_length_of_password_field_options.merge(:if => :validate_password_with_openid?)
validates_confirmation_of_password_field_options validates_confirmation_of_password_field_options.merge(:if => :validate_password_with_openid?)
validates_length_of_password_confirmation_field_options validates_length_of_password_confirmation_field_options.merge(:if => :validate_password_with_openid?)
@@ -22,71 +21,42 @@ def self.included(base)
end
end
- def openid_identifier=(value)
- write_attribute(:openid_identifier, value.blank? ? nil : value.to_s.normalize_identifier)
- reset_persistence_token if openid_identifier_changed?
- rescue Exception => e
- @openid_error = e.message
+ def authenticate_with_openid
+ @openid_error = nil
+ if !openid_response?
+ save_openid_session
+ else
+ restore_attributes
+ end
+ options = {}
+ options[:return_to] = auth_callback_url(:for_model => "1", :action => "create")
+ auth_controller.send(:authenticate_with_open_id, openid_identifier, options) do |result, openid_identifier|
+ create_openid_token(result, openid_identifier)
+ return true
+ end
+ return false
end
- def save_with_openid(perform_validation = true, &block)
- return false if perform_validation && block_given? && authenticating_with_openid? && !authenticating_with_openid
- return true
+ def create_openid_token(result, openid_identifier)
+ if result.unsuccessful?
+ @openid_error = result.message
+ elsif Token.find_by_key(openid_identifier.normalize_identifier)
+ else
+ token = OpenidToken.new(:key => openid_identifier)
+ self.tokens << token
+ self.active_token = token
+ end
end
- protected
-
- def validate_openid
- errors.add(:openid_identifier, "had the following error: #{@openid_error}") if @openid_error
- end
-
- def using_openid?
- respond_to?(:openid_identifier) && !auth_params[:openid_identifier].blank?
- end
-
- def openid_complete?
- auth_session[:openid_attributes]
- end
-
- def authenticating_with_openid?
- session_class.activated? && ((using_openid?) || openid_complete?)
- end
-
- def validate_password_with_openid?
- !using_openid? && require_password?
- end
-
- def authenticating_with_openid
- @openid_error = nil
- if !openid_complete?
- # Tell our rack callback filter what method the current request is using
- auth_session[:auth_callback_method] = auth_controller.request.method
- auth_session[:openid_attributes] = attributes_to_save
- else
- self.attributes = auth_session.delete(:openid_attributes)
- end
-
- options = {}
- options[:return_to] = auth_controller.url_for(:for_model => "1", :controller => "users", :action => "create")
- auth_controller.send(:authenticate_with_open_id, openid_identifier, options) do |result, openid_identifier, registration|
- if result.unsuccessful?
- @openid_error = result.message
- else
- self.openid_identifier = openid_identifier
- end
- return true
- end
- return false
- end
-
- def attributes_to_save
- attrs_to_save = attributes.clone.delete_if do |k, v|
- [:id, :password, crypted_password_field, password_salt_field, :persistence_token, :perishable_token, :single_access_token, :login_count,
- :failed_login_count, :last_request_at, :current_login_at, :last_login_at, :current_login_ip, :last_login_ip, :created_at,
- :updated_at, :lock_version].include?(k.to_sym)
- end
- attrs_to_save.merge!(:password => password, :password_confirmation => password_confirmation)
- end
+ def attributes_to_save
+ attr_list = [:id, :password, crypted_password_field, password_salt_field, :persistence_token, :perishable_token, :single_access_token, :login_count,
+ :failed_login_count, :last_request_at, :current_login_at, :last_login_at, :current_login_ip, :last_login_ip, :created_at,
+ :updated_at, :lock_version]
+ attrs_to_save = attributes.clone.delete_if do |k, v|
+ attr_list.include?(k.to_sym)
+ end
+ attrs_to_save.merge!(:password => password, :password_confirmation => password_confirmation)
+ end
end
end
end
View
20 lib/authlogic_connect/openid/variables.rb
@@ -1,5 +1,19 @@
-module AuthlogicConnect::Openid
- module Variables
-
+module AuthlogicConnect::Openid::Variables
+ include AuthlogicConnect::Openid::State
+
+ # openid_provider = "blogger", "myopenid", etc.
+ # openid_identifier = "viatropos.myopenid.com", etc.
+ # openid_key = "viatropos"
+# def openid_attributes
+# [:openid_provider, :openid_identifier, :openid_key]
+# end
+
+ def openid_identifier
+ auth_params[:openid_identifier]
end
+
+ def openid_provider
+ from_session_or_params(:openid_provider)
+ end
+
end
View
1  lib/authlogic_connect/token.rb
@@ -1,6 +1,5 @@
class Token < ActiveRecord::Base
belongs_to :user
- validates_presence_of :key, :secret
def client
self.class.client
View
1  lib/open_id_authentication.rb
@@ -86,7 +86,6 @@ def using_open_id?(identifier = nil) #:doc:
def authenticate_with_open_id(identifier = nil, options = {}, &block) #:doc:
identifier ||= open_id_identifier
-
if request.env[Rack::OpenID::RESPONSE]
complete_open_id_authentication(&block)
else
View
24 pkg/authlogic-connect.gemspec
@@ -2,14 +2,14 @@
Gem::Specification.new do |s|
s.name = %q{authlogic-connect}
- s.version = "0.0.3.4"
+ s.version = "0.0.3.6"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Lance Pollard"]
- s.date = %q{2010-05-18}
+ s.date = %q{2010-05-26}
s.description = %q{Let your app use all of Oauth and OpenID}
s.email = %q{lancejpollard@gmail.com}
- s.files = ["README.markdown", "Rakefile", "init.rb", "MIT-LICENSE", "lib/authlogic-connect.rb", "lib/authlogic_connect", "lib/authlogic_connect/callback_filter.rb", "lib/authlogic_connect/common", "lib/authlogic_connect/common/ext.rb", "lib/authlogic_connect/common/session.rb", "lib/authlogic_connect/common/user.rb", "lib/authlogic_connect/common/variables.rb", "lib/authlogic_connect/common.rb", "lib/authlogic_connect/engine.rb", "lib/authlogic_connect/oauth", "lib/authlogic_connect/oauth/helper.rb", "lib/authlogic_connect/oauth/process.rb", "lib/authlogic_connect/oauth/session.rb", "lib/authlogic_connect/oauth/tokens", "lib/authlogic_connect/oauth/tokens/facebook_token.rb", "lib/authlogic_connect/oauth/tokens/get_satisfaction_token.rb", "lib/authlogic_connect/oauth/tokens/google_token.rb", "lib/authlogic_connect/oauth/tokens/linked_in_token.rb", "lib/authlogic_connect/oauth/tokens/myspace_token.rb", "lib/authlogic_connect/oauth/tokens/oauth_token.rb", "lib/authlogic_connect/oauth/tokens/opensocial_token.rb", "lib/authlogic_connect/oauth/tokens/twitter_token.rb", "lib/authlogic_connect/oauth/tokens/vimeo_token.rb", "lib/authlogic_connect/oauth/tokens/yahoo_token.rb", "lib/authlogic_connect/oauth/user.rb", "lib/authlogic_connect/oauth/variables.rb", "lib/authlogic_connect/oauth.rb", "lib/authlogic_connect/openid", "lib/authlogic_connect/openid/session.rb", "lib/authlogic_connect/openid/tokens", "lib/authlogic_connect/openid/tokens/aol_token.rb", "lib/authlogic_connect/openid/tokens/blogger_token.rb", "lib/authlogic_connect/openid/tokens/flickr_token.rb", "lib/authlogic_connect/openid/tokens/my_openid_token.rb", "lib/authlogic_connect/openid/tokens/openid_token.rb", "lib/authlogic_connect/openid/user.rb", "lib/authlogic_connect/openid/variables.rb", "lib/authlogic_connect/openid.rb", "lib/authlogic_connect/token.rb", "lib/open_id_authentication.rb", "rails/init.rb", "test/database.yml", "test/test_authlogic_connect.rb", "test/test_helper.rb"]
+ s.files = ["README.markdown", "Rakefile", "init.rb", "MIT-LICENSE", "lib/authlogic-connect.rb", "lib/authlogic_connect", "lib/authlogic_connect/authlogic_connect.rb", "lib/authlogic_connect/callback_filter.rb", "lib/authlogic_connect/common", "lib/authlogic_connect/common/session.rb", "lib/authlogic_connect/common/state.rb", "lib/authlogic_connect/common/user.rb", "lib/authlogic_connect/common/variables.rb", "lib/authlogic_connect/common.rb", "lib/authlogic_connect/engine.rb", "lib/authlogic_connect/ext.rb", "lib/authlogic_connect/oauth", "lib/authlogic_connect/oauth/helper.rb", "lib/authlogic_connect/oauth/process.rb", "lib/authlogic_connect/oauth/session.rb", "lib/authlogic_connect/oauth/state.rb", "lib/authlogic_connect/oauth/tokens", "lib/authlogic_connect/oauth/tokens/facebook_token.rb", "lib/authlogic_connect/oauth/tokens/get_satisfaction_token.rb", "lib/authlogic_connect/oauth/tokens/google_token.rb", "lib/authlogic_connect/oauth/tokens/linked_in_token.rb", "lib/authlogic_connect/oauth/tokens/myspace_token.rb", "lib/authlogic_connect/oauth/tokens/oauth_token.rb", "lib/authlogic_connect/oauth/tokens/opensocial_token.rb", "lib/authlogic_connect/oauth/tokens/twitter_token.rb", "lib/authlogic_connect/oauth/tokens/vimeo_token.rb", "lib/authlogic_connect/oauth/tokens/yahoo_token.rb", "lib/authlogic_connect/oauth/user.rb", "lib/authlogic_connect/oauth/variables.rb", "lib/authlogic_connect/oauth.rb", "lib/authlogic_connect/openid", "lib/authlogic_connect/openid/process.rb", "lib/authlogic_connect/openid/session.rb", "lib/authlogic_connect/openid/state.rb", "lib/authlogic_connect/openid/tokens", "lib/authlogic_connect/openid/tokens/aol_token.rb", "lib/authlogic_connect/openid/tokens/blogger_token.rb", "lib/authlogic_connect/openid/tokens/flickr_token.rb", "lib/authlogic_connect/openid/tokens/my_openid_token.rb", "lib/authlogic_connect/openid/tokens/openid_token.rb", "lib/authlogic_connect/openid/user.rb", "lib/authlogic_connect/openid/variables.rb", "lib/authlogic_connect/openid.rb", "lib/authlogic_connect/token.rb", "lib/open_id_authentication.rb", "rails/init.rb", "test/common", "test/controllers", "test/controllers/test_users_controller.rb", "test/database.yml", "test/libs", "test/libs/database.rb", "test/libs/user.rb", "test/libs/user_session.rb", "test/oauth", "test/old.rb", "test/test_authlogic_connect.rb", "test/test_helper.rb", "test/test_user.rb"]
s.homepage = %q{http://github.com/viatropos/authlogic-connect}
s.require_paths = ["lib"]
s.rubyforge_project = %q{authlogic-connect}
@@ -23,24 +23,30 @@ Gem::Specification.new do |s|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
s.add_runtime_dependency(%q<activesupport>, [">= 2.1.2"])
s.add_runtime_dependency(%q<activerecord>, [">= 2.1.2"])
- s.add_runtime_dependency(%q<authlogic>, [">= 0"])
- s.add_runtime_dependency(%q<oauth>, [">= 0"])
s.add_runtime_dependency(%q<json>, [">= 0"])
+ s.add_runtime_dependency(%q<ruby-openid>, [">= 0"])
+ s.add_runtime_dependency(%q<rack-openid>, [">= 0.2.1"])
+ s.add_runtime_dependency(%q<oauth>, [">= 0"])
s.add_runtime_dependency(%q<oauth2>, [">= 0"])
+ s.add_runtime_dependency(%q<authlogic>, [">= 0"])
else
s.add_dependency(%q<activesupport>, [">= 2.1.2"])
s.add_dependency(%q<activerecord>, [">= 2.1.2"])
- s.add_dependency(%q<authlogic>, [">= 0"])
- s.add_dependency(%q<oauth>, [">= 0"])
s.add_dependency(%q<json>, [">= 0"])
+ s.add_dependency(%q<ruby-openid>, [">= 0"])
+ s.add_dependency(%q<rack-openid>, [">= 0.2.1"])
+ s.add_dependency(%q<oauth>, [">= 0"])
s.add_dependency(%q<oauth2>, [">= 0"])
+ s.add_dependency(%q<authlogic>, [">= 0"])
end
else
s.add_dependency(%q<activesupport>, [">= 2.1.2"])
s.add_dependency(%q<activerecord>, [">= 2.1.2"])
- s.add_dependency(%q<authlogic>, [">= 0"])
- s.add_dependency(%q<oauth>, [">= 0"])
s.add_dependency(%q<json>, [">= 0"])
+ s.add_dependency(%q<ruby-openid>, [">= 0"])
+ s.add_dependency(%q<rack-openid>, [">= 0.2.1"])
+ s.add_dependency(%q<oauth>, [">= 0"])
s.add_dependency(%q<oauth2>, [">= 0"])
+ s.add_dependency(%q<authlogic>, [">= 0"])
end
end
View
2  rails/init.rb
@@ -10,8 +10,8 @@
require 'open_id_authentication'
-config.middleware.use AuthlogicConnect::CallbackFilter
config.middleware.use OpenIdAuthentication
+config.middleware.use AuthlogicConnect::CallbackFilter
config.after_initialize do
OpenID::Util.logger = Rails.logger
View
21 test/controllers/test_users_controller.rb
@@ -0,0 +1,21 @@
+require File.dirname(__FILE__) + '/../test_helper.rb'
+
+class UsersControllerTest < ActionController::TestCase
+
+ tests UsersController
+
+ context "when signed out" do
+ # setup { sign_out }
+
+ context "on GET to #new" do
+
+ setup { get :new }
+
+ should "do something???" do
+ puts "REQUEST: #{@user.inspect}"
+ end
+
+ end
+
+ end
+end
View
48 test/libs/database.rb
@@ -0,0 +1,48 @@
+
+begin
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
+rescue ArgumentError
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => ":memory:")
+end
+
+ActiveRecord::Base.configurations = true
+
+# this schema was directly copied from
+# http://github.com/viatropos/authlogic-connect-example/blob/master/db/schema.rb
+ActiveRecord::Schema.define(:version => 1) do
+
+ create_table :sessions, :force => true do |t|
+ t.string :session_id, :null => false
+ t.text :data
+ t.datetime :created_at
+ t.datetime :updated_at
+ end
+
+ create_table :tokens, :force => true do |t|
+ t.integer :user_id
+ t.string :type, :limit => 30
+ t.string :key, :limit => 1024
+ t.string :secret
+ t.boolean :active
+ t.datetime :created_at
+ t.datetime :updated_at
+ end
+
+ create_table :users, :force => true do |t|
+ t.datetime :created_at
+ t.datetime :updated_at
+ t.string :login
+ t.string :crypted_password
+ t.string :password_salt
+ t.string :persistence_token, :null => false
+ t.integer :login_count, :default => 0, :null => false
+ t.datetime :last_request_at
+ t.datetime :last_login_at
+ t.datetime :current_login_at
+ t.string :last_login_ip
+ t.string :current_login_ip
+ t.string :openid_identifier
+ t.integer :active_token_id
+ end
+
+end
View
3  test/libs/user.rb
@@ -0,0 +1,3 @@
+class User < ActiveRecord::Base
+ acts_as_authentic
+end
View
2  test/libs/user_session.rb
@@ -0,0 +1,2 @@
+class UserSession < Authlogic::Session::Base
+end
View
53 test/old.rb
@@ -0,0 +1,53 @@
+require 'rubygems'
+require 'tempfile'
+require 'test/unit'
+
+require 'shoulda'
+gem 'activerecord', '~>3.0.0'
+gem 'activesupport', '~>3.0.0'
+gem 'actionpack', '~>3.0.0'
+require 'active_record'
+require 'active_record/version'
+require 'active_support'
+require 'action_pack'
+gem "ruby-openid"
+gem 'rack-openid', '>=0.2.1'
+gem "authlogic", :git => "git://github.com/odorcicd/authlogic.git", :branch => "rails3"
+require 'authlogic'
+gem "oauth"
+gem "oauth2"
+
+puts "Testing against version #{ActiveRecord::VERSION::STRING}"
+
+begin
+ require 'ruby-debug'
+rescue LoadError => e
+ puts "debugger disabled"
+end
+
+ROOT = File.join(File.dirname(__FILE__), '..')
+
+def silence_warnings
+ old_verbose, $VERBOSE = $VERBOSE, nil
+ yield
+ensure
+ $VERBOSE = old_verbose
+end
+
+class Test::Unit::TestCase
+ def setup
+ silence_warnings do
+ Object.const_set(:Rails, stub('Rails', :root => ROOT, :env => 'test'))
+ end
+ end
+end
+
+$LOAD_PATH << File.join(ROOT, 'lib')
+$LOAD_PATH << File.join(ROOT, 'lib', 'authlogic-connect')
+
+require File.join(ROOT, 'lib', 'authlogic-connect.rb')
+
+FIXTURES_DIR = File.join(File.dirname(__FILE__), "fixtures")
+config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
+ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
+ActiveRecord::Base.establish_connection(config['test'])
View
2  test/test_authlogic_connect.rb
@@ -1,4 +1,4 @@
-require 'test/helper'
+require File.dirname(__FILE__) + '/test_helper.rb'
class AuthlogicConnectTest < Test::Unit::TestCase
context "AuthlogicConnect.config" do
View
184 test/test_helper.rb 100755 → 100644
@@ -1,53 +1,153 @@
-require 'rubygems'
-require 'tempfile'
-require 'test/unit'
-
-require 'shoulda'
-gem 'activerecord', '~>3.0.0'
-gem 'activesupport', '~>3.0.0'
-gem 'actionpack', '~>3.0.0'
-require 'active_record'
-require 'active_record/version'
+require "test/unit"
+require "rubygems"
+require "ruby-debug"
+gem "activerecord", "= 2.3.5"
+require "active_record"
+require "active_record/fixtures"
+gem "activesupport", "= 2.3.5"
require 'active_support'
-require 'action_pack'
-gem "ruby-openid"
-gem 'rack-openid', '>=0.2.1'
-gem "authlogic", :git => "git://github.com/odorcicd/authlogic.git", :branch => "rails3"
-require 'authlogic'
-gem "oauth"
-gem "oauth2"
-
-puts "Testing against version #{ActiveRecord::VERSION::STRING}"
-
-begin
- require 'ruby-debug'
-rescue LoadError => e
- puts "debugger disabled"
+gem 'actionpack', "= 2.3.5"
+require 'action_controller'
+require 'shoulda'
+
+require File.dirname(__FILE__) + '/libs/database'
+require File.dirname(__FILE__) + '/../lib/authlogic-connect' unless defined?(AuthlogicConnect)
+require File.dirname(__FILE__) + '/libs/user'
+require File.dirname(__FILE__) + '/libs/user_session'
+require 'authlogic/test_case'
+
+# A temporary fix to bring active record errors up to speed with rails edge.
+# I need to remove this once the new gem is released. This is only here so my tests pass.
+unless defined?(::ActiveModel)
+ class ActiveRecord::Errors
+ def [](key)
+ value = on(key)
+ value.is_a?(Array) ? value : [value].compact
+ end
+ end
end
-ROOT = File.join(File.dirname(__FILE__), '..')
-def silence_warnings
- old_verbose, $VERBOSE = $VERBOSE, nil
- yield
-ensure
- $VERBOSE = old_verbose
+AuthlogicConnect.config = {
+ :default => "twitter",
+ :connect => {
+ :twitter => {
+ :key => "my_key",
+ :secret => "my_secret",
+ :label => "Twitter"
+ },
+ :facebook => {
+ :key => "my_key",
+ :secret => "my_secret",
+ :label => "Facebook"
+ },
+ :google => {
+ :key => "my_key",
+ :secret => "my_secret",
+ :label => "Google"
+ },
+ :yahoo => {
+ :key => "my_key",
+ :secret => "my_secret",
+ :label => "Yahoo"
+ },
+ :vimeo => {
+
+ }
+ }
+}
+
+# want to add a "method" property!
+Authlogic::TestCase::MockRequest.class_eval do
+ def method
+ "POST"
+ end
end
-class Test::Unit::TestCase
- def setup
- silence_warnings do
- Object.const_set(:Rails, stub('Rails', :root => ROOT, :env => 'test'))
+module ControllerHelpers
+ def controller_name
+ "users"
+ end
+
+ def action_name
+ "create"
+ end
+
+ def url_for(options = {})
+ p = []
+ option.each do |k,v|
+ p << "#{k}=#{v}"
end
+ p = "?#{p.join("&")}"
+ url = "http://localhost:3000/users#{p}"
+ end
+
+ def session=(value)
+ @session = value
end
end
+Authlogic::ControllerAdapters::AbstractAdapter.send(:include, ControllerHelpers)
-$LOAD_PATH << File.join(ROOT, 'lib')
-$LOAD_PATH << File.join(ROOT, 'lib', 'authlogic-connect')
+Authlogic::CryptoProviders::AES256.key = "myafdsfddddddddddddddddddddddddddddddddddddddddddddddd"
-require File.join(ROOT, 'lib', 'authlogic-connect.rb')
-
-FIXTURES_DIR = File.join(File.dirname(__FILE__), "fixtures")
-config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
-ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
-ActiveRecord::Base.establish_connection(config['test'])
+class ActiveSupport::TestCase
+ include ActiveRecord::TestFixtures
+ self.fixture_path = File.dirname(__FILE__) + "/fixtures"
+ self.use_transactional_fixtures = false
+ self.use_instantiated_fixtures = false
+ self.pre_loaded_fixtures = false
+ fixtures :all
+ setup :activate_authlogic
+
+ private
+ def password_for(user)
+ case user
+ when users(:ben)
+ "benrocks"
+ when users(:zack)
+ "zackrocks"
+ end
+ end
+
+ def http_basic_auth_for(user = nil, &block)
+ unless user.blank?
+ controller.http_user = user.login
+ controller.http_password = password_for(user)
+ end
+ yield
+ controller.http_user = controller.http_password = nil
+ end
+
+ def set_cookie_for(user, id = nil)
+ controller.cookies["user_credentials"] = {:value => user.persistence_token, :expires => nil}
+ end
+
+ def unset_cookie
+ controller.cookies["user_credentials"] = nil
+ end
+
+ def set_params_for(user, id = nil)
+ controller.params["user_credentials"] = user.single_access_token
+ end
+
+ def unset_params
+ controller.params["user_credentials"] = nil
+ end
+
+ def set_request_content_type(type)
+ controller.request_content_type = type
+ end
+
+ def unset_request_content_type
+ controller.request_content_type = nil
+ end
+
+ def set_session_for(user, id = nil)
+ controller.session["user_credentials"] = user.persistence_token
+ controller.session["user_credentials_id"] = user.id
+ end
+
+ def unset_session
+ controller.session["user_credentials"] = controller.session["user_credentials_id"] = nil
+ end
+end
View
255 test/test_user.rb
@@ -0,0 +1,255 @@
+require File.dirname(__FILE__) + '/test_helper.rb'
+
+module AuthlogicConnect
+ class UserTest < Test::Unit::TestCase
+ context "User creation" do
+ setup do
+ @user = User.new(:login => "viatropos")
+ end