proxes 0.9.13 → 0.10.1

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/lib/ditty/components/proxes.rb +61 -16
  3. data/lib/proxes/controllers/permissions.rb +1 -6
  4. data/lib/proxes/controllers/search.rb +12 -10
  5. data/lib/proxes/controllers/status.rb +23 -86
  6. data/lib/proxes/controllers/status_checks.rb +18 -0
  7. data/lib/proxes/forwarder.rb +44 -20
  8. data/lib/proxes/middleware/error_handling.rb +5 -3
  9. data/lib/proxes/middleware/security.rb +4 -3
  10. data/lib/proxes/models/permission.rb +47 -3
  11. data/lib/proxes/models/status_check.rb +76 -0
  12. data/lib/proxes/models/status_checks/cluster_health_status_check.rb +19 -0
  13. data/lib/proxes/models/status_checks/cpu_status_check.rb +29 -0
  14. data/lib/proxes/models/status_checks/file_system_status_check.rb +32 -0
  15. data/lib/proxes/models/status_checks/jvm_heap_status_check.rb +28 -0
  16. data/lib/proxes/models/status_checks/memory_status_check.rb +29 -0
  17. data/lib/proxes/models/status_checks/nodes_status_check.rb +53 -0
  18. data/lib/proxes/policies/permission_policy.rb +3 -3
  19. data/lib/proxes/policies/request/bulk_policy.rb +0 -12
  20. data/lib/proxes/policies/request/create_policy.rb +0 -3
  21. data/lib/proxes/policies/request/index_policy.rb +0 -5
  22. data/lib/proxes/policies/request/mget_policy.rb +12 -0
  23. data/lib/proxes/policies/request/msearch_policy.rb +12 -0
  24. data/lib/proxes/policies/request/root_policy.rb +0 -3
  25. data/lib/proxes/policies/request/snapshot_policy.rb +0 -3
  26. data/lib/proxes/policies/request_policy.rb +24 -25
  27. data/lib/proxes/policies/search_policy.rb +6 -2
  28. data/lib/proxes/policies/status_check_policy.rb +37 -0
  29. data/lib/proxes/request.rb +19 -5
  30. data/lib/proxes/request/bulk.rb +8 -24
  31. data/lib/proxes/request/cat.rb +6 -0
  32. data/lib/proxes/request/create.rb +4 -0
  33. data/lib/proxes/request/index.rb +4 -0
  34. data/lib/proxes/request/mget.rb +24 -0
  35. data/lib/proxes/request/msearch.rb +24 -0
  36. data/lib/proxes/request/multi.rb +40 -0
  37. data/lib/proxes/request/search.rb +4 -0
  38. data/lib/proxes/request/stats.rb +4 -0
  39. data/lib/proxes/services/es.rb +5 -1
  40. data/lib/proxes/services/listener.rb +29 -6
  41. data/lib/proxes/services/search.rb +4 -1
  42. data/lib/proxes/version.rb +1 -1
  43. data/migrate/20180908_index_specific_permissions.rb +9 -0
  44. data/migrate/20190109_add_status_checks_table.rb +17 -0
  45. data/views/layout.haml +17 -6
  46. metadata +81 -25
  47. data/lib/proxes/helpers/indices.rb +0 -35
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 95d76307001a88f8c450617ba32a844f2b572d221590971d3d2e78648d437c86
4
- data.tar.gz: 58d82f430b8c22f1999bc488db50422e0f57045579c4951488c32bb4be02ec18
3
+ metadata.gz: 3d4050f2df15c55cf749b63f4bb462a1e7bd5eda06dd03dff38ff96fad0f2cde
4
+ data.tar.gz: e84bcff209deac8cb463f1099637d3df5502ff0bcf31811f9da3c8ae6579f9f9
5
5
  SHA512:
