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,47 @@
1
+ <% if options[:oauth] -%>
2
+ development:
3
+ strategy: oauth
4
+ oauth_consumer_key: devkey
5
+ oauth_consumer_secret: devsecret
6
+ base_url: "https://twitter.com"
7
+ authorize_path: "/oauth/authenticate"
8
+ api_timeout: 10
9
+ remember_for: 14 # days
10
+ oauth_callback: "http://localhost:3000/oauth_callback"
11
+ test:
12
+ strategy: oauth
13
+ oauth_consumer_key: testkey
14
+ oauth_consumer_secret: testsecret
15
+ base_url: "https://twitter.com"
16
+ authorize_path: "/oauth/authenticate"
17
+ api_timeout: 10
18
+ remember_for: 14 # days
19
+ oauth_callback: "http://localhost:3000/oauth_callback"
20
+ production:
21
+ strategy: oauth
22
+ oauth_consumer_key: prodkey
23
+ oauth_consumer_secret: prodsecret
24
+ authorize_path: "/oauth/authenticate"
25
+ base_url: "https://twitter.com"
26
+ api_timeout: 10
27
+ remember_for: 14 # days
28
+ <% else -%>
29
+ development:
30
+ strategy: basic
31
+ api_timeout: 10
32
+ base_url: "https://twitter.com"
33
+ # randomly generated key for encrypting Twitter passwords
34
+ encryption_key: "<%= key = ActiveSupport::SecureRandom.hex(12) %>"
35
+ remember_for: 14 # days
36
+ test:
37
+ strategy: basic
38
+ api_timeout: 10
39
+ base_url: "https://twitter.com"
40
+ encryption_key: "<%= key %>"
41
+ remember_for: 14 # days
42
+ production:
43
+ strategy: basic
44
+ api_timeout: 10
45
+ encryption_key: "<%= key %>"
46
+ remember_for: 14 # days
47
+ <% end %>
@@ -0,0 +1,5 @@
1
+ class User < TwitterAuth::GenericUser
2
+ # Extend and define your user model as you see fit.
3
+ # All of the authentication logic is handled by the
4
+ # parent TwitterAuth::GenericUser class.
5
+ end
@@ -0,0 +1,34 @@
1
+ class TwitterAuthGenerator < Rails::Generator::Base
2
+ default_options :oauth => true, :basic => false
3
+
4
+ def manifest
5
+ record do |m|
6
+ m.class_collisions 'User'
7
+
8
+ m.migration_template 'migration.rb', 'db/migrate', :migration_file_name => 'twitter_auth_migration'
9
+ m.template 'user.rb', File.join('app','models','user.rb')
10
+ m.template 'twitter_auth.yml', File.join('config','twitter_auth.yml')
11
+ end
12
+ end
13
+
14
+ protected
15
+
16
+ def banner
17
+ "Usage: #{$0} twitter_auth"
18
+ end
19
+
20
+ def add_options!(opt)
21
+ opt.separator ''
22
+ opt.separator 'Options:'
23
+
24
+ opt.on('-O', '--oauth', 'Use the OAuth authentication strategy to connect to Twitter. (default)') { |v|
25
+ options[:oauth] = v
26
+ options[:basic] = !v
27
+ }
28
+
29
+ opt.on('-B', '--basic', 'Use the HTTP Basic authentication strategy to connect to Twitter.') { |v|
30
+ options[:basic] = v
31
+ options[:oauth] = !v
32
+ }
33
+ end
34
+ end
@@ -0,0 +1,100 @@
1
+ module TwitterAuth
2
+ class Error < StandardError; end
3
+
4
+ def self.config(environment=RAILS_ENV)
5
+ @config ||= {}
6
+ @config[environment] ||= YAML.load(File.open(RAILS_ROOT + '/config/twitter_auth.yml').read)[environment]
7
+ end
8
+
9
+ def self.base_url
10
+ config['base_url'] || 'https://twitter.com'
11
+ end
12
+
13
+ def self.path_prefix
14
+ URI.parse(base_url).path
15
+ end
16
+
17
+ def self.api_timeout
18
+ config['api_timeout'] || 10
19
+ end
20
+
21
+ def self.encryption_key
22
+ raise TwitterAuth::Cryptify::Error, 'You must specify an encryption_key in config/twitter_auth.yml' if config['encryption_key'].blank?
23
+ config['encryption_key']
24
+ end
25
+
26
+ def self.oauth_callback?
27
+ config.key?('oauth_callback')
28
+ end
29
+
30
+ def self.oauth_callback
31
+ config['oauth_callback']
32
+ end
33
+
34
+ def self.remember_for
35
+ (config['remember_for'] || 14).to_i
36
+ end
37
+
38
+ # The authentication strategy employed by this
39
+ # application. Set in +config/twitter.yml+ as
40
+ # strategy; valid options are oauth or basic.
41
+ def self.strategy
42
+ strat = config['strategy']
43
+ raise ArgumentError, 'Invalid TwitterAuth Strategy: Valid strategies are oauth and basic.' unless %w(oauth basic).include?(strat)
44
+ strat.to_sym
45
+ rescue Errno::ENOENT
46
+ :oauth
47
+ end
48
+
49
+ def self.oauth?
50
+ strategy == :oauth
51
+ end
52
+
53
+ def self.basic?
54
+ strategy == :basic
55
+ end
56
+
57
+ # The OAuth consumer used by TwitterAuth for authentication. The consumer key and secret are set in your application's +config/twitter.yml+
58
+ def self.consumer
59
+ options = {:site => TwitterAuth.base_url}
60
+ [ :authorize_path,
61
+ :request_token_path,
62
+ :access_token_path,
63
+ :scheme,
64
+ :signature_method ].each do |oauth_option|
65
+ options[oauth_option] = TwitterAuth.config[oauth_option.to_s] if TwitterAuth.config[oauth_option.to_s]
66
+ end
67
+
68
+ OAuth::Consumer.new(
69
+ config['oauth_consumer_key'],
70
+ config['oauth_consumer_secret'],
71
+ options
72
+ )
73
+ end
74
+
75
+ def self.net
76
+ uri = URI.parse(TwitterAuth.base_url)
77
+ net = Net::HTTP.new(uri.host, uri.port)
78
+ net.use_ssl = uri.scheme == 'https'
79
+ net.read_timeout = TwitterAuth.api_timeout
80
+ net
81
+ end
82
+
83
+ def self.authorize_path
84
+ config['authorize_path'] || '/oauth/authorize'
85
+ end
86
+ end
87
+
88
+ require 'twitter_auth/controller_extensions'
89
+ require 'twitter_auth/cryptify'
90
+ require 'twitter_auth/dispatcher/oauth'
91
+ require 'twitter_auth/dispatcher/basic'
92
+ require 'twitter_auth/dispatcher/shared'
93
+
94
+ module TwitterAuth
95
+ module Dispatcher
96
+ class Error < StandardError; end
97
+ class Unauthorized < Error; end
98
+ end
99
+ end
100
+
@@ -0,0 +1,82 @@
1
+ module TwitterAuth
2
+ # These methods borrow HEAVILY from Rick Olsen's
3
+ # Restful Authentication. All cleverness props
4
+ # go to him, not me.
5
+ module ControllerExtensions
6
+ def self.included(base)
7
+ base.send :helper_method, :current_user, :logged_in?, :authorized?
8
+ end
9
+
10
+ protected
11
+
12
+ def authentication_failed(message, destination='/')
13
+ flash[:error] = message
14
+ redirect_to destination
15
+ end
16
+
17
+ def authentication_succeeded(message = 'You have logged in successfully.', destination = '/')
18
+ flash[:notice] = message
19
+ redirect_back_or_default destination
20
+ end
21
+
22
+ def onboard_user(message = 'Welcome to Vowch!', destination='/')
23
+ flash[:notice] = message
24
+ redirect_back_or_default destination
25
+ end
26
+
27
+ def current_user
28
+ @current_user ||=
29
+ if session[:user_id]
30
+ User.find_by_id(session[:user_id])
31
+ elsif cookies[:remember_token]
32
+ begin
33
+ User.from_remember_token(cookies[:remember_token])
34
+ rescue
35
+ cookies.delete(:remember_token)
36
+ false
37
+ end
38
+ else
39
+ false
40
+ end
41
+ end
42
+
43
+ def current_user=(new_user)
44
+ session[:user_id] = new_user.id
45
+ @current_user = new_user
46
+ end
47
+
48
+ def authorized?
49
+ !!current_user
50
+ end
51
+
52
+ def login_required
53
+ authorized? || access_denied
54
+ end
55
+
56
+ def access_denied
57
+ store_location
58
+ redirect_to login_path
59
+ end
60
+
61
+ def store_location
62
+ session[:return_to] = request.request_uri
63
+ end
64
+
65
+ def redirect_back_or_default(default)
66
+ redirect_to(session[:return_to] || default)
67
+ session[:return_to] = nil
68
+ end
69
+
70
+ def logged_in?
71
+ !!current_user
72
+ end
73
+
74
+ def logout_keeping_session!
75
+ session[:user_id] = nil
76
+ @current_user = nil
77
+ cookies.delete(:remember_token)
78
+ end
79
+ end
80
+ end
81
+
82
+ ActionController::Base.send(:include, TwitterAuth::ControllerExtensions)
@@ -0,0 +1,31 @@
1
+ module TwitterAuth
2
+ module Cryptify
3
+ class Error < StandardError; end
4
+
5
+ def self.encrypt(data)
6
+ salt = generate_salt
7
+ key = EzCrypto::Key.with_password(TwitterAuth.encryption_key, salt)
8
+ {:encrypted_data => key.encrypt64(data), :salt => salt}
9
+ end
10
+
11
+ def self.decrypt(encrypted_data_or_hash, salt=nil)
12
+ case encrypted_data_or_hash
13
+ when String
14
+ encrypted_data = encrypted_data_or_hash
15
+ raise ArgumentError, 'Must provide a salt to decrypt.' unless salt
16
+ when Hash
17
+ encrypted_data = encrypted_data_or_hash[:encrypted_data]
18
+ salt = encrypted_data_or_hash[:salt]
19
+ else
20
+ raise ArgumentError, 'Must provide either an encrypted hash result or encrypted string and salt.'
21
+ end
22
+ key = EzCrypto::Key.with_password(TwitterAuth.encryption_key, salt)
23
+ key.decrypt64(encrypted_data)
24
+ end
25
+
26
+ def self.generate_salt
27
+ ActiveSupport::SecureRandom.hex(4)
28
+ end
29
+ end
30
+ end
31
+
@@ -0,0 +1,46 @@
1
+ require 'net/http'
2
+
3
+ module TwitterAuth
4
+ module Dispatcher
5
+ class Basic
6
+ include TwitterAuth::Dispatcher::Shared
7
+
8
+ attr_accessor :user
9
+
10
+ def initialize(user)
11
+ raise TwitterAuth::Error, 'Dispatcher must be initialized with a User.' unless user.is_a?(TwitterAuth::BasicUser)
12
+ self.user = user
13
+ end
14
+
15
+ def request(http_method, path, body=nil, *arguments)
16
+ path = TwitterAuth.path_prefix + path
17
+ path = append_extension_to(path)
18
+
19
+ response = TwitterAuth.net.start{ |http|
20
+ req = "Net::HTTP::#{http_method.to_s.capitalize}".constantize.new(path, *arguments)
21
+ req.basic_auth user.login, user.password
22
+ req.set_form_data(body) unless body.nil?
23
+ http.request(req)
24
+ }
25
+
26
+ handle_response(response)
27
+ end
28
+
29
+ def get(path, *arguments)
30
+ request(:get, path, *arguments)
31
+ end
32
+
33
+ def post(path, body='', *arguments)
34
+ request(:post, path, body, *arguments)
35
+ end
36
+
37
+ def put(path, body='', *arguments)
38
+ request(:put, path, body, *arguments)
39
+ end
40
+
41
+ def delete(path, *arguments)
42
+ request(:delete, path, *arguments)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,27 @@
1
+ require 'oauth'
2
+
3
+ module TwitterAuth
4
+ module Dispatcher
5
+ class Oauth < OAuth::AccessToken
6
+ include TwitterAuth::Dispatcher::Shared
7
+
8
+ attr_accessor :user
9
+
10
+ def initialize(user)
11
+ raise TwitterAuth::Error, 'Dispatcher must be initialized with a User.' unless user.is_a?(TwitterAuth::OauthUser)
12
+ self.user = user
13
+ super(TwitterAuth.consumer, user.access_token, user.access_secret)
14
+ end
15
+
16
+ def request(http_method, path, *arguments)
17
+ path = "http://api.twitter.com/1" + path
18
+ #path = TwitterAuth.path_prefix + path
19
+ path = append_extension_to(path)
20
+
21
+ response = super
22
+
23
+ handle_response(response)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,40 @@
1
+ module TwitterAuth
2
+ module Dispatcher
3
+ module Shared
4
+ def post!(status)
5
+ self.post('/statuses/update.json', :status => status)
6
+ end
7
+
8
+ def append_extension_to(path)
9
+ path, query_string = *(path.split("?"))
10
+ path << '.json' unless path.match(/\.(:?xml|json)\z/i)
11
+ "#{path}#{"?#{query_string}" if query_string}"
12
+ end
13
+
14
+ def handle_response(response)
15
+ case response
16
+ when Net::HTTPOK
17
+ begin
18
+ JSON.parse(response.body)
19
+ rescue JSON::ParserError
20
+ response.body
21
+ end
22
+ when Net::HTTPUnauthorized
23
+ raise TwitterAuth::Dispatcher::Unauthorized, 'The credentials provided did not authorize the user.'
24
+ else
25
+ message = begin
26
+ JSON.parse(response.body)['error']
27
+ rescue JSON::ParserError
28
+ if match = response.body.match(/<error>(.*)<\/error>/)
29
+ match[1]
30
+ else
31
+ 'An error occurred processing your Twitter request.'
32
+ end
33
+ end
34
+
35
+ raise TwitterAuth::Dispatcher::Error, message
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
data/rails/init.rb ADDED
@@ -0,0 +1,8 @@
1
+ # Gem Dependencies
2
+ config.gem 'oauth'
3
+ config.gem 'ezcrypto'
4
+
5
+ require 'json'
6
+ require 'twitter_auth'
7
+
8
+ RAILS_DEFAULT_LOGGER.info("** TwitterAuth initialized properly.")
@@ -0,0 +1,162 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ ActionController::Routing::Routes.draw do |map|
4
+ map.connect ':controller/:action/:id'
5
+ end
6
+
7
+ class TwitterAuthTestController < ApplicationController
8
+ before_filter :login_required, :only => [:login_required_action]
9
+
10
+ def login_required_action
11
+ render :text => "You are logged in!"
12
+ end
13
+
14
+ def fail_auth
15
+ authentication_failed('Auth FAIL.')
16
+ end
17
+
18
+ def pass_auth
19
+ if params[:message]
20
+ authentication_succeeded(params[:message])
21
+ else
22
+ authentication_succeeded
23
+ end
24
+ end
25
+
26
+ def access_denied_action
27
+ access_denied
28
+ end
29
+
30
+ def redirect_back_action
31
+ redirect_back_or_default(params[:to] || '/')
32
+ end
33
+
34
+ def logout_keeping_session_action
35
+ logout_keeping_session!
36
+ redirect_back_or_default('/')
37
+ end
38
+
39
+ def current_user_action
40
+ @user = current_user
41
+ render :nothing => true
42
+ end
43
+ end
44
+
45
+ describe TwitterAuthTestController do
46
+ before do
47
+ controller.stub!(:cookies).and_return({})
48
+ end
49
+
50
+ %w(authentication_failed authentication_succeeded current_user authorized? login_required access_denied store_location redirect_back_or_default logout_keeping_session!).each do |m|
51
+ it "should respond to the extension method '#{m}'" do
52
+ controller.should respond_to(m)
53
+ end
54
+ end
55
+
56
+ describe "#authentication_failed" do
57
+ it 'should set the flash[:error] to the message passed in' do
58
+ get :fail_auth
59
+ flash[:error].should == 'Auth FAIL.'
60
+ end
61
+
62
+ it 'should redirect to the root' do
63
+ get :fail_auth
64
+ should redirect_to('/')
65
+ end
66
+ end
67
+
68
+ describe "#authentication_succeeded" do
69
+ it 'should set the flash[:notice] to a default success message' do
70
+ get :pass_auth
71
+ flash[:notice].should == 'You have logged in successfully.'
72
+ end
73
+
74
+ it 'should be able ot receive a custom message' do
75
+ get :pass_auth, :message => 'Eat at Joes.'
76
+ flash[:notice].should == 'Eat at Joes.'
77
+ end
78
+ end
79
+
80
+ describe '#current_user' do
81
+ it 'should find the user based on the session user_id' do
82
+ user = Factory.create(:twitter_oauth_user)
83
+ request.session[:user_id] = user.id
84
+ get(:current_user_action)
85
+ assigns[:user].should == user
86
+ end
87
+
88
+ it 'should log the user in through a cookie' do
89
+ user = Factory(:twitter_oauth_user, :remember_token => 'abc', :remember_token_expires_at => (Time.now + 10.days))
90
+ controller.stub!(:cookies).and_return({:remember_token => 'abc'})
91
+ get :current_user_action
92
+ assigns[:user].should == user
93
+ end
94
+
95
+ it 'should return nil if there is no user matching that id' do
96
+ request.session[:user_id] = 2345
97
+ get :current_user_action
98
+ assigns[:user].should be_nil
99
+ end
100
+ end
101
+
102
+ describe "#authorized?" do
103
+ it 'should be true if there is a current_user' do
104
+ user = Factory.create(:twitter_oauth_user)
105
+ controller.stub!(:current_user).and_return(user)
106
+ controller.send(:authorized?).should be_true
107
+ end
108
+
109
+ it 'should be false if there is not current_user' do
110
+ controller.stub!(:current_user).and_return(nil)
111
+ controller.send(:authorized?).should be_false
112
+ end
113
+ end
114
+
115
+ describe '#access_denied' do
116
+ it 'should redirect to the login path' do
117
+ get :access_denied_action
118
+ should redirect_to(login_path)
119
+ end
120
+
121
+ it 'should store the location first' do
122
+ controller.should_receive(:store_location).once
123
+ get :access_denied_action
124
+ end
125
+ end
126
+
127
+ describe '#redirect_back_or_default' do
128
+ it 'should redirect if there is a session[:return_to]' do
129
+ request.session[:return_to] = '/'
130
+ get :redirect_back_action, :to => '/notroot'
131
+ should redirect_to('/')
132
+ end
133
+
134
+ it 'should redirect to the default provided otherwise' do
135
+ get :redirect_back_action, :to => '/someurl'
136
+ should redirect_to('/someurl')
137
+ end
138
+ end
139
+
140
+ describe 'logout_keeping_session!' do
141
+ before do
142
+ @user = Factory.create(:twitter_oauth_user)
143
+ request.session[:user_id] = @user.id
144
+ end
145
+
146
+ it 'should unset session[:user_id]' do
147
+ get :logout_keeping_session_action
148
+ request.session[:user_id].should be_nil
149
+ end
150
+
151
+ it 'should unset current_user' do
152
+ controller.send(:current_user).should == @user
153
+ get :logout_keeping_session_action
154
+ controller.send(:current_user).should be_false
155
+ end
156
+
157
+ it 'should unset the cookie' do
158
+ controller.send(:cookies).should_receive(:delete).with(:remember_token)
159
+ get :logout_keeping_session_action
160
+ end
161
+ end
162
+ end