hanami-controller 2.0.0.alpha6 → 2.0.0.alpha8

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.
@@ -1,131 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Hanami
4
- class Action
5
- class ApplicationAction < Module
6
- attr_reader :provider
7
- attr_reader :application
8
-
9
- def initialize(provider)
10
- @provider = provider
11
- @application = provider.respond_to?(:application) ? provider.application : Hanami.application
12
- end
13
-
14
- def included(action_class)
15
- action_class.include InstanceMethods
16
-
17
- define_initialize
18
- configure_action action_class
19
- extend_behavior action_class
20
- end
21
-
22
- def inspect
23
- "#<#{self.class.name}[#{provider.name}]>"
24
- end
25
-
26
- private
27
-
28
- def define_initialize
29
- resolve_view = method(:resolve_paired_view)
30
- resolve_context = method(:resolve_view_context)
31
- resolve_routes = method(:resolve_routes)
32
-
33
- define_method :initialize do |**deps|
34
- # Conditionally assign these to repsect any explictly auto-injected
35
- # dependencies provided by the class
36
- @view ||= deps[:view] || resolve_view.(self.class)
37
- @view_context ||= deps[:view_context] || resolve_context.()
38
- @routes ||= deps[:routes] || resolve_routes.()
39
-
40
- super(**deps)
41
- end
42
- end
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
-
55
- def resolve_view_context
56
- identifier = application.config.actions.view_context_identifier
57
-
58
- if provider.key?(identifier)
59
- provider[identifier]
60
- elsif application.key?(identifier)
61
- application[identifier]
62
- end
63
- end
64
-
65
- def resolve_routes
66
- application[:routes_helper] if application.key?(:routes_helper)
67
- end
68
-
69
- def configure_action(action_class)
70
- action_class.config.settings.each do |setting|
71
- application_value = application.config.actions.public_send(:"#{setting}")
72
- action_class.config.public_send :"#{setting}=", application_value
73
- end
74
- end
75
-
76
- def extend_behavior(action_class)
77
- if application.config.actions.sessions.enabled?
78
- require "hanami/action/session"
79
- action_class.include Hanami::Action::Session
80
- end
81
-
82
- if application.config.actions.csrf_protection
83
- require "hanami/action/csrf_protection"
84
- action_class.include Hanami::Action::CSRFProtection
85
- end
86
-
87
- if application.config.actions.cookies.enabled?
88
- require "hanami/action/cookies"
89
- action_class.include Hanami::Action::Cookies
90
- end
91
- end
92
-
93
- module InstanceMethods
94
- attr_reader :view
95
- attr_reader :view_context
96
- attr_reader :routes
97
-
98
- def build_response(**options)
99
- options = options.merge(view_options: method(:view_options))
100
- super(**options)
101
- end
102
-
103
- def view_options(req, res)
104
- {context: view_context&.with(**view_context_options(req, res))}.compact
105
- end
106
-
107
- def view_context_options(req, res)
108
- {request: req, response: res}
109
- end
110
-
111
- def finish(req, res, halted)
112
- res.render(view, **req.params) if render?(res)
113
- super
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
128
- end
129
- end
130
- end
131
- end
@@ -1,118 +0,0 @@
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
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Hanami
4
- class Action
5
- class ApplicationConfiguration
6
- # Wrapper for application-level configuration of HTTP cookies for Hanami actions.
7
- # This decorates the hash of cookie options that is otherwise directly configurable
8
- # on actions, and adds the `enabled?` method to allow `ApplicationAction` to
9
- # determine whether to include the `Action::Cookies` module.
10
- #
11
- # @since 2.0.0
12
- class Cookies
13
- attr_reader :options
14
-
15
- def initialize(options)
16
- @options = options
17
- end
18
-
19
- def enabled?
20
- !options.nil?
21
- end
22
-
23
- def to_h
24
- options.to_h
25
- end
26
- end
27
- end
28
- end
29
- end
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "dry/core/constants"
4
- require "hanami/utils/string"
5
- require "hanami/utils/class"
6
-
7
- module Hanami
8
- class Action
9
- class ApplicationConfiguration
10
- # Configuration for HTTP sessions in Hanami actions
11
- #
12
- # @since 2.0.0
13
- class Sessions
14
- attr_reader :storage, :options
15
-
16
- def initialize(storage = nil, *options)
17
- @storage = storage
18
- @options = options
19
- end
20
-
21
- def enabled?
22
- !storage.nil?
23
- end
24
-
25
- def middleware
26
- return [] if !enabled?
27
-
28
- [[storage_middleware, options]]
29
- end
30
-
31
- private
32
-
33
- def storage_middleware
34
- require_storage
35
-
36
- name = Utils::String.classify(storage)
37
- Utils::Class.load!(name, ::Rack::Session)
38
- end
39
-
40
- def require_storage
41
- require "rack/session/#{storage}"
42
- end
43
- end
44
- end
45
- end
46
- end
@@ -1,90 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "application_configuration/cookies"
4
- require_relative "application_configuration/sessions"
5
- require_relative "application_configuration/content_security_policy"
6
- require_relative "configuration"
7
- require_relative "view_name_inferrer"
8
-
9
- module Hanami
10
- class Action
11
- class ApplicationConfiguration
12
- include Dry::Configurable
13
-
14
- setting :cookies, default: {}, constructor: -> options { Cookies.new(options) }
15
- setting :sessions, constructor: proc { |storage, *options| Sessions.new(storage, *options) }
16
- setting :csrf_protection
17
-
18
- setting :name_inference_base, default: "actions"
19
- setting :view_context_identifier, default: "view.context"
20
- setting :view_name_inferrer, default: ViewNameInferrer
21
- setting :view_name_inference_base, default: "views"
22
-
23
- attr_accessor :content_security_policy
24
-
25
- def initialize(*, **options)
26
- super()
27
-
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
35
-
36
- configure_defaults
37
- end
38
-
39
- def finalize!
40
- # A nil value for `csrf_protection` means it has not been explicitly configured
41
- # (neither true nor false), so we can default it to whether sessions are enabled
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
47
- end
48
-
49
- # Returns the list of available settings
50
- #
51
- # @return [Set]
52
- #
53
- # @since 2.0.0
54
- # @api private
55
- def settings
56
- base_configuration.settings + self.class.settings
57
- end
58
-
59
- private
60
-
61
- attr_reader :base_configuration
62
-
63
- # Apply defaults for base configuration settings
64
- def configure_defaults
65
- self.default_request_format = :html
66
- self.default_response_format = :html
67
-
68
- self.default_headers = {
69
- "X-Frame-Options" => "DENY",
70
- "X-Content-Type-Options" => "nosniff",
71
- "X-XSS-Protection" => "1; mode=block"
72
- }
73
- end
74
-
75
- def method_missing(name, *args, &block)
76
- if config.respond_to?(name)
77
- config.public_send(name, *args, &block)
78
- elsif base_configuration.respond_to?(name)
79
- base_configuration.public_send(name, *args, &block)
80
- else
81
- super
82
- end
83
- end
84
-
85
- def respond_to_missing?(name, _incude_all = false)
86
- config.respond_to?(name) || base_configuration.respond_to?(name) || super
87
- end
88
- end
89
- end
90
- end