restfulness 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7c75c4e41bde16b35b2e5ea93df0958073b8d114
4
- data.tar.gz: 4d72d5c989d8d4cc1fdabdeb92475a07ee0e354a
3
+ metadata.gz: ed8387e688a8bb336c27f341003bdf225fb439c0
4
+ data.tar.gz: 02cef9941b4153e68215fb1497885a4472d28071
5
5
  SHA512:
6
- metadata.gz: f78f392d57d5d9c64fc446def406f99fe12fdad295ad415bf15a9f3d7c789def2b304face6fc1e1a60f7db8dc256002aa6bce2a936e46f69b07abdfef1046183
7
- data.tar.gz: 198200ae4575a6f756a75b142c4d07683cd1f4105cf8cd317603c018491c3d5644c5add334fbdcbeb4251b529bf6b4d648c1eb44e4256a04e1904b05c9365516
6
+ metadata.gz: 51d5acf131c42af07a033ef3e579bb2246fcbd4461b3b5accb9a98de7359d9267dbf0ae0e41db7aea0769520b8d7cd510f9f11e053902fe66fbf41f98b8c7411
7
+ data.tar.gz: 7c9890e63ccc7a98fb40e9f98ce42fe69168e94d7099a3e892df0e5a7862553ed6763ae3eff012ddd89fd64025fc2dacc05ae5c8f537e016e11a184ae52d617b
@@ -3,4 +3,7 @@ rvm:
3
3
  - 2.0.0
4
4
  - 1.9.3
5
5
  - jruby-19mode # JRuby in 1.9 mode
6
- - rbx
6
+ - rbx # Probably a bit optimistic
7
+ matrix:
8
+ allow_failures:
9
+ - rvm: rbx
data/README.md CHANGED
@@ -309,6 +309,37 @@ The `Resource#set_locale` method is called before any of the other callbacks are
309
309
  Most users will probably just want to override the `Resource#locale` method and provide the appropriate locale for the request. If you are using a User object or similar, double check your authentication process as the default `authorized?` method will be called *after* the locale is prepared.
310
310
 
311
311
 
