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.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -0
  3. data/jets.gemspec +1 -0
  4. data/lib/jets.rb +2 -1
  5. data/lib/jets/application/defaults.rb +3 -0
  6. data/lib/jets/authorizer/base.rb +36 -0
  7. data/lib/jets/authorizer/dsl.rb +64 -0
  8. data/lib/jets/authorizer/helpers/iam_helper.rb +50 -0
  9. data/lib/jets/camelizer.rb +3 -70
  10. data/lib/jets/cfn/builders/api_deployment_builder.rb +0 -2
  11. data/lib/jets/cfn/builders/api_gateway_builder.rb +0 -2
  12. data/lib/jets/cfn/builders/api_resources_builder.rb +0 -2
  13. data/lib/jets/cfn/builders/authorizer_builder.rb +67 -0
  14. data/lib/jets/cfn/builders/base_child_builder.rb +2 -2
  15. data/lib/jets/cfn/builders/controller_builder.rb +4 -1
  16. data/lib/jets/cfn/builders/interface.rb +6 -4
  17. data/lib/jets/cfn/builders/parent_builder.rb +22 -10
  18. data/lib/jets/commands/build.rb +126 -103
  19. data/lib/jets/controller/authorization.rb +72 -0
  20. data/lib/jets/controller/base.rb +25 -43
  21. data/lib/jets/controller/middleware/cors.rb +1 -1
  22. data/lib/jets/klass.rb +3 -3
  23. data/lib/jets/naming.rb +12 -2
  24. data/lib/jets/resource/api_gateway/authorizer.rb +82 -0
  25. data/lib/jets/resource/api_gateway/method.rb +23 -38
  26. data/lib/jets/resource/api_gateway/method/authorization.rb +32 -0
  27. data/lib/jets/resource/child_stack/app_class.rb +16 -5
  28. data/lib/jets/resource/child_stack/authorizer.rb +46 -0
  29. data/lib/jets/resource/child_stack/common_parameters.rb +14 -0
  30. data/lib/jets/resource/child_stack/shared.rb +2 -9
  31. data/lib/jets/router/route.rb +1 -8
  32. data/lib/jets/router/route/authorization.rb +48 -0
  33. data/lib/jets/rule/dsl.rb +0 -1
  34. data/lib/jets/version.rb +1 -1
  35. metadata +26 -2
@@ -40,8 +40,9 @@ module Jets::Commands
40
40
 
41
41
  def build_all_templates
42
42
  # CloudFormation templates
43
- # 1. Shared templates - child templates needs them
43
+ # 1. Shared and authorizer templates - child templates needs them
44
44
  build_api_gateway_templates
45
+ build_authorizer_templates # controllers can use these
45
46
  # 2. Child templates - parent template needs them
46
47
  build_app_child_templates
47
48
  # 2. Child templates - parent template needs them
@@ -51,7 +52,7 @@ module Jets::Commands
51
52
  end
52
53
 
53
54
  def build_minimal_template
54
- Jets::Cfn::Builders::ParentBuilder.new(@options).build
55
+ Jets::Cfn::Builders::ParentBuilder.new(@options).build(parent=true)
55
56
  end
56
57
 
57
58
  def build_api_gateway_templates
@@ -59,6 +60,12 @@ module Jets::Commands
59
60
  Jets::Cfn::Builders::ApiDeploymentBuilder.new(@options).build
60
61
  end
61
62
 
63
+ def build_authorizer_templates
64
+ authorizer_files.each do |path|
65
+ Jets::Cfn::Builders::AuthorizerBuilder.new(path).build
66
+ end
67
+ end
68
+
62
69
  def build_app_child_templates
63
70
  app_files.each do |path|
64
71
  build_child_template(path)
@@ -74,6 +81,8 @@ module Jets::Commands
74
81
  # path: app/controllers/comments_controller.rb
75
82
  # path: app/jobs/easy_job.rb
76
83
  def build_child_template(path)
84
+ return if authorizer?(path) # AuthorizerBuilder is built earlier
85
+
77
86
  md = path.match(%r{app/(.*?)/}) # extract: controller, job or function
