twitter-auth-with-mongo-mapper 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. data/CHANGELOG.markdown +11 -0
  2. data/README.markdown +5 -0
  3. data/Rakefile +31 -0
  4. data/VERSION.yml +4 -0
  5. data/app/controllers/sessions_controller.rb +81 -0
  6. data/app/models/twitter_auth/basic_user.rb +64 -0
  7. data/app/models/twitter_auth/generic_user.rb +135 -0
  8. data/app/models/twitter_auth/oauth_user.rb +50 -0
  9. data/app/views/sessions/_login_form.html.erb +17 -0
  10. data/app/views/sessions/new.html.erb +5 -0
  11. data/config/routes.rb +6 -0
  12. data/generators/twitter_auth/USAGE +12 -0
  13. data/generators/twitter_auth/templates/migration.rb +49 -0
  14. data/generators/twitter_auth/templates/twitter_auth.yml +47 -0
  15. data/generators/twitter_auth/templates/user.rb +5 -0
  16. data/generators/twitter_auth/twitter_auth_generator.rb +34 -0
  17. data/lib/twitter_auth.rb +100 -0
  18. data/lib/twitter_auth/controller_extensions.rb +82 -0
  19. data/lib/twitter_auth/cryptify.rb +31 -0
  20. data/lib/twitter_auth/dispatcher/basic.rb +46 -0
  21. data/lib/twitter_auth/dispatcher/oauth.rb +27 -0
  22. data/lib/twitter_auth/dispatcher/shared.rb +40 -0
  23. data/rails/init.rb +8 -0
  24. data/spec/controllers/controller_extensions_spec.rb +162 -0
  25. data/spec/controllers/sessions_controller_spec.rb +221 -0
  26. data/spec/fixtures/config/twitter_auth.yml +17 -0
  27. data/spec/fixtures/factories.rb +20 -0
  28. data/spec/fixtures/fakeweb.rb +18 -0
  29. data/spec/fixtures/twitter.rb +5 -0
  30. data/spec/models/twitter_auth/basic_user_spec.rb +138 -0
  31. data/spec/models/twitter_auth/generic_user_spec.rb +146 -0
  32. data/spec/models/twitter_auth/oauth_user_spec.rb +100 -0
  33. data/spec/schema.rb +42 -0
  34. data/spec/spec.opts +1 -0
  35. data/spec/spec_helper.rb +51 -0
  36. data/spec/twitter_auth/cryptify_spec.rb +51 -0
  37. data/spec/twitter_auth/dispatcher/basic_spec.rb +83 -0
  38. data/spec/twitter_auth/dispatcher/oauth_spec.rb +72 -0
  39. data/spec/twitter_auth/dispatcher/shared_spec.rb +26 -0
  40. data/spec/twitter_auth_spec.rb +160 -0
  41. metadata +151 -0
