rack-oauth_proxy 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5834c55b2217ffe7ab3ef9808613717e03c3d30e
4
+ data.tar.gz: 3920d54037d98faa67ae7dfe68374ee75f16a458
5
+ SHA512:
6
+ metadata.gz: 1fc62efb3ba2b9a55ee38a495277011d867d2dae4295cce6e7c3258f09b987ae82bb8b24769eb9321ee70c35bc570e4cb1708144c178f1e974367c7878e4ef62
7
+ data.tar.gz: 58eb2bc1b1e0c95a5093bbcec332fa5054661961399933befefe24472f2ccbb82efbd08e598b6c10af53c1711367ce9095f32986cbfe9dce93257fea08197145
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rack-oauth_proxy.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Ryo Nakamura
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # Rack::OauthProxy
2
+ Delegates OAuth authentication to other authentication server.
3
+
4
+ ## Usage
5
+ For Rails example:
6
+
7
+ ```ruby
8
+ class ApplicationController < ActionController::Base
9
+ use Rack::OauthProxy, url: "http://auth.example.com/oauth/token"
10
+ end
11
+ ```
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1 @@
1
+ require "rack/oauth_proxy"
@@ -0,0 +1,27 @@
1
+ require "rack/oauth_proxy/access_tokens/base"
2
+ require "rack/oauth_proxy/access_tokens/invalid"
3
+ require "rack/oauth_proxy/access_tokens/valid"
4
+ require "rack/oauth_proxy/client"
5
+ require "rack/oauth_proxy/client/request"
6
+ require "rack/oauth_proxy/client/response"
7
+ require "rack/oauth_proxy/version"
8
+
9
+ module Rack
10
+ class OauthProxy
11
+ def initialize(app, options = {})
12
+ @app = app
13
+ @options = options
14
+ end
15
+
16
+ def call(env)
17
+ env["rack-oauth_proxy.access_token"] = client.fetch(env)
18
+ @app.call(env)
19
+ end
20
+
21
+ private
22
+
23
+ def client
24
+ @client ||= Client.new(@options)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,39 @@
1
+ module Rack
2
+ class OauthProxy
3
+ module AccessTokens
4
+ class Base
5
+ ATTRIBUTE_NAMES = %w[
6
+ application_id
7
+ expired_at
8
+ refresh_token
9
+ resource_owner_id
10
+ scope
11
+ token
12
+ token_type
13
+ ]
14
+
15
+ ATTRIBUTE_NAMES.each {|name| attr_accessor name }
16
+
17
+ def initialize(attributes)
18
+ raise NotImplementedError, "You must implement #{self.class}##{__method__}"
19
+ end
20
+
21
+ def accessible?
22
+ raise NotImplementedError, "You must implement #{self.class}##{__method__}"
23
+ end
24
+
25
+ def revoked?
26
+ raise NotImplementedError, "You must implement #{self.class}##{__method__}"
27
+ end
28
+
29
+ def expired?
30
+ raise NotImplementedError, "You must implement #{self.class}##{__method__}"
31
+ end
32
+
33
+ def scopes
34
+ scope.split(" ") if scope
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,22 @@
1
+ module Rack
2
+ class OauthProxy
3
+ module AccessTokens
4
+ class Invalid < Base
5
+ def initialize(attributes = nil)
6
+ end
7
+
8
+ def accessible?
9
+ false
10
+ end
11
+
12
+ def revoked?
13
+ false
14
+ end
15
+
16
+ def expired?
17
+ false
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ module Rack
2
+ class OauthProxy
3
+ module AccessTokens
4
+ class Valid < Base
5
+ def initialize(attributes)
6
+ ATTRIBUTE_NAMES.each {|name| send("#{name}=", attributes[name]) }
7
+ end
8
+
9
+ def accessible?
10
+ true
11
+ end
12
+
13
+ def revoked?
14
+ false
15
+ end
16
+
17
+ def expired?
18
+ false
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,63 @@
1
+ require "net/http"
2
+
3
+ module Rack
4
+ class OauthProxy
5
+ class Client
6
+ READ_TIMEOUT = 1
7
+ OPEN_TIMEOUT = 1
8
+
9
+ def initialize(options = {})
10
+ @options = options
11
+ end
12
+
13
+ def fetch(env)
14
+ request = Request.new(env)
15
+ if request.has_any_valid_credentials?
16
+ path = "#{uri.path}"
17
+ path << "?#{request.to_query}" if request.to_query.present?
18
+ header = {
19
+ "Authorization" => request.authorization,
20
+ "Host" => host,
21
+ "Resource-Owner-Id" => request.resource_owner_id,
22
+ "Scopes" => request.scopes,
23
+ }.reject {|key, value| value.nil? }
24
+ raw_response = http_client.get(path, header)
25
+ response = Response.new(raw_response)
26
+ if response.valid_as_access_token?
27
+ AccessTokens::Valid.new(response.to_hash)
28
+ else
29
+ AccessTokens::Invalid.new
30
+ end
31
+ else
32
+ AccessTokens::Invalid.new
33
+ end
34
+ rescue Timeout::Error
35
+ AccessTokens::Invalid.new
36
+ end
37
+
38
+ private
39
+
40
+ def uri
41
+ @uri ||= URI.parse(url)
42
+ end
43
+
44
+ def http_client
45
+ client = Net::HTTP.new(uri.host, uri.port)
46
+ client.read_timeout = READ_TIMEOUT
47
+ client.open_timeout = OPEN_TIMEOUT
48
+ client
49
+ end
50
+
51
+ def url
52
+ @options[:url] or raise NoUrlError
53
+ end
54
+
55
+ def host
56
+ @options[:host]
57
+ end
58
+
59
+ class NoUrlError < StandardError
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,42 @@
1
+ require "active_support/core_ext/hash/slice"
2
+ require "active_support/core_ext/object/blank"
3
+ require "active_support/core_ext/object/to_query"
4
+ require "rack"
5
+
6
+ module Rack
7
+ class OauthProxy
8
+ class Client
9
+ class Request
10
+ def initialize(env)
11
+ @env = env
12
+ end
13
+
14
+ def has_any_valid_credentials?
15
+ authorization.present? ||
16
+ rack_request.params[:access_token].present? ||
17
+ rack_request.params[:bearer_token].present?
18
+ end
19
+
20
+ def rack_request
21
+ @rack_request ||= Rack::Request.new(@env)
22
+ end
23
+
24
+ def to_query
25
+ rack_request.params.slice("access_token", "bearer_token").to_query
26
+ end
27
+
28
+ def authorization
29
+ @env["HTTP_AUTHORIZATION"]
30
+ end
31
+
32
+ def resource_owner_id
33
+ @env["HTTP_RESOURCE_OWNER_ID"]
34
+ end
35
+
36
+ def scopes
37
+ @env["HTTP_SCOPES"]
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,42 @@
1
+ require "json"
2
+
3
+ module Rack
4
+ class OauthProxy
5
+ class Client
6
+ class Response
7
+ def initialize(raw)
8
+ @raw = raw
9
+ end
10
+
11
+ def valid_as_access_token?
12
+ ok? && json? && hash?
13
+ end
14
+
15
+ def to_hash
16
+ parsed_body
17
+ end
18
+
19
+ private
20
+
21
+ def ok?
22
+ @raw.code == "200"
23
+ end
24
+
25
+ def json?
26
+ parsed_body
27
+ true
28
+ rescue JSON::ParserError
29
+ false
30
+ end
31
+
32
+ def hash?
33
+ parsed_body.is_a?(Hash)
34
+ end
35
+
36
+ def parsed_body
37
+ @parsed_body ||= JSON.parse(@raw.body)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,5 @@
1
+ module Rack
2
+ class OauthProxy
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "rack/oauth_proxy/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rack-oauth_proxy"
8
+ spec.version = Rack::OauthProxy::VERSION
9
+ spec.authors = ["Ryo Nakamura"]
10
+ spec.email = ["r7kamura@gmail.com"]
11
+ spec.summary = "Delegates OAuth authentication to other authentication server"
12
+ spec.homepage = "https://github.com/r7kamura/rack-oauth_proxy"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_dependency "activesupport"
21
+ spec.add_dependency "rack"
22
+ spec.add_development_dependency "bundler", "~> 1.5"
23
+ spec.add_development_dependency "rake"
24
+ spec.add_development_dependency "rspec", "2.14.1"
25
+ spec.add_development_dependency "webmock"
26
+ end
@@ -0,0 +1,55 @@
1
+ require "spec_helper"
2
+ require "stringio"
3
+
4
+ describe Rack::OauthProxy::Client do
5
+ let(:client) do
6
+ described_class.new(url: url)
7
+ end
8
+
9
+ let(:url) do
10
+ "http://example.com/oauth/token"
11
+ end
12
+
13
+ let(:env) do
14
+ {
15
+ "HTTP_AUTHORIZATION" => "Bearer #{token}",
16
+ "rack.input" => StringIO.new,
17
+ }
18
+ end
19
+
20
+ let(:token) do
21
+ SecureRandom.hex(32)
22
+ end
23
+
24
+ context "#fetch" do
25
+ context "when authentication succeeded" do
26
+ before do
27
+ stub_request(:get, url).to_return(status: 200, body: {}.to_json)
28
+ end
29
+
30
+ it "returns valid access token" do
31
+ client.fetch(env).should be_a Rack::OauthProxy::AccessTokens::Valid
32
+ end
33
+ end
34
+
35
+ context "without no credentials in request" do
36
+ before do
37
+ env.delete("HTTP_AUTHORIZATION")
38
+ end
39
+
40
+ it "returns invalid access token" do
41
+ client.fetch(env).should be_a Rack::OauthProxy::AccessTokens::Invalid
42
+ end
43
+ end
44
+
45
+ context "when authentication failed" do
46
+ before do
47
+ stub_request(:get, url).to_return(status: 401, body: {}.to_json)
48
+ end
49
+
50
+ it "returns invalid access token" do
51
+ client.fetch(env).should be_a Rack::OauthProxy::AccessTokens::Invalid
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
2
+ require "rack/oauth_proxy"
3
+ require "webmock/rspec"
4
+
5
+ RSpec.configure do |config|
6
+ config.treat_symbols_as_metadata_keys_with_true_values = true
7
+ config.run_all_when_everything_filtered = true
8
+ config.filter_run :focus
9
+ end
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-oauth_proxy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ryo Nakamura
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rack
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '1.5'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.5'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '='
74
+ - !ruby/object:Gem::Version
75
+ version: 2.14.1
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '='
81
+ - !ruby/object:Gem::Version
82
+ version: 2.14.1
83
+ - !ruby/object:Gem::Dependency
84
+ name: webmock
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description:
98
+ email:
99
+ - r7kamura@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - .gitignore
105
+ - Gemfile
106
+ - LICENSE.txt
107
+ - README.md
108
+ - Rakefile
109
+ - lib/rack-oauth_proxy.rb
110
+ - lib/rack/oauth_proxy.rb
111
+ - lib/rack/oauth_proxy/access_tokens/base.rb
112
+ - lib/rack/oauth_proxy/access_tokens/invalid.rb
113
+ - lib/rack/oauth_proxy/access_tokens/valid.rb
114
+ - lib/rack/oauth_proxy/client.rb
115
+ - lib/rack/oauth_proxy/client/request.rb
116
+ - lib/rack/oauth_proxy/client/response.rb
117
+ - lib/rack/oauth_proxy/version.rb
118
+ - rack-oauth_proxy.gemspec
119
+ - spec/rack/oauth_proxy/client_spec.rb
120
+ - spec/spec_helper.rb
121
+ homepage: https://github.com/r7kamura/rack-oauth_proxy
122
+ licenses:
123
+ - MIT
124
+ metadata: {}
125
+ post_install_message:
126
+ rdoc_options: []
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ required_rubygems_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ requirements: []
140
+ rubyforge_project:
141
+ rubygems_version: 2.0.3
142
+ signing_key:
143
+ specification_version: 4
144
+ summary: Delegates OAuth authentication to other authentication server
145
+ test_files:
146
+ - spec/rack/oauth_proxy/client_spec.rb
147
+ - spec/spec_helper.rb