hanami-controller 2.0.0.alpha3 → 2.0.0.alpha4
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 +5 -0
- data/lib/hanami/action/application_action.rb +32 -13
- data/lib/hanami/action/application_configuration/content_security_policy.rb +118 -0
- data/lib/hanami/action/application_configuration.rb +16 -18
- data/lib/hanami/action/response.rb +1 -1
- data/lib/hanami/action/standalone_action.rb +0 -1
- data/lib/hanami/controller/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5cc85e1cc9074a18b393db3a0760c7fafb1d944914f2b755bfe0e6e6c35ee6aa
|
4
|
+
data.tar.gz: ea3998a90c5f6fb27c720ef400a729a3348b56e6bc5622b8ea07e64e3c2716b2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e843265e291cd6722bf6acf0acb4fb9dc5b16216da02797713b9eb60316802a697b48102c9edf8846c174d987b14c2748f2347224571b089e5b145c229d08579
|
7
|
+
data.tar.gz: 0d6cea465c65077a40b41547c323c1e54613f0743f6c20bfe54265b5d5ecea7431e43e33d9e1d01c1cdcddd22eccca0b1a7e186092d99fe2751d43d80c3aa42d
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
# Hanami::Controller
|
2
2
|
Complete, fast and testable actions for Rack
|
3
3
|
|
4
|
+
## v2.0.0.alpha4 - 2021-12-07
|
5
|
+
### Added
|
6
|
+
- [Luca Guidi] Manage Content Security Policy (CSP) defaults and new API via `Hanami::Action::ApplicationConfiguration#content_security_policy`
|
7
|
+
- [Tim Riley & Marc Busqué] Provide access to routes inside all application actions via `Hanami::Action::ApplicationAction#routes`
|
8
|
+
|
4
9
|
## v2.0.0.alpha3 - 2021-11-09
|
5
10
|
### Added
|
6
11
|
- [Luca Guidi] Automatically include session behavior in `Hanami::Action` when sessions are enabled via Hanami application config
|
@@ -14,7 +14,7 @@ module Hanami
|
|
14
14
|
def included(action_class)
|
15
15
|
action_class.include InstanceMethods
|
16
16
|
|
17
|
-
define_initialize
|
17
|
+
define_initialize
|
18
18
|
configure_action action_class
|
19
19
|
extend_behavior action_class
|
20
20
|
end
|
@@ -25,20 +25,33 @@ module Hanami
|
|
25
25
|
|
26
26
|
private
|
27
27
|
|
28
|
-
def define_initialize
|
28
|
+
def define_initialize
|
29
29
|
resolve_view = method(:resolve_paired_view)
|
30
30
|
resolve_context = method(:resolve_view_context)
|
31
|
+
resolve_routes = method(:resolve_routes)
|
31
32
|
|
32
33
|
define_method :initialize do |**deps|
|
33
34
|
# Conditionally assign these to repsect any explictly auto-injected
|
34
35
|
# dependencies provided by the class
|
35
36
|
@view ||= deps[:view] || resolve_view.(self.class)
|
36
37
|
@view_context ||= deps[:view_context] || resolve_context.()
|
38
|
+
@routes ||= deps[:routes] || resolve_routes.()
|
37
39
|
|
38
40
|
super(**deps)
|
39
41
|
end
|
40
42
|
end
|
41
43
|
|
44
|
+
def resolve_paired_view(action_class)
|
45
|
+
view_identifiers = application.config.actions.view_name_inferrer.(
|
46
|
+
action_name: action_class.name,
|
47
|
+
provider: provider
|
48
|
+
)
|
49
|
+
|
50
|
+
view_identifiers.detect { |identifier|
|
51
|
+
break provider[identifier] if provider.key?(identifier)
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
42
55
|
def resolve_view_context
|
43
56
|
identifier = application.config.actions.view_context_identifier
|
44
57
|
|
@@ -49,15 +62,8 @@ module Hanami
|
|
49
62
|
end
|
50
63
|
end
|
51
64
|
|
52
|
-
def
|
53
|
-
|
54
|
-
action_name: action_class.name,
|
55
|
-
provider: provider
|
56
|
-
)
|
57
|
-
|
58
|
-
view_identifiers.detect { |identifier|
|
59
|
-
break provider[identifier] if provider.key?(identifier)
|
60
|
-
}
|
65
|
+
def resolve_routes
|
66
|
+
application[:routes_helper] if application.key?(:routes_helper)
|
61
67
|
end
|
62
68
|
|
63
69
|
def configure_action(action_class)
|
@@ -87,6 +93,7 @@ module Hanami
|
|
87
93
|
module InstanceMethods
|
88
94
|
attr_reader :view
|
89
95
|
attr_reader :view_context
|
96
|
+
attr_reader :routes
|
90
97
|
|
91
98
|
def build_response(**options)
|
92
99
|
options = options.merge(view_options: method(:view_options))
|
@@ -101,11 +108,23 @@ module Hanami
|
|
101
108
|
{request: req, response: res}
|
102
109
|
end
|
103
110
|
|
104
|
-
# Automatically render the view, if the body hasn't been populated yet
|
105
111
|
def finish(req, res, halted)
|
106
|
-
res.render(view, **req.params
|
112
|
+
res.render(view, **req.params) if render?(res)
|
107
113
|
super
|
108
114
|
end
|
115
|
+
|
116
|
+
# Decide whether to render the current response with the associated view.
|
117
|
+
# This can be overridden to enable/disable automatic rendering.
|
118
|
+
#
|
119
|
+
# @param res [Hanami::Action::Response]
|
120
|
+
#
|
121
|
+
# @return [TrueClass,FalseClass]
|
122
|
+
#
|
123
|
+
# @since 2.0.0
|
124
|
+
# @api public
|
125
|
+
def render?(res)
|
126
|
+
view && res.body.empty?
|
127
|
+
end
|
109
128
|
end
|
110
129
|
end
|
111
130
|
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hanami
|
4
|
+
class Action
|
5
|
+
class ApplicationConfiguration
|
6
|
+
# Configuration for Content Security Policy in Hanami applications
|
7
|
+
#
|
8
|
+
# @since 2.0.0
|
9
|
+
class ContentSecurityPolicy
|
10
|
+
# @since 2.0.0
|
11
|
+
# @api private
|
12
|
+
def initialize(&blk)
|
13
|
+
@policy = {
|
14
|
+
base_uri: "'self'",
|
15
|
+
child_src: "'self'",
|
16
|
+
connect_src: "'self'",
|
17
|
+
default_src: "'none'",
|
18
|
+
font_src: "'self'",
|
19
|
+
form_action: "'self'",
|
20
|
+
frame_ancestors: "'self'",
|
21
|
+
frame_src: "'self'",
|
22
|
+
img_src: "'self' https: data:",
|
23
|
+
media_src: "'self'",
|
24
|
+
object_src: "'none'",
|
25
|
+
plugin_types: "application/pdf",
|
26
|
+
script_src: "'self'",
|
27
|
+
style_src: "'self' 'unsafe-inline' https:"
|
28
|
+
}
|
29
|
+
|
30
|
+
blk&.(self)
|
31
|
+
end
|
32
|
+
|
33
|
+
# @since 2.0.0
|
34
|
+
# @api private
|
35
|
+
def initialize_copy(original_object)
|
36
|
+
@policy = original_object.instance_variable_get(:@policy).dup
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
40
|
+
# Get a CSP setting
|
41
|
+
#
|
42
|
+
# @param key [Symbol] the underscored name of the CPS setting
|
43
|
+
# @return [String,NilClass] the CSP setting, if any
|
44
|
+
#
|
45
|
+
# @since 2.0.0
|
46
|
+
# @api public
|
47
|
+
#
|
48
|
+
# @example
|
49
|
+
# module MyApp
|
50
|
+
# class Application < Hanami::Application
|
51
|
+
# config.actions.content_security_policy[:base_uri] # => "'self'"
|
52
|
+
# end
|
53
|
+
# end
|
54
|
+
def [](key)
|
55
|
+
@policy[key]
|
56
|
+
end
|
57
|
+
|
58
|
+
# Set a CSP setting
|
59
|
+
#
|
60
|
+
# @param key [Symbol] the underscored name of the CPS setting
|
61
|
+
# @param value [String] the CSP setting value
|
62
|
+
#
|
63
|
+
# @since 2.0.0
|
64
|
+
# @api public
|
65
|
+
#
|
66
|
+
# @example Replace a default value
|
67
|
+
# module MyApp
|
68
|
+
# class Application < Hanami::Application
|
69
|
+
# config.actions.content_security_policy[:plugin_types] = nil
|
70
|
+
# end
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# @example Append to a default value
|
74
|
+
# module MyApp
|
75
|
+
# class Application < Hanami::Application
|
76
|
+
# config.actions.content_security_policy[:script_src] += " https://my.cdn.test"
|
77
|
+
# end
|
78
|
+
# end
|
79
|
+
def []=(key, value)
|
80
|
+
@policy[key] = value
|
81
|
+
end
|
82
|
+
|
83
|
+
# Deletes a CSP key
|
84
|
+
#
|
85
|
+
# @param key [Symbol] the underscored name of the CPS setting
|
86
|
+
#
|
87
|
+
# @since 2.0.0
|
88
|
+
# @api public
|
89
|
+
#
|
90
|
+
# @example
|
91
|
+
# module MyApp
|
92
|
+
# class Application < Hanami::Application
|
93
|
+
# config.actions.content_security_policy.delete(:object_src)
|
94
|
+
# end
|
95
|
+
# end
|
96
|
+
def delete(key)
|
97
|
+
@policy.delete(key)
|
98
|
+
end
|
99
|
+
|
100
|
+
# @since 2.0.0
|
101
|
+
# @api private
|
102
|
+
def to_str
|
103
|
+
@policy.map do |key, value|
|
104
|
+
"#{dasherize(key)} #{value}"
|
105
|
+
end.join(";\n")
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
# @since 2.0.0
|
111
|
+
# @api private
|
112
|
+
def dasherize(key)
|
113
|
+
key.to_s.gsub("_", "-")
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative "application_configuration/cookies"
|
4
4
|
require_relative "application_configuration/sessions"
|
5
|
+
require_relative "application_configuration/content_security_policy"
|
5
6
|
require_relative "configuration"
|
6
7
|
require_relative "view_name_inferrer"
|
7
8
|
|
@@ -19,10 +20,18 @@ module Hanami
|
|
19
20
|
setting :view_name_inferrer, default: ViewNameInferrer
|
20
21
|
setting :view_name_inference_base, default: "views"
|
21
22
|
|
22
|
-
|
23
|
-
|
23
|
+
attr_accessor :content_security_policy
|
24
|
+
|
25
|
+
def initialize(*, **options)
|
26
|
+
super()
|
24
27
|
|
25
28
|
@base_configuration = Configuration.new
|
29
|
+
@content_security_policy = ContentSecurityPolicy.new do |csp|
|
30
|
+
if assets_server_url = options[:assets_server_url]
|
31
|
+
csp[:script_src] += " #{assets_server_url}"
|
32
|
+
csp[:style_src] += " #{assets_server_url}"
|
33
|
+
end
|
34
|
+
end
|
26
35
|
|
27
36
|
configure_defaults
|
28
37
|
end
|
@@ -31,6 +40,10 @@ module Hanami
|
|
31
40
|
# A nil value for `csrf_protection` means it has not been explicitly configured
|
32
41
|
# (neither true nor false), so we can default it to whether sessions are enabled
|
33
42
|
self.csrf_protection = sessions.enabled? if csrf_protection.nil?
|
43
|
+
|
44
|
+
if self.content_security_policy
|
45
|
+
self.default_headers["Content-Security-Policy"] = self.content_security_policy.to_str
|
46
|
+
end
|
34
47
|
end
|
35
48
|
|
36
49
|
# Returns the list of available settings
|
@@ -55,22 +68,7 @@ module Hanami
|
|
55
68
|
self.default_headers = {
|
56
69
|
"X-Frame-Options" => "DENY",
|
57
70
|
"X-Content-Type-Options" => "nosniff",
|
58
|
-
"X-XSS-Protection" => "1; mode=block"
|
59
|
-
"Content-Security-Policy" => \
|
60
|
-
"base-uri 'self'; " \
|
61
|
-
"child-src 'self'; " \
|
62
|
-
"connect-src 'self'; " \
|
63
|
-
"default-src 'none'; " \
|
64
|
-
"font-src 'self'; " \
|
65
|
-
"form-action 'self'; " \
|
66
|
-
"frame-ancestors 'self'; " \
|
67
|
-
"frame-src 'self'; " \
|
68
|
-
"img-src 'self' https: data:; " \
|
69
|
-
"media-src 'self'; " \
|
70
|
-
"object-src 'none'; " \
|
71
|
-
"plugin-types application/pdf; " \
|
72
|
-
"script-src 'self'; " \
|
73
|
-
"style-src 'self' 'unsafe-inline' https:"
|
71
|
+
"X-XSS-Protection" => "1; mode=block"
|
74
72
|
}
|
75
73
|
end
|
76
74
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hanami-controller
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0.
|
4
|
+
version: 2.0.0.alpha4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Luca Guidi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-12-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -134,6 +134,7 @@ files:
|
|
134
134
|
- lib/hanami/action.rb
|
135
135
|
- lib/hanami/action/application_action.rb
|
136
136
|
- lib/hanami/action/application_configuration.rb
|
137
|
+
- lib/hanami/action/application_configuration/content_security_policy.rb
|
137
138
|
- lib/hanami/action/application_configuration/cookies.rb
|
138
139
|
- lib/hanami/action/application_configuration/sessions.rb
|
139
140
|
- lib/hanami/action/base_params.rb
|
@@ -181,7 +182,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
181
182
|
- !ruby/object:Gem::Version
|
182
183
|
version: 1.3.1
|
183
184
|
requirements: []
|
184
|
-
rubygems_version: 3.2.
|
185
|
+
rubygems_version: 3.2.29
|
185
186
|
signing_key:
|
186
187
|
specification_version: 4
|
187
188
|
summary: Complete, fast and testable actions for Rack and Hanami
|