roda-http-auth 0.1.2 → 0.2.0

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
  SHA256:
3
- metadata.gz: '0219bd5bc76baaeaf48e1e31d6a951dca87af4fa906af38c00099b5b8150871c'
4
- data.tar.gz: '0748fa0ac12041e995e2ee96baa02ef433d981c29be9f732eb3b0eb7222a879f'
3
+ metadata.gz: 1ff92568175fbe77129586e375501c1ef08a420b766a6f841d51f46c86c70db6
4
+ data.tar.gz: 893b85397675601e3521ab339a9fd047c76c0d8ebdcaee7e8290bffc6ec62618
5
5
  SHA512:
6
- metadata.gz: c365f2fbbf94606c0995f7b1138f4a566f44e2340d304998132671cbde59974e50fac5fb9ecc4ec54c4e1a53320902c3207f1066c09443bf794eb52311d896f7
7
- data.tar.gz: be1d27037cc49e10b867acdba4ee8d7307cceb7447748fc2625afdec779a1c36a44ef877bac9ab86e6835f15f5c66202f98bba5475e5f660c6c78a2381117c1e
6
+ metadata.gz: 1271d1c2d504363e7db7e067e2510eb4b61a2c544126b15ae5047a5135463e43b7aa35aa29db4377622aefdeea7f7b01ec8be32c1ec198500bbc937d9d98cc9c
7
+ data.tar.gz: cdc56c9d35aca78604ee22731443c5abd375401e324cec44a0d036cad3f576cfeb09ce20ac927771d8cd61dc5bdf4c2d9a96b16282d9949994db0730c688781f
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Roda Http Authorization
2
2
 
