rack-oauth-wrap 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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