authlogic-connect 0.0.3.4 → 0.0.3.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/README.markdown +156 -43
  2. data/Rakefile +1 -1
  3. data/lib/authlogic-connect.rb +2 -71
  4. data/lib/authlogic_connect/authlogic_connect.rb +46 -0
  5. data/lib/authlogic_connect/callback_filter.rb +1 -1
  6. data/lib/authlogic_connect/common.rb +1 -1
  7. data/lib/authlogic_connect/common/state.rb +16 -0
  8. data/lib/authlogic_connect/common/user.rb +102 -34
  9. data/lib/authlogic_connect/common/variables.rb +68 -16
  10. data/lib/authlogic_connect/engine.rb +0 -1
  11. data/lib/authlogic_connect/{common/ext.rb → ext.rb} +1 -0
  12. data/lib/authlogic_connect/oauth.rb +3 -1
  13. data/lib/authlogic_connect/oauth/helper.rb +17 -13
  14. data/lib/authlogic_connect/oauth/process.rb +61 -76
  15. data/lib/authlogic_connect/oauth/session.rb +3 -14
  16. data/lib/authlogic_connect/oauth/state.rb +54 -0
  17. data/lib/authlogic_connect/oauth/tokens/google_token.rb +9 -1
  18. data/lib/authlogic_connect/oauth/tokens/oauth_token.rb +67 -2
  19. data/lib/authlogic_connect/oauth/tokens/twitter_token.rb +2 -0
  20. data/lib/authlogic_connect/oauth/user.rb +57 -74
  21. data/lib/authlogic_connect/oauth/variables.rb +52 -27
  22. data/lib/authlogic_connect/openid.rb +3 -0
  23. data/lib/authlogic_connect/openid/process.rb +30 -0
  24. data/lib/authlogic_connect/openid/session.rb +6 -53
  25. data/lib/authlogic_connect/openid/state.rb +47 -0
  26. data/lib/authlogic_connect/openid/tokens/my_openid_token.rb +3 -0
  27. data/lib/authlogic_connect/openid/tokens/openid_token.rb +6 -0
  28. data/lib/authlogic_connect/openid/user.rb +38 -68
  29. data/lib/authlogic_connect/openid/variables.rb +17 -3
  30. data/lib/authlogic_connect/token.rb +0 -1
  31. data/lib/open_id_authentication.rb +0 -1
  32. data/rails/init.rb +1 -1
  33. data/test/controllers/test_users_controller.rb +21 -0
  34. data/test/libs/database.rb +48 -0
  35. data/test/libs/user.rb +3 -0
  36. data/test/libs/user_session.rb +2 -0
  37. data/test/old.rb +53 -0
  38. data/test/test_authlogic_connect.rb +1 -1
  39. data/test/test_helper.rb +142 -42
  40. data/test/test_user.rb +255 -0
  41. metadata +15 -4
data/README.markdown CHANGED
@@ -10,31 +10,6 @@ There are 3 ways you can allow your users to login with Authlogic Connect:
10
10
 
11
11
  All of that is easier than creating a new account and password.
12
12
 