78
87
  process_class = md[1].classify
79
88
  builder_class = "Jets::Cfn::Builders::#{process_class}Builder".constantize
@@ -85,14 +94,20 @@ module Jets::Commands
85
94
  # Jets::Cfn::Builders::FunctionBuilder.new(Hello)
86
95
  # Jets::Cfn::Builders::FunctionBuilder.new(HelloFunction)
87
96
  app_class = Jets::Klass.from_path(path)
88
- builder = builder_class.new(app_class)
89
- unless Jets.poly_only? && app_class == Jets::PreheatJob
90
- builder.build
97
+ if Jets.poly_only? && app_class == Jets::PreheatJob
98
+ return # No prewarm when there's only poly functions
91
99
  end
100
+
101
+ builder = builder_class.new(app_class)
102
+ builder.build
103
+ end
104
+
105
+ def authorizer?(path)
106
+ path.include?("app/authorizers")
92
107
  end
93
108
 
94
109
  def build_parent_template
95
- Jets::Cfn::Builders::ParentBuilder.new(@options).build
110
+ Jets::Cfn::Builders::ParentBuilder.new(@options).build(parent=true)
96
111
  end
97
112
 
98
113
  def clean_templates
@@ -103,126 +118,134 @@ module Jets::Commands
103
118
  self.class.app_files
104
119
  end
105
120
 
106
- # Crucial that the Dir.pwd is in the tmp_code because for
107
- # because Jets.boot set ups autoload_paths and this is how project
108
- # classes are loaded.
109
- # TODO: rework code so that Dir.pwd does not have to be in tmp_code for build to work.
110
- def self.app_files
111
- paths = []
112
- expression = "#{Jets.root}/app/**/**/*.rb"
113
- Dir.glob(expression).each do |path|
114
- return false unless File.file?(path)
115
- next unless app_file?(path)
116
- next if concerns?(path)
117
-
118
- relative_path = path.sub("#{Jets.root}/", '')
119
- # Rids of the Jets.root at beginning
120
- paths << relative_path
121
- end
122
- paths += internal_app_files
123
- paths
124
- end
125
-
126
121
  def shared_files
127
122
  self.class.shared_files
128
123
  end
129
124
 
130
- def self.shared_files
131
- paths = []
132
- expression = "#{Jets.root}/app/**/**/*.rb"
133
- Dir.glob(expression).each do |path|
134
- return false unless File.file?(path)
135
- next unless path.include?("app/shared/resources")
125
+ def authorizer_files
126
+ self.class.authorizer_files
127
+ end
128
+
129
+ class << self
130
+ # Crucial that the Dir.pwd is in the tmp_code because for because Jets.boot set ups autoload_paths and this is
131
+ # how project classes are loaded.
132
+ # TODO: rework code so that Dir.pwd does not have to be in tmp_code for build to work.
133
+ #
134
+ # app_files method is mainly used to determine what templates to build.
135
+ # app_files method is also used to determine what handlers to build.
136
+ def app_files
137
+ paths = []
138
+ expression = "#{Jets.root}/app/**/**/*.rb"
139
+ Dir.glob(expression).each do |path|
140
+ next unless app_file?(path)
141
+ relative_path = path.sub("#{Jets.root}/", '') # rid of the Jets.root at beginning
142
+ paths << relative_path
143
+ end
144
+ paths += internal_app_files
145
+ paths
146
+ end
136
147
 
137
- relative_path = path.sub("#{Jets.root}/", '')
138
- # Rids of the Jets.root at beginning
139
- paths << relative_path
148
+ APP_FOLDERS = %w[authorizers controllers functions jobs rules]
149
+ def app_file?(path)
150
+ return false unless File.extname(path) == ".rb"
151
+ return false unless File.file?(path) unless Jets.env.test?
152
+ return false if application_abstract_classes.detect { |p| path.include?(p) }
153
+ return false if concerns?(path)
154
+ return true if APP_FOLDERS.detect { |p| path.include?("app/#{p}") }
155
+ false
140
156
  end
