rack-oauth2_utils 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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