Merge pull request #35 from sickill/omniauth

User model and omniauth authentication
openid
Marcin Kulik 13 years ago
commit 3371a238b7

@ -6,6 +6,9 @@ gem 'mysql2'
gem 'tzinfo'
gem 'carrierwave'
gem 'confstruct'
gem 'omniauth'
gem 'omniauth-twitter'
gem 'omniauth-github'
# Gems used only for assets and not required
# in production environments by default.
@ -16,6 +19,7 @@ group :assets do
end
group :test, :development do
gem 'ruby-debug19', :require => false
gem "rspec-rails"
gem 'factory_girl_rails'
gem 'awesome_print', :require => 'ap'

@ -28,6 +28,8 @@ GEM
activesupport (3.2.1)
i18n (~> 0.6)
multi_json (~> 1.0)
addressable (2.2.7)
archive-tar-minitar (0.5.2)
arel (3.0.0)
awesome_print (0.4.0)
builder (3.0.0)
@ -49,6 +51,7 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.2.0)
columnize (0.3.2)
confstruct (0.2.1)
diff-lcs (1.1.3)
erubis (2.7.0)
@ -59,6 +62,10 @@ GEM
factory_girl_rails (1.6.0)
factory_girl (~> 2.5.0)
railties (>= 3.0.0)
faraday (0.7.6)
addressable (~> 2.2)
multipart-post (~> 1.1)
rack (~> 1.1)
ffi (1.0.11)
guard (1.0.0)
ffi (>= 0.5.0)
@ -69,6 +76,7 @@ GEM
multi_json
thor
haml (3.1.4)
hashie (1.2.0)
hike (1.2.1)
i18n (0.6.0)
jasmine (1.1.2)
@ -86,14 +94,35 @@ GEM
json (1.6.5)
json_pure (1.6.5)
libnotify (0.7.2)
linecache19 (0.5.12)
ruby_core_source (>= 0.1.4)
mail (2.4.1)
i18n (>= 0.4.0)
mime-types (~> 1.16)
treetop (~> 1.4.8)
mime-types (1.17.2)
multi_json (1.0.4)
multipart-post (1.1.5)
mysql2 (0.3.10)
nokogiri (1.5.0)
oauth (0.4.5)
oauth2 (0.5.2)
faraday (~> 0.7)
multi_json (~> 1.0)
omniauth (1.0.2)
hashie (~> 1.2)
rack
omniauth-github (1.0.1)
omniauth (~> 1.0)
omniauth-oauth2 (~> 1.0)
omniauth-oauth (1.0.0)
oauth
omniauth (~> 1.0)
omniauth-oauth2 (1.0.0)
oauth2 (~> 0.5.0)
omniauth (~> 1.0)
omniauth-twitter (0.0.8)
omniauth-oauth (~> 1.0)
polyglot (0.3.3)
rack (1.4.1)
rack-cache (1.1)
@ -133,6 +162,16 @@ GEM
activesupport (>= 3.0)
railties (>= 3.0)
rspec (~> 2.8.0)
ruby-debug-base19 (0.11.25)
columnize (>= 0.3.1)
linecache19 (>= 0.5.11)
ruby_core_source (>= 0.1.4)
ruby-debug19 (0.11.6)
columnize (>= 0.3.1)
linecache19 (>= 0.5.11)
ruby-debug-base19 (>= 0.11.19)
ruby_core_source (0.1.4)
archive-tar-minitar (>= 0.5.2)
rubyzip (0.9.5)
sass (3.1.15)
sass-rails (3.2.4)
@ -177,8 +216,12 @@ DEPENDENCIES
jquery-rails
libnotify
mysql2
omniauth
omniauth-github
omniauth-twitter
rails
rspec-rails
ruby-debug19
sass-rails
tzinfo
uglifier (>= 1.0.3)

@ -4,4 +4,20 @@ class ApplicationController < ActionController::Base
protect_from_forgery
rescue_from(ActiveRecord::RecordNotFound) { render 'exceptions/not_found' }
helper_method :current_user
def current_user
@current_user ||= User.first(:id => session[:user_id]) if session[:user_id]
end
def current_user=(user)
if user
@current_user = user
session[:user_id] = user.id
else
@current_user = nil
session[:user_id] = nil
end
end
end

@ -0,0 +1,27 @@
class SessionsController < ApplicationController
before_filter :load_omniauth_auth, :only => :create
def create
user = User.find_by_provider_and_uid(@auth["provider"], @auth["uid"]) ||
User.create_with_omniauth(@auth)
self.current_user = user
redirect_to root_url, :notice => "Signed in!"
end
def destroy
self.current_user = nil
redirect_to root_url, :notice => "Signed out!"
end
def failure
redirect_to root_url, :alert => params[:message]
end
private
def load_omniauth_auth
@auth = request.env["omniauth.auth"]
end
end