13
- ## Helpful links
14
-
15
- * <b>Authlogic:</b> [http://github.com/binarylogic/authlogic](http://github.com/binarylogic/authlogic)
16
- * <b>Authlogic Connect Example Project:</b> [http://github.com/viatropos/authlogic-connect-example](http://github.com/viatropos/authlogic-connect-example)
17
- * <b>Live example with Twitter and Facebook using Rails 3:</b> [http://authlogic-connect.heroku.com](http://authlogic-connect.heroku.com)
18
- * <b>Rails 2.3.5 Example:</b> [http://github.com/viatropos/authlogic-connect-example-rails2](http://github.com/viatropos/authlogic-connect-example-rails2)
19
- * **Rubygems Repository:** [http://rubygems.org/gems/authlogic-connect](http://rubygems.org/gems/authlogic-connect)
20
-
21
- ## Supported Providers
22
-
23
- ### Oauth
24
-
25
- - Twitter
26
- - Facebook
27
- - Google
28
-
29
- ### OpenID
30
-
31
- - MyOpenID
32
-
33
- Lists of all known providers here:
34
-
35
- - [Oauth Providers](http://wiki.oauth.net/ServiceProviders)
36
- - [OpenID Providers](http://en.wikipedia.org/wiki/List_of_OpenID_providers)
37
-
38
13
  ## Install and use
39
14
 
40
15
  ### 1. Install Authlogic Connect
@@ -59,6 +34,20 @@ Rails 3: `Gemfile`
59
34
  gem "oauth"
60
35
  gem "oauth2"
61
36
  gem "authlogic-connect"
37
+
38
+ ### 2b. Add the `OpenIdAuthentication.store`
39
+
40
+ 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.
41
+
42
+ So as a fix, add these at the end of your `config/environment.rb` files:
43
+
44
+ In development mode:
45
+
46
+ OpenIdAuthentication.store = :file
47
+
48
+ In production (on Heroku primarily)
49
+
50
+ OpenIdAuthentication.store = :memcache
62
51
 
63
52
  ### 3. Add the Migrations
64
53
 
@@ -77,7 +66,8 @@ Files needed are:
77
66
  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).
78
67
 
79
68
  AuthlogicConnect.config = {
80
- :services => {
69
+ :default => "facebook",
70
+ :connect => {
81
71
  :twitter => {
82
72
  :key => "my_key",
83
73
  :secret => "my_secret",
@@ -120,14 +110,21 @@ Because of the redirects involved in Oauth and OpenID, you MUST pass a block to
120
110
  You should save your `@user` objects this way as well, because you also want the user to authenticate with OAuth.
121
111
 
122
112
  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.
113
+
114
+ ### 6. Add Parameters to Forms in your Views
123
115
 
124
- ### 6. Create Custom Tokens (if they don't already exist)
116
+ <%# oauth_register_button :value => "Register with Twitter" %>
117
+ <%# oauth_login_button :value => "Login with Twitter" %>
118
+
119
+ 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.
120
+
121
+ ### 7. Create Custom Tokens (if they don't already exist)
125
122
 
126
123
  Here's an example of the FacebookToken for Oauth
127
124
 
128
125
  class FacebookToken < OauthToken
129
126
 
130
- version 2.0 # oauth 2.0
127
+ version 2.0
131
128
 
132
129
  settings "https://graph.facebook.com",
133
130
  :authorize_url => "https://graph.facebook.com/oauth/authorize",
@@ -137,16 +134,103 @@ Here's an example of the FacebookToken for Oauth
137
134
 
138
135
  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.
139
136
 
140
- Currently Implemented (some fully, some partially):
137
+ ## Examples
138
+
139
+ 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.
140
+
141
+ ### API
142
+
143
+ User model has the following public accessors and methods. This example assumes:
144
+
145
+ # You've associated your Google, OpenID, and Twitter accounts with this app.
146
+ # You're currently logged in via Google.
147
+
148
+ def show
149
+ @user = @current_user
150
+ puts @user.tokens #=> [
151
+ #<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">,
152
+ #<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">,
153
+ #<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">]
154
+ puts @user.tokens.length #=> 3
155
+ # currently logged in with...
156
+ 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">
157
+ puts @user.authenticated_with #=> ["twitter", "openid", "google"]
158
+ puts @user.authenticated_with?(:twitter) #=> true
159
+ puts @user.authenticated_with?(:facebook) #=> false
160
+ puts @user.has_token?(:google) #=> true
161
+ 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">
162
+ # change active_token
163
+ @user.active_token = @user.get_token(:twitter)
164
+ 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">
165
+ @twitter = @user.active_token
166
+ @twitter_profile = JSON.parse(@twitter.get("/account/verify_credentials.json").body) #=> twitter api stuff
167
+ # ...
168
+ end
141
169
 
142
- - [Oauth Tokens](http://github.com/viatropos/authlogic-connect/tree/master/lib/authlogic_connect/oauth/tokens/)
143
-
144
- ### 7. Add login and register buttons to your views
170
+ ### Get Facebook Data
145
171
 
146
- <%# oauth_register_button :value => "Register with Twitter" %>
147
- <%# oauth_login_button :value => "Login with Twitter" %>
172
+ If they've associated their Facebook account with your site, you can access Facebook data.
148
173
 
149
- 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.
174
+ def show
175
+ @user = @current_user
176
+ token = @user.active_token
177
+ facebook = JSON.parse(token.get("/me"))
178
+ @profile = {
179
+ :id => facebook["id"],
180
+ :name => facebook["name"],
181
+ :photo => "https://graph.facebook.com/#{facebook["id"]}/picture",
182
+ :link => facebook["link"],
183
+ :title => "Facebook"
184
+ }
185
+ @profile = @user.profile
186
+ end
187
+
188
+ ## Supported Providers
189
+
190
+ ### Oauth
191
+
192
+ - Twitter
193
+ - Facebook
194
+ - Google
195
+
196
+ ### OpenID
197
+
198
+ - MyOpenID
199
+
200
+ Lists of all known providers here:
201
+
202
+ - [Oauth Providers](http://wiki.oauth.net/ServiceProviders)
203
+ - [OpenID Providers](http://en.wikipedia.org/wiki/List_of_OpenID_providers)
204
+ - [More OpenID](http://openid.net/get-an-openid/)
205
+
206
+ ## Oauth vs. OpenID
207
+
208
+ There is a big but subtle difference between Oauth and OpenID: Oauth is NOT a login protocol. OpenID IS.
209
+
210
+ 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).
211
+
212
+ 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...
213
+
214
+ 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.
215
+
216
+ 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:
217
+
218
+ key do |access_token|
219
+ body = JSON.parse(access_token.get("https://www.google.com/m8/feeds/contacts/default/full?alt=json&max-results=0").body)
220
+ email = body["feed"]["author"].first["email"]["$t"] # $t is some weird google json thing
221
+ end
222
+
223
+ 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
224
+
225
+ 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.
226
+
227
+ ## Helpful links
228
+
229
+ * <b>Authlogic:</b> [http://github.com/binarylogic/authlogic](http://github.com/binarylogic/authlogic)
230
+ * <b>Authlogic Connect Example Project:</b> [http://github.com/viatropos/authlogic-connect-example](http://github.com/viatropos/authlogic-connect-example)
231
+ * <b>Live example with Twitter and Facebook using Rails 3:</b> [http://authlogic-connect.heroku.com](http://authlogic-connect.heroku.com)
232
+ * <b>Rails 2.3.5 Example:</b> [http://github.com/viatropos/authlogic-connect-example-rails2](http://github.com/viatropos/authlogic-connect-example-rails2)
233
+ * **Rubygems Repository:** [http://rubygems.org/gems/authlogic-connect](http://rubygems.org/gems/authlogic-connect)
150
234
 
151
235
  ## The Flow
152
236
 
@@ -192,7 +276,9 @@ Then a Rack Middleware filter converts the GET return request from the authentic
192
276
 
193
277
  ### Tests
194
278
 
195
- 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.
279
+ This only has a few unit tests. Enough to make sure the methods are returning what we are expecting.
280
+
281
+ 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 :).
196
282
 
197
283
  Goal:
198
284
 
@@ -201,14 +287,16 @@ Goal:
201
287
  - Testing style like [Paperclip Tests](http://github.com/thoughtbot/paperclip/tree/master/test/)
202
288
  - Rails 2.3+ and Rails 3 Compatability
203
289
 
204
- 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:
205
-
206
- [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)
290
+ ### TODO
207
291
 
208
- ## TODO
292
+ - 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.
293
+ - 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.
294
+ - Add rememberme functionality correctly. Right now I think it remembers you by default.
295
+ - Login should work without having to access the remote service again.
296
+ - 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
297
+ - 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?
209
298
 
210
- - Change `register_with_oauth` and related to `register_method` and `login_method`: oauth, openid, traditional
211
- - Build view helpers
299
+ 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.
212
300
 
213
301
  ## Helpful References for Rails 3
214
302
 
@@ -216,4 +304,29 @@ I have no idea how to get up and running with Autotest and Shoulda right now. I
216
304
  - [Rails 3 Plugins - Part 1, Big Picture](http://www.themodestrubyist.com/2010/03/01/rails-3-plugins---part-1---the-big-picture/)
217
305
  - [Rails 3 Plugins - Part 2, Writing an Engine](http://www.themodestrubyist.com/2010/03/05/rails-3-plugins---part-2---writing-an-engine/)
218
306
  - [Rails 3 Plugins - Part 3, Initializers](http://www.themodestrubyist.com/2010/03/16/rails-3-plugins---part-3---rake-tasks-generators-initializers-oh-my/)
219
- - [Using Gemspecs as Intended](http://yehudakatz.com/2010/04/02/using-gemspecs-as-intended/)
307
+ - [Using Gemspecs as Intended](http://yehudakatz.com/2010/04/02/using-gemspecs-as-intended/)
308
+
309
+ ## Parameters
310
+
311
+ should look like this:
312
+
313
+ Params from form:
314
+
315
+ {"authentication_type"=>"user", "submit"=>"Register", "openid_identifier"=>"", "oauth_provider"=>"twitter"}
316
+
317
+ Session just before redirect:
318
+
319
+ {"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"}
320
+
321
+ ## Details
322
+
323
+ The regular OAuth process is a four-step sequence:
324
+
325
+ 1. ask for a "request" token.
326
+ 2. ask for the token to be authorized, which triggers user approval.
327
+ 3. exchange the authorized request token for an "access" token.
328
+ 4. use the access token to interact with the user's Google service data.
329
+
330
+ ## OpenID Process
331
+
332
+ 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.
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ require 'rake/gempackagetask'
6
6
  spec = Gem::Specification.new do |s|
7
7
  s.name = "authlogic-connect"
8
8
  s.author = "Lance Pollard"
9
- s.version = "0.0.3.4"
9
+ s.version = "0.0.3.6"
10
10
  s.summary = "Authlogic Connect: Let your app use all of Oauth and OpenID"
11
11
  s.homepage = "http://github.com/viatropos/authlogic-connect"
12
12
  s.email = "lancejpollard@gmail.com"
@@ -7,78 +7,9 @@ require 'oauth2'
7
7
  this = File.dirname(__FILE__)
8
8
  library = "#{this}/authlogic_connect"
9
9
 
10
- class Hash
11
- def recursively_symbolize_keys!
12
- self.symbolize_keys!
13
- self.values.each do |v|
14
- if v.is_a? Hash
15
- v.recursively_symbolize_keys!
16
- elsif v.is_a? Array
17
- v.recursively_symbolize_keys!
18
- end
19
- end
20
- self
21
- end
22
- end
23
-
24
- class Array
25
- def recursively_symbolize_keys!
26
- self.each do |item|
27
- if item.is_a? Hash
28
- item.recursively_symbolize_keys!
29
- elsif item.is_a? Array
30
- item.recursively_symbolize_keys!
31
- end
32
- end
33
- end
34
- end
35
-
36
- module AuthlogicConnect
37
- KEY = "connect"
38
-
39
- class << self
40
-
41
- attr_accessor :config
42
-
43
- def config=(value)
44
- value.recursively_symbolize_keys!
45
- @config = value
46
- end
47
-
48
- def key(path)
49
- result = self.config
50
- path.to_s.split(".").each { |node| result = result[node.to_sym] if result }
51
- result
52
- end
53
-
54
- def credentials(service)
55
- key("#{KEY}.#{service.to_s}")
56
- end
57
-
58
- def services
59
- key(KEY)
60
- end
61
-
62
- def service_names
63
- services.keys.collect(&:to_s)
64
- end
65
-
66
- def include?(service)
67
- !credentials(service).nil?
68
- end
69
-
70
- def token(key)
71
- raise "can't find key '#{key.to_s}' in AuthlogicConnect.config" unless AuthlogicConnect.include?(key) and !key.to_s.empty?
72
- "#{key.to_s.camelcase}Token".constantize
73
- end
74
-
75
- def consumer(key)
76
- token(key).consumer
77
- end
78
- end
79
- end
80
-
81
10
  require "#{this}/open_id_authentication"
11
+ require "#{library}/ext"
12
+ require "#{library}/authlogic_connect"
82
13
  require "#{library}/callback_filter"
83
14
  require "#{library}/token"
84
15
  require "#{library}/openid"
@@ -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
@@ -16,4 +16,4 @@ class AuthlogicConnect::CallbackFilter
16
16
  end
17
17
  @app.call(env)
18
18
  end
19
- end
19
+ end
@@ -1,7 +1,7 @@
1
1
  module AuthlogicConnect::Common
2
2
  end
3
3
 
4
- require File.dirname(__FILE__) + "/common/ext"
4
+ require File.dirname(__FILE__) + "/common/state"
5
5
  require File.dirname(__FILE__) + "/common/variables"
6
6
  require File.dirname(__FILE__) + "/common/user"
7
7
  require File.dirname(__FILE__) + "/common/session"
@@ -0,0 +1,16 @@
1
+ # This class holds query/state variables common to oauth and openid
2
+ module AuthlogicConnect::Common::State
3
+
4
+ def auth_session?
5
+ !auth_session.blank?
6
+ end
7
+
8
+ def auth_params?
9
+ !auth_params.blank?
10
+ end
11
+
12
+ def is_auth_session?
13
+ self.is_a?(Authlogic::Session::Base)
14
+ end
15
+
16
+ end
@@ -1,47 +1,115 @@
1
- module AuthlogicConnect::Common
2
- module User
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
3
14
 
4
15
  def self.included(base)
5
16
  base.class_eval do
6
- add_acts_as_authentic_module(Variables, :prepend)
7
- add_acts_as_authentic_module(InstanceMethods, :append)
17
+ has_many :tokens, :class_name => "Token", :dependent => :destroy
18
+ belongs_to :active_token, :class_name => "Token", :dependent => :destroy
19
+ accepts_nested_attributes_for :tokens, :active_token
8
20
  end
9
21
  end
10
22
 
11
- module InstanceMethods
12
-
13
- def authenticated_with
14
- @authenticated_with ||= self.tokens.collect{|t| t.service_name.to_s}
15
- end
16
-
17
- def authenticated_with?(service)
18
- self.tokens.detect{|t| t.service_name.to_s == service.to_s}
19
- end
20
-
21
- # core save method coordinating how to save the user
22
- def save(perform_validation = true, &block)
23
- status = true
24
- if authenticating_with_openid?
25
- status = save_with_openid(perform_validation, &block)
26
- elsif authenticating_with_oauth?
27
- status = save_with_oauth(perform_validation, &block)
28
- end
29
- if status
30
- result = super(:validate => true)
31
- yield(result) if block_given?
32
- end
33
- result
23
+ def authenticated_with
24
+ @authenticated_with ||= self.tokens.collect{|t| t.service_name.to_s}
25
+ end
26
+
27
+ def authenticated_with?(service)
28
+ self.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.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
+ def save(options = {}, &block)
51
+ # debug_user_save_pre(options, &block)
52
+ options = {} if options == false
53
+ unless options[:skip_redirect] == true
54
+ return false if remotely_authenticating?(&block)
34
55
  end
35
-
36
- def validate_password_with_oauth?
37
- !using_openid? && super
56
+ # forces you to validate, maybe get rid of if needed,
57
+ # but everything depends on this
58
+ if ActiveRecord::VERSION::MAJOR < 3
59
+ result = super(true) # validate!
60
+ else
61
+ result = super(options.merge(:validate => true))
38
62
  end
63
+ # debug_user_save_post
64
+ yield(result) if block_given? # give back to controller
39
65
 
40
- def validate_password_with_openid?
41
- !using_oauth? && super
42
- end
66
+ cleanup_auth_session if result && !(options.has_key?(:keep_session) && options[:keep_session])
43
67
 
68
+ result
69
+ end
70
+
71
+ def remotely_authenticating?(&block)
72
+ return redirecting_to_oauth_server? if using_oauth? && block_given?
73
+ return redirecting_to_openid_server? if using_openid?
74
+ return false
75
+ end
76
+
77
+ # it only reaches this point once it has returned, or you
78
+ # have manually skipped the redirect and save was called directly.
79
+ def cleanup_auth_session
80
+ cleanup_oauth_session
81
+ cleanup_openid_session
82
+ end
83
+
84
+ def validate_password_with_oauth?
85
+ !using_openid? && super
86
+ end
87
+
88
+ def validate_password_with_openid?
89
+ !using_oauth? && super
90
+ end
91
+
92
+ # test methods for dev/debugging, commented out by default
93
+ def debug_user_save_pre(options = {}, &block)
94
+ puts "USER SAVE "
95
+ puts "block_given? #{block_given?.to_s}"
96
+ puts "using_oauth? #{using_oauth?.to_s}"
97
+ puts "using_openid? #{using_openid?.to_s}"
98
+ puts "authenticating_with_oauth? #{authenticating_with_oauth?.to_s}"
99
+ puts "authenticating_with_openid? #{authenticating_with_openid?.to_s}"
100
+ puts "validate_password_with_oauth? #{validate_password_with_oauth?.to_s}"
101
+ puts "validate_password_with_openid? #{validate_password_with_openid?.to_s}"
102
+ puts "!using_openid? && require_password? #{(!using_openid? && require_password?).to_s}"
103
+ end
104
+
105
+ def debug_user_save_post
106
+ puts "ERRORS: #{errors.full_messages}"
107
+ puts "using_oauth? #{using_oauth?.to_s}"
108
+ puts "using_openid? #{using_openid?.to_s}"
109
+ puts "validate_password_with_oauth? #{validate_password_with_oauth?.to_s}"
110
+ puts "validate_password_with_openid? #{validate_password_with_openid?.to_s}"
44
111
  end
45
112
 
46
113
  end
47
- end
114
+
115
+ end