rack-oauth-wrap 0.5.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.
@@ -0,0 +1,80 @@
1
+ require 'rack/auth/abstract/handler'
2
+ require 'rack/auth/abstract/request'
3
+ require 'lib/simplewebtoken'
4
+
5
+ module Rack
6
+ module Auth
7
+ # Rack::Auth::WRAP implements oAuth WRAP Authentication, as per draft-hardt-oauth-01.
8
+ # This is a preliminary version based on the Jan 15, 2010 Web Resource Access Profiles as
9
+ # developed by the IETF.
10
+ #
11
+ # Initialize with the Rack application that you want protecting,
12
+ # and a set of parameters that enables specific checks. The only mandatory parameter
13
+ # is **:shared_secret** which is required for HMAC-SHA256 processing.
14
+ #
15
+ # See also: SimpleWebToken::SimpleWebTokenHandler
16
+ class WRAP < AbstractHandler
17
+ # Middleware Gem Versioning
18
+ VERSION = "0.5.1"
19
+
20
+ # Creates a new instance of Rack::Auth::WRAP, the opts can be used
21
+ # as the following.
22
+ #
23
+ # use Rack::Auth::WRAP, :shared_secret => *secret*,
24
+ # :trusted_issuers => "http://sts.mycomp.com",
25
+ # :audiences => "http://app.domain.com"
26
+ #
27
+ # The parameters on the sample above are the only one that are currently supported
28
+ # by the SimpleWebToken handler. For more information see SimpleWebToken::SimpleWebTokenHandler
29
+ def initialize(app, opts = {})
30
+ @app = app
31
+ @opts = opts
32
+ end
33
+
34
+ # Authenticates the request when it has the HTTP_AUTHORIZATION header,
35
+ # and if the header has WRAP as the authentication format.
36
+ #
37
+ # NOTE: it is sent by the client as Authorization, but Rack maps it to
38
+ # HTTP_AUTHORIZATION.</strong>
39
+ #
40
+ # If the user is successfuly authenticated the resulting token is
41
+ # stored on REMOTE_USER into the enviroment. (We didn't want to couple it with session)
42
+ def call(env)
43
+ request = Request.new(env)
44
+
45
+ if(request.provided? and request.is_wrap?)
46
+ return unauthorized('WRAP') unless token_handler.valid?(request.token)
47
+ env['REMOTE_USER'] = token_handler.parse(request.token)
48
+ end
49
+
50
+ return @app.call(env)
51
+ end
52
+
53
+ # Returns a singleton instance of the SimpleWebToken::SimpleWebTokenHandler based on
54
+ # the options provided when initializing the middleware.
55
+ def token_handler
56
+ @token_handler ||= SimpleWebToken::SimpleWebTokenHandler.new(@opts)
57
+ end
58
+
59
+ # Internal class used to parse the current request based on
60
+ # the enviroment parameters.
61
+ class Request < Rack::Auth::AbstractRequest
62
+ def initialize(env)
63
+ super(env)
64
+ end
65
+
66
+ # Returns a value indicating whether the Authentication Scheme sent by
67
+ # the user is WRAP.
68
+ def is_wrap?
69
+ self.scheme == :wrap
70
+ end
71
+
72
+ # Returns the token contained inside the access_token parameter
73
+ # on the Authorization header, when it's using the WRAP Scheme.
74
+ def token
75
+ CGI.unescape(self.params[/access_token=([^&]+)/, 1])
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,8 @@
1
+ $LOAD_PATH.unshift(File.dirname __FILE__)
2
+
3
+ require 'cgi'
4
+ require 'base64'
5
+ require 'hmac/sha2'
6
+
7
+ require 'swt/exceptions'
8
+ require 'swt/simple_web_token_handler'
@@ -0,0 +1,13 @@
1
+ module SimpleWebToken
2
+ class InvalidOption < StandardError
3
+ def initialize(missing_option)
4
+ super("You did not provide one of the required parameters. Please provide the :#{missing_option}.")
5
+ end
6
+ end
7
+
8
+ class InvalidToken < StandardError
9
+ def initialize
10
+ super("The token you are trying to parse is invalid. Cannot parse invalid Tokens")
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,84 @@
1
+ module SimpleWebToken
2
+ # Handler for parsing, validating and creating (soon) Simple Web Tokens
3
+ # as it stated by the protocol under development of the IEFT v0.9.5.1.
4
+ class SimpleWebTokenHandler
5
+ attr_accessor :shared_secret, :trusted_issuers, :audiences
6
+
7
+ # Creates a new instance of the SimpleWebTokenHandler.
8
+ #
9
+ # Valid options include:
10
+ #
11
+ # -__:shared_secret__ the HMAC:SHA256 key shared between parties.
12
+ # -__:trusted_issuers__ the URI(s) of the issuers to be validated on the Issue value of the token.
13
+ # -__:audiences__ the URI(s) of the audiences (apps) to be validated on the Audience value of the token.
14
+ #
15
+ # __Only :shared_secret__ is required, the other values aren't present then no check
16
+ # is performed.
17
+ def initialize(opts = {})
18
+ raise InvalidOption, :shared_secret unless opts[:shared_secret]
19
+ self.shared_secret = opts[:shared_secret]
20
+ self.trusted_issuers = opts[:trusted_issuers]
21
+ self.audiences = opts[:audiences]
22
+ end
23
+
24
+ # Validates the signature by doing a symmetric signature comparison,
25
+ # between the value sent as HMACSHA256 on the token and the generated
26
+ # using the shared_key provided.
27
+ def valid_signature?(token)
28
+ return false unless token =~ /&HMACSHA256=(.*)$/
29
+ original_signature = CGI.unescape(token[/&HMACSHA256=(.*)$/, 1])
30
+ bare_token = token.gsub(/&HMACSHA256=(.*)$/, '')
31
+ signature = Base64.encode64(HMAC::SHA256.new(Base64.decode64(shared_secret)).update(bare_token.toutf8).digest).strip
32
+ return original_signature == signature
33
+ end
34
+
35
+ # Returns a value indicating whether the __Issuer__ value of the token
36
+ # is contained on the trusted_issuer list for the application.
37
+ def valid_issuer?(token)
38
+ issuer = token[/&?Issuer=([^&]+)/, 1]
39
+ [trusted_issuers].flatten.include?(CGI.unescape(issuer))
40
+ end
41
+
42
+ # Returns a value indicating whether the __Audience__ value of the token
43
+ # is contained on the audiences list of the application.
44
+ def valid_audience?(token)
45
+ audience = token[/&?Audience=([^&]+)/, 1]
46
+ [audiences].flatten.include?(CGI.unescape(audience))
47
+ end
48
+
49
+ # Returns a value indicating whether the __ExpiresOn__ value of the token
50
+ # is older than now.
51
+ def expired?(token)
52
+ expires_on = token[/&?ExpiresOn=([^&]+)/, 1]
53
+ expires_on.to_i < Time.now.to_i
54
+ end
55
+
56
+ # Returns a value indicating whether the token is valid, the calculation
57
+ # is done as the sum of all the other validations (when values for checking are provided)
58
+ def valid?(token)
59
+ valid = valid_signature?(token)
60
+ valid &&= valid_issuer?(token) if (trusted_issuers)
61
+ valid &&= valid_audience?(token) if (audiences)
62
+ valid &&= !expired?(token)
63
+ return valid
64
+ end
65
+
66
+ # Returns a key-value pair (hash) with the token values parsed.
67
+ #
68
+ # __NOTE__: multi-valued claims (provided as comma separated values,
69
+ # like checkboxes on HTML forms) are returned like arrays.
70
+ def parse(token)
71
+ raise InvalidToken unless valid?(token)
72
+ token.split('&').map{|p| p.split('=') } \
73
+ .inject({}){|t, i| t.merge!(CGI.unescape(i[0]) => value_for(CGI.unescape(i[1])))}
74
+ end
75
+
76
+ private
77
+ # Returns an array if the value is multi-valued
78
+ # else returns a the value plain.
79
+ def value_for(value)
80
+ values = value.split(',').map{|i| i.strip}
81
+ return values.size == 1 ? values.first() : values
82
+ end
83
+ end
84
+ end
data/rakefile ADDED
@@ -0,0 +1,51 @@
1
+ require 'rake'
2
+ require 'rubygems'
3
+ require 'spec/rake/spectask'
4
+ require 'rake/gempackagetask'
5
+ require 'rake/rdoctask'
6
+
7
+ require 'lib/rack/auth/wrap'
8
+
9
+ namespace :test do
10
+ Spec::Rake::SpecTask.new('run_with_rcov') do |t|
11
+ t.spec_files = FileList['spec/rack/auth/*.rb', 'spec/swt/*.rb'].reject{|f| f.include?('functional')}
12
+ t.rcov = true
13
+ t.rcov_opts = ['--text-report', '--exclude', "exclude.*/.gem,spec,Library,#{ENV['GEM_HOME']}", '--sort', 'coverage' ]
14
+ t.spec_opts = ["--colour", "--loadby random", "--format progress", "--backtrace"]
15
+ end
16
+ end
17
+
18
+ namespace :docs do
19
+ Rake::RDocTask.new do |t|
20
+ t.rdoc_dir = 'sdk/public/'
21
+ t.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
22
+ t.options << '--charset' << 'utf-8'
23
+ t.rdoc_files.include('README.rdoc')
24
+ t.rdoc_files.include('lib/**/*.rb')
25
+ end
26
+ end
27
+
28
+
29
+ namespace :dist do
30
+ spec = Gem::Specification.new do |s|
31
+ s.name = 'rack-oauth-wrap'
32
+ s.version = Gem::Version.new(Rack::Auth::WRAP::VERSION)
33
+ s.summary = "Rack Middleware for authenticating users using oAuth WRAP Protocol"
34
+ s.description = "A simple implementation of Web Resource Authorization Protocol (WRAP) for Rack as middleware."
35
+ s.email = 'johnny.halife@me.com'
36
+ s.author = 'Johnny G. Halife & Juan Pablo Garcia Dalolla'
37
+ s.homepage = 'http://rack-oauth-wrap.heroku.com'
38
+ s.require_paths = ["lib"]
39
+ s.files = FileList['rakefile', 'lib/**/*.rb']
40
+ s.test_files = Dir['spec/**/*']
41
+ s.has_rdoc = true
42
+ s.rdoc_options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
43
+
44
+ # Dependencies
45
+ s.add_dependency 'ruby-hmac'
46
+ end
47
+
48
+ Rake::GemPackageTask.new(spec) do |pkg|
49
+ pkg.need_tar = true
50
+ end
51
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec/specs_config'
2
+ require 'lib/rack/auth/wrap'
3
+
4
+ describe "Request behavior for Client calls protected resource using HTTP Header" do
5
+ it "should tell if the request is using WRAP Authentication method" do
6
+ env = Rack::MockRequest.env_for("/", 'HTTP_AUTHORIZATION' => 'WRAP access_token=invalid_token')
7
+ request = Rack::Auth::WRAP::Request.new(env)
8
+ request.is_wrap?.should == true
9
+ end
10
+
11
+ it "should tell if the request isn't using WRAP Authentication method" do
12
+ env = Rack::MockRequest.env_for("/", 'HTTP_AUTHORIZATION' => 'MD5 an_invalid_hash')
13
+ request = Rack::Auth::WRAP::Request.new(env)
14
+ request.is_wrap?.should == false
15
+ end
16
+
17
+ it "should tell whether the request is given or not" do
18
+ wrap_env = Rack::MockRequest.env_for("/", 'HTTP_AUTHORIZATION' => 'WRAP with_token')
19
+ wrap_request = Rack::Auth::WRAP::Request.new(wrap_env)
20
+ wrap_request.provided?.should == true
21
+
22
+ non_wrap_env = Rack::MockRequest.env_for("/")
23
+ non_wrap_request = Rack::Auth::WRAP::Request.new(non_wrap_env)
24
+ non_wrap_request.provided?.should == false
25
+ end
26
+
27
+ it "should return the token unescaped from the request" do
28
+ simple_web_token = {'Issuer' => 'http://myidentityprovider2/',
29
+ 'Audience' => 'http://site/',
30
+ 'ExpiresOn' => (Time.now.to_i + 60).to_s}.map{|k, v| "#{k}=#{CGI.escape(v)}"}.join("&")
31
+
32
+ env = Rack::MockRequest.env_for("/", 'HTTP_AUTHORIZATION' => "WRAP access_token=#{CGI.escape(simple_web_token)}")
33
+ request = Rack::Auth::WRAP::Request.new(env)
34
+ request.token.should == simple_web_token
35
+ end
36
+ end
37
+
@@ -0,0 +1,59 @@
1
+ require 'spec/specs_config'
2
+ require 'lib/rack/auth/wrap'
3
+
4
+ describe "OAuth WRAP v.0.9.7.2 Authentication Mechanism using SWT on Rack Module" do
5
+ it "should return 401 with WWW-Authenticate: WRAP when a token is invalid" do
6
+ env = Rack::MockRequest.env_for("/", 'Authorization' => 'WRAP access_token=invalid_token')
7
+
8
+ (mock_app = mock).expects(:call).with(env).never
9
+ (mock_validator = mock).expects(:valid?).with("invalid_token").returns(false)
10
+
11
+ (mock_request = mock).stubs(:token).returns("invalid_token")
12
+ mock_request.stubs(:provided?).returns(true)
13
+ mock_request.stubs(:is_wrap?).returns(true)
14
+
15
+ Rack::Auth::WRAP::Request.expects(:new).with(env).returns(mock_request)
16
+ SimpleWebToken::SimpleWebTokenHandler.expects(:new).with(:shared_secret => "foo_bar").returns(mock_validator)
17
+
18
+ response_code, headers, body = Rack::Auth::WRAP.new(mock_app, :shared_secret => "foo_bar").call(env)
19
+
20
+ response_code.should == 401
21
+ headers['WWW-Authenticate'].should == 'WRAP'
22
+ headers['Content-Length'].should == '0'
23
+ end
24
+
25
+ it "should not run assertions when not provided" do
26
+ env = Rack::MockRequest.env_for("/")
27
+ (mock_app = mock).expects(:call).with(env).returns([200, {'Content-Length' => '0'}]).once
28
+
29
+ SimpleWebToken::SimpleWebTokenHandler.expects(:new).never
30
+ response_code, headers, body = Rack::Auth::WRAP.new(mock_app, :shared_secret => "foo_bar").call(env)
31
+
32
+ response_code.should == 200
33
+ headers['Content-Length'].should == '0'
34
+ end
35
+
36
+ it "should assign pased token to REMOTE_USER when valid" do
37
+ env = Rack::MockRequest.env_for("/", 'Authorization' => 'WRAP access_token=token')
38
+
39
+ (mock_app = mock).expects(:call).with(env).returns([200, {'Content-Length' => '0'}]).once
40
+
41
+ (mock_handler = mock).expects(:valid?).with("token").returns(true)
42
+ (mock_handler).expects(:parse).with("token").returns({'UserName' => "us3r", "ExpiresOn" => "1234098765"})
43
+
44
+ (mock_request = mock).stubs(:token).returns("token")
45
+ mock_request.stubs(:provided?).returns(true)
46
+ mock_request.stubs(:is_wrap?).returns(true)
47
+
48
+ Rack::Auth::WRAP::Request.expects(:new).with(env).returns(mock_request)
49
+ SimpleWebToken::SimpleWebTokenHandler.expects(:new).with(:shared_secret => "foo_bar").returns(mock_handler)
50
+
51
+ response_code, headers, body = Rack::Auth::WRAP.new(mock_app, :shared_secret => "foo_bar").call(env)
52
+
53
+ response_code.should == 200
54
+ headers['Content-Length'].should == '0'
55
+ env['REMOTE_USER'].nil?.should == false
56
+ env['REMOTE_USER']['UserName'].should == 'us3r'
57
+ env['REMOTE_USER']['ExpiresOn'].nil?.should == false
58
+ end
59
+ end
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require 'mocha'
4
+ require 'rack/test'
5
+ require 'rack/mock'
6
+
7
+ Spec::Runner.configure do |config|
8
+ config.mock_with :mocha
9
+ end
@@ -0,0 +1,184 @@
1
+ require 'spec/specs_config'
2
+ require 'lib/simplewebtoken'
3
+
4
+ describe "simple web token hanlder behavior" do
5
+ before do
6
+ @shared_secret = "N4QeKa3c062VBjnVK6fb+rnwURkcwGXh7EoNK34n0uM="
7
+ end
8
+
9
+ it "should validate hmac256 signature of the token" do
10
+ simple_web_token = {'Issuer' => 'http://myidentityprovider/',
11
+ 'Audience' => 'http://myapp'}.map{|k, v| "#{k}=#{CGI.escape(v)}"}.join("&")
12
+
13
+ signature = Base64.encode64(HMAC::SHA256.new(Base64.decode64(@shared_secret)).update(simple_web_token.toutf8).digest).strip
14
+ simple_web_token += "&HMACSHA256=#{CGI.escape(signature)}"
15
+
16
+ handler = SimpleWebToken::SimpleWebTokenHandler.new(:shared_secret => @shared_secret)
17
+ handler.valid_signature?(simple_web_token).should == true
18
+ end
19
+
20
+ it "should validate the issuer when a single one given" do
21
+ simple_web_token = {'Issuer' => 'http://myidentityprovider/',
22
+ 'Audience' => 'http://myapp'}.map{|k, v| "#{k}=#{CGI.escape(v)}"}.join("&")
23
+
24
+ handler = SimpleWebToken::SimpleWebTokenHandler.new(:shared_secret => @shared_secret, :trusted_issuers => 'http://myidentityprovider/')
25
+ handler.valid_issuer?(simple_web_token).should == true
26
+ end
27
+
28
+ it "should validate the issuer when a multiple issuers are trusted" do
29
+ simple_web_token = {'Issuer' => 'http://myidentityprovider2/',
30
+ 'Audience' => 'http://myapp'}.map{|k, v| "#{k}=#{CGI.escape(v)}"}.join("&")
31
+
32
+ trusted_issuers = ["http://myidentityprovider/", "http://myidentityprovider2/"]
33
+ handler = SimpleWebToken::SimpleWebTokenHandler.new(:shared_secret => @shared_secret, :trusted_issuers => trusted_issuers)
34
+ handler.valid_issuer?(simple_web_token).should == true
35
+ end
36
+
37
+ it "should validate the audience when a single audience is provided" do
38
+ simple_web_token = {'Issuer' => 'http://myidentityprovider2/',
39
+ 'Audience' => 'http://myapp'}.map{|k, v| "#{k}=#{CGI.escape(v)}"}.join("&")
40
+
41
+ handler = SimpleWebToken::SimpleWebTokenHandler.new(:shared_secret => @shared_secret, :audiences => 'http://myapp')
42
+ handler.valid_audience?(simple_web_token).should == true
43
+ end
44
+
45
+ it "should validate the audience when a multiple audiences are provided" do
46
+ simple_web_token = {'Issuer' => 'http://myidentityprovider2/',
47
+ 'Audience' => 'http://site/'}.map{|k, v| "#{k}=#{CGI.escape(v)}"}.join("&")
48
+
49
+ audiences = ["http://site/", "http://mysitealias/"]
50
+ handler = SimpleWebToken::SimpleWebTokenHandler.new(:shared_secret => @shared_secret, :audiences => audiences)
51
+ handler.valid_audience?(simple_web_token).should == true
52
+ end
53
+
54
+ it "should validate if it's expired" do
55
+ simple_web_token = {'Issuer' => 'http://myidentityprovider2/',
56
+ 'ExpiresOn' => (Time.now.to_i + 60).to_s}.map{|k, v| "#{k}=#{CGI.escape(v)}"}.join("&")
57
+
58
+ handler = SimpleWebToken::SimpleWebTokenHandler.new(:shared_secret => @shared_secret)
59
+ handler.expired?(simple_web_token).should == false
60
+ end
61
+
62
+ it "should tell that the token is expired" do
63
+ simple_web_token = {'Issuer' => 'http://myidentityprovider2/',
64
+ 'ExpiresOn' => (Time.now.to_i - 60).to_s}.map{|k, v| "#{k}=#{CGI.escape(v)}"}.join("&")
65
+
66
+ signature = Base64.encode64(HMAC::SHA256.new(Base64.decode64(@shared_secret)).update(simple_web_token.toutf8).digest).strip
67
+ simple_web_token += "&HMACSHA256=#{CGI.escape(signature)}"
68
+
69
+ handler = SimpleWebToken::SimpleWebTokenHandler.new(:shared_secret => @shared_secret)
70
+ handler.expired?(simple_web_token).should == true
71
+ end
72
+
73
+ it "should throw an exception when no shared secret is provided" do
74
+ lambda {SimpleWebToken::SimpleWebTokenHandler.new}.should raise_error SimpleWebToken::InvalidOption
75
+ end
76
+
77
+ it "should tell that a token is valid when all their components are valid" do
78
+ simple_web_token = {'Issuer' => 'http://myidentityprovider2/',
79
+ 'Audience' => 'http://site/',
80
+ 'ExpiresOn' => (Time.now.to_i + 60).to_s}.map{|k, v| "#{k}=#{CGI.escape(v)}"}.join("&")
81
+
82
+ signature = Base64.encode64(HMAC::SHA256.new(Base64.decode64(@shared_secret)).update(simple_web_token.toutf8).digest).strip
83
+ simple_web_token += "&HMACSHA256=#{CGI.escape(signature)}"
84
+
85
+ handler = SimpleWebToken::SimpleWebTokenHandler.new(:shared_secret => @shared_secret)
86
+ handler.valid?(simple_web_token).should == true
87
+ end
88
+
89
+ it "should tell that a token is invalid when no HMAC signature is provided" do
90
+ simple_web_token = {'Issuer' => 'http://myidentityprovider2/',
91
+ 'Audience' => 'http://site/',
92
+ 'ExpiresOn' => (Time.now.to_i + 60).to_s}.map{|k, v| "#{k}=#{CGI.escape(v)}"}.join("&")
93
+
94
+ handler = SimpleWebToken::SimpleWebTokenHandler.new(:shared_secret => @shared_secret)
95
+ handler.valid?(simple_web_token).should == false
96
+ end
97
+
98
+ it "should tell that a token is invalid when the token is expired" do
99
+ simple_web_token = {'Issuer' => 'http://myidentityprovider2/',
100
+ 'Audience' => 'http://site/',
101
+ 'ExpiresOn' => (Time.now.to_i - 60).to_s}.map{|k, v| "#{k}=#{CGI.escape(v)}"}.join("&")
102
+
103
+ signature = Base64.encode64(HMAC::SHA256.new(Base64.decode64(@shared_secret)).update(simple_web_token.toutf8).digest).strip
104
+ simple_web_token += "&HMACSHA256=#{CGI.escape(signature)}"
105
+
106
+ handler = SimpleWebToken::SimpleWebTokenHandler.new(:shared_secret => @shared_secret)
107
+ handler.valid?(simple_web_token).should == false
108
+ end
109
+
110
+ it "should tell that a token is invalid when audience isn't trusted" do
111
+ simple_web_token = {'Issuer' => 'http://myidentityprovider2/',
112
+ 'Audience' => 'http://site/',
113
+ 'ExpiresOn' => (Time.now.to_i + 60).to_s}.map{|k, v| "#{k}=#{CGI.escape(v)}"}.join("&")
114
+
115
+ signature = Base64.encode64(HMAC::SHA256.new(Base64.decode64(@shared_secret)).update(simple_web_token.toutf8).digest).strip
116
+ simple_web_token += "&HMACSHA256=#{CGI.escape(signature)}"
117
+
118
+ handler = SimpleWebToken::SimpleWebTokenHandler.new(:shared_secret => @shared_secret,
119
+ :audiences => "http://untrusted_audience/")
120
+ handler.valid?(simple_web_token).should == false
121
+ end
122
+
123
+ it "should tell that a token is invalid when audience isn't trusted" do
124
+ simple_web_token = {'Issuer' => 'http://myidentityprovider2/',
125
+ 'Audience' => 'http://site/',
126
+ 'ExpiresOn' => (Time.now.to_i + 60).to_s}.map{|k, v| "#{k}=#{CGI.escape(v)}"}.join("&")
127
+
128
+ signature = Base64.encode64(HMAC::SHA256.new(Base64.decode64(@shared_secret)).update(simple_web_token.toutf8).digest).strip
129
+ simple_web_token += "&HMACSHA256=#{CGI.escape(signature)}"
130
+
131
+ handler = SimpleWebToken::SimpleWebTokenHandler.new(:shared_secret => @shared_secret,
132
+ :trusted_issuers => "http://untrusted_issuer")
133
+ handler.valid?(simple_web_token).should == false
134
+ end
135
+
136
+ it "should raise invalid token exception while trying to parse a token that isnt valid" do
137
+ simple_web_token = {'Issuer' => 'http://myidentityprovider2/',
138
+ 'Audience' => 'http://site/',
139
+ 'ExpiresOn' => (Time.now.to_i + 60).to_s}.map{|k, v| "#{k}=#{CGI.escape(v)}"}.join("&")
140
+
141
+ signature = Base64.encode64(HMAC::SHA256.new(Base64.decode64(@shared_secret)).update(simple_web_token.toutf8).digest).strip
142
+ simple_web_token += "&HMACSHA256=#{CGI.escape(signature)}"
143
+
144
+ handler = SimpleWebToken::SimpleWebTokenHandler.new(:shared_secret => @shared_secret,
145
+ :trusted_issuers => "http://untrusted_issuer")
146
+
147
+ lambda{ handler.parse(simple_web_token) }.should raise_error SimpleWebToken::InvalidToken
148
+ end
149
+
150
+
151
+ it "should return a dictionary when parsing a valid token" do
152
+ simple_web_token = {'Issuer' => 'http://myidentityprovider2/',
153
+ 'Audience' => 'http://site/',
154
+ 'org.security.email' => "myemail@mydomain.com",
155
+ 'ExpiresOn' => (Time.now.to_i + 60).to_s}.map{|k, v| "#{CGI.escape(k)}=#{CGI.escape(v)}"}.join("&")
156
+
157
+ signature = Base64.encode64(HMAC::SHA256.new(Base64.decode64(@shared_secret)).update(simple_web_token.toutf8).digest).strip
158
+ simple_web_token += "&HMACSHA256=#{CGI.escape(signature)}"
159
+
160
+ handler = SimpleWebToken::SimpleWebTokenHandler.new(:shared_secret => @shared_secret)
161
+ token = handler.parse(simple_web_token)
162
+
163
+ token.nil?.should == false
164
+ token['Audience'].should == "http://site/"
165
+ token['org.security.email'].should == "myemail@mydomain.com"
166
+ end
167
+
168
+ it "should return a values as tokens when sent as csv" do
169
+ simple_web_token = {'Issuer' => 'http://myidentityprovider2/',
170
+ 'Audience' => 'http://site/',
171
+ 'org.security.email' => "myemail@mydomain.com",
172
+ 'Roles' => 'roleA, roleB, role1, role2',
173
+ 'ExpiresOn' => (Time.now.to_i + 60).to_s}.map{|k, v| "#{CGI.escape(k)}=#{CGI.escape(v)}"}.join("&")
174
+
175
+ signature = Base64.encode64(HMAC::SHA256.new(Base64.decode64(@shared_secret)).update(simple_web_token.toutf8).digest).strip
176
+ simple_web_token += "&HMACSHA256=#{CGI.escape(signature)}"
177
+
178
+ handler = SimpleWebToken::SimpleWebTokenHandler.new(:shared_secret => @shared_secret)
179
+ token = handler.parse(simple_web_token)
180
+
181
+ token.nil?.should == false
182
+ token['Roles'].should == ["roleA", "roleB", "role1", "role2"]
183
+ end
184
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-oauth-wrap
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.1
5
+ platform: ruby
6
+ authors:
7
+ - Johnny G. Halife & Juan Pablo Garcia Dalolla
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-02-19 00:00:00 -03:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: ruby-hmac
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description: A simple implementation of Web Resource Authorization Protocol (WRAP) for Rack as middleware.
26
+ email: johnny.halife@me.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - rakefile
35
+ - lib/rack/auth/wrap.rb
36
+ - lib/simplewebtoken.rb
37
+ - lib/swt/exceptions.rb
38
+ - lib/swt/simple_web_token_handler.rb
39
+ has_rdoc: true
40
+ homepage: http://rack-oauth-wrap.heroku.com
41
+ licenses: []
42
+
43
+ post_install_message:
44
+ rdoc_options:
45
+ - --line-numbers
46
+ - --inline-source
47
+ - -A cattr_accessor=object
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "0"
61
+ version:
62
+ requirements: []
63
+
64
+ rubyforge_project:
65
+ rubygems_version: 1.3.5
66
+ signing_key:
67
+ specification_version: 3
68
+ summary: Rack Middleware for authenticating users using oAuth WRAP Protocol
69
+ test_files:
70
+ - spec/rack/auth/wrap_request_spec.rb
71
+ - spec/rack/auth/wrap_spec.rb
72
+ - spec/specs_config.rb
73
+ - spec/swt/simple_web_token_handler_spec.rb