hanami-controller 2.0.0.alpha6 → 2.0.0.alpha8

Sign up to get free protection for your applications and to get access to all the features.
@@ -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