jets 2.1.7 → 2.2.0
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 +3 -0
- data/jets.gemspec +1 -0
- data/lib/jets.rb +2 -1
- data/lib/jets/application/defaults.rb +3 -0
- data/lib/jets/authorizer/base.rb +36 -0
- data/lib/jets/authorizer/dsl.rb +64 -0
- data/lib/jets/authorizer/helpers/iam_helper.rb +50 -0
- data/lib/jets/camelizer.rb +3 -70
- data/lib/jets/cfn/builders/api_deployment_builder.rb +0 -2
- data/lib/jets/cfn/builders/api_gateway_builder.rb +0 -2
- data/lib/jets/cfn/builders/api_resources_builder.rb +0 -2
- data/lib/jets/cfn/builders/authorizer_builder.rb +67 -0
- data/lib/jets/cfn/builders/base_child_builder.rb +2 -2
- data/lib/jets/cfn/builders/controller_builder.rb +4 -1
- data/lib/jets/cfn/builders/interface.rb +6 -4
- data/lib/jets/cfn/builders/parent_builder.rb +22 -10
- data/lib/jets/commands/build.rb +126 -103
- data/lib/jets/controller/authorization.rb +72 -0
- data/lib/jets/controller/base.rb +25 -43
- data/lib/jets/controller/middleware/cors.rb +1 -1
- data/lib/jets/klass.rb +3 -3
- data/lib/jets/naming.rb +12 -2
- data/lib/jets/resource/api_gateway/authorizer.rb +82 -0
- data/lib/jets/resource/api_gateway/method.rb +23 -38
- data/lib/jets/resource/api_gateway/method/authorization.rb +32 -0
- data/lib/jets/resource/child_stack/app_class.rb +16 -5
- data/lib/jets/resource/child_stack/authorizer.rb +46 -0
- data/lib/jets/resource/child_stack/common_parameters.rb +14 -0
- data/lib/jets/resource/child_stack/shared.rb +2 -9
- data/lib/jets/router/route.rb +1 -8
- data/lib/jets/router/route/authorization.rb +48 -0
- data/lib/jets/rule/dsl.rb +0 -1
- data/lib/jets/version.rb +1 -1
- metadata +26 -2
@@ -52,7 +52,7 @@ module Jets::Controller::Middleware
|
|
52
52
|
# IE: Access-Control-Allow-Methods
|
53
53
|
default = {
|
54
54
|
"access-control-allow-methods" => "DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT",
|
55
|
-
"access-control-allow-headers" => "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Amz-User-Agent",
|
55
|
+
"access-control-allow-headers" => "Content-Type,X-Amz-Date,Authorization,Auth,X-Api-Key,X-Amz-Security-Token,X-Amz-User-Agent",
|
56
56
|
}
|
57
57
|
Jets.config.cors_preflight || default
|
58
58
|
end
|
data/lib/jets/klass.rb
CHANGED
@@ -45,15 +45,15 @@ class Jets::Klass
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
+
APP_TYPES = %w[controller job rule authorizer]
|
48
49
|
def from_task(task)
|
49
50
|
class_name = task.class_name
|
50
51
|
filename = class_name.underscore
|
51
52
|
|
52
|
-
# Examples of filename: posts_controller, hard_job, security_rule,
|
53
|
+
# Examples of filename: posts_controller, hard_job, security_rule, main_authorizer
|
53
54
|
# hello_function, hello
|
54
|
-
valid_types = %w[controller job rule]
|
55
55
|
type = filename.split('_').last
|
56
|
-
type = "function" unless
|
56
|
+
type = "function" unless APP_TYPES.include?(type)
|
57
57
|
|
58
58
|
path = "app/#{type.pluralize}/#{filename}.rb"
|
59
59
|
from_path(path)
|
data/lib/jets/naming.rb
CHANGED
@@ -7,12 +7,12 @@ class Jets::Naming
|
|
7
7
|
extend Memoist
|
8
8
|
|
9
9
|
def app_template_path(app_class)
|
10
|
-
underscored =
|
10
|
+
underscored = underscore(app_class)
|
11
11
|
"#{template_path_prefix}-app-#{underscored}.yml"
|
12
12
|
end
|
13
13
|
|
14
14
|
def shared_template_path(shared_class)
|
15
|
-
underscored =
|
15
|
+
underscored = underscore(shared_class)
|
16
16
|
"#{template_path_prefix}-shared-#{underscored}.yml"
|
17
17
|
end
|
18
18
|
|
@@ -49,5 +49,15 @@ class Jets::Naming
|
|
49
49
|
def gateway_api_name
|
50
50
|
"#{Jets.config.project_namespace}"
|
51
51
|
end
|
52
|
+
|
53
|
+
def authorizer_template_path(path)
|
54
|
+
underscored = underscore(path)
|
55
|
+
underscored.sub!(/^app-/, '')
|
56
|
+
"#{template_path_prefix}-#{underscored}.yml"
|
57
|
+
end
|
58
|
+
|
59
|
+
def underscore(s)
|
60
|
+
s.to_s.underscore.sub(/\.rb$/,'').gsub('/','-')
|
61
|
+
end
|
52
62
|
end
|
53
63
|
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Jets::Resource::ApiGateway
|
2
|
+
class Authorizer < Jets::Resource::Base
|
3
|
+
def initialize(props={})
|
4
|
+
@props = props # associated_properties from dsl.rb
|
5
|
+
end
|
6
|
+
|
7
|
+
def definition
|
8
|
+
{
|
9
|
+
authorizer_logical_id => {
|
10
|
+
type: "AWS::ApiGateway::Authorizer",
|
11
|
+
properties: props,
|
12
|
+
}
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
def props
|
17
|
+
default = {
|
18
|
+
# authorizer_credentials: '',
|
19
|
+
# authorizer_result_ttl_in_seconds: '',
|
20
|
+
# auth_type: '',
|
21
|
+
# identity_source: '', # required
|
22
|
+
# identity_validation_expression: '',
|
23
|
+
# name: '',
|
24
|
+
# provider_arns: [],
|
25
|
+
rest_api_id: '!Ref RestApi', # Required: Yes
|
26
|
+
type: '', # Required: Yes
|
27
|
+
}
|
28
|
+
|
29
|
+
unless @props[:type].to_s.upcase == 'COGNITO_USER_POOLS'
|
30
|
+
@props[:authorizer_uri] = { # Required: Conditional
|
31
|
+
"Fn::Join" => ['', [
|
32
|
+
'arn:aws:apigateway:',
|
33
|
+
"!Ref 'AWS::Region'",
|
34
|
+
':lambda:path/2015-03-31/functions/',
|
35
|
+
{"Fn::GetAtt" => ["{namespace}LambdaFunction", "Arn"]},
|
36
|
+
'/invocations'
|
37
|
+
]]
|
38
|
+
}
|
39
|
+
end
|
40
|
+
@props[:authorizer_result_ttl_in_seconds] = @props.delete(:ttl) if @props[:ttl]
|
41
|
+
|
42
|
+
normalize_type!(@props)
|
43
|
+
normalize_identity_source!(@props)
|
44
|
+
default.merge(@props)
|
45
|
+
end
|
46
|
+
|
47
|
+
def authorizer_logical_id
|
48
|
+
"{namespace}_authorizer" # IE: protect_authorizer
|
49
|
+
end
|
50
|
+
|
51
|
+
def outputs
|
52
|
+
# IE: ProtectAuthorizer: !Ref ProtectAuthorizer
|
53
|
+
{
|
54
|
+
logical_id => "!Ref #{logical_id}",
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
# Also sets a default if it's not provided
|
60
|
+
def normalize_type!(props)
|
61
|
+
type = props[:type] || :request
|
62
|
+
@props[:type] = type.to_s.upcase
|
63
|
+
end
|
64
|
+
|
65
|
+
# Also sets a default if it's not provided
|
66
|
+
def normalize_identity_source!(props)
|
67
|
+
identity_source = props[:identity_source] || Jets.config.api.authorizers.default_token_source
|
68
|
+
# request authorizer type can have multiple identity sources.
|
69
|
+
# token authorizer type has only one identity source.
|
70
|
+
# We handle both cases.
|
71
|
+
identity_sources = identity_source.split(',') # to handle multipe
|
72
|
+
identity_sources.map! do |source|
|
73
|
+
if source.include?(".") # if '.' is detected assume full identify source provided
|
74
|
+
source
|
75
|
+
else
|
76
|
+
"method.request.header.#{source}" # convention
|
77
|
+
end
|
78
|
+
end
|
79
|
+
@props[:identity_source] = identity_sources.join(',')
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -1,9 +1,10 @@
|
|
1
1
|
# Converts a Jets::Route to a CloudFormation Jets::Resource::ApiGateway::Method resource
|
2
2
|
module Jets::Resource::ApiGateway
|
3
3
|
class Method < Jets::Resource::Base
|
4
|
+
include Authorization
|
5
|
+
|
4
6
|
# also delegate permission for a method
|
5
|
-
delegate :permission,
|
6
|
-
to: :resource
|
7
|
+
delegate :permission, to: :resource
|
7
8
|
|
8
9
|
# route - Jets::Route
|
9
10
|
def initialize(route)
|
@@ -14,24 +15,30 @@ module Jets::Resource::ApiGateway
|
|
14
15
|
{
|
15
16
|
method_logical_id => {
|
16
17
|
type: "AWS::ApiGateway::Method",
|
17
|
-
properties:
|
18
|
-
resource_id: "!Ref #{resource_id}",
|
19
|
-
rest_api_id: "!Ref #{RestApi.logical_id}",
|
20
|
-
http_method: @route.method,
|
21
|
-
request_parameters: {},
|
22
|
-
authorization_type: authorization_type,
|
23
|
-
api_key_required: api_key_required?,
|
24
|
-
integration: {
|
25
|
-
integration_http_method: "POST",
|
26
|
-
type: "AWS_PROXY",
|
27
|
-
uri: "!Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${{namespace}LambdaFunction.Arn}/invocations"
|
28
|
-
},
|
29
|
-
method_responses: []
|
30
|
-
}
|
18
|
+
properties: props
|
31
19
|
}
|
32
20
|
}
|
33
21
|
end
|
34
22
|
|
23
|
+
def props
|
24
|
+
props = {
|
25
|
+
resource_id: "!Ref #{resource_id}",
|
26
|
+
rest_api_id: "!Ref #{RestApi.logical_id}",
|
27
|
+
http_method: @route.method,
|
28
|
+
request_parameters: {},
|
29
|
+
authorization_type: authorization_type,
|
30
|
+
api_key_required: api_key_required?,
|
31
|
+
integration: {
|
32
|
+
integration_http_method: "POST",
|
33
|
+
type: "AWS_PROXY",
|
34
|
+
uri: "!Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${{namespace}LambdaFunction.Arn}/invocations"
|
35
|
+
},
|
36
|
+
method_responses: []
|
37
|
+
}
|
38
|
+
props[:authorizer_id] = authorizer_id if authorizer_id
|
39
|
+
props
|
40
|
+
end
|
41
|
+
|
35
42
|
def method_logical_id
|
36
43
|
# https://stackoverflow.com/questions/6104240/how-do-i-strip-non-alphanumeric-characters-from-a-string-and-keep-spaces
|
37
44
|
# Add path to the logical id to allow 2 different paths to be connected to the same controller action.
|
@@ -65,28 +72,6 @@ module Jets::Resource::ApiGateway
|
|
65
72
|
|
66
73
|
private
|
67
74
|
|
68
|
-
def authorization_type
|
69
|
-
type = @route.authorization_type ||
|
70
|
-
controller_auth_type ||
|
71
|
-
Jets.config.api.authorization_type
|
72
|
-
type.to_s.upcase
|
73
|
-
end
|
74
|
-
|
75
|
-
def controller_auth_type
|
76
|
-
# Already handles inheritance via class_attribute
|
77
|
-
controller_klass.authorization_type
|
78
|
-
end
|
79
|
-
|
80
|
-
def api_key_required?
|
81
|
-
api_key_required == true
|
82
|
-
end
|
83
|
-
|
84
|
-
def api_key_required
|
85
|
-
@route.api_key_required ||
|
86
|
-
controller_klass.api_key_required ||
|
87
|
-
Jets.config.api.api_key_required
|
88
|
-
end
|
89
|
-
|
90
75
|
def controller_klass
|
91
76
|
@controller_klass ||= "#{controller_name}_controller".camelize.constantize
|
92
77
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class Jets::Resource::ApiGateway::Method
|
2
|
+
module Authorization
|
3
|
+
private
|
4
|
+
def authorizer_id
|
5
|
+
if @route.authorizer
|
6
|
+
logical_id = @route.authorizer_id
|
7
|
+
elsif controller_klass.authorizer
|
8
|
+
logical_id = controller_klass.authorizer_logical_id(@route.action_name)
|
9
|
+
end
|
10
|
+
|
11
|
+
"!Ref #{logical_id}" if logical_id
|
12
|
+
end
|
13
|
+
|
14
|
+
def authorization_type
|
15
|
+
type = @route.authorization_type ||
|
16
|
+
controller_klass.authorization_type || # Already handles inheritance via class_attribute, applies controller-wide
|
17
|
+
controller_klass.infer_authorization_type_for(@route.action_name) || # Applies specifically to route
|
18
|
+
Jets.config.api.authorization_type
|
19
|
+
type.to_s.upcase
|
20
|
+
end
|
21
|
+
|
22
|
+
def api_key_required?
|
23
|
+
api_key_required == true
|
24
|
+
end
|
25
|
+
|
26
|
+
def api_key_required
|
27
|
+
@route.api_key_required ||
|
28
|
+
controller_klass.api_key_required ||
|
29
|
+
Jets.config.api.api_key_required
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -5,6 +5,8 @@
|
|
5
5
|
#
|
6
6
|
module Jets::Resource::ChildStack
|
7
7
|
class AppClass < Base
|
8
|
+
include CommonParameters
|
9
|
+
|
8
10
|
def initialize(s3_bucket, options={})
|
9
11
|
super
|
10
12
|
@path = options[:path]
|
@@ -52,10 +54,10 @@ module Jets::Resource::ChildStack
|
|
52
54
|
end
|
53
55
|
|
54
56
|
def parameters
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
57
|
+
params = self.class.common_parameters
|
58
|
+
params.merge!(controller_params) if controller?
|
59
|
+
params.merge!(depends.params) if depends
|
60
|
+
params
|
59
61
|
end
|
60
62
|
|
61
63
|
def self.common_parameters
|
@@ -76,10 +78,13 @@ module Jets::Resource::ChildStack
|
|
76
78
|
|
77
79
|
template_path = @path
|
78
80
|
template = Jets::Cfn::BuiltTemplate.get(template_path)
|
79
|
-
template['Parameters'].
|
81
|
+
template['Parameters'].each do |p,data|
|
80
82
|
case p
|
81
83
|
when /Resource$/ # AWS::ApiGateway::Resource in api-resources templates. IE: demo-dev-api-resources-2.yml
|
82
84
|
params[p] = "!GetAtt #{api_resource_page(p)}.Outputs.#{p}"
|
85
|
+
when /Authorizer$/ # AWS::ApiGateway::Authorizer in authorizers templates. IE: demo-dev-authorizers.yml
|
86
|
+
# Description contains metadata to get the Authorizer logical id
|
87
|
+
params[p] = "!GetAtt #{authorizer_output(data["Description"])}"
|
83
88
|
when 'RootResourceId'
|
84
89
|
params[p] = "!GetAtt ApiGateway.Outputs.RootResourceId"
|
85
90
|
end
|
@@ -91,6 +96,12 @@ module Jets::Resource::ChildStack
|
|
91
96
|
ApiResource::Page.logical_id(parameter)
|
92
97
|
end
|
93
98
|
|
99
|
+
def authorizer_output(desc)
|
100
|
+
authorizer_stack, authorizer_logical_id = desc.split('.')
|
101
|
+
# IE: MainAuthorizer.Outputs.ProtectAuthorizer
|
102
|
+
"#{authorizer_stack}.Outputs.#{authorizer_logical_id}"
|
103
|
+
end
|
104
|
+
|
94
105
|
def controller?
|
95
106
|
@path.include?('_controller.yml')
|
96
107
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Jets::Resource::ChildStack
|
2
|
+
class Authorizer < Base
|
3
|
+
include CommonParameters
|
4
|
+
|
5
|
+
def initialize(s3_bucket, options={})
|
6
|
+
super
|
7
|
+
@path = options[:path]
|
8
|
+
end
|
9
|
+
|
10
|
+
def definition
|
11
|
+
logical_id = authorizer_logical_id
|
12
|
+
{
|
13
|
+
logical_id => {
|
14
|
+
type: "AWS::CloudFormation::Stack",
|
15
|
+
properties: {
|
16
|
+
template_url: template_url,
|
17
|
+
parameters: parameters,
|
18
|
+
}
|
19
|
+
}
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def parameters
|
24
|
+
params = common_parameters
|
25
|
+
params[:RestApi] = "!GetAtt ApiGateway.Outputs.RestApi"
|
26
|
+
params
|
27
|
+
end
|
28
|
+
|
29
|
+
def outputs
|
30
|
+
{
|
31
|
+
logical_id => "!Ref #{logical_id}",
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
# map the path to a camelized logical_id. IE: ProtectAuthorizer
|
36
|
+
def authorizer_logical_id
|
37
|
+
regexp = Regexp.new(".*#{Jets.config.project_namespace}-authorizers-")
|
38
|
+
authorizer_name = @path.sub(regexp, '').sub('.yml', '')
|
39
|
+
authorizer_name.underscore.camelize
|
40
|
+
end
|
41
|
+
|
42
|
+
def template_filename
|
43
|
+
@path
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Jets::Resource::ChildStack
|
2
|
+
module CommonParameters
|
3
|
+
def common_parameters
|
4
|
+
parameters = {
|
5
|
+
IamRole: "!GetAtt IamRole.Arn",
|
6
|
+
S3Bucket: "!Ref S3Bucket",
|
7
|
+
}
|
8
|
+
parameters[:GemLayer] = "!Ref GemLayer" unless Jets.poly_only?
|
9
|
+
parameters
|
10
|
+
end
|
11
|
+
|
12
|
+
extend self
|
13
|
+
end
|
14
|
+
end
|
@@ -5,6 +5,8 @@
|
|
5
5
|
#
|
6
6
|
module Jets::Resource::ChildStack
|
7
7
|
class Shared < AppClass
|
8
|
+
include CommonParameters
|
9
|
+
|
8
10
|
def initialize(s3_bucket, options={})
|
9
11
|
super
|
10
12
|
@path = options[:path]
|
@@ -39,15 +41,6 @@ module Jets::Resource::ChildStack
|
|
39
41
|
props
|
40
42
|
end
|
41
43
|
|
42
|
-
def common_parameters
|
43
|
-
parameters = {
|
44
|
-
IamRole: "!GetAtt IamRole.Arn",
|
45
|
-
S3Bucket: "!Ref S3Bucket",
|
46
|
-
}
|
47
|
-
parameters[:GemLayer] = "!Ref GemLayer" unless Jets.poly_only?
|
48
|
-
parameters
|
49
|
-
end
|
50
|
-
|
51
44
|
# Returns output keys associated with the stack. They are the resource logical ids.
|
52
45
|
def dependency_outputs(dependency)
|
53
46
|
dependency.to_s.camelize.constantize.output_keys
|
data/lib/jets/router/route.rb
CHANGED
@@ -6,6 +6,7 @@
|
|
6
6
|
class Jets::Router
|
7
7
|
class Route
|
8
8
|
include Util
|
9
|
+
include Authorization
|
9
10
|
|
10
11
|
CAPTURE_REGEX = "([^/]*)" # as string
|
11
12
|
|
@@ -184,14 +185,6 @@ class Jets::Router
|
|
184
185
|
end.to_h
|
185
186
|
end
|
186
187
|
|
187
|
-
def authorization_type
|
188
|
-
@options[:authorization_type]
|
189
|
-
end
|
190
|
-
|
191
|
-
def api_key_required
|
192
|
-
@options[:api_key_required]
|
193
|
-
end
|
194
|
-
|
195
188
|
private
|
196
189
|
def ensure_jets_format(path)
|
197
190
|
path.split('/').map do |s|
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class Jets::Router::Route
|
2
|
+
module Authorization
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
class_methods do
|
6
|
+
# Use by both Route and Controller
|
7
|
+
def authorizer_logical_id(authorizer, prefix_class: true)
|
8
|
+
klass, meth = authorizer.split("#")
|
9
|
+
words = [meth, "authorizer"]
|
10
|
+
words.unshift(klass) if prefix_class
|
11
|
+
words.join('_').camelize # logical_id
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# IE: main#protect => MainProtectAuthorizer
|
16
|
+
def authorizer_id(prefix_class: true)
|
17
|
+
return unless authorizer
|
18
|
+
self.class.authorizer_logical_id(authorizer, prefix_class: prefix_class)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Metadata about the authorizer class that can be used later. Stored in the Authorizer template parameters.
|
22
|
+
# In app_class.rb `def controller_params` it is used to build the input parameters for controller templates.
|
23
|
+
def authorizer_metadata
|
24
|
+
klass = authorizer.split("#").first
|
25
|
+
authorizer_class = "#{klass}_authorizer".camelize
|
26
|
+
logical_id = authorizer_id(prefix_class: false)
|
27
|
+
"#{authorizer_class}.#{logical_id}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def authorizer
|
31
|
+
@options[:authorizer]
|
32
|
+
end
|
33
|
+
|
34
|
+
def authorization_type
|
35
|
+
@options[:authorization_type] || inferred_authorization_type
|
36
|
+
end
|
37
|
+
|
38
|
+
def api_key_required
|
39
|
+
@options[:api_key_required]
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
def inferred_authorization_type
|
44
|
+
return unless authorizer
|
45
|
+
Jets::Authorizer::Base.authorization_type(authorizer)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|