marty 2.4.0 → 2.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile.lock +1 -1
  3. data/app/components/marty/api_auth_view.rb +4 -27
  4. data/app/components/marty/extras/layout.rb +7 -1
  5. data/app/components/marty/grid.rb +10 -9
  6. data/app/controllers/marty/rpc_controller.rb +4 -2
  7. data/app/models/marty/api_auth.rb +1 -80
  8. data/app/models/marty/api_config.rb +4 -4
  9. data/app/models/marty/delorean_rule.rb +2 -3
  10. data/config/routes.rb +1 -1
  11. data/db/migrate/{501_add_api_class_to_marty_api_config.rb → 500_add_api_class_to_marty_api_config.rb} +0 -0
  12. data/lib/marty/aws/base.rb +98 -0
  13. data/lib/marty/util.rb +15 -0
  14. data/lib/marty/version.rb +1 -1
  15. data/other/marty/api/base.rb +3 -0
  16. data/spec/controllers/job_controller_spec.rb +1 -1
  17. data/spec/dummy/app/components/gemini/xyz_rule_view.rb +0 -1
  18. data/spec/dummy/config/application.rb +0 -1
  19. data/spec/features/enum_spec.rb +100 -35
  20. data/spec/features/log_view_spec.rb +5 -5
  21. data/spec/features/rule_spec.rb +30 -30
  22. data/spec/features/user_view_spec.rb +2 -0
  23. data/spec/lib/logger_spec.rb +1 -1
  24. data/spec/models/event_spec.rb +1 -1
  25. data/spec/models/promise_spec.rb +1 -1
  26. data/spec/models/user_spec.rb +6 -6
  27. data/spec/spec_helper.rb +9 -69
  28. data/spec/support/chromedriver.rb +41 -0
  29. data/spec/support/components/netzke_combobox.rb +57 -0
  30. data/spec/support/components/netzke_grid.rb +356 -0
  31. data/spec/support/custom_matchers.rb +18 -0
  32. data/spec/support/custom_selectors.rb +49 -0
  33. data/spec/support/delayed_job_helpers.rb +4 -5
  34. data/spec/support/download_helper.rb +52 -0
  35. data/spec/support/helper.rb +20 -0
  36. data/spec/support/netzke.rb +306 -0
  37. data/spec/support/performance_helper.rb +26 -0
  38. data/spec/support/post_run_logger.rb +32 -0
  39. data/spec/support/{spec_setup.rb → setup.rb} +19 -6
  40. data/spec/support/shared_connection.rb +31 -0
  41. data/spec/support/{clean_db_helpers.rb → shared_connection_db_helpers.rb} +2 -2
  42. data/spec/support/structure_compare.rb +62 -0
  43. data/spec/support/suite.rb +27 -0
  44. data/spec/support/{integration_helpers.rb → users.rb} +11 -9
  45. metadata +20 -8
  46. data/db/migrate/502_add_parameters_to_marty_api_auth.rb +0 -5
  47. data/spec/support/user_helpers.rb +0 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: 8d6215dc0386a1fa52611c23d576350432d8dc8867f72dec1a7865717182e9e5
4
- data.tar.gz: 66fe4f011556128fe04d5b897f653db57eb6032c4ef3f440ab363c105f0052ef
2
+ SHA1:
3
+ metadata.gz: c3db866b4afffd7cef3f93ecdaf8698dc45bf169
4
+ data.tar.gz: f1ba792e54124bd28c8a50a5080a3ea556dfe4bb
5
5
  SHA512:
6
- metadata.gz: 8bde7326c5b29b7561371316e43207ded0b9d5b4246b5eb6948aa711eef74fc7e5fbed0fdf5758b61e7eb1549f0109ada0bcfed3910f86dc4e0cf4eeff128153
7
- data.tar.gz: 4b2a99929bb2f8fcf4bd75a3b719c3c689dc3cd5e6e04726ea6e6fd07e900bcce821aaec76071d9befd33f604e9a3d4c3884bb0d0051a64669a5dd5c80ccfdd7
6
+ metadata.gz: d40dc1b4535d794f22bd51916449cf48738030780b37a7392abf7ee95ed3ee7f890415090c5b189e271a6f26fa519bf60f8075e9912ff0861896deb73531a366
7
+ data.tar.gz: 59c87e1a2e69ba55ad37a0b43ae5876487a3226ca1f34b060bf3b889b318f8616cfa3d10f511450a806223a31107d84b4fa5a142af089da7a206ebad1c0151a5
data/Gemfile.lock CHANGED
@@ -106,7 +106,7 @@ GEM
106
106
  i18n (0.9.5)