@@ -0,0 +1,11 @@
1
+ Change Log
2
+ ==========
3
+
4
+ == 0.1.22 - June 19, 2009
5
+
6
+ - Merged in @pengwynn's OAuth 1.0a support patch. Thanks!
7
+
8
+ == 0.1.19 - April 23, 2009
9
+
10
+ - Added this changelog to keep track of additions to TwitterAuth
11
+ - All authentication is now keyed via `id` instead of `screen_name`
data/README.markdown ADDED
@@ -0,0 +1,5 @@
1
+ TwitterAuth is an awesome gem written by mbleigh, which you can check out [here](http://github.com/mbleigh/twitter-auth/).
2
+
3
+ MongoMapper is an awesome gem written by jnunemaker, which you can check out [here](http://github.com/jnunemaker).
4
+
5
+ This was a proof of concept for using them together. Really very little needed to be changed.
data/Rakefile ADDED
@@ -0,0 +1,31 @@
1
+ require 'rake'
2
+ require 'spec/rake/spectask'
3
+
4
+ desc 'Default: run specs.'
5
+ task :default => :spec
6
+
7
+ desc 'Run the specs'
8
+ Spec::Rake::SpecTask.new(:spec) do |t|
9
+ t.spec_opts = ['--colour --format progress --loadby mtime --reverse']
10
+ t.spec_files = FileList['spec/**/*_spec.rb']
11
+ end
12
+
13
+ begin
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |s|
16
+ s.name = "twitter-auth-with-mongo-mapper"
17
+ s.summary = "TwitterAuth is a Rails plugin gem that provides Single Sign-On capabilities for Rails applications via Twitter."
18
+ s.email = "michael@intridea.com"
19
+ s.homepage = "http://github.com/mbleigh/twitter-auth"
20
+ s.description = "TwitterAuth is a Rails plugin gem that provides Single Sign-On capabilities for Rails applications via Twitter. Both OAuth and HTTP Basic are supported."
21
+ s.files = FileList["[A-Z]*", "{bin,generators,lib,spec,config,app,rails}/**/*"] - FileList["**/*.log"]
22
+
23
+ s.authors = ["Michael Bleigh"]
24
+ s.add_dependency('oauth', '>= 0.3.1')
25
+ s.add_dependency('ezcrypto', '>= 0.7.2')
26
+ s.rubyforge_project = 'twitter-auth'
27
+ end
28
+ rescue LoadError
29
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
30
+ end
31
+
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 9
3
+ :major: 0
4
+ :minor: 0
@@ -0,0 +1,81 @@
1
+ class SessionsController < ApplicationController
2
+ #unloadable
3
+
4
+ def new
5
+ if TwitterAuth.oauth?
6
+ oauth_callback = request.protocol + request.host_with_port + '/oauth_callback'
7
+ @request_token = TwitterAuth.consumer.get_request_token({:oauth_callback=>oauth_callback})
8
+ session[:request_token] = @request_token.token
9
+ session[:request_token_secret] = @request_token.secret
10
+
11
+ url = @request_token.authorize_url
12
+ url << "&oauth_callback=#{CGI.escape(TwitterAuth.oauth_callback)}" if TwitterAuth.oauth_callback?
13
+ redirect_to url
14
+ else
15
+ # we don't have to do anything, it's just a simple form for HTTP basic!
16
+ end
17
+ end
18
+
19
+ def create
20
+ logout_keeping_session!
21
+ if user = User.authenticate(params[:login], params[:password])
22
+ self.current_user = user
23
+ authentication_succeeded and return
24
+ else
25
+ authentication_failed('Unable to verify your credentials through Twitter. Please try again.', '/login') and return
26
+ end
27
+ end
28
+
29
+ def oauth_callback
30
+ unless session[:request_token] && session[:request_token_secret]
31
+ authentication_failed('No authentication information was found in the session. Please try again.') and return
32
+ end
33
+
34
+ unless params[:oauth_token].blank? || session[:request_token] == params[:oauth_token]
35
+ authentication_failed('Authentication information does not match session information. Please try again.') and return
36
+ end
37
+
38
+ @request_token = OAuth::RequestToken.new(TwitterAuth.consumer, session[:request_token], session[:request_token_secret])
39
+
40
+ oauth_verifier = params["oauth_verifier"]
41
+ @access_token = @request_token.get_access_token(:oauth_verifier => oauth_verifier)
42
+
43
+ # The request token has been invalidated
44
+ # so we nullify it in the session.
45
+ session[:request_token] = nil
46
+ session[:request_token_secret] = nil
47
+
48
+ @user = User.identify_or_create_from_access_token(@access_token)
49
+
50
+ if @user.onboard_status == 0
51
+ authentication_failed("You need to be vowched for before you can sign up.")
52
+ elsif @user.onboard_status == 1
53
+ @user.update_attributes(:onboard_status => 2)
54
+ session[:user_id] = @user.id
55
+ cookies[:remember_token] = { :value => @user.remember_me, :expires => 1.year.from_now}
56
+ onboard_user
57
+ else
58
+ session[:user_id] = @user.id
59
+ cookies[:remember_token] = { :value => @user.remember_me, :expires => 1.year.from_now}
60
+ if @user.onboard_status == 2
61
+ #onboard_user
62
+ authentication_succeeded
63
+ else
64
+ authentication_succeeded
65
+ end
66
+ end
67
+
68
+ rescue Net::HTTPServerException => e
69
+ case e.message
70
+ when '401 "Unauthorized"'
71
+ authentication_failed('This authentication request is no longer valid. Please try again.') and return
72
+ else
73
+ authentication_failed('There was a problem trying to authenticate you. Please try again.') and return
74
+ end
75
+ end
76
+
77
+ def destroy
78
+ logout_keeping_session!
79
+ redirect_back_or_default('/')
80
+ end
81
+ end
@@ -0,0 +1,64 @@
1
+ require 'net/http'
2
+
3
+ module TwitterAuth
4
+ module BasicUser
5
+ def self.included(base)
6
+ base.class_eval do
7
+ attr_accessor :crypted_password, :salt
8
+ end
9
+
10
+ base.extend TwitterAuth::BasicUser::ClassMethods
11
+ end
12
+
13
+ module ClassMethods
14
+ def verify_credentials(login, password)
15
+ response = TwitterAuth.net.start { |http|
16
+ request = Net::HTTP::Get.new('/account/verify_credentials.json')
17
+ request.basic_auth login, password
18
+ http.request(request)
19
+ }
20
+
21
+ if response.code == '200'
22
+ JSON.parse(response.body)
23
+ else
24
+ false
25
+ end
26
+ end
27
+
28
+ def authenticate(login, password)
29
+ if twitter_hash = verify_credentials(login, password)
30
+ user = identify_or_create_from_twitter_hash_and_password(twitter_hash, password)
31
+ user
32
+ else
33
+ nil
34
+ end
35
+ end
36
+
37
+ def identify_or_create_from_twitter_hash_and_password(twitter_hash, password)
38
+ if user = User.find_by_twitter_id(twitter_hash['id'].to_s)
39
+ user.login = twitter_hash['screen_name']
40
+ user.assign_twitter_attributes(twitter_hash)
41
+ user.password = password
42
+ user.save
43
+ user
44
+ else
45
+ user = User.new_from_twitter_hash(twitter_hash)
46
+ user.password = password
47
+ user.save
48
+ user
49
+ end
50
+ end
51
+ end
52
+
53
+ def password=(new_password)
54
+ encrypted = TwitterAuth::Cryptify.encrypt(new_password)
55
+ self.crypted_password = encrypted[:encrypted_data]
56
+ self.salt = encrypted[:salt]
57
+ end
58
+
59
+ def password
60
+ TwitterAuth::Cryptify.decrypt(self.crypted_password, self.salt)
61
+ end
62
+ end
63
+ end
64
+
@@ -0,0 +1,135 @@
1
+ module TwitterAuth
2
+ class GenericUser
3
+ include MongoMapper::Document
4
+
5
+ attr_accessor :twitter_id, :remember_token, :remember_token_expires_at
6
+
7
+ key :access_secret, String
8
+ key :access_token, String
9
+ key :created_at, String
10
+ key :crypted_password, String
11
+ key :description, String
12
+ key :favourites_count, String
13
+ key :followers_count, String
14
+ key :friends_count, String
15
+ key :location, String
16
+ key :login, String
17
+ key :name, String
18
+ key :profile_background_color, String
19
+ key :profile_background_image_url, String
20
+ key :profile_background_tile, String
21
+ key :profile_image_url, String
22
+ key :profile_link_color, String
23
+ key :profile_sidebar_border_color, String
24
+ key :profile_sidebar_fill_color, String
25
+ key :profile_text_color, String
26
+ key :protected, String
27
+ key :remember_token, String
28
+ key :remember_token_expires_at, String
29
+ key :salt, String
30
+ key :statuses_count, String
31
+ key :time_zone, String
32
+ key :twitter_id, String
33
+ key :updated_at, String
34
+ key :url, String
35
+ key :utc_offset, String
36
+
37
+ TWITTER_ATTRIBUTES = [
38
+ :name,
39
+ :location,
40
+ :description,
41
+ :profile_image_url,
42
+ :url,
43
+ :protected,
44
+ :profile_background_color,
45
+ :profile_sidebar_fill_color,
46
+ :profile_link_color,
47
+ :profile_sidebar_border_color,
48
+ :profile_text_color,
49
+ :profile_background_image_url,
50
+ :profile_background_tile,
51
+ :friends_count,
52
+ :statuses_count,
53
+ :followers_count,
54
+ :favourites_count,
55
+ :time_zone,
56
+ :utc_offset
57
+ ]
58
+
59
+ with_options :if => :utilize_default_validations do |v|
60
+ # v.validates_presence_of :login, :twitter_id
61
+ # v.validates_format_of :login, :with => /\A[a-z0-9_]+\z/i
62
+ # v.validates_length_of :login, :in => 1..15
63
+ # v.validates_uniqueness_of :login, :case_sensitive => false
64
+ # v.validates_uniqueness_of :twitter_id, :message => "ID has already been taken."
65
+ # v.validates_uniqueness_of :remember_token, :allow_blank => true
66
+ end
67
+
68
+ def self.table_name; 'users' end
69
+
70
+ def self.new_from_twitter_hash(hash)
71
+ raise ArgumentError, 'Invalid hash: must include screen_name.' unless hash.key?('screen_name')
72
+
73
+ raise ArgumentError, 'Invalid hash: must include id.' unless hash.key?('id')
74
+
75
+ user = User.new
76
+ user.twitter_id = hash['id'].to_s
77
+ user.login = hash['screen_name']
78
+
79
+ TWITTER_ATTRIBUTES.each do |att|
80
+ user.send("#{att}=", hash[att.to_s]) if user.respond_to?("#{att}=")
81
+ end
82
+
83
+ user
84
+ end
85
+
86
+ def self.from_remember_token(token)
87
+ first(:conditions => ["remember_token = ? AND remember_token_expires_at > ?", token, Time.now])
88
+ end
89
+
90
+ def assign_twitter_attributes(hash)
91
+ TWITTER_ATTRIBUTES.each do |att|
92
+ send("#{att}=", hash[att.to_s]) if respond_to?("#{att}=")
93
+ end
94
+ end
95
+
96
+ def update_twitter_attributes(hash)
97
+ assign_twitter_attributes(hash)
98
+ save
99
+ end
100
+
101
+ if TwitterAuth.oauth?
102
+ include TwitterAuth::OauthUser
103
+ else
104
+ include TwitterAuth::BasicUser
105
+ end
106
+
107
+ def utilize_default_validations
108
+ true
109
+ end
110
+
111
+ def twitter
112
+ if TwitterAuth.oauth?
113
+ TwitterAuth::Dispatcher::Oauth.new(self)
114
+ else
115
+ TwitterAuth::Dispatcher::Basic.new(self)
116
+ end
117
+ end
118
+
119
+ def remember_me
120
+ return false unless respond_to?(:remember_token)
121
+
122
+ self.remember_token = ActiveSupport::SecureRandom.hex(10)
123
+ self.remember_token_expires_at = Time.now + TwitterAuth.remember_for.days
124
+
125
+ save
126
+
127
+ {:value => self.remember_token, :expires => self.remember_token_expires_at}
128
+ end
129
+
130
+ def forget_me
131
+ self.remember_token = self.remember_token_expires_at = nil
132
+ self.save
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,50 @@
1
+ module TwitterAuth
2
+ module OauthUser
3
+ def self.included(base)
4
+ base.class_eval do
5
+ attr_accessor :access_token, :access_secret
6
+ end
7
+
8
+ base.extend TwitterAuth::OauthUser::ClassMethods
9
+ base.extend TwitterAuth::Dispatcher::Shared
10
+ end
11
+
12
+ module ClassMethods
13
+ def identify_or_create_from_access_token(token, secret=nil)
14
+ raise ArgumentError, 'Must authenticate with an OAuth::AccessToken or the string access token and secret.' unless (token && secret) || token.is_a?(OAuth::AccessToken)
15
+
16
+ token = OAuth::AccessToken.new(TwitterAuth.consumer, token, secret) unless token.is_a?(OAuth::AccessToken)
17
+
18
+ response = token.get(TwitterAuth.path_prefix + '/account/verify_credentials.json')
19
+ user_info = handle_response(response)
20
+
21
+ if user = User.find_by_login(user_info['screen_name'].to_s)
22
+ if user.onboard_status == 1
23
+ user.onboard_status = 2
24
+ end
25
+ user.login = user_info['screen_name']
26
+ user.assign_twitter_attributes(user_info)
27
+ user.access_token = token.token
28
+ user.access_secret = token.secret
29
+ user.save
30
+ user
31
+ else
32
+ User.create_from_twitter_hash_and_token(user_info, token)
33
+ end
34
+ end
35
+
36
+ def create_from_twitter_hash_and_token(user_info, access_token)
37
+ user = User.new_from_twitter_hash(user_info)
38
+ user.access_token = access_token.token
39
+ user.access_secret = access_token.secret
40
+ user.onboard_status = 0
41
+ user.save
42
+ user
43
+ end
44
+ end
45
+
46
+ def token
47
+ OAuth::AccessToken.new(TwitterAuth.consumer, access_token, access_secret)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,17 @@
1
+ <% form_tag session_path, :id => 'login_form' do %>
2
+ <div class='field'>
3
+ <label for='login'>Twitter Username:</label>
4
+ <%= text_field_tag 'login', nil, :class => 'text_field' %>
5
+ </div>
6
+ <div class='field'>
7
+ <label for='password'>Password:</label>
8
+ <%= password_field_tag 'password', nil, :class => 'password_field' %>
9
+ </div>
10
+ <!--<div class='checkbox-field'>
11
+ <%= check_box_tag 'remember_me' %> <label for='remember_me'>Keep Me Logged In</label>
12
+ </div>-->
13
+ <div class='field submit'>
14
+ <%= submit_tag 'Log In', :class => 'submit' %>
15
+ </div>
16
+ <% end %>
17
+
@@ -0,0 +1,5 @@
1
+ <h1>Log In Via Twitter</h1>
2
+
3
+ <p>This application utilizes your Twitter username and password for authentication; you do not have to create a separate account here. To log in, just enter your Twitter credentials in the form below.</p>
4
+
5
+ <%= render :partial => 'login_form' %>
data/config/routes.rb ADDED
@@ -0,0 +1,6 @@
1
+ ActionController::Routing::Routes.draw do |map|
2
+ map.login '/login', :controller => 'sessions', :action => 'new'
3
+ map.logout '/logout', :controller => 'sessions', :action => 'destroy'
4
+ map.resource :session
5
+ map.oauth_callback '/oauth_callback', :controller => 'sessions', :action => 'oauth_callback'
6
+ end
@@ -0,0 +1,12 @@
1
+ TwitterAuth Generator
2
+ =====================
3
+
4
+ The TwitterAuth generator allows you to generate the components necessary to implement Twitter as a Single Sign-On provider for your site.
5
+
6
+ To run it, you simply need to call it:
7
+
8
+ script/generate twitter_auth
9
+
10
+ This will generate the migration necessary for the users table as well as generate a User model that extends the appropriate TwitterAuth model template and a config/twitter.yml that allows you to set your OAuth consumer key and secret.
11
+
12
+ By default, TwitterAuth uses OAuth as its authentication strategy. If you wish to use HTTP Basic you can pass in the --basic option.
@@ -0,0 +1,49 @@
1
+ class TwitterAuthMigration < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :users do |t|
4
+ t.string :twitter_id
5
+ t.string :login
6
+ <% if options[:oauth] -%>
7
+ t.string :access_token
8
+ t.string :access_secret
9
+ <% elsif options[:basic] -%>
10
+ t.binary :crypted_password
11
+ t.string :salt
12
+ <% end -%>
13
+
14
+ t.string :remember_token
15
+ t.datetime :remember_token_expires_at
16
+
17
+ # This information is automatically kept
18
+ # in-sync at each login of the user. You
19
+ # may remove any/all of these columns.
20
+ t.string :name
21
+ t.string :location
22
+ t.string :description
23
+ t.string :profile_image_url
24
+ t.string :url
25
+ t.boolean :protected
26
+ t.string :profile_background_color
27
+ t.string :profile_sidebar_fill_color
28
+ t.string :profile_link_color
29
+ t.string :profile_sidebar_border_color
30
+ t.string :profile_text_color
31
+ t.string :profile_background_image_url
32
+ t.boolean :profile_background_tile
33
+ t.integer :friends_count
34
+ t.integer :statuses_count
35
+ t.integer :followers_count
36
+ t.integer :favourites_count
37
+
38
+ # Probably don't need both, but they're here.
39
+ t.integer :utc_offset
40
+ t.string :time_zone
41
+
42
+ t.timestamps
43
+ end
44
+ end
45
+
46
+ def self.down
47
+ drop_table :users
48
+ end
49
+ end