restfulness 0.3.0 → 0.3.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.
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