@ -1,2 +1,9 @@
module ApplicationHelper
def twitter_auth_path
"/auth/twitter"
end
def github_auth_path
"/auth/github"
end
end

@ -0,0 +1,17 @@
class User < ActiveRecord::Base
validate :provider, :presence => true
validate :uid, :presence => true
validate :nickname, :presence => true
def self.create_with_omniauth(auth)
create! do |user|
user.provider = auth["provider"]
user.uid = auth["uid"]
user.nickname = auth["info"]["nickname"]
user.name = auth["info"]["name"]
user.avatar_url = OauthHelper.get_avatar_url(auth)
end
end
end

@ -15,8 +15,12 @@
</div>
<div id="menu">
<ul class="links">
<li>Sign in</li>
<li>Sign up</li>
<% if current_user %>
<li><%= link_to "Sign out", logout_path %></li>
<% else %>
<li><%= link_to "Twitter Sign in", twitter_auth_path %></li>
<li><%= link_to "Github Sign in", github_auth_path %></li>
<% end %>
</ul>
</div>
</div>

@ -1,6 +1,17 @@
defaults: &defaults
carrierwave_storage: :fog
oauth:
twitter:
consumer_key: "qAFaJs7lyIm3xjtdtFaQ"
consumer_secret: "Ytqj4IfXmQ7gn0yWf2GgFUpFDAWfqYpVkX3P1ng0TU"
github:
consumer_key: "455d7cde838004d7d46a"
consumer_secret: "5e076dbe06e79f6a7e329a4c7b306c0a0c6055c1"
development:
<<: *defaults
carrierwave_storage: :file
test:
<<: *defaults
carrierwave_storage: :file

@ -24,7 +24,7 @@ module AsciiIo
# -- all .rb files in that directory are automatically loaded.
# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)
config.autoload_paths += %W(#{config.root}/lib)
# Only load the plugins named here, in the order given (default is alphabetical).
# :all can be used as a placeholder for all plugins not explicitly named.

@ -0,0 +1,4 @@
Rails.application.config.middleware.use OmniAuth::Builder do
provider :twitter, CFG.oauth.twitter.consumer_key, CFG.oauth.twitter.consumer_secret
provider :github, CFG.oauth.github.consumer_key, CFG.oauth.github.consumer_secret
end

@ -6,63 +6,10 @@ AsciiIo::Application.routes.draw do
resources :asciicasts
end
# The priority is based upon order of creation:
# first created -> highest priority.
match "/auth/:provider/callback" => "sessions#create"
match "/auth/failure" => "sessions#failure"
# Sample of regular route:
# match 'products/:id' => 'catalog#view'
# Keep in mind you can assign values other than :controller and :action
match "/logout" => "sessions#destroy"
# Sample of named route:
# match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
# This route can be invoked with purchase_url(:id => product.id)
# Sample resource route (maps HTTP verbs to controller actions automatically):
# resources :products
# Sample resource route with options:
# resources :products do
# member do
# get 'short'
# post 'toggle'
# end
#
# collection do
# get 'sold'
# end
# end
# Sample resource route with sub-resources:
# resources :products do
# resources :comments, :sales
# resource :seller
# end
# Sample resource route with more complex sub-resources
# resources :products do
# resources :comments
# resources :sales do
# get 'recent', :on => :collection
# end
# end
# Sample resource route within a namespace:
# namespace :admin do
# # Directs /admin/products/* to Admin::ProductsController
# # (app/controllers/admin/products_controller.rb)
# resources :products
# end
# You can have the root of your site routed with "root"
# just remember to delete public/index.html.
root :to => 'asciicasts#index'
# See how all your routes lay out with "rake routes"
# This is a legacy wild controller route that's not recommended for RESTful applications.
# Note: This route will make all actions in every controller accessible via GET requests.
# match ':controller(/:action(/:id(.:format)))'
# 404 for routing error (prevents engines' routes to work though)
# match "*a", :to => "exceptions#not_found"
end

@ -0,0 +1,15 @@
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :provider, :null => false
t.string :uid, :null => false
t.string :email
t.string :name
t.string :avatar_url
t.timestamps
end
add_index :users, [ :provider, :uid ], :unique => true
end
end

@ -0,0 +1,5 @@
class AddNicknameColumnToUsersTable < ActiveRecord::Migration
def change
add_column :users, :nickname, :string, :null => false
end
end

@ -11,7 +11,7 @@
#
# It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20120114195316) do
ActiveRecord::Schema.define(:version => 20120226184448) do
create_table "asciicasts", :force => true do |t|
t.integer "user_id"
@ -24,8 +24,8 @@ ActiveRecord::Schema.define(:version => 20120114195316) do
t.string "command"
t.string "shell"
t.string "uname"
t.datetime "created_at"
t.datetime "updated_at"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "stdin"
t.string "stdin_timing"
t.string "stdout"
@ -36,4 +36,17 @@ ActiveRecord::Schema.define(:version => 20120114195316) do
add_index "asciicasts", ["recorded_at"], :name => "index_asciicasts_on_recorded_at"
add_index "asciicasts", ["user_id"], :name => "index_asciicasts_on_user_id"
create_table "users", :force => true do |t|
t.string "provider", :null => false
t.string "uid", :null => false
t.string "email"
t.string "name"
t.string "avatar_url"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "nickname", :null => false
end
add_index "users", ["provider", "uid"], :name => "index_users_on_provider_and_uid", :unique => true
end

@ -0,0 +1,11 @@
class OauthHelper
def self.get_avatar_url(auth)
if auth["provider"] == "twitter"
auth["info"]["image"]
elsif auth["provider"] == "github"
auth["extra_info"]["avatar_url"]
end
end
end

@ -0,0 +1,87 @@
require 'spec_helper'
describe SessionsController do
describe "#create" do
let(:provider) { "twitter" }
let(:uid) { 1234 }
before do
OmniAuth.config.mock_auth[:twitter] = {
"provider" => provider,
"uid" => uid
}
request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:twitter]
end
context "user exists" do
before do
Factory(:user, :provider => provider, :uid => uid)
post :create
end
it "should create session" do
session[:user_id].should_not be_nil
@controller.current_user.should_not be_nil
end
it "should redirects user to root url" do
flash[:notice].should == "Signed in!"
should redirect_to(root_url)
end
end
context "user doesn't exist" do
let(:auth) { request.env["omniauth.auth"] }
let(:user) { stub("user", :id => 1) }
it "should call create_with_omniauth" do
User.should_receive(:create_with_omniauth).
with(auth).
and_return(user)
post :create
end
it "should login user" do
User.stub(:create_with_omniauth).and_return(user)
post :create
session[:user_id].should_not be_nil
end
end
end
describe "#destroy" do
before do
session[:user_id] = "123"
get :destroy
end
it "should destroy session" do
session[:user_id].should be_nil
@controller.current_user.should be_nil
end
it "should redirects to root_url" do
flash[:notice].should == "Signed out!"
should redirect_to(root_url)
end
end
describe "#failure" do
let(:message) { "something went wrong" }
before do
get :failure, :message => message
end
it "should redirects to root_url and set error message" do
flash[:alert].should == message
should redirect_to(root_url)
end
end
end