107
107
  concurrent-ruby (~> 1.0)
108
108
  io-like (0.3.0)
109
- json-schema (2.8.0)
109
+ json-schema (2.8.1)
110
110
  addressable (>= 2.4)
111
111
  loofah (2.2.2)
112
112
  crass (~> 1.0.2)
@@ -7,37 +7,14 @@ class Marty::ApiAuthView < Marty::McflyGridPanel
7
7
  def configure(c)
8
8
  super
9
9
 
10
- c.title = I18n.t('api_auth', default: "API Authorization")
11
- c.editing = :in_form
12
- c.pagination = :pagination
13
- c.model = "Marty::ApiAuth"
14
- c.attributes = [:aws, :entity_name, :api_key, :script_name]
10
+ c.title = I18n.t('api_auth', default: "API Authorization")
11
+ c.model = "Marty::ApiAuth"
12
+ c.attributes = [:app_name, :api_key, :script_name]
15
13
  c.store_config.merge!({sorters: [{property: :app_name, direction: 'ASC'}]})
16
14
  end
17
15
 
18
- attribute :aws do |c|
19
- c.width = 60
20
- c.read_only = true
21
- c.text = "AWS"
22
- c.type = :boolean
23
- c.getter = lambda do |r|
24
- !!r.parameters['aws_api_key']
25
- end
26
- c.sorting_scope = get_json_sorter('parameters', 'aws_api_key')
27
- c.filterable = true
28
- c.filter_with = lambda do |rel, value, op|
29
- rel.where("parameters->>'aws_api_key' IS #{value ? 'NOT' : ''} NULL")
30
- end
31
- end
32
-
33
- attribute :entity_name do |c|
16
+ attribute :app_name do |c|
34
17
  c.flex = 1
35
- c.text = "Entity Name"
36
- c.getter = lambda do |r|
37
- aws = !!r.parameters['aws_api_key']
38
- entity = r.entity
39
- entity ? entity.name : (aws ? nil : r.app_name)
40
- end
41
18
  end
42
19
 
43
20
  attribute :api_key do |c|
@@ -73,7 +73,8 @@ module Layout
73
73
  ######################################################################
74
74
  # PG ENUM field handling
75
75
 
76
- def enum_column(c, class_or_array)
76
+ def enum_column(c, class_or_array, col=nil)
77
+ col ||= c.name.demodulize.tableize.singularize
77
78
  vals = class_or_array.is_a?(Array) ? class_or_array : class_or_array::VALUES
78
79
  editor_config = {
79
80
  trigger_action: :all,
@@ -91,6 +92,7 @@ module Layout
91
92
  field_config: editor_config,
92
93
  type: :string,
93
94
  setter: enum_setter(c.name),
95
+ sorting_scope: get_sorter(col)
94
96
  )
95
97
  end
96
98
 
@@ -112,6 +114,10 @@ module Layout
112
114
  lambda {|r, v| r.send("#{name}=", v.blank? || v == '---' ? nil : v)}
113
115
  end
114
116
 
117
+ def get_sorter(col)
118
+ lambda {|rel, dir| rel.order("#{col}::text #{dir.to_s}")}
119
+ end
120
+
115
121
  ######################################################################
116
122
  # employ lots of hakery to implement NULLable boolean field in
117
123
  # Netzke 8.x.
@@ -25,7 +25,7 @@ class Marty::Grid < ::Netzke::Grid::Base
25
25
  }
26
26
  JS
27
27
 
