belt 0.6.0 → 0.6.2
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 +4 -4
- data/CHANGELOG.md +27 -0
- data/lib/belt/authentication/controller.rb +1 -3
- data/lib/belt/docs/authentication.md +2 -2
- data/lib/belt/docs/controllers.md +13 -2
- data/lib/belt/version.rb +1 -1
- data/lib/belt_controller/base.rb +12 -2
- data/lib/belt_controller/callback_halting.rb +50 -0
- data/lib/templates/module/dns.tf.erb +30 -18
- data/lib/templates/module/main.tf.erb +3 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 05d5db875d229ad413bf8af05004a92961776b347db0884ded768f35fcfe245e
|
|
4
|
+
data.tar.gz: d6f1590f52f2a04b9457b82cf0c0cad094b96b8e04e112672df37d1e5ccf173b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5b9c29157f8d7b698c8f68c807564299f881b220473ef4a51194352092f5a8967dfce0495fb12bc24a813b4ab2e077358c3800151ccf8737059888560a62ad97
|
|
7
|
+
data.tar.gz: 6629a4a02a92e571d1e835ba0f12894f8bad945eb89bf79a2bc4696538378f45dfd7b5f5e3fc4090d9e61120c51d032b2fab5d5e2bdc17cfdfe81d25950061db
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.2
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **First deploy of a new env with a `domain` no longer fails at plan.** The generated
|
|
8
|
+
module keyed `aws_route53_record.cert_validation` by the ACM validation CNAME name,
|
|
9
|
+
which is unknown until the cert exists, so `belt plan`/`belt deploy` on a brand-new
|
|
10
|
+
standalone env failed with `Invalid for_each argument` and needed a `-target` apply
|
|
11
|
+
of the cert first. The record is now `aws_route53_record.acm_validation`, keyed by
|
|
12
|
+
the env's domain (known at plan). A `removed` block forgets old `cert_validation`
|
|
13
|
+
instances without deleting the live CNAME. The module now needs Terraform >= 1.7.
|
|
14
|
+
Existing apps: see [UPGRADING.md](UPGRADING.md#acm-validation-record-062).
|
|
15
|
+
|
|
16
|
+
## 0.6.1
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- **`before_action` now halts the request when it produces a response.** The docs said
|
|
21
|
+
a callback could halt by calling `error_response`, but `dispatch` ignored callback
|
|
22
|
+
output and ran the action anyway, so the action's response replaced the error. A
|
|
23
|
+
guard like `error_response('Forbidden', 403) unless admin?` let everyone through.
|
|
24
|
+
Now, once a callback calls a response helper (`error_response`, `success_response`,
|
|
25
|
+
`html_response`, `head`, `render`) or returns a response, the remaining callbacks, the
|
|
26
|
+
action and the `after_action`s are skipped and that response is returned
|
|
27
|
+
(Rails-style). Apps that worked around this with inline guards in each action keep
|
|
28
|
+
working; the inline checks are now redundant.
|
|
29
|
+
|
|
3
30
|
## 0.6.0
|
|
4
31
|
|
|
5
32
|
### Added
|
|
@@ -88,9 +88,7 @@ module Belt
|
|
|
88
88
|
false
|
|
89
89
|
end
|
|
90
90
|
|
|
91
|
-
# before_action guard. Raises,
|
|
92
|
-
# (see BeltController::Base#run_before_actions) — returning a response hash would
|
|
93
|
-
# let the action run anyway. Belt maps the error to 401.
|
|
91
|
+
# before_action guard. Raises NotAuthenticated, which Belt maps to 401.
|
|
94
92
|
def authenticate_user!
|
|
95
93
|
raise NotAuthenticated, 'Authentication required' unless user_signed_in?
|
|
96
94
|
end
|
|
@@ -71,8 +71,8 @@ end
|
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
`authenticate_user!` raises `Belt::Authentication::NotAuthenticated`, which Belt already
|
|
74
|
-
maps to **401**.
|
|
75
|
-
|
|
74
|
+
maps to **401**. (Your own guards can also halt by calling a response helper, e.g.
|
|
75
|
+
`error_response('Forbidden', 403) unless admin?`.)
|
|
76
76
|
|
|
77
77
|
| Method | |
|
|
78
78
|
|---|---|
|
|
@@ -96,8 +96,19 @@ class ApplicationController < BeltController::Base
|
|
|
96
96
|
end
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
Callbacks run in definition order. `before_action`
|
|
100
|
-
calling a response helper (
|
|
99
|
+
Callbacks run in definition order. A `before_action` halts the request by
|
|
100
|
+
calling a response helper (`error_response`, `success_response`, `head`,
|
|
101
|
+
`render`, …). The remaining callbacks, the action and the `after_action`s are
|
|
102
|
+
skipped, and that response is returned:
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
def require_admin!
|
|
106
|
+
error_response('Forbidden', 403) unless admin?
|
|
107
|
+
end
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Raising works too: the exception goes through `rescue_from` as usual (e.g.
|
|
111
|
+
`Belt::AuthenticationError` → 401).
|
|
101
112
|
|
|
102
113
|
## Strong Parameters
|
|
103
114
|
|
data/lib/belt/version.rb
CHANGED
data/lib/belt_controller/base.rb
CHANGED
|
@@ -9,12 +9,15 @@ require_relative '../belt/helpers/error_logging'
|
|
|
9
9
|
require_relative '../belt/helpers/cors_origin'
|
|
10
10
|
require_relative '../belt/rendering'
|
|
11
11
|
require_relative 'implicit_response'
|
|
12
|
+
require_relative 'callback_halting'
|
|
12
13
|
|
|
13
14
|
module BeltController
|
|
14
15
|
class Base
|
|
15
16
|
include Belt::Helpers::Response
|
|
16
17
|
include Belt::Rendering
|
|
17
18
|
include ImplicitResponse
|
|
19
|
+
# Must come after Belt::Helpers::Response / Belt::Rendering (wraps their helpers).
|
|
20
|
+
include CallbackHalting
|
|
18
21
|
# Cognito identity: current_user, authenticate_user!, cognito_admin?.
|
|
19
22
|
# Inert unless the app declares `cognito_authenticatable` on a model.
|
|
20
23
|
include Belt::Authentication::Controller
|
|
@@ -139,7 +142,11 @@ module BeltController
|
|
|
139
142
|
raise Belt::ActionNotFound, "The action '#{action_sym}' could not be found for #{self.class.name}"
|
|
140
143
|
end
|
|
141
144
|
|
|
142
|
-
|
|
145
|
+
# A before_action that produced a response halts the request (Rails-style):
|
|
146
|
+
# the action and after_actions don't run.
|
|
147
|
+
halted = run_before_actions(action_sym)
|
|
148
|
+
return halted if halted
|
|
149
|
+
|
|
143
150
|
# Snapshot ivars so we only auto-serialize assigns set by the action (Rails-style)
|
|
144
151
|
@__assigns_before = instance_variables
|
|
145
152
|
result = send(action_sym)
|
|
@@ -178,14 +185,17 @@ module BeltController
|
|
|
178
185
|
|
|
179
186
|
private
|
|
180
187
|
|
|
188
|
+
# Returns the halting response if a callback produced one, else nil.
|
|
181
189
|
def run_before_actions(action_sym)
|
|
182
190
|
self.class.all_before_actions.each do |callback|
|
|
183
191
|
next if callback[:only] && !callback[:only].include?(action_sym)
|
|
184
192
|
next if callback[:except]&.include?(action_sym)
|
|
185
193
|
next if should_skip_callback?(callback[:method], action_sym)
|
|
186
194
|
|
|
187
|
-
|
|
195
|
+
response = run_halting_callback(callback[:method])
|
|
196
|
+
return response if response
|
|
188
197
|
end
|
|
198
|
+
nil
|
|
189
199
|
end
|
|
190
200
|
|
|
191
201
|
def run_after_actions(action_sym)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BeltController
|
|
4
|
+
# Rails-style halting for before_action callbacks.
|
|
5
|
+
#
|
|
6
|
+
# A before_action halts the request by producing a response. Either form works:
|
|
7
|
+
#
|
|
8
|
+
# def require_admin!
|
|
9
|
+
# error_response('Forbidden', 403) unless admin?
|
|
10
|
+
# end
|
|
11
|
+
#
|
|
12
|
+
# def require_admin!
|
|
13
|
+
# return if admin?
|
|
14
|
+
#
|
|
15
|
+
# error_response('Forbidden', 403)
|
|
16
|
+
# Belt::Observability::Logger.warn('non-admin hit admin route')
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# Once a callback halts, the remaining callbacks, the action and the
|
|
20
|
+
# after_actions are skipped, and that response is returned.
|
|
21
|
+
#
|
|
22
|
+
# Belt's response helpers are pure (they build and return a hash), so we
|
|
23
|
+
# record any response built while a callback is running. That catches the
|
|
24
|
+
# second form, where the response isn't the callback's return value.
|
|
25
|
+
# Included by BeltController::Base after the response helpers so these
|
|
26
|
+
# wrappers sit in front of them in the ancestor chain.
|
|
27
|
+
module CallbackHalting
|
|
28
|
+
# render builds its response via html_response, so it's covered too.
|
|
29
|
+
%i[success_response error_response html_response head].each do |helper|
|
|
30
|
+
define_method(helper) do |*args|
|
|
31
|
+
response = super(*args)
|
|
32
|
+
@__callback_response = response if @__running_callback
|
|
33
|
+
response
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
# Run one before_action. Returns the response it produced (halt), or nil.
|
|
40
|
+
# A returned response wins over one that was only built along the way.
|
|
41
|
+
def run_halting_callback(method_name)
|
|
42
|
+
@__callback_response = nil
|
|
43
|
+
@__running_callback = true
|
|
44
|
+
result = send(method_name)
|
|
45
|
+
lambda_response?(result) ? result : @__callback_response
|
|
46
|
+
ensure
|
|
47
|
+
@__running_callback = false
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -62,33 +62,45 @@ resource "aws_acm_certificate" "app" {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
# DNS validation record for the certificate
|
|
66
|
-
#
|
|
67
|
-
#
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
resource "aws_route53_record" "
|
|
72
|
-
for_each = local.dns_enabled && !local.is_nested ?
|
|
73
|
-
for dvo in aws_acm_certificate.app[0].domain_validation_options : dvo.resource_record_name => {
|
|
74
|
-
name = dvo.resource_record_name
|
|
75
|
-
type = dvo.resource_record_type
|
|
76
|
-
record = dvo.resource_record_value
|
|
77
|
-
}...
|
|
78
|
-
} : {}
|
|
65
|
+
# DNS validation record for the certificate.
|
|
66
|
+
# ACM issues one identical validation CNAME for both the base domain and the
|
|
67
|
+
# wildcard, so one record covers the cert. Key it by app_domain (known at plan)
|
|
68
|
+
# rather than by anything from domain_validation_options: those values are
|
|
69
|
+
# unknown until the cert exists, and for_each over them fails the first plan
|
|
70
|
+
# of a new env with "Invalid for_each argument".
|
|
71
|
+
resource "aws_route53_record" "acm_validation" {
|
|
72
|
+
for_each = local.dns_enabled && !local.is_nested ? toset([local.app_domain]) : toset([])
|
|
79
73
|
|
|
80
74
|
zone_id = aws_route53_zone.app[0].zone_id
|
|
81
|
-
name = each.
|
|
82
|
-
type = each.
|
|
75
|
+
name = local.acm_validation_options[each.key].resource_record_name
|
|
76
|
+
type = local.acm_validation_options[each.key].resource_record_type
|
|
83
77
|
ttl = 300
|
|
84
|
-
records = [each.
|
|
78
|
+
records = [local.acm_validation_options[each.key].resource_record_value]
|
|
85
79
|
allow_overwrite = true
|
|
86
80
|
}
|
|
87
81
|
|
|
82
|
+
locals {
|
|
83
|
+
acm_validation_options = local.dns_enabled && !local.is_nested ? {
|
|
84
|
+
for dvo in aws_acm_certificate.app[0].domain_validation_options : dvo.domain_name => dvo
|
|
85
|
+
} : {}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
# Older Belt modules created this record as cert_validation, keyed by the
|
|
89
|
+
# validation CNAME name. Forget those instances without deleting them:
|
|
90
|
+
# acm_validation upserts the same CNAME (allow_overwrite), and a delete would
|
|
91
|
+
# pull the record ACM needs for renewal. No-op for envs that never had it.
|
|
92
|
+
removed {
|
|
93
|
+
from = aws_route53_record.cert_validation
|
|
94
|
+
|
|
95
|
+
lifecycle {
|
|
96
|
+
destroy = false
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
88
100
|
resource "aws_acm_certificate_validation" "app" {
|
|
89
101
|
count = local.dns_enabled && !local.is_nested ? 1 : 0
|
|
90
102
|
certificate_arn = aws_acm_certificate.app[0].arn
|
|
91
|
-
validation_record_fqdns = [for record in aws_route53_record.
|
|
103
|
+
validation_record_fqdns = [for record in aws_route53_record.acm_validation : record.fqdn]
|
|
92
104
|
}
|
|
93
105
|
|
|
94
106
|
# =============================================================================
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: belt
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stowzilla
|
|
@@ -192,6 +192,7 @@ files:
|
|
|
192
192
|
- lib/belt/version.rb
|
|
193
193
|
- lib/belt/views/welcome/show.html.erb
|
|
194
194
|
- lib/belt_controller/base.rb
|
|
195
|
+
- lib/belt_controller/callback_halting.rb
|
|
195
196
|
- lib/belt_controller/implicit_response.rb
|
|
196
197
|
- lib/templates/dns/backend.tf.erb
|
|
197
198
|
- lib/templates/dns/belt.rb.erb
|