6
- metadata.gz: 5cfc7eb05f037be6502e5f15dc8e2add30c14579489bdcf382b6fda300859b910585ba0c94df82221f8e611d87050f8ea1693fa4812b4c15307b5cd1b9f93178
7
- data.tar.gz: 918a2bcc7730276a8e9cd27c532bdef62da8e5f1509b330da0cc4f5ffe6afa912a4a230dff464fa3eb7dc184c502a3cd61ed4b9ceefb34c4496e8245285d4662
6
+ metadata.gz: 91355b745d0af5560ad753f052bcb8729b431eb4ed4618f8574c052242f5589c1689b60078cd812daff1f295541da2d308a41ab8cc99240c2919459fde82247c
7
+ data.tar.gz: 851dc6564798817a56e7e504dfce4594fc01f1f448ba9ead76a2e088070a8c6b90d29d68183604a092377ae3e6e0c9f71093a6dc128f22d4326d1b9c7d1ae0f3
@@ -7,6 +7,7 @@ module Ditty
7
7
  def self.load
8
8
  controllers = File.expand_path('../../proxes/controllers', __dir__)
9
9
  Dir.glob("#{controllers}/*.rb").each { |f| require f }
10
+ ENV['ELASTICSEARCH_URL'] ||= 'http://localhost:9200'
10
11
  require 'proxes/models/permission'
11
12
  require 'proxes/services/listener'
12
13
  end
@@ -28,6 +29,7 @@ module Ditty
28
29
  {
29
30
  '/search' => ::ProxES::Search,
30
31
  '/status' => ::ProxES::Status,
32
+ '/status-checks' => ::ProxES::StatusChecks,
31
33
  '/permissions' => ::ProxES::Permissions
32
34
  }
33
35
  end
@@ -36,7 +38,8 @@ module Ditty
36
38
  load
37
39
  [
38
40
  { order: 0, link: '/status/check', text: 'Status Check', target: ::ProxES::Status, icon: 'dashboard' },
39
- { order: 1, link: '/search', text: 'Search', target: ::ProxES::Status, icon: 'search' },
41
+ { order: 1, link: '/status-checks', text: 'Mange Status Checks', target: ::ProxES::StatusCheck, icon: 'dashboard' },
42
+ { order: 1, link: '/search', text: 'Search', target: ::ProxES::Search, icon: 'search' },
40
43
  { order: 15, link: '/permissions', text: 'Permissions', target: ::ProxES::Permission, icon: 'check-square' }
41
44
  ]
42
45
  end
@@ -46,10 +49,11 @@ module Ditty
46
49
  require 'ditty/models/user'
47
50
  require 'ditty/models/role'
48
51
  require 'proxes/models/permission'
52
+ require 'proxes/models/status_check'
49
53
 
50
54
  sa = ::Ditty::Role.find_or_create(name: 'super_admin')
51
- %w[GET POST PUT DELETE HEAD OPTIONS INDEX].each do |verb|
52
- ::ProxES::Permission.find_or_create(role: sa, verb: verb, pattern: '.*')
55
+ %w[GET POST PUT DELETE HEAD OPTIONS].each do |verb|
56
+ ::ProxES::Permission.find_or_create(role: sa, verb: verb, pattern: '*', index: '*')
53
57
  end
54
58
 
55
59
  # Admin Role
@@ -61,25 +65,66 @@ module Ditty
61
65
  ::ProxES::Permission.find_or_create(role: user_role, verb: 'GET', pattern: '/_nodes')
62
66
  ::ProxES::Permission.find_or_create(role: user_role, verb: 'GET', pattern: '/_nodes/stats')
63
67
  ::ProxES::Permission.find_or_create(role: user_role, verb: 'GET', pattern: '/_stats')
64
- ::ProxES::Permission.find_or_create(role: user_role, verb: 'INDEX', pattern: 'user-{user.id}.*')
68
+ # TODO
69
+ # ::ProxES::Permission.find_or_create(role: user_role, verb: 'INDEX', pattern: 'user-{user.id}*')
65
70
 
66
71
  # Kibana Specific
67
- # actions: ["indices:data/read/field_stats", "indices:admin/mappings/fields/get", "indices:admin/get", "indices:data/read/msearch"]
68
72
  anon_role = ::Ditty::Role.find_or_create(name: 'anonymous')