28
- c.toggle_component_actions = l(<<-JS)
28
+ c.set_disable_component_actions = l(<<-JS)
29
29
  function(prefix, flag) {
30
30
  for (var key in this.actions) {
31
31
  if (key.substring(0, prefix.length) == prefix) {
@@ -87,14 +87,14 @@ class Marty::Grid < ::Netzke::Grid::Base
87
87
  }
88
88
 
89
89
  me.serverConfig.selected = rid;
90
- me.toggleComponentActions('do', !has_sel);
90
+ me.setDisableComponentActions('do', !has_sel);
91
91
 
92
92
  for (var child of children) {
93
- var comp = me.getComponent(child)
93
+ var comp = me.findComponent(child)
94
94
  if (comp) {
95
95
  comp.serverConfig.parent_id = rid;
96
- if (comp.toggleComponentActions) {
97
- comp.toggleComponentActions('parent', !has_sel);
96
+ if (comp.setDisableComponentActions) {
97
+ comp.setDisableComponentActions('parent', !has_sel);
98
98
  }
99
99
  if (comp.reload) { comp.reload() }
100
100
  }
@@ -117,9 +117,7 @@ class Marty::Grid < ::Netzke::Grid::Base
117
117
  c.on_selection_change = l(<<-JS)
118
118
  function(f) {
119
119
  var me = this;
120
- me.getSelectionModel().on('selectionchange', function(m) {
121
- f(m);
122
- });
120
+ me.getSelectionModel().on('selectionchange', f);
123
121
  }
124
122
  JS
125
123
 
@@ -150,7 +148,7 @@ class Marty::Grid < ::Netzke::Grid::Base
150
148
  var children = me.serverConfig.child_components || [];
151
149
  this.store.reload();
152
150
  for (child of children) {
153
- var comp = me.getComponent(child);
151
+ var comp = me.findComponent(child);
154
152
  if (comp && comp.reload) { comp.reload() }
155
153
  }
156
154
  }
@@ -182,6 +180,9 @@ class Marty::Grid < ::Netzke::Grid::Base
182
180
  c.editing = :both
183
181
  c.store_config = {page_size: 30}
184
182
  c.view_config = {preserve_scroll_on_reload: true}
183
+
184
+ # disable buffered renderer plugin to avoid white space on reload
185
+ c.buffered_renderer = false
185
186
  end
186
187
 
187
188
  def has_search_action?
@@ -20,8 +20,8 @@ class Marty::RpcController < ActionController::Base
20
20
 
21
21
  api_params = api.process_params(massaged_params)
22
22
  api.before_evaluate(api_params)
23
- result = api.evaluate(api_params.deep_dup, request, api_config.deep_dup)
24
- api.after_evaluate(api_params.deep_dup, result)
23
+ result = api.evaluate(api_params, request, api_config)
24
+ api.after_evaluate(api_params, result)
25
25
 
26
26
  log_params = {start_time: start_time, auth: auth}
27
27
  api.log(result, api_params + log_params, request) if
@@ -67,6 +67,8 @@ class Marty::RpcController < ActionController::Base
67
67
  when String
68
68
  params = ActiveSupport::JSON.decode(params)
69
69
  when ActionController::Parameters
70
+ # must permit params before conversion to_h
71
+ # convert hash to json and parse to get expected hash (not indifferent)
70
72
  params.permit!
71
73
  params = JSON.parse(params.to_h.to_json)
72
74
  when nil
@@ -1,8 +1,6 @@
1
1
  class Marty::ApiAuth < Marty::Base
2
2
  has_mcfly
3
3
 
4
- belongs_to :entity, polymorphic: true, optional: true
5
-
6
4
  KEY_SIZE = 19
7
5
 
8
6
  validates_presence_of :app_name, :api_key, :script_name
@@ -26,87 +24,10 @@ class Marty::ApiAuth < Marty::Base
26
24
 
27
25
  before_validation do
28
26
  self.api_key = Marty::ApiAuth.generate_key if
29
- self.api_key.nil? || self.api_key.length == 0
30
- end
31
-
32
- before_save do
33
- return unless changed.include?(:entity_id) && !parameters['aws_api_key']
34
-
35
- msg = 'API Auth must be associated with an AWS API KEY before '\
36
- 'it can be associated with an entity'
37
-
38
- errors.add(:base, msg)
39
- end
40
-
41
- before_destroy do
42
- next unless aws = parameters['aws_api_key']
43
- begin
44
- client = Marty::Aws::Apigateway.new
45
- resp = client.delete_usage_plan_key(aws['api_usage_plan_id'],
46
- aws['aid'])
47
- client.delete_api_key(aws['aid']) if resp
48
- rescue => e
49
- Marty::Logger.log('api_test', 'error', e.message)
50
- throw :abort unless e.message.include?('Invalid API Key')
51
- end
27
+ self.api_key.blank?
52
28
  end
53
29
 
54
30
  def self.generate_key
55
31
  SecureRandom.hex(KEY_SIZE)
56
32
  end
57
-
58
- def create_aws_api_key api_id, api_usage_plan_id
59
- client = Marty::Aws::Apigateway.new
60
- app_id = Marty::Config['AWS_APP_IDENTIFIER'] || 'marty'
61
- name = "#{app_id}-#{api_id}-#{api_key[0..3]}"
62
-
63
- key = nil
64
- begin
65
- key = client.create_api_key(name, 'marty_api_key', api_key)
66
- rescue => e
67
- #Marty::Logger.log('api_test', 'error', e.message)
68
- end
69
-
70
- upkey = nil
71
- begin
72
- upkey = key &&
73
- client.create_usage_plan_key(api_usage_plan_id, key.id)
74
- rescue => e
75
- #Marty::Logger.log('api_test', 'error', e.message)
76
- # remove api key we created
77
- client.delete_api_key(key.id)
78
- end
79
-
80
- raise "Unable to create AWS API Key" unless key && upkey
81
-
82
- parameters['aws_api_key'] = {
83
- 'aid' => key.id,
84
- 'api_usage_plan_id' => api_usage_plan_id,
85
- 'api_id' => api_id,
86
- }
87
-
88
- save!
89
- end
90
-
91
- def move_aws_key usage_plan_id
92
- return unless aws = parameters['aws_api_key']
93
- return if aws['api_usage_plan_id'] == usage_plan_id
94
-
95
- begin
96
- client = Marty::Aws::Apigateway.new
97
- resp = client.delete_usage_plan_key(aws['api_usage_plan_id'],
98
- aws['aid'])
99
- rescue => e
100
- # on fail recreate usage plan key
101
- Marty::Logger.log('api', 'api_test', aws)
102
- client.create_usage_plan_key(aws['api_usage_plan_id'], aws['aid']) if
103
- client
104
- return
105
- else
106
- client.create_usage_plan_key(usage_plan_id, aws['aid']) if resp
107
- end
108
-
109
- parameters['aws_api_key'] += {'api_usage_plan_id' => usage_plan_id}
110
- save!
111
- end
112
33
  end
@@ -2,10 +2,10 @@ class Marty::ApiConfig < Marty::Base
2
2
  validates_presence_of :script
3
3
 
4
4
  def self.lookup(script, node, attr)
5
- res = where(["script = ? AND (node IS NULL OR node = ?) "\
6
- "AND (attr IS NULL OR attr = ?)",
7
- script, node, attr]).
8
- order('node nulls last, attr nulls last').first
5
+ res = where('node IS NULL OR node = ?', node).
6
+ where('attr IS NULL OR attr = ?', attr).
7
+ order('node nulls last, attr nulls last').
8
+ find_by(script: script)
9
9
 
10
10
  res && res.as_json.except('id',
11
11
  'created_at',
@@ -152,10 +152,9 @@ class Marty::DeloreanRule < Marty::BaseRule
152
152
  estack_full = resh.delete(:err_stack)
153
153
  estack = estack_full && {
154
154
  err_stack: estack_full.select{ |l| l.starts_with?('DELOREAN')}} || {}
155
+ detail = { input: params, dgparams: dgparams} + resh + estack
155
156
  Marty::Logger.info("Rule Log #{ruleh['name']}",
156
- { input: params,
157
- dgparams: dgparams } + resh + estack
158
- )
157
+ Marty::Util.scrub_obj(detail))
159
158
  end
160
159
  end
161
160
  end
data/config/routes.rb CHANGED
@@ -6,5 +6,5 @@ Marty::Engine.routes.draw do
6
6
  match via: [:get, :post], "rpc/evaluate(.:format)" => "rpc", as: :rpc
7
7
  match via: [:get, :post], "report(.:format)" => "report#index", as: :report
8
8
  get 'job/download' => 'job', as: :job
9
- get 'diag', to: 'diagnostic/#op'
9
+ get 'diag', to: 'diagnostic/#op'
10
10
  end
@@ -0,0 +1,98 @@
1
+ class Marty::Aws::Base
2
+ # aws reserved host used to get instance meta-data
3
+ META_DATA_HOST = '169.254.169.254'
4
+
5
+ attr_reader :id,
6
+ :doc,
7
+ :role,
8
+ :creds,
9
+ :version,
10
+ :host,
11
+
12
+ def self.get url
13
+ uri = URI.parse(url)
14
+ req = Net::HTTP.new(uri.host, uri.port)
15
+ req.read_timeout = req.open_timeout = ENV['AWS_REQUEST_TIMEOUT'] || 0.25
16
+ req.start {|http| http.get(uri.to_s) }.body
17
+ end
18
+
19
+ def self.is_aws?
20
+ response = get("http://#{META_DATA_HOST}") rescue nil
21
+ response.present?
22
+ end
23
+
24
+ def initialize
25
+ @id = get_instance_id
26
+ @doc = get_document
27
+ @role = get_role
28
+ @creds = get_credentials
29
+ @version = '2016-11-15'
30
+ end
31
+
32
+ def query_meta_data query
33
+ self.class.get("http://#{META_DATA_HOST}/latest/meta-data/#{query}/")
34
+ end
35
+
36
+ def query_dynamic query
37
+ self.class.get("http://#{META_DATA_HOST}/latest/dynamic/#{query}/")
38
+ end
39
+
40
+ private
41
+ def get_instance_id
42
+ query_meta_data('instance-id').to_s
43
+ end
44
+
45
+ def get_role
46
+ query_meta_data('iam/security-credentials').to_s
47
+ end
48
+
49
+ def get_credentials
50
+ res = JSON.parse(query_meta_data("iam/security-credentials/#{@role}"))
51
+ res.symbolize_keys
52
+ end
53
+
54
+ def get_document
55
+ res = JSON.parse(query_dynamic('instance-identity/document'))
56
+ res.symbolize_keys
57
+ end
58
+
59
+ def request info, params = {}
60
+ action = info[:action]
61
+ endpoint = info[:endpoint]
62
+ method = info[:method] || :get
63
+
64
+ default = action ? {'Action' => action, 'Version' => @version} : {}
65
+
66
+ host = "#{@service}.#{@doc[:region]}.amazonaws.com"
67
+
68
+ url = "https://#{host}/"
69
+ url += endpoint if endpoint
70
+ url += '?' + (default + params).map{|a, v| "#{a}=#{v}"}.join('&') unless
71
+ params.empty?
72
+
73
+ sig = Aws::Sigv4::Signer.new(service: @service,
74
+ region: @doc[:region],
75
+ access_key_id: @creds[:access_key_id],
76
+ secret_access_key: @creds[:secret_access_key],
77
+ session_token: @creds[:token])
78
+ signed_url = sig.presign_url(http_method:'GET', url: url)
79
+
80
+ http = Net::HTTP.new(host, 443)
81
+ http.use_ssl = true
82
+ Net::HTTP.send(method, signed_url)
83
+ end
84
+
85
+ def ensure_resp path, obj
86
+ if path == []
87
+ obj.is_a?(Array) ? obj : [obj]
88
+ elsif obj.is_a?(Hash)
89
+ key = path.shift
90
+ raise "Unexpected AWS Response: #{key} missing" unless
91
+ (obj.is_a?(Hash) && obj[key])
92
+
93
+ ensure_resp(path, obj[key])
94
+ else
95
+ obj.map{|s| ensure_resp(path.clone, s)}.flatten(1)
96
+ end
97
+ end
98
+ end
data/lib/marty/util.rb CHANGED
@@ -132,4 +132,19 @@ module Marty::Util
132
132
  URI.encode("#{Marty::Util.marty_path}/report?data=#{data}"\
133
133
  "&reptitle=#{title}&format=#{format}")
134
134
  end
135
+
136
+ def self.scrub_obj(obj)
137
+ trav = lambda {|o|
138
+ if o.is_a?(Hash)
139
+ return o.each_with_object({}) {|(k, v), h| h[k] = trav.call(v)}
140
+ elsif o.is_a?(Array)
141
+ return o.map {|v| trav.call(v)}
142
+ elsif o.to_s.length > 10000
143
+ o.class.to_s
144
+ else
145
+ o
146
+ end
147
+ }
148
+ trav.call(obj)
149
+ end
135
150
  end
data/lib/marty/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Marty
2
- VERSION = "2.4.0"
2
+ VERSION = "2.4.1"
3
3
  end
@@ -44,6 +44,9 @@ class Marty::Api::Base
44
44
  end
45
45
 
46
46
  def self.evaluate params, request, config
47
+ # prevent script evaluation from modifying passed in params
48
+ params = params.deep_dup
49
+
47
50
  # validate input schema
48
51
  if config[:input_validated]
49
52
  begin