Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Working copy of code.

- included License
- Stub README
  • Loading branch information...
commit 3fbfbe8f9a7258287d6370528d67fcb121e45e29 0 parents
@tonywok authored
19 .gitignore
@@ -0,0 +1,19 @@
+*.rbc
+*.sassc
+*.swp
+.sass-cache
+capybara-*.html
+.rspec
+/.bundle
+/vendor/bundle
+/log/*
+/tmp/*
+/db/*.sqlite3
+/public/system/*
+/coverage/
+/spec/tmp/*
+**.orig
+rerun.txt
+pickle-email-*.html
+public/assets/*
+.zsh_rake_cache
1  .rvmrc
@@ -0,0 +1 @@
+rvm 1.9.2@rack_test --create
7 Gemfile
@@ -0,0 +1,7 @@
+# A sample Gemfile
+source "http://rubygems.org"
+gemspec
+
+group :test, :development do
+ gem 'ruby-debug19', :require => 'ruby-debug'
+end
43 Gemfile.lock
@@ -0,0 +1,43 @@
+PATH
+ remote: .
+ specs:
+ forcefield (0.0.1)
+ rack (~> 1.4)
+ simple_oauth
+
+GEM
+ remote: http://rubygems.org/
+ specs:
+ archive-tar-minitar (0.5.2)
+ columnize (0.3.6)
+ diff-lcs (1.1.3)
+ linecache19 (0.5.12)
+ ruby_core_source (>= 0.1.4)
+ rack (1.4.1)
+ rspec (2.8.0)
+ rspec-core (~> 2.8.0)
+ rspec-expectations (~> 2.8.0)
+ rspec-mocks (~> 2.8.0)
+ rspec-core (2.8.0)
+ rspec-expectations (2.8.0)
+ diff-lcs (~> 1.1.2)
+ rspec-mocks (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.5)
+ archive-tar-minitar (>= 0.5.2)
+ simple_oauth (0.1.5)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ forcefield!
+ rspec (~> 2.7)
+ ruby-debug19
20 LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2011 EdgeCase, Tony Schneider
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
0  README.md
No changes.
8 Rakefile
@@ -0,0 +1,8 @@
+#!/usr/bin/env rake
+
+require "bundler/gem_tasks"
+require 'rspec/core/rake_task'
+
+RSpec::Core::RakeTask.new(:spec)
+
+task :default => :spec
19 forcefield.gemspec
@@ -0,0 +1,19 @@
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |gem|
+ gem.authors = ["Tony Schneider"]
+ gem.email = ["tony@edgecase.com"]
+ gem.description = %q{OAuth 1.0 RFC 5849#3.4.1 request verifier. Accompanies a blogpost, and is for learning purposes only.}
+ gem.summary = %q{OAuth 1.0 RFC 5849#3.4.1 request verifier}
+
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
+ gem.files = `git ls-files`.split("\n")
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ gem.name = "forcefield"
+ gem.require_paths = ["lib"]
+ gem.version = "0.0.1"
+
+ gem.add_dependency 'rack', '~> 1.4'
+ gem.add_dependency 'simple_oauth'
+ gem.add_development_dependency 'rspec', '~> 2.7'
+end
2  lib/forcefield.rb
@@ -0,0 +1,2 @@
+require 'simple_oauth'
+require 'forcefield/middleware'
31 lib/forcefield/middleware.rb
@@ -0,0 +1,31 @@
+require 'forcefield/request'
+
+module Forcefield
+ class Middleware
+
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ @request = Request.new(env)
+
+ @request.with_valid_request do
+ if client_verified?
+ env["oauth_client"] = client
+ @app.call(env)
+ else
+ [401, {}, ["Unauthorized. You are part of the Rebel Alliance and a Trader!"]]
+ end
+ end
+ end
+
+ private
+
+ def client_verified?
+ @client = ImperialClient.find_by_consumer_key(@request.consumer_key)
+ @request.verify_signature(@client)
+ end
+
+ end
+end
53 lib/forcefield/request.rb
@@ -0,0 +1,53 @@
+require 'rack/auth/abstract/request'
+
+module Forcefield
+ class Request < Rack::Auth::AbstractRequest
+
+ def with_valid_request
+ if provided?
+ if !oauth?
+ [401, {}, ["Unauthorized. Pst! You forgot to include the Auth scheme"]]
+ elsif params[:consumer_key].nil?
+ [401, {}, ["Unauthorized. Pst! You forgot the consumer key"]]
+ elsif params[:signature].nil?
+ [401, {}, ["Unauthorized. Pst! You forgot to sign the request."]]
+ elsif params[:signature_method].nil?
+ [401, {}, ["Unauthorized. Pst! You forgot to include the OAuth signature method."]]
+ else
+ yield(request.env)
+ end
+ else
+ [401, {}, ["Unauthorized. You are part of the Rebel Alliance and a Trader!"]]
+ end
+ end
+
+ def verify_signature(client)
+ return false unless client
+
+ header = SimpleOAuth::Header.new(request.request_method, request.url, included_request_params, auth_header)
+ header.valid?(:consumer_secret => client.consumer_secret)
+ end
+
+ def consumer_key
+ params[:consumer_key]
+ end
+
+ private
+
+ def params
+ @params ||= SimpleOAuth::Header.parse(auth_header)
+ end
+
+ def oauth?
+ scheme == :oauth
+ end
+
+ def auth_header # :nodoc:
+ @env[authorization_key]
+ end
+
+ def included_request_params # :nodoc:
+ request.content_type == "application/x-www-form-urlencoded" ? request.params : nil
+ end
+ end
+end
94 spec/lib/forcefield/middleware_spec.rb
@@ -0,0 +1,94 @@
+require 'spec_helper'
+
+describe Forcefield::Middleware do
+
+ let(:death_star) { lambda { |env| [200, {}, []] } }
+ let(:middleware) { Forcefield::Middleware.new(death_star) }
+ let(:mock_request) { Rack::MockRequest.new(middleware) }
+
+ context "incoming request has no Authorization header" do
+
+ let(:resp) { mock_request.get("/") }
+
+ it("returns a 401") { resp.status.should == 401 }
+
+ it "notifies the client they are Unauthorized" do
+ resp.body.should == "Unauthorized. You are part of the Rebel Alliance and a Trader!"
+ end
+
+ end
+
+ context "incoming request has a Authorization header" do
+ context "when incoming request has a Authorization header" do
+ context "but is missing an OAuth Authorization scheme" do
+
+ let(:header_with_bad_scheme) {{ "HTTP_AUTHORIZATION" => "Force" }}
+ let(:resp) { mock_request.get("/", header_with_bad_scheme) }
+
+ it("returns a 401") { resp.status.should == 401 }
+
+ it "notifies client that they sent the wrong Authorization scheme" do
+ resp.body.should == "Unauthorized. Pst! You forgot to include the Auth scheme"
+ end
+ end
+
+ context "but is missing an oauth_consumer_key" do
+
+ let(:header_with_no_key) {{ "HTTP_AUTHORIZATION" => "OAuth realm=\"Endor\"" }}
+ let(:resp) { mock_request.get("/", header_with_no_key) }
+
+ it("returns a 401") { resp.status.should == 401 }
+
+ it "notifies the client that they have not included a consumer key" do
+ resp.body.should == "Unauthorized. Pst! You forgot the consumer key"
+ end
+ end
+
+ context "but is missing an oauth_signature" do
+
+ let(:header_without_sig) {{ "HTTP_AUTHORIZATION" => "OAuth realm=\"foo\", oauth_consumer_key=\"123\"" }}
+ let(:resp) { mock_request.get("/", header_without_sig) }
+
+ it("returns a 401") { resp.status.should == 401 }
+
+ it "notifies the client that they have not signed the request" do
+ resp.body.should == "Unauthorized. Pst! You forgot to sign the request."
+ end
+ end
+
+ context "but is missing an oauth_signature_method" do
+
+ let(:header_without_sig_method) do
+ { "HTTP_AUTHORIZATION" => "OAuth realm=\"foo\", oauth_consumer_key=\"123\", oauth_signature=\"SIGNATURE\"" }
+ end
+ let(:resp) { mock_request.get("/", header_without_sig_method) }
+
+ it("returns a 401") { resp.status.should == 401 }
+
+ it "notifies the client that they haven't specified how they signed the request" do
+ resp.body.should == "Unauthorized. Pst! You forgot to include the OAuth signature method."
+ end
+
+ end
+ end
+
+ context 'client makes request with sufficient, but incorrect OAuth header' do
+
+ let(:test_uri) { "http://api.deathstar.com" }
+ let(:incorrect_secret) { "!!#{ImperialClient::DUMMY_SECRET}!!" }
+ let(:incorrect_consumer_credentials) {{ :consumer_key => ImperialClient::DUMMY_KEY, :consumer_secret => incorrect_secret }}
+ let(:invalid_auth_header) {{ "HTTP_AUTHORIZATION" => SimpleOAuth::Header.new(:get, test_uri, {}, incorrect_consumer_credentials).to_s }}
+ let(:resp) { mock_request.get(test_uri, invalid_auth_header) }
+ let(:client_with_correct_credentials) { ImperialClient.new(ImperialClient::DUMMY_KEY, ImperialClient::DUMMY_SECRET) }
+
+ before { ImperialClient.stub(:find_by_consumer_key).and_return(client_with_correct_credentials) }
+
+ it('returns a status of 401') { resp.status.should == 401 }
+
+ it "notifies the client that they have failed at thwarting the Imperials" do
+ resp.body.should == "Unauthorized. You are part of the Rebel Alliance and a Trader!"
+ end
+
+ end
+ end
+end
10 spec/spec_helper.rb
@@ -0,0 +1,10 @@
+require 'bundler/setup'
+require 'rspec'
+require 'rack'
+require 'json'
+require 'forcefield'
+
+Dir[File.expand_path('../support/**/*', __FILE__)].each { |f| require f }
+
+RSpec.configure do |config|
+end
14 spec/support/imperial_client.rb
@@ -0,0 +1,14 @@
+class ImperialClient < Struct.new(:consumer_key, :consumer_secret)
+
+ DUMMY_KEY = 'key'
+ DUMMY_SECRET = 'shhhh'
+
+
+ def self.find_by_consumer_key(key)
+ if key == DUMMY_KEY
+ new(key, DUMMY_SECRET)
+ end
+ end
+
+end
+

0 comments on commit 3fbfbe8

Please sign in to comment.
Something went wrong with that request. Please try again.