141
- paths
142
- end
143
157
 
144
- # Finds out of the app has polymorphic functions only and zero ruby functions.
145
- # In this case, we can skip a lot of the ruby related building and speed up the
146
- # deploy process.
147
- def self.poly_only?
148
- !app_has_ruby? && !shared_has_ruby?
149
- end
158
+ # Do not define lamda functions for abstract application parent classes. Examples:
159
+ #
160
+ # application_controller.rb
161
+ # application_job.rb
162
+ # application_authorizer.rb
163
+ def application_abstract_classes
164
+ APP_FOLDERS.map { |a| "application_#{a.singularize}.rb" }
165
+ end
150
166
 
151
- def self.app_has_ruby?
152
- has_ruby = app_files.detect do |path|
153
- app_class = Jets::Klass.from_path(path) # IE: PostsController, Jets::PublicController
154
- langs = app_class.tasks.map(&:lang)
155
- langs.include?(:ruby) && app_class != Jets::PreheatJob
167
+ def concerns?(path)
168
+ path =~ %r{app/\w+/concerns/}
156
169
  end
157
- !!has_ruby
158
- end
159
170
 
160
- def self.shared_has_ruby?
161
- has_ruby = false
162
- Jets::Stack.subclasses.each do |klass|
163
- klass.functions.each do |fun|
164
- if fun.lang == :ruby
165
- has_ruby = true
166
- break
167
- end
168
- end
171
+ def authorizer_files
172
+ app_files.select { |p| p.include?("app/authorizers") }
169
173
  end
170
- has_ruby
171
- end
172
174
 
173
- # Add internal Jets controllers if they are being used
174
- # TODO: Interesting, this eventually just used to generate handlers and controllers only.
175
- # Maybe rename to make that clear.
176
- # The copying of other internal files like views is done in builders/code_builder.rb copy_internal_jets_code
177
- def self.internal_app_files
178
- paths = []
179
- controllers = File.expand_path("../../internal/app/controllers/jets", __FILE__)
175
+ def shared_files
176
+ find_app_paths("shared/resources")
177
+ end
180
178
 
181
- public_catchall = Jets::Router.has_controller?("Jets::PublicController")
182
- paths << "#{controllers}/public_controller.rb" if public_catchall
179
+ def find_app_paths(app_path)
180
+ paths = []
181
+ expression = "#{Jets.root}/app/#{app_path}/**/*.rb"
182
+ Dir.glob(expression).each do |path|
183
+ return false unless File.file?(path)
183
184
 
184
- rack_catchall = Jets::Router.has_controller?("Jets::RackController")
185
- paths << "#{controllers}/rack_controller.rb" if rack_catchall
185
+ relative_path = path.sub("#{Jets.root}/", '')
186
+ # Rids of the Jets.root at beginning
187
+ paths << relative_path
188
+ end
189
+ paths
190
+ end
186
191
 
187
- mailer_controller = Jets::Router.has_controller?("Jets::MailersController")
188
- paths << "#{controllers}/mailers_controller.rb" if mailer_controller
192
+ # Finds out of the app has polymorphic functions only and zero ruby functions.
193
+ # In this case, we can skip a lot of the ruby related building and speed up the
194
+ # deploy process.
195
+ def poly_only?
196
+ !app_has_ruby? && !shared_has_ruby?
197
+ end
189
198
 
190
- if Jets.config.prewarm.enable
191
- jobs = File.expand_path("../../internal/app/jobs/jets", __FILE__)
192
- paths << "#{jobs}/preheat_job.rb"
199
+ def app_has_ruby?
200
+ has_ruby = app_files.detect do |path|
201
+ app_class = Jets::Klass.from_path(path) # IE: PostsController, Jets::PublicController
202
+ langs = app_class.tasks.map(&:lang)
203
+ langs.include?(:ruby) && app_class != Jets::PreheatJob
204
+ end
205
+ !!has_ruby
193
206
  end
194
207
 
