Skip to content

Commit

Permalink
A lot of breaking changes to routing, refs socialcast#23
Browse files Browse the repository at this point in the history
This commit removes the rails engine routes and introduces a
devise_oauth_for helper which constructs the necessary routes.

Benefits of this change:

* Can now have more than one oauth endpoint per app
* Can now safely override controllers
* All current tests are now passing!

Although there are tests for some aspects of the new routing system,
they're not 100% comprehensive and don't cover the authenticate_scope! method
  • Loading branch information
BRMatt committed Dec 4, 2011
1 parent 4d083d1 commit aa89aab
Show file tree
Hide file tree
Showing 14 changed files with 268 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
module Devise
module Oauth2Providable
class AuthorizationsController < ApplicationController
# If the devise internal helpers aren't loaded in the controller then it
# has trouble resolving scope on the DeviseHelper module
include ::Devise::Controllers::InternalHelpers
include Devise::Oauth2Providable::Controllers::Helpers
before_filter :authenticate_scope!

Expand Down
39 changes: 24 additions & 15 deletions app/controllers/devise/oauth2_providable/tokens_controller.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
class Devise::Oauth2Providable::TokensController < ApplicationController
include Devise::Oauth2Providable::Controllers::Helpers
before_filter :authenticate_scope!
skip_before_filter :verify_authenticity_token, :only => :create
module Devise
module Oauth2Providable
class TokensController < ApplicationController
# If the devise internal helpers aren't loaded in the controller then it
# has trouble resolving scope on the DeviseHelper module
include ::Devise::Controllers::InternalHelpers
include Devise::Oauth2Providable::Controllers::Helpers

def create
@refresh_token = oauth2_current_refresh_token || oauth2_current_client.refresh_tokens.create!(:user => self.resource)
@access_token = @refresh_token.access_tokens.create!(:client => oauth2_current_client, :user => self.resource)
render :json => @access_token.token_response
end
private
def oauth2_current_client
env[Devise::Oauth2Providable::CLIENT_ENV_REF]
end
def oauth2_current_refresh_token
env[Devise::Oauth2Providable::REFRESH_TOKEN_ENV_REF]
before_filter :authenticate_scope!
skip_before_filter :verify_authenticity_token, :only => :create

def create
@refresh_token = oauth2_current_refresh_token || oauth2_current_client.refresh_tokens.create!(:user => self.resource)
@access_token = @refresh_token.access_tokens.create!(:client => oauth2_current_client, :user => self.resource)
render :json => @access_token.token_response
end
private
def oauth2_current_client
env[Devise::Oauth2Providable::CLIENT_ENV_REF]
end
def oauth2_current_refresh_token
env[Devise::Oauth2Providable::REFRESH_TOKEN_ENV_REF]
end
end
end
end

7 changes: 0 additions & 7 deletions config/routes.rb

This file was deleted.

20 changes: 6 additions & 14 deletions lib/devise/oauth2_providable/controllers/helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,13 @@ module Devise
module Oauth2Providable
module Controllers
module Helpers
extend ActiveSupport::Concern

module ClassMethods
include Devise::Controllers::InternalHelpers
include LocalInstanceMethods
end

module LocalInstanceMethods
# Authenticates the current scope and gets the current resource from the session.
# Taken from devise
def authenticate_scope!
send(:"authenticate_#{resource_name}!", :force => true)
self.resource = send(:"current_#{resource_name}")
end
end
# Authenticates the current scope and gets the current resource from the session.
# Taken from devise
def authenticate_scope!
send(:"authenticate_#{resource_name}!", :force => true)
self.resource = send(:"current_#{resource_name}")
end
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/devise/oauth2_providable/expirable_token.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module ExpirableToken
module ClassMethods
def expires_according_to(config_name)
cattr_accessor :default_lifetime
self.default_lifetime = Rails.application.config.devise_oauth2_providable[config_name]
self.default_lifetime = ::Rails.application.config.devise_oauth2_providable[config_name]

