twitter-auth-with-mongo-mapper 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
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