Skip to content

Commit

Permalink
Basic auth stuff is in.
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Bleigh committed Mar 19, 2009
1 parent 52bb18e commit c3dd6d2
Show file tree
Hide file tree
Showing 10 changed files with 222 additions and 10 deletions.
12 changes: 11 additions & 1 deletion app/controllers/sessions_controller.rb
Expand Up @@ -9,7 +9,17 @@ def new
url << "&oauth_callback=#{CGI.escape(TwitterAuth.oauth_callback)}" if TwitterAuth.oauth_callback?
redirect_to url
else

# we don't have to do anything, it's just a simple form for HTTP basic!
end
end

def create
logout_keeping_session!
if user = User.authenticate(params[:login], params[:password])
self.current_user = user
authentication_succeeded and return
else
authentication_failed('Unable to verify your credentials through Twitter. Please try again.', '/login') and return
end
end

Expand Down
53 changes: 53 additions & 0 deletions app/models/twitter_auth/basic_user.rb
@@ -1,9 +1,62 @@
require 'net/http'

module TwitterAuth
module BasicUser
def self.included(base)
base.class_eval do
attr_protected :crypted_password, :salt
end

base.extend TwitterAuth::BasicUser::ClassMethods
end

module ClassMethods
def authenticate(login, password)
Twitter::Base.new(login, password).verify_credentials

user = find_by_login(login)
rescue Twitter::CantConnect
nil
end

def verify_credentials(login, password)
uri = URI.parse(TwitterAuth.base_url)
net = Net::HTTP.new(uri.host, uri.port)
net.use_ssl = TwitterAuth.base_url.match(/\Ahttps/)
response = net.start { |http|
request = Net::HTTP::Get.new('/account/verify_credentials.json')
request.basic_auth login, password
http.request(request)
}
if response.code == '200'
JSON.parse(response.body)
else
false
end
end

def authorize(login, password)
if twitter_hash = verify_credentials(login, password)
user = identify_or_create_from_twitter_hash_and_password(twitter_hash, password)
user
else
nil
end
end

def identify_or_create_from_twitter_hash_and_password(twitter_hash, password)
if user = User.find_by_login(twitter_hash['screen_name'])
user.assign_twitter_attributes(twitter_hash)
user.password = password
user.save
user
else
user = User.new_from_twitter_hash(twitter_hash)
user.password = password
user.save
user
end
end
end

def password=(new_password)
Expand Down
17 changes: 17 additions & 0 deletions app/views/sessions/_login_form.html.erb
@@ -0,0 +1,17 @@
<% form_tag session_path, :id => 'login_form' do %>
<div class='field'>
<label for='login'>Twitter Username:</label>
<%= text_field_tag 'login', nil, :class => 'text_field' %>
</div>
<div class='field'>
<label for='password'>Password:</label>
<%= password_field_tag 'password', nil, :class => 'password_field' %>
</div>
<!--<div class='checkbox-field'>
<%= check_box_tag 'remember_me' %> <label for='remember_me'>Keep Me Logged In</label>
</div>-->
<div class='field submit'>
<%= submit_tag 'Log In', :class => 'submit' %>
</div>
<% end %>

5 changes: 5 additions & 0 deletions app/views/sessions/new.html.erb
@@ -0,0 +1,5 @@
<h1>Log In Via Twitter</h1>

<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>

<%= render :partial => 'login_form' %>
2 changes: 1 addition & 1 deletion generators/twitter_auth/templates/user.rb
@@ -1,5 +1,5 @@
class User < TwitterAuth::GenericUser
# Extend and define your user model as you see fit.
# All of the authentication logic is handled by the
# parent TwitterAuth user class.
# parent TwitterAuth::GenericUser class.
end
13 changes: 9 additions & 4 deletions lib/twitter_auth/controller_extensions.rb
Expand Up @@ -9,20 +9,25 @@ def self.included(base)

protected

def authentication_failed(message)
def authentication_failed(message, destination='/')
flash[:error] = message
redirect_to '/'
redirect_to destination
end

def authentication_succeeded(message = 'You have logged in successfully.')
def authentication_succeeded(message = 'You have logged in successfully.', destination = '/')
flash[:notice] = message
redirect_to '/'
redirect_to destination
end

def current_user
@current_user ||= User.find_by_id(session[:user_id])
end

def current_user=(new_user)
session[:user_id] = new_user.id
@current_user = new_user
end

def authorized?
!!current_user
end
Expand Down
2 changes: 1 addition & 1 deletion rails/init.rb
@@ -1,7 +1,7 @@
# Gem Dependencies
config.gem 'oauth'
config.gem 'ezcrypto'
config.gem 'httparty'
config.gem 'twitter'

require 'json'
require 'twitter_auth'
Expand Down
54 changes: 54 additions & 0 deletions spec/controllers/sessions_controller_spec.rb
@@ -1,6 +1,8 @@
require File.dirname(__FILE__) + '/../spec_helper'

describe SessionsController do
integrate_views

describe 'routes' do
it 'should route /session/new to SessionsController#new' do
params_from(:get, '/session/new').should == {:controller => 'sessions', :action => 'new'}
Expand All @@ -21,6 +23,10 @@
it 'should route /oauth_callback to SessionsController#oauth_callback' do
params_from(:get, '/oauth_callback').should == {:controller => 'sessions', :action => 'oauth_callback'}
end

it 'should route POST /session to SessionsController#create' do
params_from(:post, '/session').should == {:controller => 'sessions', :action => 'create'}
end
end