belongs_to :user
belongs_to :client
Expand Down
48 changes: 48 additions & 0 deletions lib/devise/oauth2_providable/mapping.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@

module Devise
module Oauth2Providable
# Responsible for mapping oauth endpoints onto devise scopes
#
# You must declare your devise scope before
#
# map.devise_oauth_for :users
#
# mapping = Devise::Oauth2Providable.mappings[:user]
#
# mapping.scope_name #=> :user
# # The name of the devise scope that this endpoint will use
#
# mapping.devise_scope #=> Devise.mappings[:user]
# # Returns the devise scope associated with this mapping
#
# mapping.prefix
class Mapping
attr_reader :scope_name, :path_prefix, :controllers

class << self
def default_controllers
{
:authorizations => "devise/oauth2_providable/authorizations",
:tokens => "devise/oauth2_providable/tokens"
}
end
end

def initialize(scope_name, options = {})
@scope_name = (options[:scope_name] || scope_name.to_s.singularize).to_sym
@path_prefix = options[:path_prefix]
@controllers = self.select_controllers(options)
end

# Returns the devise scope mapping object associated with this oauth endpoint
def devise_scope
Devise.mappings[self.scope_name]
end

protected
def select_controllers(options)
self.class.default_controllers.merge(options[:controllers] || {})
end
end
end
end
46 changes: 46 additions & 0 deletions lib/devise/oauth2_providable/rails/routes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

module Devise
module Oauth2Providable
module Rails
module Routes
def devise_oauth_for(scope, options = {})
mapping = Devise::Oauth2Providable.add_mapping(scope, options)

path_prefix = mapping.path_prefix
as = mapping.scope_name
constraints = {}
defaults = {}

devise_scope mapping.scope_name do
scope(:as => "#{as}_oauth", :path => path_prefix) do
devise_oauth_authorization_routes(mapping, mapping.controllers)
devise_oauth_token_routes(mapping, mapping.controllers)
end
end
end

protected
def devise_oauth_authorization_routes(mapping, controllers)
controller = controllers[:authorizations]

root :controller => controller, :action => 'new'

resources :authorizations, :only => :create,
:controller => controller

match 'authorize', :controller => controller, :action => 'new'
end

def devise_oauth_token_routes(mapping, controllers)
controller = controllers[:tokens]

resource :token, :only => :create, :controller => controller
end
end
end
end
end

ActionDispatch::Routing::Mapper.class_eval do
include Devise::Oauth2Providable::Rails::Routes
end
13 changes: 13 additions & 0 deletions lib/devise_oauth2_providable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,26 @@
require 'devise/oauth2_providable/models/oauth2_password_grantable'
require 'devise/oauth2_providable/models/oauth2_refresh_token_grantable'
require 'devise/oauth2_providable/models/oauth2_authorization_code_grantable'
require 'devise/oauth2_providable/controllers/helpers'
require 'devise/oauth2_providable/mapping'
require 'devise/oauth2_providable/rails/routes'

module Devise
module Oauth2Providable
CLIENT_ENV_REF = 'oauth2.client'
REFRESH_TOKEN_ENV_REF = "oauth2.refresh_token"

mattr_reader :mappings
@@mappings = ActiveSupport::OrderedHash.new

class << self
def add_mapping(name, options)
mapping = Devise::Oauth2Providable::Mapping.new(name, options)

@@mappings[mapping.scope_name] = mapping
mapping
end

def random_id
SecureRandom.hex
end
Expand Down
4 changes: 1 addition & 3 deletions spec/dummy/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,5 @@

resources :protected

devise_scope :user do
mount Devise::Oauth2Providable::Engine => '/oauth2'
end
devise_oauth_for :users, :path_prefix => 'oauth2'
end
66 changes: 66 additions & 0 deletions spec/lib/devise/oauth2_providable/mapping_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
require 'spec_helper'

describe Devise::Oauth2Providable::Mapping do

let (:mapping_class) { Devise::Oauth2Providable::Mapping }

describe "devise_oauth_for" do


