heroku-bouncer 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 85c6929c3208eb743c042d9dbef6f9d43db08987
4
- data.tar.gz: b08dd7bac4866f854c050a65011ec6403936cb93
2
+ SHA256:
3
+ metadata.gz: c50847511ce57c696fca30c5a7cfa9c6bf8f70d16ee66130b9f83f81c612c252
4
+ data.tar.gz: 0ace5e96d9dc4dd373b670fddcf7dccc23db906166ccefeeda5761b70a037c67
5
5
  SHA512:
6
- metadata.gz: 5370531c477251b6247116d86597c9cc4d35ade54fa1dd5bb513e2c51a5db3f09b926a99cf573a5b1674c5dcc0980887c41db12598d9f9459c8f93ae011e17c2
7
- data.tar.gz: a748a855a7be7e07e9756c5e9407f2db0a08d12dedff83f0fe4473a53ecc205058a6b1eb2113f7ff524bb5fae25d642dc83fcc1dc18e23d3bb1218319dbf671b
6
+ metadata.gz: 341161c33386f2db7f369ad4dfa68e54046e4a2d794ae785d883a3925cb2c10a3ba178beeae65b8197ac9e8571248c7949a17d5ba6d020cb0763c7f1f72906eb
7
+ data.tar.gz: a79e4c4d4866135fd34d88fb9d4f66096fc20941d306f8bf343eee57f2c007af1672ea9604ea1548f3b99e46424d208fe0dad93ab5cf0aa1698663591f830c1a
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # 0.9.0
2
+
3
+ * #68: Loosen `omniauth-heroku` constraint, allowing `>= 0.1, < 2`,
4
+ enabling support of OmniAuth 2. This also adds the new [`:login_path`
5
+ option](README#prompt-to-login). @stevenharman
6
+ * #66: Loosen Faraday constraints, allowing `>= 0.8", < 2`. @stevenharman
7
+
1
8
  # 0.8.0
2
9
 
3
10
  * #55: Ruby >= 2.4 support and Ruby <2.2 deprecation. Thanks @maxbeizer!
data/README.md CHANGED
@@ -1,5 +1,4 @@
1
- [![Build Status](https://travis-ci.org/heroku/heroku-bouncer.png)](https://travis-ci.org/heroku/heroku-bouncer)
2
- [![Dependency Status](https://gemnasium.com/heroku/heroku-bouncer.png)](https://gemnasium.com/heroku/heroku-bouncer)
1
+ [![Build Status](https://github.com/sharpstone/heroku-bouncer/actions/workflows/ci.yml/badge.svg)](https://github.com/sharpstone/heroku-bouncer/actions)
3
2
 
4
3
  # Heroku Bouncer
5
4
 
@@ -8,9 +7,9 @@ requires Heroku OAuth on all requests.
8
7
 
9
8
  ## Ruby and Rack compatibility
10
9
 
11
- * **Ruby**: Versions >= 0.8.0 require Ruby >= 2.2. If you need a version
12
- that works with prior versions of Ruby, please use version `~> 0.7.1`.
13
- Note, however, that 0.7.1 does not support Rack 2 (Rails 5).
10
+ * **Ruby**: Versions `>= 0.8.0` require Ruby >= 2.2.
11
+ If you need a version that works with prior versions of Ruby, please use version `~> 0.7.1`.
12
+ Note, however, that `0.7.1` does not support Rack 2 (Rails 5+).
14
13
 
15
14
  * **Rack**: Rack 1 and 2 are supported.
16
15
 
@@ -23,7 +22,7 @@ Sinatra app that uses heroku-bouncer.
23
22
 
24
23
  1. Install the Heroku OAuth CLI plugin.
25
24
 
26
- ```sh
25
+ ```console
27
26
  heroku plugins:install heroku-cli-oauth
28
27
  ```
29
28
 
@@ -31,7 +30,7 @@ Sinatra app that uses heroku-bouncer.
31
30
  callback endpoint. Use `http://localhost:5000/auth/heroku/callback`
32
31
  for local development with Foreman.
33
32
 
34
- ```sh
33
+ ```console
35
34
  heroku clients:create localhost http://localhost:5000/auth/heroku/callback
36
35
  heroku clients:create myapp https://myapp.herokuapp.com/auth/heroku/callback
37
36
  ```
@@ -113,8 +112,8 @@ Here are the supported options you can pass to the middleware:
113
112
  representing the user. If the lambda evaluates to true, allow the user
114
113
  through. If false, redirects to `redirect_url`. By default, all users are
115
114
  allowed through after authenticating.
116
- * `redirect_url`: Where unauthorized users are redirected to. Defaults to
117
- `www.heroku.com`.
115
+ * `login_path`: Where unauthorized users are redirected so they can be [prompted to login](#prompt-to-login). Defaults to `heroku-bouncer`'s own `/auth/login` path.
116
+ * `redirect_url`: Where unauthorized users are redirected during the OAuth callback phase. Defaults to `www.heroku.com`.
118
117
  * `expose_token`: Expose the OAuth token in the session, allowing you to
119
118
  make API calls as the user. Default: `false`
120
119
  * `expose_email`: Expose the user's email address in the session.
@@ -138,7 +137,6 @@ Here are the supported options you can pass to the middleware:
138
137
 
139
138
  You use these by passing a hash to the `use` call, for example:
140
139
 
141
-
142
140
  ```ruby
143
141
  use Heroku::Bouncer,
144
142
  oauth: { id: "...", secret: "...", scope: "global" },
@@ -146,6 +144,29 @@ use Heroku::Bouncer,
146
144
  expose_token: true
147
145
  ```
148
146
 
147
+ ### Prompt to Login
148
+
149
+ To mitigate Cross-Site Request Forgery attacks, [OmniAuth no longer allows `GET` requests to the `/auth/heroku`](https://github.com/omniauth/omniauth/wiki/Upgrading-to-2.0) path.
150
+ To support this, `heroku-bouncer` no longer redirects unauthorized requests to the `/auth/heroku` path.
151
+ Instead users are redirected to `/auth/login` where a simple HTML template is rendered, prompting the user to authenticate with Heroku.
152
+
153
+ The template includes a `<form>` with a button which will `POST` to the `/auth/heroku` path.
154
+ It also includes the Authenticity Token from `Rack::Protection`.
155
+ The view provides no styling; it is the most basic example of what's required.
156
+
157
+ To render your own prompt UI, provide the `:login_path` option.
158
+ Unauthenticated users will be redirected to this path, allowing you to control the UI.
159
+ The resulting page should render an HTML `<form>` which will `POST` to the `/auth/heroku` path.
160
+ The form needs to include a field named `authenticity_token` with the token from `Rack::Protection`.
161
+
162
+ An example to get you started:
163
+
164
+ ```erb
165
+ <form method="post" action="/auth/heroku">
166
+ <input type="hidden" name="authenticity_token" value="<%= request.env["rack.session"]["csrf"] %>">
167
+ <button type="submit">Sign in via Heroku</button>
168
+ ```
169
+
149
170
  ## How to get the data
150
171
 
151
172
  Based on your choice of the expose options above, the middleware adds
@@ -161,13 +182,13 @@ You can access this in Sinatra and Rails by `request.env[key]`, e.g.
161
182
 
162
183
  ## Using the Heroku API
163
184
 
164
- If you set `expose_token` to `true`, you'll get an API token that you
185
+ If you set `expose_token` to `true`, you'll get an OAuth token that you
165
186
  can use to make Heroku API calls on behalf of the logged-in user using
166
- [heroku.rb][] .
187
+ the [platform API][platform-api].
167
188
 
168
189
  ```ruby
169
- heroku = Heroku::API.new(:api_key => request.env["bouncer.token"])
170
- apps = heroku.get_apps.body
190
+ heroku = PlatformAPI.connect_oauth(request.env["bouncer.token"])
191
+ info = heroku.app.info('sushi')
171
192
  ```
172
193
 
173
194
  Keep in mind that this adds substantial security risk to your
@@ -209,4 +230,4 @@ actions that modify data are also recommended.
209
230
  [Rack::Builder]: http://rack.rubyforge.org/doc/Rack/Builder.html
210
231
  [inheritance]: https://gist.github.com/wuputah/5534428
211
232
  [OAuth scope]: https://devcenter.heroku.com/articles/oauth#scopes
212
- [heroku.rb]: https://github.com/heroku/heroku.rb
233
+ [platform-api]: https://github.com/heroku/platform-api
@@ -1,5 +1,6 @@
1
1
  require 'heroku/bouncer/middleware'
2
2
  require 'rack/builder'
3
+ require 'rack/protection'
3
4
  require 'omniauth-heroku'
4
5
 
5
6
  class Heroku::Bouncer::Builder
@@ -8,6 +9,7 @@ class Heroku::Bouncer::Builder
8
9
  builder = Rack::Builder.new
9
10
  id, secret, scope = extract_options!(options)
10
11
  unless options[:disabled]
12
+ builder.use Rack::Protection
11
13
  builder.use OmniAuth::Builder do
12
14
  provider :heroku, id, secret, :scope => scope
13
15
  end
@@ -6,10 +6,12 @@ require 'heroku/bouncer/json_parser'
6
6
  require 'heroku/bouncer/decrypted_hash'
7
7
 
8
8
  class Heroku::Bouncer::Middleware < Sinatra::Base
9
-
10
9
  DecryptedHash = ::Heroku::Bouncer::DecryptedHash
11
10
  UnableToFetchUserError = Class.new(RuntimeError)
12
11
 
12
+ DEFAULT_LOGIN_PATH = "/auth/login".freeze
13
+ private_constant :DEFAULT_LOGIN_PATH
14
+
13
15
  enable :raise_errors
14
16
  disable :show_exceptions
15
17
 
@@ -20,8 +22,10 @@ class Heroku::Bouncer::Middleware < Sinatra::Base
20
22
  # super is not called; we're not using sinatra if we're disabled
21
23
  else
22
24
  super(app)
25
+ @disabled = false
23
26
  @cookie_secret = extract_option(options, :secret, SecureRandom.hex(64))
24
27
  @allow_if_user = extract_option(options, :allow_if_user, nil)
28
+ @login_path = extract_option(options, :login_path, DEFAULT_LOGIN_PATH)
25
29
  @redirect_url = extract_option(options, :redirect_url, 'https://www.heroku.com')
26
30
 
27
31
  # backwards-compatibilty for `herokai_only`:
@@ -125,7 +129,7 @@ class Heroku::Bouncer::Middleware < Sinatra::Base
125
129
  auth_url = ENV["HEROKU_AUTH_URL"] || "https://id.heroku.com"
126
130
  logout_url = "#{auth_url}/logout"
127
131
 
128
- # id.heroku.com whitelists this return_to param, as any auth provider should do
132
+ # id.heroku.com allowlists this return_to param, as any auth provider should do
129
133
  logout_url += "?url=#{params['return_to']}" if params['return_to']
130
134
 
131
135
  redirect to(logout_url)
@@ -138,15 +142,24 @@ class Heroku::Bouncer::Middleware < Sinatra::Base
138
142
  end
139
143
 
140
144
  # login, setting the URL to return to
141
- get '/auth/login' do
145
+ get DEFAULT_LOGIN_PATH do
142
146
  if params['return_to'] && params['return_to'].length <= 255
143
147
  store_write(:return_to, params['return_to'])
144
148
  end
145
- redirect to('/auth/heroku')
149
+
150
+ if custom_login_path?
151
+ redirect to(login_path)
152
+ else
153
+ erb(:login, locals: {
154
+ authenticity_token: request.env["rack.session"]["csrf"]
155
+ })
156
+ end
146
157
  end
147
158
 
148
159
  private
149
160
 
161
+ attr_reader :login_path
162
+
150
163
  def unlock_session_data(env, &block)
151
164
  decrypt_store(env)
152
165
  yield
@@ -154,8 +167,20 @@ private
154
167
  encrypt_store(env)
155
168
  end
156
169
 
170
+ def auth_paths
171
+ @auth_paths ||= [
172
+ "/auth/heroku/callback",
173
+ "/auth/heroku",
174
+ "/auth/failure",
175
+ "/auth/sso-logout",
176
+ "/auth/logout",
177
+ DEFAULT_LOGIN_PATH,
178
+ login_path
179
+ ].compact.freeze
180
+ end
181
+
157
182
  def auth_request?
158
- %w[/auth/heroku/callback /auth/heroku /auth/failure /auth/sso-logout /auth/logout /auth/login].include?(request.path_info)
183
+ auth_paths.include?(request.path_info)
159
184
  end
160
185
 
161
186
  def session_nonce_mismatch?
@@ -175,13 +200,17 @@ private
175
200
  ts.nil? || Time.now.to_i > ts
176
201
  end
177
202
 
203
+ def custom_login_path?
204
+ login_path != DEFAULT_LOGIN_PATH
205
+ end
206
+
178
207
  def skip?(env)
179
208
  @skip && @skip.call(env)
180
209
  end
181
210
 
182
211
  def require_authentication
183
212
  store_write(:return_to, request.url)
184
- redirect to('/auth/heroku')
213
+ redirect to(login_path)
185
214
  end
186
215
 
187
216
  def extract_option(options, option, default = nil)
@@ -257,5 +286,4 @@ private
257
286
  return_to.port = port unless port == 80
258
287
  return_to.to_s
259
288
  end
260
-
261
289
  end
@@ -0,0 +1,15 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>Login with Heroku</title>
6
+ <%# Avoid extra requests for favicon.ico %>
7
+ <link rel="icon" href="data:;base64,iVBORw0KGgo=">
8
+ </head>
9
+ <body class="heroku_bouncer">
10
+ <form method="post" action="/auth/heroku" class="heroku_bouncer-login_form">
11
+ <input type="hidden" name="authenticity_token" value="<%= authenticity_token %>">
12
+ <button type="submit">Login with Heroku</button>
13
+ </form>
14
+ </body>
15
+ </html>
metadata CHANGED
@@ -1,29 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: heroku-bouncer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Dance
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-07 00:00:00.000000000 Z
11
+ date: 2021-08-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: omniauth-heroku
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0.1'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '2'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
- - - "~>"
27
+ - - ">="
25
28
  - !ruby/object:Gem::Version
26
29
  version: '0.1'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '2'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: sinatra
29
35
  requirement: !ruby/object:Gem::Requirement
@@ -48,16 +54,22 @@ dependencies:
48
54
  name: faraday
49
55
  requirement: !ruby/object:Gem::Requirement
50
56
  requirements:
51
- - - "~>"
57
+ - - ">="
52
58
  - !ruby/object:Gem::Version
53
59
  version: '0.8'
60
+ - - "<"
61
+ - !ruby/object:Gem::Version
62
+ version: '2'
54
63
  type: :runtime
55
64
  prerelease: false
56
65
  version_requirements: !ruby/object:Gem::Requirement
57
66
  requirements:
58
- - - "~>"
67
+ - - ">="
59
68
  - !ruby/object:Gem::Version
60
69
  version: '0.8'
70
+ - - "<"
71
+ - !ruby/object:Gem::Version
72
+ version: '2'
61
73
  - !ruby/object:Gem::Dependency
62
74
  name: rack
63
75
  requirement: !ruby/object:Gem::Requirement
@@ -82,16 +94,16 @@ dependencies:
82
94
  name: rake
83
95
  requirement: !ruby/object:Gem::Requirement
84
96
  requirements:
85
- - - "~>"
97
+ - - ">="
86
98
  - !ruby/object:Gem::Version
87
- version: '10.0'
99
+ version: 12.3.3
88
100
  type: :development
89
101
  prerelease: false
90
102
  version_requirements: !ruby/object:Gem::Requirement
91
103
  requirements:
92
- - - "~>"
104
+ - - ">="
93
105
  - !ruby/object:Gem::Version
94
- version: '10.0'
106
+ version: 12.3.3
95
107
  - !ruby/object:Gem::Dependency
96
108
  name: minitest
97
109
  requirement: !ruby/object:Gem::Requirement
@@ -126,14 +138,14 @@ dependencies:
126
138
  requirements:
127
139
  - - "~>"
128
140
  - !ruby/object:Gem::Version
129
- version: '0.6'
141
+ version: '1.1'
130
142
  type: :development
131
143
  prerelease: false
132
144
  version_requirements: !ruby/object:Gem::Requirement
133
145
  requirements:
134
146
  - - "~>"
135
147
  - !ruby/object:Gem::Version
136
- version: '0.6'
148
+ version: '1.1'
137
149
  - !ruby/object:Gem::Dependency
138
150
  name: mocha
139
151
  requirement: !ruby/object:Gem::Requirement
@@ -148,6 +160,20 @@ dependencies:
148
160
  - - "~>"
149
161
  - !ruby/object:Gem::Version
150
162
  version: '1.1'
163
+ - !ruby/object:Gem::Dependency
164
+ name: nokogiri
165
+ requirement: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - "~>"
168
+ - !ruby/object:Gem::Version
169
+ version: '1.9'
170
+ type: :development
171
+ prerelease: false
172
+ version_requirements: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - "~>"
175
+ - !ruby/object:Gem::Version
176
+ version: '1.9'
151
177
  - !ruby/object:Gem::Dependency
152
178
  name: delorean
153
179
  requirement: !ruby/object:Gem::Requirement
@@ -164,7 +190,7 @@ dependencies:
164
190
  version: '2.1'
165
191
  description: ID please.
166
192
  email:
167
- - jd@heroku.com
193
+ - jd@wuputah.com
168
194
  executables: []
169
195
  extensions: []
170
196
  extra_rdoc_files:
@@ -183,6 +209,7 @@ files:
183
209
  - lib/heroku/bouncer/json_parser.rb
184
210
  - lib/heroku/bouncer/lockbox.rb
185
211
  - lib/heroku/bouncer/middleware.rb
212
+ - lib/heroku/bouncer/views/login.erb
186
213
  homepage: https://github.com/heroku/heroku-bouncer
187
214
  licenses:
188
215
  - MIT
@@ -202,8 +229,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
202
229
  - !ruby/object:Gem::Version
203
230
  version: '0'
204
231
  requirements: []
205
- rubyforge_project:
206
- rubygems_version: 2.5.1
232
+ rubygems_version: 3.0.3
207
233
  signing_key:
208
234
  specification_version: 4
209
235
  summary: Rapidly add Heroku OAuth to your Ruby app.