195
- paths
196
- end
208
+ def shared_has_ruby?
209
+ has_ruby = false
210
+ Jets::Stack.subclasses.each do |klass|
211
+ klass.functions.each do |fun|
212
+ if fun.lang == :ruby
213
+ has_ruby = true
214
+ break
215
+ end
216
+ end
217
+ end
218
+ has_ruby
219
+ end
197
220
 
198
- def self.app_file?(path)
199
- return false unless File.extname(path) == ".rb"
200
- # Do not define lamda functions for the application_controller.rb or
201
- # application_job.rb
202
- excludes = %w[
203
- application_controller.rb
204
- application_job.rb
205
- ]
206
- return false if excludes.detect { |p| path.include?(p) }
221
+ # Add internal Jets controllers if they are being used
222
+ # TODO: Interesting, this eventually just used to generate handlers and controllers only.
223
+ # Maybe rename to make that clear.
224
+ # The copying of other internal files like views is done in builders/code_builder.rb copy_internal_jets_code
225
+ def internal_app_files
226
+ paths = []
227
+ controllers = File.expand_path("../../internal/app/controllers/jets", __FILE__)
207
228
 
208
- includes = %w[
209
- app/controllers
210
- app/jobs
211
- app/functions
212
- app/rules
213
- ]
214
- return true if includes.detect { |p| path.include?(p) }
229
+ public_catchall = Jets::Router.has_controller?("Jets::PublicController")
230
+ paths << "#{controllers}/public_controller.rb" if public_catchall
215
231
 
216
- false
217
- end
232
+ rack_catchall = Jets::Router.has_controller?("Jets::RackController")
233
+ paths << "#{controllers}/rack_controller.rb" if rack_catchall
218
234
 
219
- def self.concerns?(path)
220
- path =~ %r{app/\w+/concerns/}
221
- end
235
+ mailer_controller = Jets::Router.has_controller?("Jets::MailersController")
236
+ paths << "#{controllers}/mailers_controller.rb" if mailer_controller
222
237
 
223
- def self.tmp_code(full_build_path=false)
224
- full_build_path ? "#{Jets.build_root}/stage/code" : "stage/code"
225
- end
238
+ if Jets.config.prewarm.enable
239
+ jobs = File.expand_path("../../internal/app/jobs/jets", __FILE__)
240
+ paths << "#{jobs}/preheat_job.rb"
241
+ end
226
242
 
243
+ paths
244
+ end
245
+
246
+ def tmp_code(full_build_path=false)
247
+ full_build_path ? "#{Jets.build_root}/stage/code" : "stage/code"
248
+ end
249
+ end
227
250
  end
228
251
  end
@@ -0,0 +1,72 @@
1
+ class Jets::Controller
2
+ module Authorization
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ class_attribute :auth_type,
7
+ :auth_to,
8
+ :auth_options, # for only and except filters
9
+ :api_key_needed
10
+ end
11
+
12
+ class_methods do
13
+ def controller_path
14
+ name.sub(/Controller$/, "".freeze).underscore
15
+ end
16
+
17
+ def authorization_type(value=nil)
18
+ if !value.nil?
19
+ self.auth_type = value
20
+ else
21
+ self.auth_type
22
+ end
23
+ end
24
+
25
+ def authorizer(value=nil, options={})
26
+ if !value.nil?
27
+ self.auth_to = value # IE: main#protect
28
+ self.auth_options = options # IE: only: %w[index] or expect: [:show]
29
+ else
30
+ self.auth_to
31
+ end
32
+ end
33
+
34
+ def authorizer_logical_id(action_name)
35
+ return unless auth_to
36
+
37
+ only = auth_options[:only].map(&:to_s) if auth_options && auth_options[:only]
38
+ except = auth_options[:except].map(&:to_s) if auth_options && auth_options[:except]
39
+
40
+ if except and !except.include?(action_name)
41
+ logical_id = Jets::Router::Route.authorizer_logical_id(auth_to)
42
+ end
43
+
44
+ # only overrides except
45
+ if only and only.include?(action_name)
46
+ logical_id = Jets::Router::Route.authorizer_logical_id(auth_to)
47
+ end
48
+
49
+ # if both only and except are not set then always set the logical_id
50
+ if !only && !except
51
+ logical_id = Jets::Router::Route.authorizer_logical_id(auth_to)
52
+ end
53
+
54
+ logical_id
55
+ end
56
+
57
+ # Autoamtically sets authorization_type for the specific route based on the controller authorizer
58
+ def infer_authorization_type_for(action_name)
59
+ return unless authorizer_logical_id(action_name)
60
+ Jets::Authorizer::Base.authorization_type(auth_to)
61
+ end
62
+
63
+ def api_key_required(value=nil)
64
+ if !value.nil?
65
+ self.api_key_needed = value
66
+ else
67
+ self.api_key_needed
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -4,14 +4,15 @@ require "rack/utils" # Rack::Utils.parse_nested_query
4
4
  # Controller public methods get turned into Lambda functions.
