Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Basic middleware to catch and authorize access_token
- Loading branch information
0 parents
commit 3116c26
Showing
10 changed files
with
310 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,5 @@ | |||
*.gem | |||
.bundle | |||
Gemfile.lock | |||
pkg/* | |||
.rvmrc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,2 @@ | |||
require 'bundler' | |||
Bundler::GemHelper.install_tasks |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,5 @@ | |||
module Rack | |||
module OAuth2Utils | |||
VERSION = "0.0.1" | |||
end | |||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -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' |