Skip to content

Commit 80f73a1

Browse files
committed
Merged from master.
2 parents fec39fd + 316d863 commit 80f73a1

File tree

18 files changed

+444
-221
lines changed

18 files changed

+444
-221
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,6 @@ pkg
2020
.rvmrc
2121
.bundle
2222
.yardoc/*
23+
dist
2324

2425
## PROJECT::SPECIFIC

Gemfile.lock

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
grape (0.1.3)
4+
grape (0.1.5)
55
multi_json
66
multi_xml
77
rack
@@ -11,41 +11,42 @@ PATH
1111
GEM
1212
remote: http://rubygems.org/
1313
specs:
14+
ZenTest (4.5.0)
1415
diff-lcs (1.1.2)
15-
json_pure (1.4.3)
16+
json_pure (1.5.2)
1617
maruku (0.6.0)
1718
syntax (>= 1.0.0)
18-
mg (0.0.8)
19-
rake
20-
multi_json (0.0.5)
19+
multi_json (1.0.3)
2120
multi_xml (0.2.2)
22-
rack (1.2.1)
23-
rack-jsonp (1.1.0)
21+
rack (1.3.0)
22+
rack-jsonp (1.2.0)
2423
rack
25-
rack-mount (0.7.1)
24+
rack-mount (0.8.1)
2625
rack (>= 1.0.0)
27-
rack-test (0.5.4)
26+
rack-test (0.6.0)
2827
rack (>= 1.0)
29-
rake (0.8.7)
30-
rspec (2.5.0)
31-
rspec-core (~> 2.5.0)
32-
rspec-expectations (~> 2.5.0)
33-
rspec-mocks (~> 2.5.0)
34-
rspec-core (2.5.1)
35-
rspec-expectations (2.5.0)
28+
rake (0.9.2)
29+
rspec (2.6.0)
30+
rspec-core (~> 2.6.0)
31+
rspec-expectations (~> 2.6.0)
32+
rspec-mocks (~> 2.6.0)
33+
rspec-core (2.6.4)
34+
rspec-expectations (2.6.0)
3635
diff-lcs (~> 1.1.2)
37-
rspec-mocks (2.5.0)
36+
rspec-mocks (2.6.0)
3837
syntax (1.0.0)
39-
yard (0.6.1)
38+
yard (0.7.1)
4039

4140
PLATFORMS
4241
ruby
4342

4443
DEPENDENCIES
44+
ZenTest
45+
bundler
4546
grape!
4647
json_pure
4748
maruku
48-
mg
4949
rack-test
50-
rspec (~> 2.5.0)
50+
rake
51+
rspec (~> 2.6.0)
5152
yard

README.markdown

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Grape
2+
[![Build Status](http://travis-ci.org/intridea/grape.png)](http://travis-ci.org/intridea/grape)
23

34
Grape is a REST-like API micro-framework for Ruby. It is built to complement existing web application frameworks such as Rails and Sinatra by providing a simple DSL to easily provide APIs. It has built-in support for common conventions such as multiple formats, subdomain/prefix restriction, and versioning.
45

@@ -74,15 +75,12 @@ You can also return JSON formatted objects explicitly by raising error! and pass
7475

7576
## Exception Handling
7677

77-
By default Grape does not catch all unexpected exceptions. This means that the web server will handle the error and render the default error page as a result. It is possible to trap all exceptions by setting `rescue_all_errors true` instead. You may change the error format to JSON by using `error_format :json` and set the default error status to 200 with `default_error_status 200`. You may also include the complete backtrace of the exception with `rescue_with_backtrace true` either as text (for the :txt format) or as a :backtrace field in the json (for the :json format).
78+
Grape can be told to rescue certain (or all) exceptions in your
79+
application and instead display them in text or json form. To do this,
80+
you simply use the `rescue_from` method inside your API declaration:
7881

7982
class Twitter::API < Grape::API
80-
rescue_all_errors true
81-
rescue_with_backtrace true
82-
error_format :json
83-
default_error_status 200
84-
85-
# api methods
83+
rescue_from ArgumentError, NotImplementedError # :all for all errors
8684
end
8785

8886
## Note on Patches/Pull Requests

Rakefile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ require 'rubygems'
22
require 'bundler'
33
Bundler.setup :default, :test, :development
44

5-
require 'mg'
6-
MG.new('grape.gemspec')
5+
Bundler::GemHelper.install_tasks
76

87
require 'rspec/core/rake_task'
98
RSpec::Core::RakeTask.new(:spec) do |spec|

grape.gemspec

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@ Gem::Specification.new do |s|
1919
s.add_runtime_dependency 'multi_json'
2020
s.add_runtime_dependency 'multi_xml'
2121

22-
s.add_development_dependency 'mg'
22+
s.add_development_dependency 'rake'
2323
s.add_development_dependency 'maruku'
2424
s.add_development_dependency 'yard'
2525
s.add_development_dependency 'rack-test'
26-
s.add_development_dependency 'rspec', '~> 2.5.0'
26+
s.add_development_dependency 'rspec', '~> 2.6.0'
2727
s.add_development_dependency 'json_pure'
28+
s.add_development_dependency 'ZenTest'
29+
s.add_development_dependency 'bundler'
2830

2931
s.files = `git ls-files`.split("\n")
3032
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")

lib/grape.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ module Middleware
1717
module Auth
1818
autoload :OAuth2, 'grape/middleware/auth/oauth2'
1919
autoload :Basic, 'grape/middleware/auth/basic'
20+
autoload :Digest, 'grape/middleware/auth/digest'
2021
end
2122
end
2223
end

lib/grape/api.rb

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require 'rack/mount'
22
require 'rack/auth/basic'
3+
require 'rack/auth/digest/md5'
34
require 'logger'
45

56
module Grape
@@ -90,14 +91,26 @@ def default_error_status(new_status = nil)
9091
new_status ? set(:default_error_status, new_status) : settings[:default_error_status]
9192
end
9293

93-
# Specify whether to rescue all errors.
94-
def rescue_all_errors(new_value = true)
95-
set(:rescue_all_errors, new_value)
96-
end
97-
98-
# Specify whether to include error backtrace with errors.
99-
def rescue_with_backtrace(new_value = true)
100-
set(:rescue_with_backtrace, new_value)
94+
# Allows you to rescue certain exceptions that occur to return
95+
# a grape error rather than raising all the way to the
96+
# server level.
97+
#
98+
# @example Rescue from custom exceptions
99+
# class ExampleAPI < Grape::API
100+
# class CustomError < StandardError; end
101+
#
102+
# rescue_from CustomError
103+
# end
104+
#
105+
# @overload rescue_from(*exception_classes, options = {})
106+
# @param [Array] exception_classes A list of classes that you want to rescue, or
107+
# the symbol :all to rescue from all exceptions.
108+
# @param [Hash] options Options for the rescue usage.
109+
# @option options [Boolean] :backtrace Include a backtrace in the rescue response.
110+
def rescue_from(*args)
111+
set(:rescue_options, args.pop) if args.last.is_a?(Hash)
112+
set(:rescue_all, true) and return if args.include?(:all)
113+
set(:rescued_errors, args)
101114
end
102115

103116
# Add helper methods that will be accessible from any
@@ -126,7 +139,7 @@ def helpers(&block)
126139
end
127140

128141
# Add an authentication type to the API. Currently
129-
# only `:http_basic` is supported.
142+
# only `:http_basic`, `:http_digest` and `:oauth2` are supported.
130143
def auth(type = nil, options = {}, &block)
131144
if type
132145
set(:auth, {:type => type.to_sym, :proc => block}.merge(options))
@@ -143,6 +156,12 @@ def http_basic(options = {}, &block)
143156
options[:realm] ||= "API Authorization"
144157
auth :http_basic, options, &block
145158
end
159+
160+
def http_digest(options = {}, &block)
161+
options[:realm] ||= "API Authorization"
162+
options[:opaque] ||= "secret"
163+
auth :http_digest, options, &block
164+
end
146165

147166
# Defines a route that will be recognized
148167
# by the Grape API.
@@ -166,7 +185,7 @@ def route(methods, paths, &block)
166185

167186
methods.each do |method|
168187
paths.each do |path|
169-
path = Rack::Mount::Strexp.compile(compile_path(path), options, ['/'], true)
188+
path = Rack::Mount::Strexp.compile(compile_path(path), options, %w( / . ? ), true)
170189
route_set.add_route(endpoint,
171190
:path_info => path,
172191
:request_method => (method.to_s.upcase unless method == :any)
@@ -238,8 +257,14 @@ def nest(*blocks, &block)
238257

239258
def build_endpoint(&block)
240259
b = Rack::Builder.new
241-
b.use Grape::Middleware::Error, :default_status => settings[:default_error_status] || 403, :rescue => settings[:rescue_all_errors], :format => settings[:error_format] || :txt, :backtrace => settings[:rescue_with_backtrace]
260+
b.use Grape::Middleware::Error,
261+
:default_status => settings[:default_error_status] || 403,
262+
:rescue_all => settings[:rescue_all],
263+
:rescued_errors => settings[:rescued_errors],
264+
:format => settings[:error_format] || :txt,
265+
:rescue_options => settings[:rescue_options]
242266
b.use Rack::Auth::Basic, settings[:auth][:realm], &settings[:auth][:proc] if settings[:auth] && settings[:auth][:type] == :http_basic
267+
b.use Rack::Auth::Digest::MD5, settings[:auth][:realm], settings[:auth][:opaque], &settings[:auth][:proc] if settings[:auth] && settings[:auth][:type] == :http_digest
243268
b.use Grape::Middleware::Prefixer, :prefix => prefix if prefix
244269
b.use Grape::Middleware::Versioner, :versions => (version if version.is_a?(Array)) if version
245270
b.use Grape::Middleware::Formatter, :default_format => default_format || :json
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
require 'rack/auth/digest/md5'
2+
3+
module Grape
4+
module Middleware
5+
module Auth
6+
class Digest < Grape::Middleware::Base
7+
attr_reader :authenticator
8+
9+
def initialize(app, options = {}, &authenticator)
10+
super(app, options)
11+
@authenticator = authenticator
12+
end
13+
14+
def digest_request
15+
Rack::Auth::Digest::Request.new(env)
16+
end
17+
18+
def credentials
19+
digest_request.provided?? digest_request.credentials : [nil, nil]
20+
end
21+
22+
def before
23+
unless authenticator.call(*credentials)
24+
throw :error, :status => 401, :message => "API Authorization Failed."
25+
end
26+
end
27+
end
28+
end
29+
end
30+
end
Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,42 @@
11
module Grape::Middleware::Auth
2+
# OAuth 2.0 authorization for Grape APIs.
23
class OAuth2 < Grape::Middleware::Base
34
def default_options
45
{
56
:token_class => 'AccessToken',
6-
:realm => 'OAuth API'
7+
:realm => 'OAuth API',
8+
:parameter => %w(bearer_token oauth_token),
9+
:accepted_headers => %w(HTTP_AUTHORIZATION X_HTTP_AUTHORIZATION X-HTTP_AUTHORIZATION REDIRECT_X_HTTP_AUTHORIZATION),
10+
:header => [/Bearer (.*)/i, /OAuth (.*)/i]
711
}
812
end
913

1014
def before
11-
if request['oauth_token']
12-
verify_token(request['oauth_token'])
13-
elsif env['Authorization'] && t = parse_authorization_header
14-
verify_token(t)
15+
verify_token(token_parameter || token_header)
16+
end
17+
18+
def token_parameter
19+
Array(options[:parameter]).each do |p|
20+
return request[p] if request[p]
21+
end
22+
nil
23+
end
24+
25+
def token_header
26+
return false unless authorization_header
27+
Array(options[:header]).each do |regexp|
28+
if authorization_header =~ regexp
29+
return $1
30+
end
31+
end
32+
nil
33+
end
34+
35+
def authorization_header
36+
options[:accepted_headers].each do |head|
37+
return env[head] if env[head]
1538
end
39+
nil
1640
end
1741

1842
def token_class
@@ -21,10 +45,10 @@ def token_class
2145

2246
def verify_token(token)
2347
if token = token_class.verify(token)
24-
if token.expired?
48+
if token.respond_to?(:expired?) && token.expired?
2549
error_out(401, 'expired_token')
2650
else
27-
if token.permission_for?(env)
51+
if !token.respond_to?(:permission_for?) || token.permission_for?(env)
2852
env['api.token'] = token
2953
else
3054
error_out(403, 'insufficient_scope')
@@ -35,15 +59,9 @@ def verify_token(token)
3559
end
3660
end
3761

38-
def parse_authorization_header
39-
if env['Authorization'] =~ /oauth (.*)/i
40-
$1
41-
end
42-
end
43-
4462
def error_out(status, error)
4563
throw :error, {
46-
:message => 'The token provided has expired.',
64+
:message => error,
4765
:status => status,
4866
:headers => {
4967
'WWW-Authenticate' => "OAuth realm='#{options[:realm]}', error='#{error}'"
@@ -52,4 +70,4 @@ def error_out(status, error)
5270
end
5371
end
5472
end
55-
73+

0 commit comments

Comments
 (0)