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.
- 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
|
|