millsb-twitter-auth 0.1.16

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 (40) hide show
  1. data/README.markdown +96 -0
  2. data/Rakefile +31 -0
  3. data/VERSION.yml +4 -0
  4. data/app/controllers/sessions_controller.rb +64 -0
  5. data/app/models/twitter_auth/basic_user.rb +63 -0
  6. data/app/models/twitter_auth/generic_user.rb +93 -0
  7. data/app/models/twitter_auth/oauth_user.rb +43 -0
  8. data/app/views/sessions/_login_form.html.erb +17 -0
  9. data/app/views/sessions/new.html.erb +5 -0
  10. data/config/routes.rb +6 -0
  11. data/generators/twitter_auth/USAGE +12 -0
  12. data/generators/twitter_auth/templates/migration.rb +48 -0
  13. data/generators/twitter_auth/templates/twitter_auth.yml +44 -0
  14. data/generators/twitter_auth/templates/user.rb +5 -0
  15. data/generators/twitter_auth/twitter_auth_generator.rb +34 -0
  16. data/lib/twitter_auth.rb +87 -0
  17. data/lib/twitter_auth/controller_extensions.rb +65 -0
  18. data/lib/twitter_auth/cryptify.rb +30 -0
  19. data/lib/twitter_auth/dispatcher/basic.rb +46 -0
  20. data/lib/twitter_auth/dispatcher/oauth.rb +26 -0
  21. data/lib/twitter_auth/dispatcher/shared.rb +40 -0
  22. data/rails/init.rb +8 -0
  23. data/spec/controllers/controller_extensions_spec.rb +162 -0
  24. data/spec/controllers/sessions_controller_spec.rb +221 -0
  25. data/spec/fixtures/config/twitter_auth.yml +17 -0
  26. data/spec/fixtures/factories.rb +18 -0
  27. data/spec/fixtures/fakeweb.rb +18 -0
  28. data/spec/fixtures/twitter.rb +5 -0
  29. data/spec/models/twitter_auth/basic_user_spec.rb +122 -0
  30. data/spec/models/twitter_auth/generic_user_spec.rb +142 -0
  31. data/spec/models/twitter_auth/oauth_user_spec.rb +94 -0
  32. data/spec/schema.rb +41 -0
  33. data/spec/spec.opts +1 -0
  34. data/spec/spec_helper.rb +51 -0
  35. data/spec/twitter_auth/cryptify_spec.rb +51 -0
  36. data/spec/twitter_auth/dispatcher/basic_spec.rb +83 -0
  37. data/spec/twitter_auth/dispatcher/oauth_spec.rb +72 -0
  38. data/spec/twitter_auth/dispatcher/shared_spec.rb +26 -0
  39. data/spec/twitter_auth_spec.rb +154 -0
  40. metadata +127 -0
