proxes 0.9.13 → 0.10.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 +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