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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 015e9bf69c01ea01cbdd9a27e7bf4d4cb4f8f9a98f01a14d707fd68457096006
4
- data.tar.gz: 0f2c2e2569453264b41e131451caae329cfbd7d6773d50f610355f5f6fd0b62b
3
+ metadata.gz: 5cc85e1cc9074a18b393db3a0760c7fafb1d944914f2b755bfe0e6e6c35ee6aa
4
+ data.tar.gz: ea3998a90c5f6fb27c720ef400a729a3348b56e6bc5622b8ea07e64e3c2716b2
5
5
  SHA512:
6
- metadata.gz: 7eb2b778608f9dd27cbf777dd00b98e079bc1ec790da5e388113d645babbc9972d10272c083e187dfafa875fe0e2387592f34fc4d48b0ff96be920d1d82c12b8
7
- data.tar.gz: e1290141bb9128031b0593b1edcf2975fe5f01eadb4661fe0bf18c164f339671bf195b43f8933731cab13180b68a7141a4ce6caa01e1df9aabe3e5afb9e0c9a7
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 action_class
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(action_class)
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 resolve_paired_view(action_class)
53
- view_identifiers = application.config.actions.view_name_inferrer.(
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, **res.exposures) if view && res.body.empty?
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
- def initialize(*)
23
- super
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
 
@@ -76,7 +76,7 @@ module Hanami
76
76
  end
77
77
 
78
78
  def render(view, **options)
79
- self.body = view.(**view_options.(request, self), **options).to_str
79
+ self.body = view.(**view_options.(request, self), **exposures.merge(options)).to_str
80
80
  end
81
81
 
82
82
  def format=(args)
@@ -559,7 +559,6 @@ module Hanami
559
559
  # @api private
560
560
  # @abstract
561
561
  #
562
- # @see Hanami::Action::Callable#finish
563
562
  # @see Hanami::Action::Session#finish
564
563
  # @see Hanami::Action::Cookies#finish
565
564
  # @see Hanami::Action::Cache#finish
@@ -3,6 +3,6 @@ module Hanami
3
3
  # Defines the version
4
4
  #
5
5
  # @since 0.1.0
6
- VERSION = '2.0.0.alpha3'.freeze
6
+ VERSION = '2.0.0.alpha4'.freeze
7
7
  end
8
8
  end
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.alpha3
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-09 00:00:00.000000000 Z
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.3
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