mbleigh-twitter-auth 0.1.8 → 0.1.10

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -76,6 +76,10 @@ TwitterAuth provides some default controller methods that may be overridden in y
76
76
  * `authentication_succeeded(message=default)`: called when Twitter authorization has completed successfully. By default, simply redirects to the site root and sets the `flash[:notice]`.
77
77
  * `access_denied`: what happens when the `login_required` before filter fails. By default it stores the current location to return to and redirects to the login process.
78
78
 
79
+ Resources
80
+ ---------
81
+
82
+ * **Bug Reports:** See the [Lighthouse Project](http://mbleigh.lighthouseapp.com/projects/27783-twitterauth) to report any problems you have using TwitterAuth.
79
83
 
80
84
  Copyright
81
85
  ---------
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :minor: 1
3
- :patch: 8
3
+ :patch: 10
4
4
  :major: 0
@@ -45,6 +45,8 @@ class SessionsController < ApplicationController
45
45
 
46
46
  session[:user_id] = @user.id
47
47
 
48
+ cookies[:remember_token] = @user.remember_me
49
+
48
50
  authentication_succeeded
49
51
  rescue Net::HTTPServerException => e
50
52
  case e.message
@@ -1,7 +1,7 @@
1
1
  module TwitterAuth
2
2
  class GenericUser < ActiveRecord::Base
3
- attr_protected :login
4
-
3
+ attr_protected :login, :remember_token, :remember_token_expires_at
4
+
5
5
  TWITTER_ATTRIBUTES = [
6
6
  :name,
7
7
  :location,
@@ -26,6 +26,7 @@ module TwitterAuth
26
26
  validates_format_of :login, :with => /\A[a-z0-9_]+\z/i
27
27
  validates_length_of :login, :in => 1..15
28
28
  validates_uniqueness_of :login, :case_sensitive => false
29
+ validates_uniqueness_of :remember_token, :allow_blank => true
29
30
 
30
31
  def self.table_name; 'users' end
31
32
 
@@ -41,6 +42,10 @@ module TwitterAuth
41
42
 
42
43
  user
43
44
  end
45
+
46
+ def self.from_remember_token(token)
47
+ first(:conditions => ["remember_token = ? AND remember_token_expires_at > ?", token, Time.now])
48
+ end
44
49
 
45
50
  def assign_twitter_attributes(hash)
46
51
  TWITTER_ATTRIBUTES.each do |att|
@@ -66,5 +71,21 @@ module TwitterAuth
66
71
  TwitterAuth::Dispatcher::Basic.new(self)
67
72
  end
68
73
  end
74
+
75
+ def remember_me
76
+ return false unless respond_to?(:remember_token)
77
+
78
+ self.remember_token = ActiveSupport::SecureRandom.hex(10)
79
+ self.remember_token_expires_at = Time.now + TwitterAuth.remember_for.days
80
+
81
+ save
82
+
83
+ {:value => self.remember_token, :expires => self.remember_token_expires_at}
84
+ end
85
+
86
+ def forget_me
87
+ self.remember_token = self.remember_token_expires_at = nil
88
+ self.save
89
+ end
69
90
  end
70
91
  end
@@ -10,6 +10,9 @@ class TwitterAuthMigration < ActiveRecord::Migration
10
10
  t.string :salt
11
11
  <% end -%>
12
12
 
13
+ t.string :remember_token
14
+ t.datetime :remember_token_expires_at
15
+
13
16
  # This information is automatically kept
14
17
  # in-sync at each login of the user. You
15
18
  # may remove any/all of these columns.
@@ -1,24 +1,27 @@
1
1
  <% if options[:oauth] -%>
2
2
  development:
3
3
  strategy: oauth
4
- base_url: "https://twitter.com"
5
- api_timeout: 10
6
4
  oauth_consumer_key: devkey
7
5
  oauth_consumer_secret: devsecret
6
+ base_url: "https://twitter.com"
7
+ api_timeout: 10
8
+ remember_for: 14 # days
8
9
  oauth_callback: "http://localhost:3000/oauth_callback"
9
10
  test:
10
11
  strategy: oauth
11
- base_url: "https://twitter.com"
12
- api_timeout: 10
13
12
  oauth_consumer_key: testkey
14
13
  oauth_consumer_secret: testsecret
14
+ base_url: "https://twitter.com"
15
+ api_timeout: 10
16
+ remember_for: 14 # days
15
17
  oauth_callback: "http://localhost:3000/oauth_callback"
16
18
  production:
17
19
  strategy: oauth
18
- api_timeout: 10
19
- base_url: "https://twitter.com"
20
20
  oauth_consumer_key: prodkey
21
21
  oauth_consumer_secret: prodsecret
22
+ base_url: "https://twitter.com"
23
+ api_timeout: 10
24
+ remember_for: 14 # days
22
25
  <% else -%>
23
26
  development:
24
27
  strategy: basic
@@ -26,13 +29,16 @@ development:
26
29
  base_url: "https://twitter.com"
27
30
  # randomly generated key for encrypting Twitter passwords
28
31
  encryption_key: "<%= key = ActiveSupport::SecureRandom.hex(12) %>"
32
+ remember_for: 14 # days
29
33
  test:
30
34
  strategy: basic
31
35
  api_timeout: 10
32
36
  base_url: "https://twitter.com"
33
37
  encryption_key: "<%= key %>"
38
+ remember_for: 14 # days
34
39
  production:
35
40
  strategy: basic
36
41
  api_timeout: 10
37
42
  encryption_key: "<%= key %>"
43
+ remember_for: 14 # days
38
44
  <% end %>
@@ -20,7 +20,7 @@ module TwitterAuth
20
20
  end
21
21
 
22
22
  def current_user
23
- @current_user ||= User.find_by_id(session[:user_id])
23
+ @current_user ||= User.find_by_id(session[:user_id]) || User.from_remember_token(cookies[:remember_token])
24
24
  end
25
25
 
26
26
  def current_user=(new_user)
@@ -57,6 +57,7 @@ module TwitterAuth
57
57
  def logout_keeping_session!
58
58
  @current_user = nil
59
59
  session[:user_id] = nil
60
+ cookies.delete(:remember_token)
60
61
  end
61
62
  end
62
63
  end
data/lib/twitter_auth.rb CHANGED
@@ -27,6 +27,10 @@ module TwitterAuth
27
27
  config['oauth_callback']
28
28
  end
29
29
 
30
+ def self.remember_for
31
+ (config['remember_for'] || 14).to_i
32
+ end
33
+
30
34
  # The authentication strategy employed by this
31
35
  # application. Set in +config/twitter.yml+ as
32
36
  # strategy; valid options are oauth or basic.
@@ -35,9 +35,18 @@ class TwitterAuthTestController < ApplicationController
35
35
  logout_keeping_session!
36
36
  redirect_back_or_default('/')
37
37
  end
38
+
39
+ def current_user_action
40
+ @user = current_user
41
+ render :nothing => true
42
+ end
38
43
  end
39
44
 
40
45
  describe TwitterAuthTestController do
46
+ before do
47
+ controller.stub!(:cookies).and_return({})
48
+ end
49
+
41
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|
42
51
  it "should respond to the extension method '#{m}'" do
43
52
  controller.should respond_to(m)
@@ -72,19 +81,21 @@ describe TwitterAuthTestController do
72
81
  it 'should find the user based on the session user_id' do
73
82
  user = Factory.create(:twitter_oauth_user)
74
83
  request.session[:user_id] = user.id
75
- controller.send(:current_user).should == user
84
+ get(:current_user_action)
85
+ assigns[:user].should == user
76
86
  end
77
87
 
78
- it 'should return nil if there is no user matching that id' do
79
- request.session[:user_id] = 2345
80
- controller.send(:current_user).should be_nil
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
81
93
  end
82
94
 
83
- it 'should memoize the result (and not do a double find)' do
84
- user = Factory.create(:twitter_oauth_user)
85
- User.should_receive(:find_by_id).once.and_return(user)
86
- controller.send(:current_user).should == user
87
- controller.send(:current_user).should == user
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
88
99
  end
89
100
  end
90
101
 
@@ -142,5 +153,10 @@ describe TwitterAuthTestController do
142
153
  get :logout_keeping_session_action
143
154
  controller.send(:current_user).should be_nil
144
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
145
161
  end
146
162
  end
@@ -82,6 +82,12 @@ describe SessionsController do
82
82
  describe 'with proper info' do
83
83
  before do
84
84
  @user = Factory.create(:twitter_oauth_user)
85
+ @time = Time.now
86
+ @remember_token = ActiveSupport::SecureRandom.hex(10)
87
+
88
+ Time.stub!(:now).and_return(@time)
89
+ ActiveSupport::SecureRandom.stub!(:hex).and_return(@remember_token)
90
+
85
91
  request.session[:request_token] = 'faketoken'
86
92
  request.session[:request_token_secret] = 'faketokensecret'
87
93
  get :oauth_callback, :oauth_token => 'faketoken'
@@ -116,6 +122,15 @@ describe SessionsController do
116
122
  it "should assign the user id to the session" do
117
123
  session[:user_id].should == @user.id
118
124
  end
125
+
126
+ it "should call remember me" do
127
+ @user.reload
128
+ @user.remember_token.should == @remember_token
129
+ end
130
+
131
+ it "should set a cookie" do
132
+ cookies[:remember_token].should == @remember_token
133
+ end
119
134
  end
120
135
 
121
136
  describe "when OAuth doesn't work" do
@@ -5,12 +5,17 @@ describe TwitterAuth::GenericUser do
5
5
  should_validate_format_of :login, 'some_guy', 'awesome', 'cool_man'
6
6
  should_not_validate_format_of :login, 'with-dashes', 'with.periods', 'with spaces'
7
7
  should_validate_length_of :login, :in => 1..15
8
-
8
+
9
9
  it 'should validate uniqueness of login' do
10
10
  Factory.create(:twitter_oauth_user)
11
11
  Factory.build(:twitter_oauth_user).should have_at_least(1).errors_on(:login)
12
12
  end
13
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
+
14
19
  it 'should allow capital letters in the username' do
15
20
  Factory.build(:twitter_oauth_user, :login => 'TwitterMan').should have(:no).errors_on(:login)
16
21
  end
@@ -54,4 +59,84 @@ describe TwitterAuth::GenericUser do
54
59
  lambda{user.update_twitter_attributes({'name' => 'Twitter Man', 'description' => 'Works.', 'whoopsy' => 'noworks.'})}.should_not raise_error
55
60
  end
56
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
57
142
  end
data/spec/schema.rb CHANGED
@@ -10,6 +10,10 @@ ActiveRecord::Schema.define :version => 0 do
10
10
  t.binary :crypted_password
11
11
  t.string :salt
12
12
 
13
+ # Remember token fields
14
+ t.string :remember_token
15
+ t.datetime :remember_token_expires_at
16
+
13
17
  # This information is automatically kept
14
18
  # in-sync at each login of the user. You
15
19
  # may remove any/all of these columns.
@@ -13,7 +13,7 @@ describe TwitterAuth do
13
13
  end
14
14
  end
15
15
 
16
- describe '#api_timeout' do
16
+ describe '.api_timeout' do
17
17
  it 'should default to 10' do
18
18
  TwitterAuth.stub!(:config).and_return({})
19
19
  TwitterAuth.api_timeout.should == 10
@@ -25,6 +25,18 @@ describe TwitterAuth do
25
25
  end
26
26
  end
27
27
 
28
+ describe '.remember_for' do
29
+ it 'should default to 14' do
30
+ TwitterAuth.stub!(:config).and_return({})
31
+ TwitterAuth.remember_for.should == 14
32
+ end
33
+
34
+ it 'should be settable via config' do
35
+ TwitterAuth.stub!(:config).and_return({'remember_for' => '7'})
36
+ TwitterAuth.remember_for.should == 7
37
+ end
38
+ end
39
+
28
40
  describe '.net' do
29
41
  before do
30
42
  stub_basic!
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mbleigh-twitter-auth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.1.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Bleigh