312
+ #### Authentication in Resources
313
+
314
+ Restfulness now provides very basic support for the [HTTP Basic Authentication](http://en.wikipedia.org/wiki/Basic_access_authentication). To use it, simply call the `authenticate_with_http_basic` method in your resource definition.
315
+
316
+ Here's an example with the authentication details in the code, you'd obviously want to use something a bit more advanced than this in production:
317
+
318
+ ```ruby
319
+ def authorized?
320
+ authenticate_with_http_basic do |username, password|
321
+ return (username == 'user' && password == 'pass')
322
+ end
323
+ false
324
+ end
325
+ ```
326
+
327
+ The `request` object provided in the resource, described below, provides access to the HTTP `Authorization` header via the `Reqest#authorization` method. If you want to use an alternative authentication method you can use this to extract the details you might need. For example:
328
+
329
+ ```ruby
330
+ def authorized?
331
+ auth = request.authorization
332
+ if auth && auth.schema == 'Token'
333
+ if our_secret_token == auth.params
334
+ return true
335
+ end
336
+ end
337
+ false
338
+ end
339
+ ```
340
+
341
+ We don't yet provide support for Digest authentication, but your contributions would be more than welcome. Checkout the [HttpAuthentication/basic.rb](https://github.com/samlown/restfulness/blob/master/lib/restfulness/http_authentication/basic.rb) source for an example.
342
+
312
343
  ### Requests
313
344
 
314
345
  All resource instances have access to a `Request` object via the `#request` method, much like you'd find in a Rails project. It provides access to the details including in the HTTP request: headers, the request URL, path entries, the query, body and/or parameters.
@@ -618,6 +649,10 @@ Restfulness is still a work in progress but at Cabify we are using it in product
618
649
 
619
650
  ## History
620
651
 
652
+ ### 0.3.1 - September 19, 2014
653
+
654
+ * Added support for HTTP Basic Authentication, no breaking changes. (@samlown)
655
+
621
656
  ### 0.3.0 - May 13, 2014
622
657
 
623
658
  * Possible breaking change: `put` requests no longer check for existing resource via `exists?` callback. (@samlown)
@@ -1,6 +1,7 @@
1
1
 
2
2
  require 'uri'
3
3
  require 'multi_json'
4
+ require 'active_support'
4
5
  require 'active_support/core_ext'
5
6
  require 'active_support/dependencies'
6
7
  require 'active_support/logger'
@@ -11,7 +12,13 @@ require 'rack/builder'
11
12
 
12
13
  require 'http_accept_language/parser'
13
14
 
15
+ require "restfulness/http_authentication/basic"
16
+
14
17
  require "restfulness/resources/events"
18
+ require "restfulness/resources/authentication"
19
+
20
+ require "restfulness/requests/authorization"
21
+ require "restfulness/requests/authorization_header"
15
22
 
16
23
  require "restfulness/application"
17
24
  require "restfulness/dispatcher"
@@ -0,0 +1,37 @@
1
+ module Restfulness
2
+ module HttpAuthentication
3
+
4
+ class Basic
5
+
6
+ # The Requests::AuthorizationHeader object generated in the request
7
+ attr_accessor :header
8
+
9
+ def initialize(header)
10
+ self.header = header
11
+ end
12
+
13
+ # Determine if the header we were provided is valid.
14
+ def valid?
15
+ header.schema == 'Basic' && credentials.length == 2
16
+ end
17
+
18
+ # Attempt to decode the credentials provided in the header.
19
+ def credentials
20
+ @credentials ||= begin
21
+ txt = ::Base64.decode64(header.params || '')
22
+ txt.split(/:/, 2)
23
+ end
24
+ end
25
+
26
+ def username
27
+ credentials[0]
28
+ end
29
+
30
+ def password
31
+ credentials[1]
32
+ end
33
+
34
+ end
35
+
36
+ end
37
+ end
@@ -5,6 +5,7 @@ module Restfulness
5
5
  #
6
6
  # Currently wraps around the information provided in a Rack Request object.
7
7
  class Request
8
+ include Requests::Authorization
8
9
 
9
10
  # Who does this request belong to?
10
11
  attr_reader :app
@@ -0,0 +1,22 @@
1
+ module Restfulness
2
+ module Requests
3
+
4
+ module Authorization
5
+
6
+ def authorization
7
+ @authorization ||= begin
8
+ payload = authorization_header_payload
9
+ AuthorizationHeader.new(payload) unless payload.nil?
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def authorization_header_payload
16
+ headers[:authorization]
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ module Restfulness
2
+ module Requests
3
+
4
+ # Handle the HTTP Authorization header payload to automatically extract the scheme
5
+ # and parameters.
6
+ class AuthorizationHeader
7
+
8
+ attr_accessor :schema, :params
9
+
10
+ def initialize(payload)
11
+ (self.schema, self.params) = payload.strip.split(' ', 2)
12
+ end
13
+
14
+ def schema=(txt)
15
+ # Make sure we're in Titlecase
16
+ @schema = txt.slice(0,1).capitalize + txt.slice(1..-1).downcase
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+ end
@@ -2,6 +2,7 @@ module Restfulness
2
2
 
3
3
  class Resource
4
4
  include Resources::Events
5
+ include Resources::Authentication
5
6
 
6
7
  attr_reader :request, :response
7
8
 
@@ -0,0 +1,21 @@
1
+ module Restfulness
2
+ module Resources
3
+
4
+ # Module to support authentication in Restfulness resources.
5
+ module Authentication
6
+
7
+ # Parse the request headers for HTTP Basic Authentication details and
8
+ # run the provided block.
9
+ # If the request does not include and basic headers or the details are invalid,
10
+ # the block will not be called.
11
+ def authenticate_with_http_basic
12
+ header = request.authorization
13
+ auth = HttpAuthentication::Basic.new(header) if header
14
+ if auth && auth.valid?
15
+ yield auth.username, auth.password
16
+ end
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -1,3 +1,3 @@
1
1
  module Restfulness
2
- VERSION = "0.3.0"
2
+ VERSION = "0.3.1"
3
3
  end
@@ -25,5 +25,5 @@ Gem::Specification.new do |spec|
25
25
 
26
26
  spec.add_development_dependency "bundler", "~> 1.3"
27
27
  spec.add_development_dependency "rake"
28
- spec.add_development_dependency "rspec"
28
+ spec.add_development_dependency "rspec", "~> 2.14.1"
29
29
  end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe Restfulness::HttpAuthentication::Basic do
4
+
5
+ let :klass do
6
+ Restfulness::HttpAuthentication::Basic
7
+ end
8
+ let :header_klass do
9
+ Restfulness::Requests::AuthorizationHeader
10
+ end
11
+ let :header do
12
+ Restfulness::Requests::AuthorizationHeader.new("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==")
13
+ end
14
+
15
+ describe "#initialize" do
16
+ it "should set the header" do
17
+ obj = klass.new(header)
18
+ obj.header.should eql(header)
19
+ end
20
+ end
21
+
22
+ describe "#valid?" do
23
+ it "should detect valid schema and credentials" do
24
+ obj = klass.new(header)
25
+ obj.valid?.should be_true
26
+ end
27
+
28
+ it "should reject different schema" do
29
+ obj = klass.new(header_klass.new("Fooo Bar"))
30
+ obj.valid?.should be_false
31
+ end
32
+
33
+ it "should reject if the basic request credentials are of invalid length" do
34
+ creds = ::Base64.strict_encode64("username")
35
+ obj = klass.new(header_klass.new("Fooo #{creds}"))
36
+ obj.valid?.should be_false
37
+ end
38
+ end
39
+
40
+ describe "#credentials #username and #password" do
41
+
42
+ it "should decode and prepare the params" do
43
+ obj = klass.new(header)
44
+ obj.credentials.length.should eql(2)
45
+ obj.username.should eql('Aladdin')
46
+ obj.password.should eql('open sesame')
47
+ end
48
+
49
+ end
50
+
51
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe Restfulness::Requests::AuthorizationHeader do
4
+
5
+ describe "#initialize" do
6
+
7
+ let :klass do
8
+ Restfulness::Requests::AuthorizationHeader
9
+ end
10
+
11
+ it "should accept standard header" do
12
+ obj = klass.new("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==")
13
+ obj.schema.should eql("Basic")
14
+ obj.params.should eql("QWxhZGRpbjpvcGVuIHNlc2FtZQ==")
15
+ end
16
+
17
+ it "should accept non-standard schema" do
18
+ obj = klass.new("bAsic QWxhZGRpbjpvcGVuIHNlc2FtZQ==")
19
+ obj.schema.should eql("Basic")
20
+ obj.params.should eql("QWxhZGRpbjpvcGVuIHNlc2FtZQ==")
21
+ end
22
+
23
+ it "should ignore any whitespace" do
24
+ obj = klass.new(" Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== ")
25
+ obj.schema.should eql("Basic")
26
+ obj.params.should eql("QWxhZGRpbjpvcGVuIHNlc2FtZQ==")
27
+ end
28
+
29
+ it "should append additional stuff" do
30
+ obj = klass.new("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== foooo")
31
+ obj.schema.should eql("Basic")
32
+ obj.params.should eql("QWxhZGRpbjpvcGVuIHNlc2FtZQ== foooo")
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe Restfulness::Requests::Authorization do
4
+
5
+ let :app do
6
+ Class.new(Restfulness::Application) do
7
+ routes do
8
+ # empty
9
+ end
10
+ end
11
+ end
12
+ let :request do
13
+ Restfulness::Request.new(app)
14
+ end
15
+
16
+ describe "#authorization" do
17
+
18
+ it "should be nil if no authorization header resent" do
19
+ auth = request.authorization
20
+ auth.should be_nil
21
+ end
22
+
23
+ it "should build new authorization header when present" do
24
+ request.headers[:authorization] = "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
25
+ auth = request.authorization
26
+ auth.should be_a(Restfulness::Requests::AuthorizationHeader)
27
+ auth.schema.should eql("Basic")
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,50 @@
1
+
2
+ require 'spec_helper'
3
+
4
+ describe Restfulness::Resources::Authentication do
5
+
6
+ let :app do
7
+ Class.new(Restfulness::Application) do
8
+ routes do
9
+ # empty
10
+ end
11
+ end
12
+ end
13
+ let :request do
14
+ Restfulness::Request.new(app).tap do |req|
15
+ req.headers[:authorization] = "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
16
+ end
17
+ end
18
+ let :response do
19
+ Restfulness::Response.new(request)
20
+ end
21
+
22
+ describe "#authenticate_with_http_basic" do
23
+
24
+ class AuthResource < Restfulness::Resource
25
+ end
26
+
27
+ it "should run block and provide user and password" do
28
+ obj = AuthResource.new(request, response)
29
+ expect { |b| obj.authenticate_with_http_basic(&b) }.to yield_control
30
+ obj.authenticate_with_http_basic do |username, password|
31
+ username.should eql('Aladdin')
32
+ password.should eql('open sesame')
33
+ end
34
+ end
35
+
36
+ it "should not run block if no authorization header" do
37
+ request.headers[:authorization] = nil
38
+ obj = AuthResource.new(request, response)
39
+ expect { |b| obj.authenticate_with_http_basic(&b) }.not_to yield_control
40
+ end
41
+
42
+ it "should not run block if non-basic authorization header" do
43
+ request.headers[:authorization] = "Digest QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
44
+ obj = AuthResource.new(request, response)
45
+ expect { |b| obj.authenticate_with_http_basic(&b) }.not_to yield_control
46
+ end
47
+
48
+ end
49
+
50
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: restfulness
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Lown
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-13 00:00:00.000000000 Z
11
+ date: 2014-09-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -98,16 +98,16 @@ dependencies:
98
98
  name: rspec
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - ">="
101
+ - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '0'
103
+ version: 2.14.1
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - ">="
108
+ - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '0'
110
+ version: 2.14.1
111
111
  description: Simple REST server that focuses on resources instead of routes.
112
112
  email:
113
113
  - me@samlown.com
@@ -130,9 +130,13 @@ files:
130
130
  - lib/restfulness/dispatcher.rb
131
131
  - lib/restfulness/dispatchers/rack.rb
132
132
  - lib/restfulness/exceptions.rb
133
+ - lib/restfulness/http_authentication/basic.rb
133
134
  - lib/restfulness/path.rb
134
135
  - lib/restfulness/request.rb
136
+ - lib/restfulness/requests/authorization.rb
137
+ - lib/restfulness/requests/authorization_header.rb
135
138
  - lib/restfulness/resource.rb
139
+ - lib/restfulness/resources/authentication.rb
136
140
  - lib/restfulness/resources/events.rb
137
141
  - lib/restfulness/response.rb
138
142
  - lib/restfulness/route.rb
@@ -146,9 +150,13 @@ files:
146
150
  - spec/unit/dispatcher_spec.rb
147
151
  - spec/unit/dispatchers/rack_spec.rb
148
152
  - spec/unit/exceptions_spec.rb
153
+ - spec/unit/http_authentication/basic_spec.rb
149
154
  - spec/unit/path_spec.rb
150
155
  - spec/unit/request_spec.rb
156
+ - spec/unit/requests/authorization_header_spec.rb
157
+ - spec/unit/requests/authorization_spec.rb
151
158
  - spec/unit/resource_spec.rb
159
+ - spec/unit/resources/authentication_spec.rb
152
160
  - spec/unit/resources/events_spec.rb
153
161
  - spec/unit/response_spec.rb
154
162
  - spec/unit/route_spec.rb
@@ -184,9 +192,13 @@ test_files:
184
192
  - spec/unit/dispatcher_spec.rb
185
193
  - spec/unit/dispatchers/rack_spec.rb
186
194
  - spec/unit/exceptions_spec.rb
195
+ - spec/unit/http_authentication/basic_spec.rb
187
196
  - spec/unit/path_spec.rb
188
197
  - spec/unit/request_spec.rb
198
+ - spec/unit/requests/authorization_header_spec.rb
199
+ - spec/unit/requests/authorization_spec.rb
189
200
  - spec/unit/resource_spec.rb
201
+ - spec/unit/resources/authentication_spec.rb
190
202
  - spec/unit/resources/events_spec.rb
191
203
  - spec/unit/response_spec.rb
192
204
  - spec/unit/route_spec.rb