@@ -0,0 +1,142 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe TwitterAuth::GenericUser do
4
+ should_validate_presence_of :login
5
+ should_validate_format_of :login, 'some_guy', 'awesome', 'cool_man'
6
+ should_not_validate_format_of :login, 'with-dashes', 'with.periods', 'with spaces'
7
+ should_validate_length_of :login, :in => 1..15
8
+
9
+ it 'should validate uniqueness of login' do
10
+ Factory.create(:twitter_oauth_user)
11
+ Factory.build(:twitter_oauth_user).should have_at_least(1).errors_on(:login)
12
+ end
13
+
14
+ it 'should validate uniqueness of remember_token' do
15
+ Factory.create(:twitter_oauth_user, :remember_token => 'abc')
16
+ Factory.build(:twitter_oauth_user, :remember_token => 'abc').should have_at_least(1).errors_on(:remember_token)
17
+ end
18
+
19
+ it 'should allow capital letters in the username' do
20
+ Factory.build(:twitter_oauth_user, :login => 'TwitterMan').should have(:no).errors_on(:login)
21
+ end
22
+
23
+ it 'should not allow the same login with different capitalization' do
24
+ Factory.create(:twitter_oauth_user, :login => 'twitterman')
25
+ Factory.build(:twitter_oauth_user, :login => 'TwitterMan').should have_at_least(1).errors_on(:login)
26
+ end
27
+
28
+ describe '.new_from_twitter_hash' do
29
+ it 'should raise an argument error if the hash does not have a screen_name attribute' do
30
+ lambda{User.new_from_twitter_hash({})}.should raise_error(ArgumentError, 'Invalid hash: must include screen_name.')
31
+ end
32
+
33
+ it 'should return a user' do
34
+ User.new_from_twitter_hash({'screen_name' => 'twitterman'}).should be_a(User)
35
+ end
36
+
37
+ it 'should assign login to the screen_name' do
38
+ User.new_from_twitter_hash({'screen_name' => 'twitterman'}).login.should == 'twitterman'
39
+ end
40
+
41
+ it 'should assign twitter attributes that are provided' do
42
+ u = User.new_from_twitter_hash({'screen_name' => 'twitterman', 'name' => 'Twitter Man', 'description' => 'Saving the world for all Tweet kind.'})
43
+ u.name.should == 'Twitter Man'
44
+ u.description.should == 'Saving the world for all Tweet kind.'
45
+ end
46
+ end
47
+
48
+ describe '#update_twitter_attributes' do
49
+ it 'should assign values to the user' do
50
+ user = Factory.create(:twitter_oauth_user, :name => "Dude", :description => "Awesome, man.")
51
+ user.update_twitter_attributes({'name' => 'Twitter Man', 'description' => 'Works.'})
52
+ user.reload
53
+ user.name.should == 'Twitter Man'
54
+ user.description.should == 'Works.'
55
+ end
56
+
57
+ it 'should not throw an error with extraneous info' do
58
+ user = Factory.create(:twitter_oauth_user, :name => "Dude", :description => "Awesome, man.")
59
+ lambda{user.update_twitter_attributes({'name' => 'Twitter Man', 'description' => 'Works.', 'whoopsy' => 'noworks.'})}.should_not raise_error
60
+ end
61
+ end
62
+
63
+ describe '#remember_me' do
64
+ before do
65
+ @user = Factory(:twitter_oauth_user)
66
+ end
67
+
68
+ it 'should check for the remember_token column' do
69
+ @user.should_receive(:respond_to?).with(:remember_token).and_return(false)
70
+ @user.remember_me
71
+ end
72
+
73
+ it 'should return nil if there is no remember_token column' do
74
+ @user.should_receive(:respond_to?).with(:remember_token).and_return(false)
75
+ @user.remember_me.should be_false
76
+ end
77
+
78
+ describe ' with proper columns' do
79
+ it 'should generate a secure random token' do
80
+ ActiveSupport::SecureRandom.should_receive(:hex).with(10).and_return('abcdef')
81
+ @user.remember_me
82
+ @user.remember_token.should == 'abcdef'
83
+ end
84
+
85
+ it 'should set the expiration to the current time plus the remember_for period' do
86
+ TwitterAuth.stub!(:remember_for).and_return(10)
87
+ time = Time.now
88
+ Time.stub!(:now).and_return(time)
89
+
90
+ @user.remember_me
91
+
92
+ @user.remember_token_expires_at.should == Time.now + 10.days
93
+ end
94
+
95
+ it 'should return a hash with a :value and :expires key' do
96
+ result = @user.remember_me
97
+ result.should be_a(Hash)
98
+ result.key?(:value).should be_true
99
+ result.key?(:expires).should be_true
100
+ end
101
+
102
+ it 'should return a hash with appropriate values' do
103
+ TwitterAuth.stub!(:remember_for).and_return(10)
104
+ time = Time.now
105
+ Time.stub!(:now).and_return(time)
106
+ ActiveSupport::SecureRandom.stub!(:hex).and_return('abcdef')
107
+
108
+ @user.remember_me.should == {:value => 'abcdef', :expires => (Time.now + 10.days)}
109
+ end
110
+ end
111
+ end
112
+
113
+ describe '#forget_me' do
114
+ it 'should reset remember_token and remember_token_expires_at' do
115
+ @user = Factory(:twitter_oauth_user, :remember_token => "abcdef", :remember_token_expires_at => Time.now + 10.days)
116
+ @user.forget_me
117
+ @user.reload
118
+ @user.remember_token.should be_nil
119
+ @user.remember_token_expires_at.should be_nil
120
+ end
121
+ end
122
+
123
+ describe '.from_remember_token' do
124
+ before do
125
+ @user = Factory(:twitter_oauth_user, :remember_token => 'abcdef', :remember_token_expires_at => (Time.now + 10.days))
126
+ end
127
+
128
+ it 'should find the user with the specified remember_token' do
129
+ User.from_remember_token('abcdef').should == @user
130
+ end
131
+
132
+ it 'should not find a user with an expired token' do
133
+ user2 = Factory(:twitter_oauth_user, :login => 'walker', :remember_token => 'ghijkl', :remember_token_expires_at => (Time.now - 10.days))
134
+ User.from_remember_token('ghijkl').should be_nil
135
+ end
136
+
137
+ it 'should not find a user with a nil token and an expiration' do
138
+ user = Factory(:twitter_oauth_user, :login => 'stranger', :remember_token => nil, :remember_token_expires_at => (Time.now + 10.days))
139
+ User.from_remember_token(nil).should be_nil
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,94 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe TwitterAuth::OauthUser do
4
+ before do
5
+ stub_oauth!
6
+ end
7
+
8
+ describe '.identify_or_create_from_access_token' do
9
+ before do
10
+ @token = OAuth::AccessToken.new(TwitterAuth.consumer, 'faketoken', 'fakesecret')
11
+ end
12
+
13
+ it 'should accept an OAuth::AccessToken' do
14
+ lambda{ User.identify_or_create_from_access_token(@token) }.should_not raise_error(ArgumentError)
15
+ end
16
+
17
+ it 'should accept two strings' do
18
+ lambda{ User.identify_or_create_from_access_token('faketoken', 'fakesecret') }.should_not raise_error(ArgumentError)
19
+ end
20
+
21
+ it 'should not accept one string' do
22
+ lambda{ User.identify_or_create_from_access_token('faketoken') }.should raise_error(ArgumentError, 'Must authenticate with an OAuth::AccessToken or the string access token and secret.')
23
+ end
24
+
25
+ it 'should make a call to verify_credentials' do
26
+ # this is in the before, just making it explicit
27
+ User.identify_or_create_from_access_token(@token)
28
+ end
29
+
30
+ it 'should try to find the user with that login' do
31
+ User.should_receive(:find_by_login).once.with('twitterman')
32
+ User.identify_or_create_from_access_token(@token)
33
+ end
34
+
35
+ it 'should return the user if he/she exists' do
36
+ user = Factory.create(:twitter_oauth_user, :login => 'twitterman')
37
+ user.reload
38
+ User.identify_or_create_from_access_token(@token).should == user
39
+ end
40
+
41
+ it 'should update the access_token and access_secret for the user if he/she exists' do
42
+ user = Factory.create(:twitter_oauth_user, :login => 'twitterman', :access_token => 'someothertoken', :access_secret => 'someothersecret')
43
+ User.identify_or_create_from_access_token(@token)
44
+ user.reload
45
+ user.access_token.should == @token.token
46
+ user.access_secret.should == @token.secret
47
+ end
48
+
49
+ it 'should update the user\'s attributes based on the twitter info' do
50
+ user = Factory.create(:twitter_oauth_user, :login => 'twitterman', :name => 'Not Twitter Man')
51
+ User.identify_or_create_from_access_token(@token).name.should == 'Twitter Man'
52
+ end
53
+
54
+ it 'should create a user if one does not exist' do
55
+ lambda{User.identify_or_create_from_access_token(@token)}.should change(User, :count).by(1)
56
+ end
57
+
58
+ it 'should assign the oauth access token and secret' do
59
+ user = User.identify_or_create_from_access_token(@token)
60
+ user.access_token.should == @token.token
61
+ user.access_secret.should == @token.secret
62
+ end
63
+ end
64
+
65
+ describe '#token' do
66
+ before do
67
+ @user = Factory.create(:twitter_oauth_user, :access_token => 'token', :access_secret => 'secret')
68
+ end
69
+
70
+ it 'should return an AccessToken' do
71
+ @user.token.should be_a(OAuth::AccessToken)
72
+ end
73
+
74
+ it "should use the user's access_token and secret" do
75
+ @user.token.token.should == @user.access_token
76
+ @user.token.secret.should == @user.access_secret
77
+ end
78
+ end
79
+
80
+ describe '#twitter' do
81
+ before do
82
+ @user = Factory.create(:twitter_oauth_user, :access_token => 'token', :access_secret => 'secret')
83
+ end
84
+
85
+ it 'should return a TwitterAuth::Dispatcher::Oauth' do
86
+ @user.twitter.should be_a(TwitterAuth::Dispatcher::Oauth)
87
+ end
88
+
89
+ it 'should use my token and secret' do
90
+ @user.twitter.token.should == @user.access_token
91
+ @user.twitter.secret.should == @user.access_secret
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,41 @@
1
+ ActiveRecord::Schema.define :version => 0 do
2
+ create_table :twitter_auth_users, :force => true do |t|
3
+ t.string :login
4
+
5
+ # OAuth fields
6
+ t.string :access_token
7
+ t.string :access_secret
8
+
9
+ # Basic fields
10
+ t.binary :crypted_password
11
+ t.string :salt
12
+
13
+ # Remember token fields
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.integer :friends_count
32
+ t.integer :statuses_count
33
+ t.integer :followers_count
34
+ t.integer :favourites_count
35
+ t.integer :utc_offset
36
+ t.string :time_zone
37
+
38
+ t.timestamps
39
+ end
40
+ end
41
+
@@ -0,0 +1 @@
1
+ --colour
@@ -0,0 +1,51 @@
1
+ begin
2
+ require File.dirname(__FILE__) + '/../../../../spec/spec_helper'
3
+ rescue LoadError
4
+ puts "You need to install rspec in your base app"
5
+ exit
6
+ end
7
+
8
+ require File.dirname(__FILE__) + '/../app/models/twitter_auth/generic_user'
9
+
10
+ class TwitterAuth::GenericUser
11
+ def self.table_name; 'twitter_auth_users' end
12
+ end
13
+
14
+ class User < TwitterAuth::GenericUser; end
15
+
16
+ require 'remarkable'
17
+ require File.dirname(__FILE__) + '/fixtures/factories'
18
+ require File.dirname(__FILE__) + '/fixtures/fakeweb'
19
+ require File.dirname(__FILE__) + '/fixtures/twitter'
20
+
21
+ plugin_spec_dir = File.dirname(__FILE__)
22
+ ActiveRecord::Base.logger = Logger.new(plugin_spec_dir + "/debug.log")
23
+
24
+ load(File.dirname(__FILE__) + '/schema.rb')
25
+
26
+ def define_basic_user_class!
27
+ TwitterAuth::GenericUser.send :include, TwitterAuth::BasicUser
28
+ end
29
+
30
+ def define_oauth_user_class!
31
+ TwitterAuth::GenericUser.send :include, TwitterAuth::OauthUser
32
+ end
33
+
34
+ def stub_oauth!
35
+ TwitterAuth.stub!(:config).and_return({
36
+ 'strategy' => 'oauth',
37
+ 'oauth_consumer_key' => 'testkey',
38
+ 'oauth_consumer_secret' => 'testsecret'
39
+ })
40
+ define_oauth_user_class!
41
+ end
42
+
43
+ def stub_basic!
44
+ TwitterAuth.stub!(:config).and_return({
45
+ 'strategy' => 'basic',
46
+ 'encryption_key' => 'secretcode'
47
+ })
48
+ define_basic_user_class!
49
+ end
50
+
51
+ define_oauth_user_class!
@@ -0,0 +1,51 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe TwitterAuth::Cryptify do
4
+ before do
5
+ stub_basic!
6
+ end
7
+
8
+ it 'should have encrypt and decrypt methods' do
9
+ TwitterAuth::Cryptify.should respond_to(:encrypt)
10
+ TwitterAuth::Cryptify.should respond_to(:decrypt)
11
+ end
12
+
13
+ describe '.encrypt' do
14
+ it 'should return a hash with :encrypted_data and :salt keys' do
15
+ result = TwitterAuth::Cryptify.encrypt('some string')
16
+ result.should be_a(Hash)
17
+ result.key?(:encrypted_data).should be_true
18
+ result.key?(:salt).should be_true
19
+ end
20
+
21
+ it 'should make a call to EzCrypto::Key.encrypt_with_password' do
22
+ EzCrypto::Key.should_receive(:encrypt_with_password).once.and_return('gobbledygook')
23
+ TwitterAuth::Cryptify.encrypt('some string')
24
+ end
25
+
26
+ it 'should not have the same encrypted as plaintext data' do
27
+ TwitterAuth::Cryptify.encrypt('some string')[:encrypted_data].should_not == 'some string'
28
+ end
29
+ end
30
+
31
+ describe '.decrypt' do
32
+ before do
33
+ @salt = TwitterAuth::Cryptify.generate_salt
34
+ TwitterAuth::Cryptify.stub!(:generate_salt).and_return(@salt)
35
+ @string = 'decrypted string'
36
+ @encrypted = TwitterAuth::Cryptify.encrypt(@string)
37
+ end
38
+
39
+ it 'should return the original string' do
40
+ TwitterAuth::Cryptify.decrypt(@encrypted).should == @string
41
+ end
42
+
43
+ it 'should raise an argument error if encrypted data is provided without a salt' do
44
+ lambda{TwitterAuth::Cryptify.decrypt('asodiaoie2')}.should raise_error(ArgumentError)
45
+ end
46
+
47
+ it 'should raise an argument error if a string or hash are not provided' do
48
+ lambda{TwitterAuth::Cryptify.decrypt(23)}.should raise_error(ArgumentError)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,83 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe TwitterAuth::Dispatcher::Basic do
4
+ before do
5
+ stub_basic!
6
+ @user = Factory.create(:twitter_basic_user, :login => 'twitterman', :password => 'test')
7
+ end
8
+
9
+ it 'should require a user as the initialization argument' do
10
+ lambda{TwitterAuth::Dispatcher::Basic.new(nil)}.should raise_error(TwitterAuth::Error, 'Dispatcher must be initialized with a User.')
11
+ end
12
+
13
+ it 'should store the user in an attr_accessor' do
14
+ TwitterAuth::Dispatcher::Basic.new(@user).user.should == @user
15
+ end
16
+
17
+ describe '#request' do
18
+ before do
19
+ @dispatcher = TwitterAuth::Dispatcher::Basic.new(@user)
20
+ FakeWeb.register_uri('https://twitter.com:443/fake.json', :string => {'fake' => true}.to_json)
21
+ FakeWeb.register_uri('https://twitter.com:443/fake.xml', :string => '<fake>true</fake>')
22
+ end
23
+
24
+ it 'should automatically parse JSON if valid' do
25
+ @dispatcher.request(:get, '/fake.json').should == {'fake' => true}
26
+ end
27
+
28
+ it 'should return XML as a string' do
29
+ @dispatcher.request(:get, '/fake.xml').should == "<fake>true</fake>"
30
+ end
31
+
32
+ it 'should append .json to the path if no extension is provided' do
33
+ @dispatcher.request(:get, '/fake.json').should == @dispatcher.request(:get, '/fake')
34
+ end
35
+
36
+ %w(get post put delete).each do |method|
37
+ it "should build a #{method} class based on a :#{method} http_method" do
38
+ @req = "Net::HTTP::#{method.capitalize}".constantize.new('/fake.json')
39
+ "Net::HTTP::#{method.capitalize}".constantize.should_receive(:new).and_return(@req)
40
+ @dispatcher.request(method.to_sym, '/fake')
41
+ end
42
+ end
43
+
44
+ it 'should start the HTTP session' do
45
+ @net = TwitterAuth.net
46
+ TwitterAuth.stub!(:net).and_return(@net)
47
+ @net.should_receive(:start)
48
+ lambda{@dispatcher.request(:get, '/fake')}.should raise_error(NoMethodError)
49
+ end
50
+
51
+ it "should raise a TwitterAuth::Dispatcher::Error if response code isn't 200" do
52
+ FakeWeb.register_uri('https://twitter.com:443/bad_response.json', :string => {'error' => 'bad response'}.to_json, :status => ['401', 'Unauthorized'])
53
+ lambda{@dispatcher.request(:get, '/bad_response')}.should raise_error(TwitterAuth::Dispatcher::Error)
54
+ end
55
+
56
+ it 'should set the error message to the JSON message' do
57
+ FakeWeb.register_uri('https://twitter.com:443/bad_response.json', :string => {'error' => 'bad response'}.to_json, :status => ['403', 'Forbidden'])
58
+ lambda{@dispatcher.request(:get, '/bad_response')}.should raise_error(TwitterAuth::Dispatcher::Error, 'bad response')
59
+ end
60
+
61
+ it 'should raise a TwitterAuth::Dispatcher::Unauthorized on 401' do
62
+ FakeWeb.register_uri('https://twitter.com:443/unauthenticated_response.xml', :string => "<hash>\n<request>/unauthenticated_response.xml</request>\n<error>bad response</error>\n</hash>", :status => ['401', 'Unauthorized'])
63
+ lambda{@dispatcher.request(:get, '/unauthenticated_response.xml')}.should raise_error(TwitterAuth::Dispatcher::Unauthorized)
64
+ end
65
+
66
+ it 'should set the error message to the XML message' do
67
+ FakeWeb.register_uri('https://twitter.com:443/bad_response.xml', :string => "<hash>\n<request>/bad_response.xml</request>\n<error>bad response</error>\n</hash>", :status => ['403', 'Forbidden'])
68
+ lambda{@dispatcher.request(:get, '/bad_response')}.should raise_error(TwitterAuth::Dispatcher::Error, 'bad response')
69
+ end
70
+ end
71
+
72
+ %w(get post delete put).each do |method|
73
+ it "should have a ##{method} method that calls request(:#{method})" do
74
+ dispatcher = TwitterAuth::Dispatcher::Basic.new(@user)
75
+ if %w(get delete).include?(method)
76
+ dispatcher.should_receive(:request).with(method.to_sym, '/fake.json')
77
+ else
78
+ dispatcher.should_receive(:request).with(method.to_sym, '/fake.json', '')
79
+ end
80
+ dispatcher.send(method, '/fake.json')
81
+ end
82
+ end
83
+ end