proxes 0.9.13 → 0.10.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/ditty/components/proxes.rb +61 -16
- data/lib/proxes/controllers/permissions.rb +1 -6
- data/lib/proxes/controllers/search.rb +12 -10
- data/lib/proxes/controllers/status.rb +23 -86
- data/lib/proxes/controllers/status_checks.rb +18 -0
- data/lib/proxes/forwarder.rb +44 -20
- data/lib/proxes/middleware/error_handling.rb +5 -3
- data/lib/proxes/middleware/security.rb +4 -3
- data/lib/proxes/models/permission.rb +47 -3
- data/lib/proxes/models/status_check.rb +76 -0
- data/lib/proxes/models/status_checks/cluster_health_status_check.rb +19 -0
- data/lib/proxes/models/status_checks/cpu_status_check.rb +29 -0
- data/lib/proxes/models/status_checks/file_system_status_check.rb +32 -0
- data/lib/proxes/models/status_checks/jvm_heap_status_check.rb +28 -0
- data/lib/proxes/models/status_checks/memory_status_check.rb +29 -0
- data/lib/proxes/models/status_checks/nodes_status_check.rb +53 -0
- data/lib/proxes/policies/permission_policy.rb +3 -3
- data/lib/proxes/policies/request/bulk_policy.rb +0 -12
- data/lib/proxes/policies/request/create_policy.rb +0 -3
- data/lib/proxes/policies/request/index_policy.rb +0 -5
- data/lib/proxes/policies/request/mget_policy.rb +12 -0
- data/lib/proxes/policies/request/msearch_policy.rb +12 -0
- data/lib/proxes/policies/request/root_policy.rb +0 -3
- data/lib/proxes/policies/request/snapshot_policy.rb +0 -3
- data/lib/proxes/policies/request_policy.rb +24 -25
- data/lib/proxes/policies/search_policy.rb +6 -2
- data/lib/proxes/policies/status_check_policy.rb +37 -0
- data/lib/proxes/request.rb +19 -5
- data/lib/proxes/request/bulk.rb +8 -24
- data/lib/proxes/request/cat.rb +6 -0
- data/lib/proxes/request/create.rb +4 -0
- data/lib/proxes/request/index.rb +4 -0
- data/lib/proxes/request/mget.rb +24 -0
- data/lib/proxes/request/msearch.rb +24 -0
- data/lib/proxes/request/multi.rb +40 -0
- data/lib/proxes/request/search.rb +4 -0
- data/lib/proxes/request/stats.rb +4 -0
- data/lib/proxes/services/es.rb +5 -1
- data/lib/proxes/services/listener.rb +29 -6
- data/lib/proxes/services/search.rb +4 -1
- data/lib/proxes/version.rb +1 -1
- data/migrate/20180908_index_specific_permissions.rb +9 -0
- data/migrate/20190109_add_status_checks_table.rb +17 -0
- data/views/layout.haml +17 -6
- metadata +81 -25
- data/lib/proxes/helpers/indices.rb +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3d4050f2df15c55cf749b63f4bb462a1e7bd5eda06dd03dff38ff96fad0f2cde
|
4
|
+
data.tar.gz: e84bcff209deac8cb463f1099637d3df5502ff0bcf31811f9da3c8ae6579f9f9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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: '/
|
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
|
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
|
-
|
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: '
|
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: '
|
75
|
-
::ProxES::Permission.find_or_create(role: kibana, verb: '
|
76
|
-
::ProxES::Permission.find_or_create(role: kibana, verb: 'GET', pattern: '/
|
77
|
-
::ProxES::Permission.find_or_create(role: kibana, verb: 'GET', pattern: '/_cluster/
|
78
|
-
::ProxES::Permission.find_or_create(role: kibana, verb: '
|
79
|
-
::ProxES::Permission.find_or_create(role: kibana, verb: 'POST', pattern: '/
|
80
|
-
::ProxES::Permission.find_or_create(role: kibana, verb: 'POST', pattern: '/
|
81
|
-
::ProxES::Permission.find_or_create(role: kibana, verb: 'POST', pattern: '/
|
82
|
-
|
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, :
|
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:
|
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
|
-
|
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/
|
5
|
+
require 'proxes/models/status_check'
|
6
6
|
|
7
7
|
module ProxES
|
8
8
|
class Status < Ditty::Application
|
9
|
-
|
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
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
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
|
-
|
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
|
data/lib/proxes/forwarder.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'proxes/services/es'
|
2
|
-
require '
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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
|
23
|
-
headers = (response.respond_to?(:headers) && response.headers) ||
|
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
|
-
#
|
28
|
-
|
29
|
-
#
|
30
|
-
headers.
|
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
|
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
|
-
|
41
|
-
|
42
|
-
|
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
|
-
|
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
|
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="
|
52
|
+
headers['WWW-Authenticate'] = 'Basic realm="Authorization Required"' if code == 401
|
51
53
|
[code, headers, ['{"error":"' + message + '"}']]
|
52
54
|
end
|
53
55
|
|