Skip to content

Commit

Permalink
Basic middleware to catch and authorize access_token
Browse files Browse the repository at this point in the history
  • Loading branch information
ismasan committed Jul 7, 2011
0 parents commit 3116c26
Show file tree
Hide file tree
Showing 10 changed files with 310 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
@@ -0,0 +1,5 @@
*.gem
.bundle
Gemfile.lock
pkg/*
.rvmrc
28 changes: 28 additions & 0 deletions Gemfile
@@ -0,0 +1,28 @@
source "http://rubygems.org"

# Specify your gem's dependencies in rack-oauth2_utils.gemspec
#gemspec

gem 'rack'

group :test do
gem 'minitest'
gem "rack-test"
end

# use Rack::OAuth2::Access::Middleware, :store => Rack::OAuth2::Access::MemoryStore
#
#
# run App
#
# before do
#
# end
#
# Rack::OAuth2::Access::AppHelpers
#
# before do
# account = Account.find(oauth.identity)
# end
#
# get '/' do
2 changes: 2 additions & 0 deletions Rakefile
@@ -0,0 +1,2 @@
require 'bundler'
Bundler::GemHelper.install_tasks
9 changes: 9 additions & 0 deletions lib/rack-oauth2_utils.rb
@@ -0,0 +1,9 @@
require 'rack'
require 'rack-oauth2_utils/middleware'
require 'rack-oauth2_utils/oauth_request'

module Rack
module OAuth2Utils
# Your code goes here...
end
end
45 changes: 45 additions & 0 deletions lib/rack-oauth2_utils/middleware.rb
@@ -0,0 +1,45 @@
module Rack
module OAuth2Utils

class Middleware

attr_reader :store

def initialize(app, options = {})
@app = app
@store = options[:store] || {}
@realm = options[:realm]
@logger = options[:logger]
end

def call(env)
request = OAuthRequest.new(env)
logger = @logger || env["rack.logger"]

# If not oauth header / param, leave it up to the app.
return @app.call(env) unless request.oauth?

# Fetch identity
if identity = store[request.access_token] # identity found, forward to backend
env["oauth.identity"] = identity
logger.info "RO2U: Authorized #{identity}" if logger
else # invalid token
logger.info "RO2U: Invalid token" if logger
return unauthorized(request)
end
@app.call(env)
end

protected

# Returns WWW-Authenticate header.
def unauthorized(request)
challenge = 'OAuth realm="%s"' % (@realm || request.host)
challenge << ', error="invalid_token", error_description="The access token is invalid."'
return [401, { "WWW-Authenticate" => challenge, 'Content-Type' => 'text/plain' }, ['The access token is invalid.']]
end

end

end
end
41 changes: 41 additions & 0 deletions lib/rack-oauth2_utils/oauth_request.rb
@@ -0,0 +1,41 @@
# Wraps Rack::Request to expose Basic and OAuth authentication
# credentials.
# Borrowed from https://github.com/flowtown/rack-oauth2-server
#
module Rack
module OAuth2Utils

class OAuthRequest < Rack::Request

AUTHORIZATION_KEYS = %w{HTTP_AUTHORIZATION X-HTTP_AUTHORIZATION X_HTTP_AUTHORIZATION}

# Returns authorization header.
def authorization_header
@authorization_header ||= (
h = AUTHORIZATION_KEYS.inject(nil) { |auth, key| auth || @env[key] }
if h && h[/^oauth/i]
h.gsub(/\n/, "").split[1]
else
nil
end
)
end

def authorization_param
@authorization_param ||= self.GET['oauth_token']
end

# True if authentication scheme is OAuth.
def oauth?
authorization_header || authorization_param
end

# If OAuth, returns access token.
#
def access_token
@access_token ||= oauth?
end
end

end
end
5 changes: 5 additions & 0 deletions lib/rack-oauth2_utils/version.rb
@@ -0,0 +1,5 @@
module Rack
module OAuth2Utils
VERSION = "0.0.1"
end
end
25 changes: 25 additions & 0 deletions rack-oauth2_utils.gemspec
@@ -0,0 +1,25 @@
# -*- encoding: utf-8 -*-
$:.push File.expand_path("../lib", __FILE__)
require "rack-oauth2_utils/version"

Gem::Specification.new do |s|
s.name = "rack-oauth2_utils"
s.version = Rack::OAuth2Utils::VERSION
s.platform = Gem::Platform::RUBY
s.authors = ["TODO: Write your name"]
s.email = ["TODO: Write your email address"]
s.homepage = ""
s.summary = %q{TODO: Write a gem summary}
s.description = %q{TODO: Write a gem description}

s.rubyforge_project = "rack-oauth2_utils"

s.add_dependency 'rack', ">= 1.2.2"
s.add_development_dependency "bundler", ">= 1.0.0"
s.add_development_dependency "minitest"

s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"]
end
136 changes: 136 additions & 0 deletions test/middleware_test.rb
@@ -0,0 +1,136 @@
require File.expand_path(File.dirname(__FILE__) + '/test_helper')

describe Rack::OAuth2Utils::Middleware do

include Rack::Test::Methods

OK_RESPONSE = [200, {'Content-Type' => 'text/plain'}, ['Hello world']]
FORBIDDEN_RESPONSE = [403, {'Content-Type' => 'text/plain'}, ['Nono']]

def app
@app ||= Rack::Builder.new do
# Simple token / identity store
use Rack::OAuth2Utils::Middleware, :store => {
# token # identity
'aaaaa' => 'ismasan',
'bbbbb' => 'sachi'
}
# Public endpoint
map('/public'){
run lambda {|env| OK_RESPONSE }
}
# Private, o auth protected
map('/private'){
run lambda {|env|
if env['oauth.identity']
OK_RESPONSE
else
FORBIDDEN_RESPONSE
end
}
}
end
end

describe "no token" do

describe 'public resource' do
before {get '/public'}

it 'should return 200 Ok' do
last_response.status.must_equal 200
end

it 'should return body' do
last_response.body.must_equal 'Hello world'
end
end

describe 'private resource' do
before {get '/private'}

it 'should return 200 Ok' do
last_response.status.must_equal 403
end

it 'should return body' do
last_response.body.must_equal 'Nono'
end
end
end

describe 'with invalid token' do

before {
header "Authorization", "OAuth invalidtoken"
}

describe 'public resource' do
before {get '/public'}

it 'should return 401 Unauthorized' do
last_response.status.must_equal 401
end

it 'should return WWW-Authenticate header with realm and error info' do
last_response.headers['WWW-Authenticate'].must_equal "OAuth realm=\"example.org\", error=\"invalid_token\", error_description=\"The access token is invalid.\""
end
end

describe 'private resource' do
before {get '/private'}

it 'should return 401 Unauthorized' do
last_response.status.must_equal 401
end

it 'should return WWW-Authenticate header with realm and error info' do
last_response.headers['WWW-Authenticate'].must_equal "OAuth realm=\"example.org\", error=\"invalid_token\", error_description=\"The access token is invalid.\""
end
end
end

describe 'with valid token' do

before {
header "Authorization", "OAuth aaaaa"
}

describe 'public resource' do
before {get '/public'}

it 'should return 200 Ok' do
last_response.status.must_equal 200
end

it 'should return body' do
last_response.body.must_equal 'Hello world'
end
end

describe 'private resource' do
before {get '/private'}

it 'should return 200 Ok' do
last_response.status.must_equal 200
end

it 'should return body' do
last_response.body.must_equal 'Hello world'
end
end
end

describe 'with valid token as query param' do
before {get '/private', 'oauth_token' => 'aaaaa'}

it 'should return 200 Ok' do
last_response.status.must_equal 200
end

it 'should return body' do
last_response.body.must_equal 'Hello world'
end
end

end
14 changes: 14 additions & 0 deletions test/test_helper.rb
@@ -0,0 +1,14 @@
require 'rubygems'
require 'bundler'
Bundler.setup :default, :test

ENV["RACK_ENV"] = "test"

$LOAD_PATH.unshift(File.dirname(__FILE__))
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))

require 'rack-oauth2_utils'
require "rack/test"


require 'minitest/autorun'

0 comments on commit 3116c26

Please sign in to comment.