69
73
  ::Ditty::User.create_anonymous_user('anonymous@proxes.io')
70
- ::ProxES::Permission.find_or_create(role: anon_role, verb: 'GET', pattern: '/.kibana/config/.*')
71
- ::ProxES::Permission.find_or_create(role: anon_role, verb: 'INDEX', pattern: '.kibana')
74
+ ::ProxES::Permission.find_or_create(role: anon_role, verb: 'GET', pattern: '/.kibana/config/*', index: '.kibana')
75
+ ::ProxES::Permission.find_or_create(role: anon_role, verb: 'GET', pattern: '/.kibana/doc/config/*', index: '.kibana')
72
76
 
73
77
  kibana = ::Ditty::Role.find_or_create(name: 'kibana')
74
- ::ProxES::Permission.find_or_create(role: kibana, verb: 'INDEX', pattern: '.kibana')
75
- ::ProxES::Permission.find_or_create(role: kibana, verb: 'HEAD', pattern: '/')
76
- ::ProxES::Permission.find_or_create(role: kibana, verb: 'GET', pattern: '/_nodes*')
77
- ::ProxES::Permission.find_or_create(role: kibana, verb: 'GET', pattern: '/_cluster/health.*')
78
- ::ProxES::Permission.find_or_create(role: kibana, verb: 'GET', pattern: '/_cluster/settings.*')
79
- ::ProxES::Permission.find_or_create(role: kibana, verb: 'POST', pattern: '/_mget')
80
- ::ProxES::Permission.find_or_create(role: kibana, verb: 'POST', pattern: '/_search')
81
- ::ProxES::Permission.find_or_create(role: kibana, verb: 'POST', pattern: '/_msearch')
82
- ::ProxES::Permission.find_or_create(role: kibana, verb: 'POST', pattern: '/_refresh')
78
+ ::ProxES::Permission.find_or_create(role: kibana, verb: 'HEAD', pattern: '/', index: '.kibana')
79
+ ::ProxES::Permission.find_or_create(role: kibana, verb: 'GET', pattern: '/_nodes*', index: '.kibana')
80
+ ::ProxES::Permission.find_or_create(role: kibana, verb: 'GET', pattern: '/_cluster/health*', index: '.kibana')
81
+ ::ProxES::Permission.find_or_create(role: kibana, verb: 'GET', pattern: '/_cluster/settings*', index: '.kibana')
82
+ ::ProxES::Permission.find_or_create(role: kibana, verb: 'POST', pattern: '/_mget', index: '.kibana')
83
+ ::ProxES::Permission.find_or_create(role: kibana, verb: 'POST', pattern: '/_search', index: '.kibana')
84
+ ::ProxES::Permission.find_or_create(role: kibana, verb: 'POST', pattern: '/_msearch', index: '.kibana')
85
+ ::ProxES::Permission.find_or_create(role: kibana, verb: 'POST', pattern: '/_refresh', index: '.kibana')
86
+
87
+ # Status Check
88
+ ::ProxES::StatusCheck.find_or_create(
89
+ type: 'ProxES::ClusterHealthStatusCheck',
90
+ name: 'Cluster Health',
91
+ source: 'health'
92
+ ) { |r| r.set(required_value: 'green', order: 20) }
93
+ ::ProxES::StatusCheck.find_or_create(
94
+ type: 'ProxES::MasterNodesStatusCheck',
95
+ name: 'Master Nodes',
96
+ source: 'node_stats'
97
+ ) { |r| r.set(required_value: 1, order: 30) }
98
+ ::ProxES::StatusCheck.find_or_create(
99
+ type: 'ProxES::DataNodesStatusCheck',
100
+ name: 'Data Nodes',
101
+ source: 'node_stats'
102
+ ) { |r| r.set(required_value: 1, order: 40) }
103
+ ::ProxES::StatusCheck.find_or_create(
104
+ type: 'ProxES::IngestNodesStatusCheck',
105
+ name: 'Ingest Nodes',
106
+ source: 'node_stats'
107
+ ) { |r| r.set(required_value: 1, order: 50) }
108
+ ::ProxES::StatusCheck.find_or_create(
109
+ type: 'ProxES::FileSystemStatusCheck',
110
+ name: 'Node File Systems',
111
+ source: 'node_stats'
112
+ ) { |r| r.set(required_value: 10, order: 60) }
113
+ ::ProxES::StatusCheck.find_or_create(
114
+ type: 'ProxES::JVMHeapStatusCheck',
115
+ name: 'Node JVM Heap',
116
+ source: 'node_stats'
117
+ ) { |r| r.set(required_value: 85, order: 70) }
118
+ ::ProxES::StatusCheck.find_or_create(
119
+ type: 'ProxES::CPUStatusCheck',
120
+ name: 'Node CPU Usage',
121
+ source: 'node_stats'
122
+ ) { |r| r.set(required_value: 70, order: 80) }
123
+ ::ProxES::StatusCheck.find_or_create(
124
+ type: 'ProxES::MemoryStatusCheck',
125
+ name: 'Node Memory Usage',
126
+ source: 'node_stats'
127
+ ) { |r| r.set(required_value: 99, order: 90) }
83
128
  end