@ -0,0 +1,12 @@
# Read about factories at http://github.com/thoughtbot/factory_girl
FactoryGirl.define do
factory :user do
provider "twitter"
uid "1234"
nickname "mrFoo"
email nil
name nil
avatar_url nil
end
end

@ -0,0 +1,42 @@
require 'spec_helper'
describe OauthHelper do
describe ".avatar_url" do
let(:avatar_url) { "http://foo.bar/foo.png" }
context "when github auth" do
let(:auth) do
{
"provider" => "github",
"extra_info" => {
"avatar_url" => avatar_url
}
}
end
it { OauthHelper.get_avatar_url(auth).should == avatar_url}
end
context "when twitter auth" do
let(:auth) do
{
"provider" => "twitter",
"info" => {
"image" => avatar_url
}
}
end
it { OauthHelper.get_avatar_url(auth).should == avatar_url}
end
context "when other provider" do
let(:auth) do
{ "provider" => "other" }
end
it { OauthHelper.get_avatar_url(auth).should be_nil }
end
end
end

@ -0,0 +1,50 @@
require 'spec_helper'
describe User do
let(:user) { Factory.build(:user) }
it "has valid factory" do
Factory.build(:user).should be_valid
end
describe ".create_with_omniauth" do
let(:uid) { "123" }
let(:provider) { "twitter" }
let(:nickname) { "foo" }
let(:name) { "Foo Bar" }
let(:auth) do
{
"provider" => provider,
"uid" => uid,
"info" => {
"name" => name,
"nickname" => nickname }
}
end
it "creates user with valid attributes" do
user = User.create_with_omniauth(auth)
user.provider.should == provider
user.uid.should == uid
user.nickname.should == nickname
user.name.should == name
user.avatar_url.should be_nil
end
context "when avatar available" do
let(:avatar_url) { "http://foo.bar/avatar.jpg"}
before do
OauthHelper.stub(:get_avatar_url).and_return(avatar_url)
end
it "assigns avatar_url" do
user = User.create_with_omniauth(auth)
user.avatar_url.should == avatar_url
end
end
end
end

@ -1,4 +1,3 @@
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
@ -9,25 +8,9 @@ require 'rspec/autorun'
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
# == Mock Framework
#
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
#
# config.mock_with :mocha
# config.mock_with :flexmock
# config.mock_with :rr
config.mock_with :rspec
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
# config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.use_transactional_fixtures = true
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
# config.use_transactional_fixtures = true
# If true, the base class of anonymous controllers will be inferred
# automatically. This will be the default behavior in future versions of
# rspec-rails.
config.infer_base_class_for_anonymous_controllers = false
end

Loading…
Cancel
Save