describe "#scope_name" do
it "is the singular version of the name provided in initializer" do
mapping = mapping_class.new(:users)

mapping.scope_name.should == :user
end

it "can be overriden by the :scope_name option" do
mapping = mapping_class.new(:users, :scope_name => 'member')

mapping.scope_name.should == :member
end
end

describe "devise_scope" do
let (:devise_scope) { stub() }
it "returns the devise mapping object" do
Devise.mappings.should_receive(:[]).with(:user).and_return(devise_scope)

mapping = mapping_class.new(:users)

mapping.devise_scope.should eql(devise_scope)
end
end

describe "path_prefix" do
context "is specified" do
subject { mapping = mapping_class.new(:users, {:path_prefix => "member"}) }

it "is set in the mapping object" do
subject.path_prefix.should eql("member")
end
end
end

context "when custom controllers are specified" do
it "the custom ones are used instead of defaults" do
controllers = {:authorizations => "authorizations", :tokens => "tokens"}

mapping = mapping_class.new(:users, {:controllers => controllers})

mapping.controllers.should eql(controllers)
end

it "the custom ones should be merged with defaults" do
controllers = {:authorizations => "authorizations"}

mapping = mapping_class.new(:users, {:controllers => controllers})

mapping.controllers.should eql({
:authorizations => "authorizations",
:tokens => "devise/oauth2_providable/tokens"
})
end
end
end
end
29 changes: 29 additions & 0 deletions spec/lib/devise/oauth2_providable/rails/routes_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require 'spec_helper'

describe Devise::Oauth2Providable::Rails::Routes do
let (:devise_mod) { Devise::Oauth2Providable }
describe "#devise_oauth_for" do

let(:scope_name) { :users }
let(:options) { {} }
let(:mapper) do
set = stub(:resources_path_names => {:new => 'new', :edit => 'edit'}).as_null_object
ActionDispatch::Routing::Mapper.new(set)
end
let(:mock_mapping) do
stub(
:path_prefix => '',
:scope_name => scope_name,
:controllers => Devise::Oauth2Providable::Mapping.default_controllers
)
end

it "creates a new mapping" do
devise_mod.should_receive(:add_mapping).
with(scope_name, options).
and_return(mock_mapping)

mapper.devise_oauth_for(scope_name)
end
end
end
29 changes: 29 additions & 0 deletions spec/lib/devise_oauth2_providable_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,33 @@
it 'should be defined' do
# success
end

describe "#add_mapping" do
let(:mock_mapping) { stub(:scope_name => scope_name) }
let(:scope_name) { "user" }
let(:options) { {} }

before(:each) do
Devise::Oauth2Providable::Mapping.
should_receive(:new).
with(scope_name, options).
and_return(mock_mapping)
end

subject { Devise::Oauth2Providable.add_mapping(scope_name, options) }

it "allows you to add a new mapping" do
subject
end

it "returns the new mapping" do
subject.should eql(mock_mapping)
end

it "assigns the mapping to the Devise::Oauth2Providable.mapping hash using scope_name" do
subject

Devise::Oauth2Providable.mappings[scope_name].should == mock_mapping
end
end
end
4 changes: 1 addition & 3 deletions spec/routing/authorizations_routing_spec.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
require 'spec_helper'

describe Devise::Oauth2Providable::AuthorizationsController do
before :all do
Devise::Oauth2Providable::Engine.load_engine_routes
end

describe 'routing' do
it 'routes POST /oauth2/authorizations' do
post('/oauth2/authorizations').should route_to('devise/oauth2_providable/authorizations#create')
Expand Down
4 changes: 1 addition & 3 deletions spec/routing/tokens_routing_spec.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
require 'spec_helper'

describe Devise::Oauth2Providable::TokensController do
before :all do
Devise::Oauth2Providable::Engine.load_engine_routes
end

describe 'routing' do
it 'routes POST /oauth2/token' do
post('/oauth2/token').should route_to('devise/oauth2_providable/tokens#create')
Expand Down

0 comments on commit aa89aab

Please sign in to comment.