describe 'with OAuth strategy' do
Expand Down Expand Up @@ -138,6 +144,54 @@
end
end

describe 'with Basic strategy' do
before do
stub_basic!
end

describe '#new' do
it 'should render the new action' do
get :new
response.should render_template('sessions/new')
end

it 'should render the login form' do
get :new
response.should have_tag('form[action=/session][id=login_form][method=post]')
end

describe '#create' do
before do
@user = Factory.create(:twitter_basic_user)
end

it 'should call logout_keeping_session! to remove session info' do
controller.should_receive(:logout_keeping_session!)
post :create
end

it 'should try to authenticate the user' do
User.should_receive(:authenticate)
post :create
end

it 'should call authentication_failed on authenticate failure' do
User.should_receive(:authenticate).and_return(nil)
post :create, :login => 'wrong', :password => 'false'
response.should redirect_to('/login')
end

it 'should call authentication_succeeded on authentication success' do
User.should_receive(:authenticate).and_return(@user)
post :create, :login => 'twitterman', :password => 'cool'
response.should redirect_to('/')
flash[:notice].should_not be_blank
end
end
end

end

describe '#destroy' do
it 'should call logout_keeping_session!' do
controller.should_receive(:logout_keeping_session!).once
Expand Down
6 changes: 3 additions & 3 deletions spec/fixtures/factories.rb
Expand Up @@ -10,9 +10,9 @@
end

Factory.define(:twitter_basic_user, :class => User) do |u|
u.login 'tweetkid'
u.login 'twitterman'
u.password 'test'

u.name 'Tweet Kid'
u.description 'Twitter Man\'s trusty sidekick.'
u.name 'Twitter Man'
u.description 'Saving the world for all Twitter kind.'
end
68 changes: 68 additions & 0 deletions spec/models/twitter_auth/basic_user_spec.rb
Expand Up @@ -37,4 +37,72 @@
@user['password'].should_not == 'monkey'
end
end

describe '.verify_credentials' do
before do
@user = Factory.create(:twitter_basic_user)
end

it 'should return a JSON hash of the user when successful' do
hash = User.verify_credentials('twitterman','test')
hash.should be_a(Hash)
hash['screen_name'].should == 'twitterman'
hash['name'].should == 'Twitter Man'
end

it 'should return false when a 401 unauthorized happens' do
FakeWeb.register_uri(:get, 'https://twitter.com:443/account/verify_credentials.json', :string => '401 "Unauthorized"', :status => ['401',' Unauthorized'])
User.verify_credentials('twitterman','wrong').should be_false
end
end

describe '.authorize' do
before do
@user = Factory.create(:twitter_basic_user)
end

it 'should make a call to verify_credentials' do
User.should_receive(:verify_credentials).with('twitterman','test')
User.authorize('twitterman','test')
end

it 'should return nil if verify_credentials returns false' do
User.stub!(:verify_credentials).and_return(false)
User.authorize('twitterman','test').should be_nil
end

it 'should return the user if verify_credentials succeeds' do
User.stub!(:verify_credentials).and_return(JSON.parse("{\"profile_image_url\":\"http:\\/\\/static.twitter.com\\/images\\/default_profile_normal.png\",\"description\":\"Saving the world for all Twitter kind.\",\"utc_offset\":null,\"favourites_count\":0,\"profile_sidebar_fill_color\":\"e0ff92\",\"screen_name\":\"twitterman\",\"statuses_count\":0,\"profile_background_tile\":false,\"profile_sidebar_border_color\":\"87bc44\",\"friends_count\":2,\"url\":null,\"name\":\"Twitter Man\",\"time_zone\":null,\"protected\":false,\"profile_background_image_url\":\"http:\\/\\/static.twitter.com\\/images\\/themes\\/theme1\\/bg.gif\",\"profile_background_color\":\"9ae4e8\",\"created_at\":\"Fri Feb 06 18:10:32 +0000 2009\",\"profile_text_color\":\"000000\",\"followers_count\":2,\"location\":null,\"id\":20256865,\"profile_link_color\":\"0000ff\"}"))
User.authorize('twitterman','test').should == @user
end
end

describe '.find_or_create_by_twitter_hash_and_password' do
before do
@user = Factory.create(:twitter_basic_user)
end

it 'should return the existing user if there is one' do
User.identify_or_create_from_twitter_hash_and_password({'screen_name' => 'twitterman'},'test').should == @user
end

it 'should update the attributes from the hash' do
User.identify_or_create_from_twitter_hash_and_password({'screen_name' => 'twitterman', 'name' => 'New Name'}, 'test').name.should == 'New Name'
end

it 'should update the password from the argument' do
User.identify_or_create_from_twitter_hash_and_password({'screen_name' => 'twitterman', 'name' => 'New Name'}, 'test2').password.should == 'test2'
end

it 'should create a user if one does not exist' do
lambda{User.identify_or_create_from_twitter_hash_and_password({'screen_name' => 'dude', 'name' => "Lebowski"}, 'test')}.should change(User, :count).by(1)
end

it 'should assign the attributes from the hash to a created user' do
user = User.identify_or_create_from_twitter_hash_and_password({'screen_name' => 'dude', 'name' => "Lebowski"}, 'test')
user.login.should == 'dude'
user.name.should == 'Lebowski'
user.password.should == 'test'
end
end
end

0 comments on commit c3dd6d2

Please sign in to comment.