5
5
  class Jets::Controller
6
6
  class Base < Jets::Lambda::Functions
7
+ include ActiveSupport::Rescuable
8
+ include Authorization
7
9
  include Callbacks
8
10
  include Cookies
11
+ include ForgeryProtection
12
+ include Jets::Router::Helpers
9
13
  include Layout
10
14
  include Params
11
15
  include Rendering
12
- include ActiveSupport::Rescuable
13
- include Jets::Router::Helpers
14
- include ForgeryProtection
15
16
 
16
17
  delegate :headers, to: :request
17
18
  delegate :set_header, to: :response
@@ -23,6 +24,16 @@ class Jets::Controller
23
24
  @response = Response.new
24
25
  end
25
26
 
27
+ # Overrides Base.process
28
+ def self.process(event, context={}, meth)
29
+ controller = new(event, context, meth)
30
+ # Using send because process! is private method in Jets::RackController so
31
+ # it doesnt create a lambda function. It's doesnt matter what scope process!
32
+ # is in Controller::Base because Jets lambda functions inheritance doesnt
33
+ # include methods in Controller::Base.
34
+ controller.send(:process!)
35
+ end
36
+
26
37
  # One key difference between process! vs dispatch!
27
38
  #
28
39
  # process! - takes the request through the middleware stack
@@ -101,49 +112,20 @@ class Jets::Controller
101
112
  @meth
102
113
  end
103
114
 
104
- def self.controller_path
105
- name.sub(/Controller$/, "".freeze).underscore
106
- end
107
-
108
- def self.process(event, context={}, meth)
109
- controller = new(event, context, meth)
110
- # Using send because process! is private method in Jets::RackController so
111
- # it doesnt create a lambda function. It's doesnt matter what scope process!
112
- # is in Controller::Base because Jets lambda functions inheritance doesnt
113
- # include methods in Controller::Base.
114
- controller.send(:process!)
115
- end
116
-
117
115
  class_attribute :internal_controller
118
- def self.internal(value=nil)
119
- if !value.nil?
120
- self.internal_controller = value
121
- else
122
- self.internal_controller
123
- end
124
- end
125
-
126
- class_attribute :auth_type
127
- def self.authorization_type(value=nil)
128
- if !value.nil?
129
- self.auth_type = value
130
- else
131
- self.auth_type
132
- end
133
- end
134
-
135
- class_attribute :api_key_needed
136
- def self.api_key_required(value=nil)
137
- if !value.nil?
138
- self.api_key_needed = value
139
- else
140
- self.api_key_needed
116
+ class << self
117
+ def internal(value=nil)
118
+ if !value.nil?
119
+ self.internal_controller = value
120
+ else
121
+ self.internal_controller
122
+ end
141
123
  end
142
- end
143
124
 
144
- def self.helper_method(*meths)
145
- meths.each do |meth|
146
- Jets::Router::Helpers.define_helper_method(meth)
125
+ def helper_method(*meths)
126
+ meths.each do |meth|
127
+ Jets::Router::Helpers.define_helper_method(meth)
128
+ end
147
129
  end
148
130
  end
149
131
  end