hanami-controller 2.0.0.alpha3 → 2.0.0.alpha4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|