rack-oauth2_utils 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,28 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in rack-oauth2_utils.gemspec
4
+ #gemspec
5
+
6
+ gem 'rack'
7
+
8
+ group :test do
9
+ gem 'minitest'
10
+ gem "rack-test"
11
+ end
12
+
13
+ # use Rack::OAuth2::Access::Middleware, :store => Rack::OAuth2::Access::MemoryStore
14
+ #
15
+ #
16
+ # run App
17
+ #
18
+ # before do
19
+ #
20
+ # end
21
+ #
22
+ # Rack::OAuth2::Access::AppHelpers
23
+ #
24
+ # before do
25
+ # account = Account.find(oauth.identity)
26
+ # end
27
+ #
28
+ # get '/' do
data/README.mkd ADDED
@@ -0,0 +1,52 @@
1
+ # Rack OAuth Utils
2
+
3
+ Simple Rack middleware that catches OAuth2 access tokens and validates identity
4
+
5
+ This gem only covers the simple use of "using a token". You must implement the authorization and "getting a token" part in your app.
6
+
7
+ ## USAGE
8
+
9
+ class API < Sinatra::Base
10
+ use Rack::OAuth2Utils::Middleware, :store => SomeKeyValueStore
11
+
12
+ helpers do
13
+
14
+ def authorized?
15
+ !!identity
16
+ end
17
+
18
+ def identity
19
+ requets.env['oauth.identity']
20
+ end
21
+
22
+ def current_account
23
+ Account.find(identity) if authorized?
24
+ end
25
+
26
+ end
27
+
28
+ get '/private' do
29
+ if authorized?
30
+ content_type 'application/json'
31
+ current_account.to_json
32
+ else
33
+ halt 403, 'Access forbidden'
34
+ end
35
+ end
36
+
37
+ end
38
+
39
+ :store is anything that responds to [], []= and delete. Can be Redis, PStore, some ORM wrapper, etc.
40
+
41
+ Store is expected to store access_tokens mapped to some identity string (for example account IDs).
42
+
43
+ There is a test store based on PStore (filesystem. Do no use in production):
44
+
45
+ store = Rack::OAuth2Utils::TestStore.new('tmp/access_tokens.store')
46
+
47
+ store['some_access_token'] = 'some_account_id'
48
+
49
+ It is up to you how you store tokens and identities.
50
+
51
+
52
+ See test/middlewate_test.rb for details
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ desc 'Load everything'
5
+ task :console do
6
+ system "irb -I lib -r rack-oauth2_utils.rb"
7
+ end
@@ -0,0 +1,45 @@
1
+ module Rack
2
+ module OAuth2Utils
3
+
4
+ class Middleware
5
+
6
+ attr_reader :store
7
+
8
+ def initialize(app, options = {})
9
+ @app = app
10
+ @store = options[:store] || {}
11
+ @realm = options[:realm]
12
+ @logger = options[:logger]
13
+ end
14
+
15
+ def call(env)
16
+ request = OAuthRequest.new(env)
17
+ logger = @logger || env["rack.logger"]
18
+
19
+ # If not oauth header / param, leave it up to the app.
20
+ return @app.call(env) unless request.oauth?
21
+
22
+ # Fetch identity
23
+ if identity = store[request.access_token] # identity found, forward to backend
24
+ env["oauth.identity"] = identity
25
+ logger.info "RO2U: Authorized #{identity}" if logger
26
+ else # invalid token
27
+ logger.info "RO2U: Invalid token" if logger
28
+ return unauthorized(request)
29
+ end
30
+ @app.call(env)
31
+ end
32
+
33
+ protected
34
+
35
+ # Returns WWW-Authenticate header.
36
+ def unauthorized(request)
37
+ challenge = 'OAuth realm="%s"' % (@realm || request.host)
38
+ challenge << ', error="invalid_token", error_description="The access token is invalid."'
39
+ return [401, { "WWW-Authenticate" => challenge, 'Content-Type' => 'text/plain' }, ['The access token is invalid.']]
40
+ end
41
+
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,41 @@
1
+ # Wraps Rack::Request to expose Basic and OAuth authentication
2
+ # credentials.
3
+ # Borrowed from https://github.com/flowtown/rack-oauth2-server
4
+ #
5
+ module Rack
6
+ module OAuth2Utils
7
+
8
+ class OAuthRequest < Rack::Request
9
+
10
+ AUTHORIZATION_KEYS = %w{HTTP_AUTHORIZATION X-HTTP_AUTHORIZATION X_HTTP_AUTHORIZATION}
11
+
12
+ # Returns authorization header.
13
+ def authorization_header
14
+ @authorization_header ||= (
15
+ h = AUTHORIZATION_KEYS.inject(nil) { |auth, key| auth || @env[key] }
16
+ if h && h[/^oauth/i]
17
+ h.gsub(/\n/, "").split[1]
18
+ else
19
+ nil
20
+ end
21
+ )
22
+ end
23
+
24
+ def authorization_param
25
+ @authorization_param ||= self.GET['oauth_token']
26
+ end
27
+
28
+ # True if authentication scheme is OAuth.
29
+ def oauth?
30
+ authorization_header || authorization_param
31
+ end
32
+
33
+ # If OAuth, returns access token.
34
+ #
35
+ def access_token
36
+ @access_token ||= oauth?
37
+ end
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,39 @@
1
+ require 'pstore'
2
+ module Rack
3
+ module OAuth2Utils
4
+
5
+ # Test persistent store. Stores to FS using PStore
6
+ # Not meant for production!
7
+ #
8
+ class TestStore
9
+ def initialize(file_path = '.')
10
+ @store = PStore.new(file_path)
11
+ end
12
+
13
+ def []=(key, value)
14
+ @store.transaction do
15
+ @store[key] = value
16
+ end
17
+ end
18
+
19
+ def [](key)
20
+ @store.transaction do
21
+ @store[key]
22
+ end
23
+ end
24
+
25
+ def delete(key)
26
+ @store.transaction do
27
+ @store.delete(key)
28
+ end
29
+ end
30
+
31
+ def roots
32
+ @store.transaction do
33
+ @store.roots
34
+ end
35
+ end
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,5 @@
1
+ module Rack
2
+ module OAuth2Utils
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,10 @@
1
+ require 'rack'
2
+ require 'rack-oauth2_utils/middleware'
3
+ require 'rack-oauth2_utils/oauth_request'
4
+ require 'rack-oauth2_utils/test_store'
5
+
6
+ module Rack
7
+ module OAuth2Utils
8
+ # Your code goes here...
9
+ end
10
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "rack-oauth2_utils/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "rack-oauth2_utils"
7
+ s.version = Rack::OAuth2Utils::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Ismael Celis"]
10
+ s.email = ["ismaelct@gmail.com"]
11
+ s.homepage = ""
12
+ s.summary = %q{Middleware for catching OAuth2 access tokens in Rack apps}
13
+ s.description = %q{Simple Rack middleware that catches OAuth2 access tokens and validates identity}
14
+
15
+ s.rubyforge_project = "rack-oauth2_utils"
16
+
17
+ s.add_dependency 'rack', ">= 1.2.2"
18
+ s.add_development_dependency "bundler", ">= 1.0.0"
19
+ s.add_development_dependency "minitest"
20
+
21
+ s.files = `git ls-files`.split("\n")
22
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
23
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
+ s.require_paths = ["lib"]
25
+ end
@@ -0,0 +1,136 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
2
+
3
+ describe Rack::OAuth2Utils::Middleware do
4
+
5
+ include Rack::Test::Methods
6
+
7
+ OK_RESPONSE = [200, {'Content-Type' => 'text/plain'}, ['Hello world']]
8
+ FORBIDDEN_RESPONSE = [403, {'Content-Type' => 'text/plain'}, ['Nono']]
9
+
10
+ def app
11
+ @app ||= Rack::Builder.new do
12
+ # Simple token / identity store
13
+ use Rack::OAuth2Utils::Middleware, :store => {
14
+ # token # identity
15
+ 'aaaaa' => 'ismasan',
16
+ 'bbbbb' => 'sachi'
17
+ }
18
+ # Public endpoint
19
+ map('/public'){
20
+ run lambda {|env| OK_RESPONSE }
21
+ }
22
+ # Private, o auth protected
23
+ map('/private'){
24
+ run lambda {|env|
25
+ if env['oauth.identity']
26
+ OK_RESPONSE
27
+ else
28
+ FORBIDDEN_RESPONSE
29
+ end
30
+ }
31
+ }
32
+ end
33
+ end
34
+
35
+ describe "no token" do
36
+
37
+ describe 'public resource' do
38
+ before {get '/public'}
39
+
40
+ it 'should return 200 Ok' do
41
+ last_response.status.must_equal 200
42
+ end
43
+
44
+ it 'should return body' do
45
+ last_response.body.must_equal 'Hello world'
46
+ end
47
+ end
48
+
49
+ describe 'private resource' do
50
+ before {get '/private'}
51
+
52
+ it 'should return 200 Ok' do
53
+ last_response.status.must_equal 403
54
+ end
55
+
56
+ it 'should return body' do
57
+ last_response.body.must_equal 'Nono'
58
+ end
59
+ end
60
+ end
61
+
62
+ describe 'with invalid token' do
63
+
64
+ before {
65
+ header "Authorization", "OAuth invalidtoken"
66
+ }
67
+
68
+ describe 'public resource' do
69
+ before {get '/public'}
70
+
71
+ it 'should return 401 Unauthorized' do
72
+ last_response.status.must_equal 401
73
+ end
74
+
75
+ it 'should return WWW-Authenticate header with realm and error info' do
76
+ last_response.headers['WWW-Authenticate'].must_equal "OAuth realm=\"example.org\", error=\"invalid_token\", error_description=\"The access token is invalid.\""
77
+ end
78
+ end
79
+
80
+ describe 'private resource' do
81
+ before {get '/private'}
82
+
83
+ it 'should return 401 Unauthorized' do
84
+ last_response.status.must_equal 401
85
+ end
86
+
87
+ it 'should return WWW-Authenticate header with realm and error info' do
88
+ last_response.headers['WWW-Authenticate'].must_equal "OAuth realm=\"example.org\", error=\"invalid_token\", error_description=\"The access token is invalid.\""
89
+ end
90
+ end
91
+ end
92
+
93
+ describe 'with valid token' do
94
+
95
+ before {
96
+ header "Authorization", "OAuth aaaaa"
97
+ }
98
+
99
+ describe 'public resource' do
100
+ before {get '/public'}
101
+
102
+ it 'should return 200 Ok' do
103
+ last_response.status.must_equal 200
104
+ end
105
+
106
+ it 'should return body' do
107
+ last_response.body.must_equal 'Hello world'
108
+ end
109
+ end
110
+
111
+ describe 'private resource' do
112
+ before {get '/private'}
113
+
114
+ it 'should return 200 Ok' do
115
+ last_response.status.must_equal 200
116
+ end
117
+
118
+ it 'should return body' do
119
+ last_response.body.must_equal 'Hello world'
120
+ end
121
+ end
122
+ end
123
+
124
+ describe 'with valid token as query param' do
125
+ before {get '/private', 'oauth_token' => 'aaaaa'}
126
+
127
+ it 'should return 200 Ok' do
128
+ last_response.status.must_equal 200
129
+ end
130
+
131
+ it 'should return body' do
132
+ last_response.body.must_equal 'Hello world'
133
+ end
134
+ end
135
+
136
+ end
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.setup :default, :test
4
+
5
+ ENV["RACK_ENV"] = "test"
6
+
7
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
8
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
9
+
10
+ require 'rack-oauth2_utils'
11
+ require "rack/test"
12
+
13
+
14
+ require 'minitest/autorun'
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-oauth2_utils
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Ismael Celis
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-07-07 00:00:00 +01:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rack
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: 1.2.2
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: 1.0.0
36
+ type: :development
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: minitest
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ type: :development
48
+ version_requirements: *id003
49
+ description: Simple Rack middleware that catches OAuth2 access tokens and validates identity
50
+ email:
51
+ - ismaelct@gmail.com
52
+ executables: []
53
+
54
+ extensions: []
55
+
56
+ extra_rdoc_files: []
57
+
58
+ files:
59
+ - .gitignore
60
+ - Gemfile
61
+ - README.mkd
62
+ - Rakefile
63
+ - lib/rack-oauth2_utils.rb
64
+ - lib/rack-oauth2_utils/middleware.rb
65
+ - lib/rack-oauth2_utils/oauth_request.rb
66
+ - lib/rack-oauth2_utils/test_store.rb
67
+ - lib/rack-oauth2_utils/version.rb
68
+ - rack-oauth2_utils.gemspec
69
+ - test/middleware_test.rb
70
+ - test/test_helper.rb
71
+ has_rdoc: true
72
+ homepage: ""
73
+ licenses: []
74
+
75
+ post_install_message:
76
+ rdoc_options: []
77
+
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: "0"
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: "0"
92
+ requirements: []
93
+
94
+ rubyforge_project: rack-oauth2_utils
95
+ rubygems_version: 1.6.2
96
+ signing_key:
97
+ specification_version: 3
98
+ summary: Middleware for catching OAuth2 access tokens in Rack apps
99
+ test_files:
100
+ - test/middleware_test.rb
101
+ - test/test_helper.rb