3
- [![Build Status](https://travis-ci.org/badosu/roda-basic-auth.png)](https://travis-ci.org/badosu/roda-basic-auth)
3
+ [![Build Status](https://travis-ci.org/badosu/roda-http-auth.png)](https://travis-ci.org/badosu/roda-http-auth)
4
4
 
5
5
  Add http authorization methods to Roda.
6
6
 
@@ -13,49 +13,42 @@ plugin :http_auth
13
13
  ```
14
14
 
15
15
  You can pass global options, in this context they'll be shared between all
16
- `r.http_auth` calls.
16
+ `http_auth` calls.
17
17
 
18
18
  ```ruby
19
- plugin :http_auth, authenticator: proc {|user, pass| [user, pass] == %w[foo bar]},
19
+ plugin :http_auth, authenticator: ->(user, pass) { [user, pass] == %w[foo bar] },
20
20
  realm: 'Restricted Area', # default
21
21
  schemes: %w[basic] # default
22
22
  ```
23
23
 
24
- ### Additional Configuration
24
+ ## Usage
25
25
 
26
- The header sent when the user is unauthorized can be configured via
27
- `unauthorized_headers` option, globally or locally:
26
+ Call `http_auth` inside the routes you want to authenticate the user, it will halt
27
+ the request with an empty response with status 401 if the authenticator is false.
28
28
 
29
- ```ruby
30
- unauthorized_headers: proc do |opts|
31
- {'Content-Type' => 'text/plain',
32
- 'Content-Length' => '0',
33
- 'WWW-Authenticate' => ('Basic realm="%s"' % opts[:realm])}
34
- end, # default
35
- ```
36
-
37
- The `unauthorized` option can receive a block to be invoked whenever the user
38
- is unathorized:
29
+ You can provide an `unauthorized` block to be invoked whenever the user is
30
+ unathorized, it's executed in the context of the instance:
39
31
 
40
32
  ```ruby
41
- plugin :http_auth, unauthorized: proc do |r|
42
- logger.warn("Unathorized attempt to access #{r.path}!!")
43
- end
44
- ```
33
+ plugin :http_auth, unauthorized: -> { view('401.html') }
45
34
 
46
- ## Usage
35
+ # ...
47
36
 
48
- Call `r.http_auth` inside the routes you want to authenticate the user, it will halt
49
- the request with 401 response code if the authenticator is false.
37
+ r.root do
38
+ http_auth {|u, p| [u, p] == %w[foo bar] }
50
39
 
51
- An additional `WWW-Authenticate` header is sent as specified on [rfc7235](https://tools.ietf.org/html/rfc7235#section-4.1) and it's realm can be configured.
40
+ "If you can see this you were authorized! \
41
+ Otherwise you'll be served with the 401.html.erb template"
42
+ end
43
+ ```
52
44
 
53
45
  ### Basic Auth
54
46
 
55
47
  Basic authorization is the default method:
56
48
 
57
49
  ```ruby
58
- r.http_auth { |user, pass| [user, pass] == %w[foo bar] }
50
+ # Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
51
+ http_auth { |user, pass| [user, pass] == ['Aladdin', 'open sesame'] }
59
52
  ```
60
53
 
61
54
  ### Schemes
@@ -70,19 +63,20 @@ plugin :http_auth, schemes: %w[bearer]
70
63
  You can also whitelist schemes for a specific route:
71
64
 
72
65
  ```ruby
73
- r.http_auth(schemes: %w[bearer]) { |token| token == '4t0k3n' }
66
+ http_auth(schemes: %w[bearer]) { |token| token == '4t0k3n' }
74
67
  ```
75
68
 
76
69
  ### Scheme: Bearer
77
70
 
78
- When the `Bearer` authorization is scheme is passed, if whitelisted, the token
79
- is passed to the authenticator:
71
+ When the `Bearer` scheme is passed, if whitelisted, the token is passed to
72
+ the authenticator:
80
73
 
81
74
  ```ruby
82
- r.http_auth { |token| token == '4t0k3n' }
75
+ # Authorization: Bearer 4t0k3n
76
+ http_auth { |token| token == '4t0k3n' }
83
77
  ```
84
78
 
85
- ### Formatted parameters schemes
79
+ ### Schemes with formatted parameters
86
80
 
87
81
  For schemes that require formatted params authorization header, like `Digest`,
88
82
  the scheme and the parsed params are passed to the authenticator:
@@ -102,13 +96,7 @@ Authorization: Digest username="Mufasa",
102
96
  ```
103
97
 
104
98
  ```ruby
105
- r.http_auth { |s, p| [s, p['username']] == ['digest', 'Mufasa'] }
106
- ```
107
-
108
- ## Test
109
-
110
- ```sh
111
- bundle exec ruby test/*.rb
99
+ http_auth { |s, p| [s, p['username']] == ['digest', 'Mufasa'] }
112
100
  ```
113
101
 
114
102
  ## Warden
@@ -117,12 +105,30 @@ To avoid having your 401 responses intercepted by warden, you need to configure
117
105
  the unauthenticated callback that is called just before the request is halted:
118
106
 
119
107
  ```ruby
120
- plugin :http_auth, unauthorized: proc {|r| r.env['warden'].custom_failure! }
108
+ plugin :http_auth, unauthorized: -> { env['warden'].custom_failure! }
109
+ ```
110
+
111
+ ## Additional Configuration
112
+
113
+ The header sent when the user is unauthorized can be configured via
114
+ `unauthorized_headers` and `realm` options, globally or locally:
115
+
116
+ ```ruby
117
+ unauthorized_headers: ->(opts) do
118
+ { 'WWW-Authenticate' => ('Basic realm="%s"' % opts[:realm]) }
119
+ end, # default
120
+ realm: "Restricted Area", # default
121
+ ```
122
+
123
+ ## Test
124
+
125
+ ```sh
126
+ bundle exec ruby spec/*_spec.rb
121
127
  ```
122
128
 
123
129
  ## Contributing
124
130
 
125
- Bug reports and pull requests are welcome on GitHub at https://github.com/badosu/roda-basic-auth.
131
+ Bug reports and pull requests are welcome on GitHub at https://github.com/badosu/roda-http-auth.
126
132
 
127
133
  ## License
128
134
 
@@ -5,14 +5,10 @@ module Roda::RodaPlugins
5
5
  module HttpAuth
6
6
  DEFAULTS = {
7
7
  realm: "Restricted Area",
8
- unauthorized_headers: proc do |opts|
9
- {'Content-Type' => 'text/plain',
10
- 'Content-Length' => '0',
11
- 'WWW-Authenticate' => ('Basic realm="%s"' % opts[:realm])}
12
- end,
13
- bad_request_headers: proc do |opts|
14
- {'Content-Type' => 'text/plain', 'Content-Length' => '0'}
8
+ unauthorized_headers: ->(opts) do
9
+ { 'WWW-Authenticate' => ('Basic realm="%s"' % opts[:realm]) }
15
10
  end,
11
+ unauthorized: ->(r) {},
16
12
  schemes: %w[basic]
17
13
  }
18
14
 
@@ -22,50 +18,48 @@ module Roda::RodaPlugins
22
18
  app.opts[:http_auth].freeze
23
19
  end
24
20
 
25
- module RequestMethods
21
+ module InstanceMethods
26
22
  def http_auth(opts={}, &authenticator)
27
- auth_opts = roda_class.opts[:http_auth].merge(opts)
23
+ auth_opts = request.roda_class.opts[:http_auth].merge(opts)
28
24
  authenticator ||= auth_opts[:authenticator]
29
25
 
30
26
  raise "Must provide an authenticator block" if authenticator.nil?
31
27
 
32
- begin
33
- auth = Rack::Auth::Basic::Request.new(env)
28
+ auth = Rack::Auth::Basic::Request.new(env)
29
+
30
+ unless auth.provided? && auth_opts[:schemes].include?(auth.scheme)
31
+ unauthorized(auth_opts)
32
+ end
34
33
 
35
- unless auth.provided? && auth_opts[:schemes].include?(auth.scheme)
36
- auth_opts[:unauthorized].call(self) if auth_opts[:unauthorized]
37
- halt [401, auth_opts[:unauthorized_headers].call(auth_opts), []]
38
- end
34
+ credentials = if auth.basic?
35
+ auth.credentials
36
+ elsif auth.scheme == 'bearer'
37
+ [env['HTTP_AUTHORIZATION'].split(' ', 2).last]
38
+ else
39
+ http_auth = env['HTTP_AUTHORIZATION'].split(' ', 2)
40
+ .last
39
41
 
40
- credentials = if auth.basic?
41
- auth.credentials
42
- elsif auth.scheme == 'bearer'
43
- [env['HTTP_AUTHORIZATION'].strip.split(' ').last]
44
- else
45
- [auth.scheme, _extract_credentials]
46
- end
42
+ creds = !http_auth.include?('=') ? http_auth :
43
+ Rack::Auth::Digest::Params.parse(http_auth)
47
44
 
48
- if authenticator.call(*credentials)
49
- env['REMOTE_USER'] = auth.username
50
- else
51
- opts[:unauthorized].call(self) if auth_opts[:unauthorized]
52
- halt [401, auth_opts[:unauthorized_headers].call(auth_opts), []]
53
- end
54
- rescue StandardError
55
- halt [400, auth_opts[:bad_request_headers].call(auth_opts), []]
45
+ [auth.scheme, creds]
46
+ end
47
+
48
+ if authenticator.call(*credentials)
49
+ env['REMOTE_USER'] = auth.username
50
+ else
51
+ unauthorized(auth_opts)
56
52
  end
57
53
  end
58
54
 
59
- def _extract_credentials
60
- authorization = env['HTTP_AUTHORIZATION'].split(' ', 2).last
61
- parts = authorization.split(',')
62
-
63
- return parts.first if parts.one? && !parts.first.include?('=')
55
+ private
64
56
 
65
- key_values = parts.map {|p| p.strip.split(/\=\"?/) }
66
- .map {|k, v| [k, v.chomp('"').gsub(/\\\"/, '"')] }
57
+ def unauthorized(opts)
58
+ response.status = 401
59
+ response.headers.merge!(opts[:unauthorized_headers].call(opts))
67
60
 
68
- Hash[key_values]
61
+ request.block_result(instance_exec(request, &opts[:unauthorized]))
62
+ request.halt response.finish
69
63
  end
70
64
  end
71
65
  end
@@ -1,7 +1,7 @@
1
1
  class Roda
2
2
  module RodaPlugins
3
3
  module HttpAuth
4
- VERSION = "0.1.2"
4
+ VERSION = "0.2.0"
5
5
  end
6
6
  end
7
7
  end
@@ -24,4 +24,5 @@ Gem::Specification.new do |spec|
24
24
  spec.add_development_dependency "rake", "~> 12.3"
25
25
  spec.add_development_dependency "minitest"
26
26
  spec.add_development_dependency "rack-test"
27
+ spec.add_development_dependency "tilt"
27
28
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: roda-http-auth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Amadeus Folego
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-10-02 00:00:00.000000000 Z
11
+ date: 2018-10-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: roda
@@ -100,6 +100,20 @@ dependencies:
100
100
  - - ">="
101
101
  - !ruby/object:Gem::Version
102
102
  version: '0'
103
+ - !ruby/object:Gem::Dependency
104
+ name: tilt
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
103
117
  description: Add http authorization methods to Roda
104
118
  email:
105
119
  - amadeusfolego@gmail.com