84
129
  end
85
130
  end
@@ -9,6 +9,7 @@ require 'proxes/policies/permission_policy'
9
9
  module ProxES
10
10
  class Permissions < Ditty::Component
11
11
  set model_class: Permission
12
+ set view_folder: ::Ditty::ProxES.view_folder
12
13
 
13
14
  FILTERS = [
14
15
  { name: :user, field: 'user.email' },
@@ -31,11 +32,5 @@ module ProxES
31
32
  ProxES::Permission.verbs
32
33
  end
33
34
  end
34
-
35
- def find_template(views, name, engine, &block)
36
- super(views, name, engine, &block) # Root
37
- super(::Ditty::ProxES.view_folder, name, engine, &block) # This Component
38
- super(::Ditty::App.view_folder, name, engine, &block) # Ditty
39
- end
40
35
  end
41
36
  end
@@ -8,9 +8,17 @@ require 'proxes/policies/search_policy'
8
8
  module ProxES
9
9
  class Search < Ditty::Application
10
10
  set base_path: "#{settings.map_path}/search"
11
+ set view_folder: ::Ditty::ProxES.view_folder
12
+
13
+ helpers do
14
+ def fields(indices, names_only)
15
+ standard = { '_index' => 'text', '_type' => 'text', '_id' => 'text' }
16
+ standard.merge ProxES::Services::Search.fields(index: indices, names_only: names_only)
17
+ end
18
+ end
11
19
 
12
20
  get '/' do
13
- authorize self, :search
21
+ authorize self, :list
14
22
 
15
23
  param :page, Integer, min: 0, default: 1
16
24
  param :count, Integer, min: 0, default: 25
@@ -21,16 +29,16 @@ module ProxES
21
29
  locals: {
22
30
  title: 'Search',
23
31
  indices: ProxES::Services::Search.indices,
24
- fields: ProxES::Services::Search.fields(index: params[:indices], names_only: true),
32
+ fields: fields(params[:indices], true),
25
33
  result: result
26
34
  }
27
35
  end
28
36
 
29
37
  get '/fields/?:indices?/?' do
38
+ param :names_only, Boolean, default: false
30
39
  authorize self, :fields
31
40
 
32
- param :names_only, Boolean, default: false
33
- json ProxES::Services::Search.fields index: params[:indices], names_only: params[:names_only]
41
+ json fields(params[:indices], params[:names_only])
34
42
  end
35
43
 
36
44
  get '/indices/?' do
@@ -45,11 +53,5 @@ module ProxES
45
53
  param :size, Integer, min: 0, default: 25
46
54
  json ProxES::Services::Search.values(field, size: params[:size], index: params[:indices])
47
55
  end
48
-
49
- def find_template(views, name, engine, &block)
50
- super(views, name, engine, &block) # Root
51
- super(::Ditty::ProxES.view_folder, name, engine, &block) # This Component
52
- super(::Ditty::App.view_folder, name, engine, &block) # Ditty
53
- end
54
56
  end
55
57
  end
@@ -2,112 +2,49 @@
2
2
 
3
3
  require 'ditty/controllers/application'
4
4
  require 'proxes/policies/status_policy'
5
- require 'proxes/services/es'
5
+ require 'proxes/models/status_check'
6
6
 
7
7
  module ProxES
8
8
  class Status < Ditty::Application
9
- helpers ProxES::Services::ES
10
-
11
- def find_template(views, name, engine, &block)
12
- super(views, name, engine, &block) # Root
13
- super(::Ditty::ProxES.view_folder, name, engine, &block) # This Component
14
- super(::Ditty::App.view_folder, name, engine, &block) # Ditty
15
- end
9
+ set view_folder: ::Ditty::ProxES.view_folder
16
10
 
17
11
  # This provides a URL that can be polled by a monitoring system. It will return
18
12
  # 200 OK if all the checks pass, or 500 if any of the checks fail.
19
13
  get '/check' do
20
14
  checks = []
21
15
  begin
22
- health = client.cluster.health level: 'cluster'
23
- checks << { text: 'Cluster Reachable', passed: true, value: health['cluster_name'] }
24
- checks << { text: 'Cluster Health', passed: health['status'] == 'green', value: health['status'] }
25
-
26
- node_stats = client.nodes.stats
27
-
28
- master_nodes = []
29
- data_nodes = []
30
- ingestion_nodes = []
31
- node_stats['nodes'].each_value do |node|
32
- if node['roles']
33
- master_nodes << node['name'] if node['roles'].include? 'master'
34
- data_nodes << node['name'] if node['roles'].include? 'data'
35
- ingestion_nodes << node['name'] if node['roles'].include? 'ingest'
36
- elsif node['attributes']
37
- master_nodes << node['name'] unless node['attributes']['master'] == 'false'
38
- data_nodes << node['name'] unless node['attributes']['data'] == 'false'
39
- ingestion_nodes << node['name'] unless node['attributes']['ingest'] == 'false'
40
- elsif node['settings']
41
- master_nodes << node['name'] unless node['settings']['node']['master'] == 'false'
42
- data_nodes << node['name'] unless node['settings']['node']['data'] == 'false'
43
- ingestion_nodes << node['name'] unless node['settings']['node']['ingest'] == 'false'
44
- end
45
- end
46
- checks << {
47
- text: 'Master Nodes',
48
- passed: master_nodes.count > 0,
49
- value: master_nodes.count > 0 ? master_nodes.sort : 'None'
50
- }
51
- checks << {
52
- text: 'Data Nodes',
53
- passed: data_nodes.count > 0,
54
- value: data_nodes.count > 0 ? data_nodes.sort : 'None'
55
- }
56
- checks << {
57
- text: 'Ingestion Nodes',
58
- passed: true,
59
- value: ingestion_nodes.count > 0 ? ingestion_nodes.sort : 'None'
60
- }
61
-
62
- jvm_values = []
63
- jvm_passed = true
64
- node_stats['nodes'].each_value do |node|
65
- jvm_values << "#{node['name']}: #{node['jvm']['mem']['heap_used_percent']}%"
66
- jvm_passed = false if node['jvm']['mem']['heap_used_percent'] > 85
16
+ # Programmed checks
17
+ ProxES::StatusCheck.order(:order).all.each do |sc|
18
+ checks << {
19
+ text: sc.name,
20
+ passed: sc.passed?,
21
+ check: sc.required_value,
22
+ value: sc.value,
23
+ children: sc.children,
24
+ entity: sc
25
+ }
67
26
  end
68
- checks << { text: 'Node JVM Heap', passed: jvm_passed, value: jvm_values.sort }
69
27
 
70
- fs_values = []
71
- fs_passed = true
72
- node_stats['nodes'].each_value do |node|
73
- next if node['attributes'] && node['attributes']['data'] == 'false'
74
- next if node['roles'] && node['roles'].include?('data') == false
75
- stats = node['fs']['total']
76
- left = stats['available_in_bytes'] / stats['total_in_bytes'].to_f * 100
77
- fs_values << "#{node['name']}: #{format('%.02f', left)}% Free"
78
- fs_passed = false if left < 10
79
- end
80
- checks << { text: 'Node File Systems', passed: fs_passed, value: fs_values.sort }
81
-
82
- cpu_values = []
83
- cpu_passed = true
84
- node_stats['nodes'].each_value do |node|
85
- value = (node['os']['cpu_percent'] || node['os']['cpu']['percent'])
86
- cpu_values << "#{node['name']}: #{value}"
87
- cpu_passed = false if value.to_i > 70
88
- end
89
- checks << { text: 'Node CPU Usage', passed: cpu_passed, value: cpu_values.sort }
90
-
91
- memory_values = []
92
- memory_sum = 0
93
- node_stats['nodes'].each_value do |node|
94
- memory_sum += node['os']['mem']['used_percent']
95
- memory_values << "#{node['name']}: #{node['os']['mem']['used_percent']}"
96
- end
97
- memory_passed = (memory_sum / memory_values.size).to_i < 100
98
- checks << { text: 'Node Memory Usage', passed: memory_passed, value: memory_values.sort }
28
+ # Default Check
29
+ checks.unshift(
30
+ text: 'Cluster Reachable',
31
+ passed: true,
32
+ value: ProxES::StatusCheck.source_result('health')['cluster_name']['cluster_name']
33
+ )
99
34
  rescue Faraday::Error => e
100
35
  checks << { text: 'Cluster Reachable', passed: false, value: e.message }
101
36
  end
102
37
 
103
- status checks.find { |c| c[:passed] == false } ? 500 : 200
38
+ passed = checks.find { |c| c[:passed] == false }.nil?
39
+ code = passed ? 200 : 500
104
40
 
41
+ status code
105
42
  respond_to do |format|
106
43
  format.html do
107
- haml :'status/check', locals: { title: 'Status Check', checks: checks }
44
+ haml :'status/check', locals: { title: 'Status Check', checks: checks, passed: passed }
108
45
  end
109
46
  format.json do
110
- json checks
47
+ json checks: checks, passed: passed, code: code
111
48
  end
112
49
  end
113
50
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ditty/controllers/component'
4
+ require 'proxes/policies/status_check_policy'
5
+ require 'proxes/models/status_check'
6
+
7
+ module ProxES
8
+ class StatusChecks < Ditty::Component
9
+ set model_class: StatusCheck
10
+ set view_folder: ::Ditty::ProxES.view_folder
11
+
12
+ def ordering
13
+ return Sequel.asc(:order) if params[:sort].blank?
14
+
15
+ Sequel.send(params[:order].to_sym, params[:sort].to_sym)
16
+ end
17
+ end
18
+ end
@@ -1,5 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'proxes/services/es'
2
- require 'net/http/persistent'
4
+ require 'typhoeus'
5
+ require 'typhoeus/adapters/faraday'
3
6
  require 'singleton'
4
7
  require 'rack'
5
8
 
@@ -10,40 +13,61 @@ module ProxES
10
13
  include ProxES::Services::ES
11
14
 
12
15
  def call(env)
13
- source = Rack::Request.new(env)
14
- response = conn.send(source.request_method.downcase) do |req|
15
- source_body = body_from(source)
16
- req.body = source_body if source_body
17
- req.url source.fullpath == '' ? URI.parse(env['REQUEST_URI']).request_uri : source.fullpath
16
+ rewrite_response(
17
+ perform_request(
18
+ Rack::Request.new(
19
+ rewrite_env(env)
20
+ )
21
+ )
22
+ )
23
+ end
24
+
25
+ def rewrite_env(env)
26
+ env
27
+ end
28
+
29
+ # TODO: Consider moving these methods to the ProxES ES Service to enable reuse
30
+ def perform_request(request)
31
+ conn.send(request.request_method.downcase) do |req|
32
+ body = body_from(request)
33
+ req.body = body if body
34
+ req.url request.fullpath == '' ? URI.parse(env['REQUEST_URI']).request_uri : request.fullpath
18
35
  end
19
- mangle response
20
36
  end
21
37
 
22
- def mangle(response)
23
- headers = (response.respond_to?(:headers) && response.headers) || self.class.normalize_headers(response.to_hash)
38
+ def rewrite_response(response)
39
+ headers = (response.respond_to?(:headers) && response.headers) || normalize_headers(response.to_hash)
24
40
  body = response.body || ['']
25
41
  body = [body] unless body.respond_to?(:each)
26
42
 
27
- # Not sure where this is coming from, but it causes timeouts on the client
28
- headers.delete('transfer-encoding')
29
- # Ensure that the content length rack middleware kicks in
30
- headers.delete('content-length')
43
+ # Only keep certain headers.
44
+ # See point 1 on https://www.mnot.net/blog/2011/07/11/what_proxies_must_do
45
+ # TODO: Extend on the above
46
+ headers.delete_if { |k, _v| !header_list.include? k.downcase }
31
47
 
32
48
  [response.status, headers, body]
33
49
  end
34
50
 
51
+ def header_list
52
+ [
53
+ 'date',
54
+ 'content-type',
55
+ 'cache-control',
56
+ ]
57
+ end
58
+
35
59
  def body_from(request)
36
- return nil if request.body.nil? || (Kernel.const_defined?('::Puma::NullIO') && request.body.is_a?(Puma::NullIO))
60
+ return if request.body.nil? || request.body.respond_to?(:read) == false
61
+
37
62
  request.body.read.tap { |_r| request.body.rewind }
38
63
  end
39
64
 
40
- class << self
41
- def normalize_headers(headers)
42
- mapped = headers.map do |k, v|
65
+ def normalize_headers(headers)
66
+ Rack::Utils::HeaderHash.new(
67
+ headers.map do |k, v|
43
68
  [k, v.is_a?(Array) ? v.join("\n") : v]
44
- end
45
- Rack::Utils::HeaderHash.new Hash[mapped]
46
- end
69
+ end.to_h
70
+ )
47
71
  end
48
72
  end
49
73
  end
@@ -13,7 +13,7 @@ module ProxES
13
13
 
14
14
  def initialize(app, logger = nil)
15
15
  @app = app
16
- @logger = logger || ::Ditty::Services::Logger.instance
16
+ @logger = logger || ::Ditty::Services::Logger
17
17
  end
18
18
 
19
19
  def call(env)
@@ -30,24 +30,26 @@ module ProxES
30
30
  log_not_authorized request
31
31
  raise e if ENV['APP_ENV'] == 'development'
32
32
  return [401, {}, []] if request.head?
33
+
33
34
  request.html? && request.user.nil? ? login_and_redirect(request) : error('Not Authorized', 401)
34
35
  rescue StandardError => e
35
36
  broadcast(:es_request_denied, request, e)
36
37
  log_not_authorized request
37
38
  raise e if ENV['APP_ENV'] == 'development'
38
39
  return [403, {}. []] if request.head?
40
+
39
41
  error 'Forbidden', 403
40
42
  end
41
43
 
42
44
  def log_not_authorized(request)
43
45
  user = request.user ? request.user.email : 'unauthenticated request'
44
- logger.error "Access denied for #{user} by security layer: #{request.detail}"
46
+ logger.error "Access denied for #{user} by security layer: #{request.detail} #{request.indices.join(',')}"
45
47
  end
46
48
 
47
49
  # Response Helpers
48
50
  def error(message, code = 500)
49
51
  headers = { 'Content-Type' => 'application/json' }
50
- headers['WWW-Authenticate'] = 'Basic realm="security"' if code == 401
52
+ headers['WWW-Authenticate'] = 'Basic realm="Authorization Required"' if code == 401
51
53
  [code, headers, ['{"error":"' + message + '"}']]
52
54
  end
53
55