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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +1 -1
- data/lib/hanami/action.rb +553 -14
- data/lib/hanami/controller/version.rb +1 -1
- metadata +3 -9
- data/lib/hanami/action/application_action.rb +0 -131
- data/lib/hanami/action/application_configuration/content_security_policy.rb +0 -118
- data/lib/hanami/action/application_configuration/cookies.rb +0 -29
- data/lib/hanami/action/application_configuration/sessions.rb +0 -46
- data/lib/hanami/action/application_configuration.rb +0 -90
- data/lib/hanami/action/standalone_action.rb +0 -578
@@ -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
|