Skip to content

Commit

Permalink
Merge pull request #703 from dspaeth-faber/probosal_for_auth_middelwa…
Browse files Browse the repository at this point in the history
…re_refactoring

Make auth middleware extendable
  • Loading branch information
dblock committed Aug 6, 2014
2 parents 1fe9e43 + ac60d6c commit c6cd3a1
Show file tree
Hide file tree
Showing 16 changed files with 221 additions and 380 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
* [#691](https://github.com/intridea/grape/issues/691): Added `at_least_one_of` parameter validator - [@dblock](https://github.com/dblock).
* [#687](https://github.com/intridea/grape/pull/687): Fix: `mutually_exclusive` and `exactly_one_of` validation error messages now label parameters as strings, consistently with `requires` and `optional` - [@dblock](https://github.com/dblock).
* [#698](https://github.com/intridea/grape/pull/698): `error!` sets `status` for `Endpoint` too - [@dspaeth-faber](https://github.com/dspaeth-faber).
* [#703](https://github.com/intridea/grape/pull/703): Added support for Auth-Middleware extension - [@dspaeth-faber](https://github.com/dspaeth-faber).
* [#703](https://github.com/intridea/grape/pull/703): Removed `Grape::Middleware::Auth::Basic` - [@dspaeth-faber](https://github.com/dspaeth-faber).
* [#703](https://github.com/intridea/grape/pull/703): Removed `Grape::Middleware::Auth::Digest` - [@dspaeth-faber](https://github.com/dspaeth-faber).
* [#703](https://github.com/intridea/grape/pull/703): Removed `Grape::Middleware::Auth::OAuth2` - [@dspaeth-faber](https://github.com/dspaeth-faber).
* Your contribution here.

0.8.0 (7/10/2014)
Expand Down
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1468,7 +1468,8 @@ formatter.

### Basic and Digest Auth

Grape has built-in Basic and Digest authentication.
Grape has built-in Basic and Digest authentication (the given `block`
is executed in the context of the current `Endpoint`).

```ruby
http_basic do |username, password|
Expand All @@ -1484,6 +1485,33 @@ http_digest({ realm: 'Test Api', opaque: 'app secret' }) do |username|
end
```

### Register custom middleware for authentication

Grape can use custom Middleware for authentication. How to implement these
Middleware have a look at `Rack::Auth::Basic` or similar implementations.


For registering a Middlewar you need the following options:

* `label` - the name for your authenticator to use it later
* `MiddlewareClass` - the MiddlewareClass to use for authentication
* `option_lookup_proc` - A Proc with one Argument to lookup the options at
runtime (return value is an `Array` as Paramter for the Middleware).

Example:

```ruby

Grape::Middleware::Auth::Strategies.add(:my_auth, AuthMiddleware, ->(options) { [options[:realm]] } )


auth :my_auth ,{ real: 'Test Api'} do |credentials|
# lookup the user's password here
{ 'user1' => 'password1' }[username]
end

```

Use [warden-oauth2](https://github.com/opperator/warden-oauth2) or [rack-oauth2](https://github.com/nov/rack-oauth2) for OAuth2 support.

## Describing and Inspecting an API
Expand Down
8 changes: 4 additions & 4 deletions lib/grape.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,10 @@ module Middleware
autoload :Error, 'grape/middleware/error'

module Auth
autoload :OAuth2, 'grape/middleware/auth/oauth2'
autoload :Base, 'grape/middleware/auth/base'
autoload :Basic, 'grape/middleware/auth/basic'
autoload :Digest, 'grape/middleware/auth/digest'
autoload :Base, 'grape/middleware/auth/base'
autoload :DSL, 'grape/middleware/auth/dsl'
autoload :StrategyInfo, 'grape/middleware/auth/strategy_info'
autoload :Strategies, 'grape/middleware/auth/strategies'
end

module Versioner
Expand Down
26 changes: 1 addition & 25 deletions lib/grape/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module Grape
# class in order to build an API.
class API
extend Validations::ClassMethods
extend Grape::Middleware::Auth::DSL

class << self
attr_reader :endpoints, :instance, :routes, :route_set, :settings, :versions
Expand Down Expand Up @@ -301,31 +302,6 @@ def helpers(new_mod = nil, &block)
end
end

# Add an authentication type to the API. Currently
# only `:http_basic`, `:http_digest` and `:oauth2` are supported.
def auth(type = nil, options = {}, &block)
if type
set(:auth, { type: type.to_sym, proc: block }.merge(options))
else
settings[:auth]
end
end

# Add HTTP Basic authorization to the API.
#
# @param [Hash] options A hash of options.
# @option options [String] :realm "API Authorization" The HTTP Basic realm.
def http_basic(options = {}, &block)
options[:realm] ||= "API Authorization"
auth :http_basic, options, &block
end

def http_digest(options = {}, &block)
options[:realm] ||= "API Authorization"
options[:opaque] ||= "secret"
auth :http_digest, options, &block
end

def mount(mounts)
mounts = { mounts => '/' } unless mounts.respond_to?(:each_pair)
mounts.each_pair do |app, path|
Expand Down
16 changes: 0 additions & 16 deletions lib/grape/endpoint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -456,22 +456,6 @@ def build_middleware
end
end

if settings[:auth]
auth_proc = settings[:auth][:proc]
auth_proc_context = self
auth_middleware = {
http_basic: { class: Rack::Auth::Basic, args: [settings[:auth][:realm]] },
http_digest: { class: Rack::Auth::Digest::MD5, args: [settings[:auth][:realm], settings[:auth][:opaque]] }
}[settings[:auth][:type]]

# evaluate auth proc in context of endpoint
if auth_middleware
b.use auth_middleware[:class], *auth_middleware[:args] do |*args|
auth_proc_context.instance_exec(*args, &auth_proc)
end
end
end

if settings[:version]
b.use Grape::Middleware::Versioner.using(settings[:version_options][:using]),
versions: settings[:version] ? settings[:version].flatten : nil,
Expand Down
40 changes: 28 additions & 12 deletions lib/grape/middleware/auth/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,41 @@
module Grape
module Middleware
module Auth
class Base < Grape::Middleware::Base
attr_reader :authenticator
class Base
attr_accessor :options, :app, :env

def initialize(app, options = {}, &authenticator)
super(app, options)
@authenticator = authenticator
def initialize(app, options = {})
@app = app
@options = options || {}
end

def base_request
raise NotImplementedError, "You must implement base_request."
def context
env['api.endpoint']
end

def credentials
base_request.provided? ? base_request.credentials : [nil, nil]
def call(env)
dup._call(env)
end

def before
unless authenticator.call(*credentials)
throw :error, status: 401, message: "API Authorization Failed."
def _call(env)
self.env = env

if options.key?(:type)
auth_proc = options[:proc]
auth_proc_context = context

strategy_info = Grape::Middleware::Auth::Strategies[options[:type]]

throw(:error, status: 401, message: "API Authorization Failed.") unless strategy_info.present?

strategy = strategy_info.create(@app, options) do |*args|
auth_proc_context.instance_exec(*args, &auth_proc)
end

strategy.call(env)

else
app.call(env)
end
end
end
Expand Down
13 changes: 0 additions & 13 deletions lib/grape/middleware/auth/basic.rb

This file was deleted.

13 changes: 0 additions & 13 deletions lib/grape/middleware/auth/digest.rb

This file was deleted.

35 changes: 35 additions & 0 deletions lib/grape/middleware/auth/dsl.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
require 'rack/auth/basic'

module Grape
module Middleware
module Auth
module DSL
# Add an authentication type to the API. Currently
# only `:http_basic`, `:http_digest` are supported.
def auth(type = nil, options = {}, &block)
if type
set(:auth, { type: type.to_sym, proc: block }.merge(options))
use Grape::Middleware::Auth::Base, settings[:auth]
else
settings[:auth]
end
end

# Add HTTP Basic authorization to the API.
#
# @param [Hash] options A hash of options.
# @option options [String] :realm "API Authorization" The HTTP Basic realm.
def http_basic(options = {}, &block)
options[:realm] ||= "API Authorization"
auth :http_basic, options, &block
end

def http_digest(options = {}, &block)
options[:realm] ||= "API Authorization"
options[:opaque] ||= "secret"
auth :http_digest, options, &block
end
end
end
end
end
83 changes: 0 additions & 83 deletions lib/grape/middleware/auth/oauth2.rb

This file was deleted.

24 changes: 24 additions & 0 deletions lib/grape/middleware/auth/strategies.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module Grape
module Middleware
module Auth
module Strategies
module_function

def add(label, strategy, option_fetcher = ->(_) { [] })
auth_strategies[label] = StrategyInfo.new(strategy, option_fetcher)
end

def auth_strategies
@auth_strategies ||= {
http_basic: StrategyInfo.new(Rack::Auth::Basic, ->(settings) {[settings[:realm]] }),
http_digest: StrategyInfo.new(Rack::Auth::Digest::MD5, ->(settings) { [settings[:realm], settings[:opaque]] })
}
end

def [](label)
auth_strategies[label]
end
end
end
end
end
15 changes: 15 additions & 0 deletions lib/grape/middleware/auth/strategy_info.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module Grape
module Middleware
module Auth
StrategyInfo = Struct.new(:auth_class, :settings_fetcher) do

def create(app, options, &block)
strategy_args = settings_fetcher.call(options)

auth_class.new(app, *strategy_args, &block)
end

end
end
end
end
Loading

0 comments on commit c6cd3a1

Please sign in to comment.