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 +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
|