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 +4 -4
- data/.travis.yml +4 -1
- data/README.md +35 -0
- data/lib/restfulness.rb +7 -0
- data/lib/restfulness/http_authentication/basic.rb +37 -0
- data/lib/restfulness/request.rb +1 -0
- data/lib/restfulness/requests/authorization.rb +22 -0
- data/lib/restfulness/requests/authorization_header.rb +22 -0
- data/lib/restfulness/resource.rb +1 -0
- data/lib/restfulness/resources/authentication.rb +21 -0
- data/lib/restfulness/version.rb +1 -1
- data/restfulness.gemspec +1 -1
- data/spec/unit/http_authentication/basic_spec.rb +51 -0
- data/spec/unit/requests/authorization_header_spec.rb +37 -0
- data/spec/unit/requests/authorization_spec.rb +32 -0
- data/spec/unit/resources/authentication_spec.rb +50 -0
- metadata +18 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed8387e688a8bb336c27f341003bdf225fb439c0
|
4
|
+
data.tar.gz: 02cef9941b4153e68215fb1497885a4472d28071
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 51d5acf131c42af07a033ef3e579bb2246fcbd4461b3b5accb9a98de7359d9267dbf0ae0e41db7aea0769520b8d7cd510f9f11e053902fe66fbf41f98b8c7411
|
7
|
+
data.tar.gz: 7c9890e63ccc7a98fb40e9f98ce42fe69168e94d7099a3e892df0e5a7862553ed6763ae3eff012ddd89fd64025fc2dacc05ae5c8f537e016e11a184ae52d617b
|
data/.travis.yml
CHANGED
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)
|
data/lib/restfulness.rb
CHANGED
@@ -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
|
data/lib/restfulness/request.rb
CHANGED
@@ -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
|
data/lib/restfulness/resource.rb
CHANGED
@@ -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
|
data/lib/restfulness/version.rb
CHANGED
data/restfulness.gemspec
CHANGED
@@ -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.
|
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-
|
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:
|
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:
|
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
|