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
@@ -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)
|
@@ -22,8 +22,8 @@ module ProxES
|
|
22
22
|
|
23
23
|
check_basic request
|
24
24
|
authorize request
|
25
|
-
|
26
25
|
request.index = policy_scope(request) if request.indices?
|
26
|
+
|
27
27
|
log(request, 'AFTER')
|
28
28
|
|
29
29
|
@app.call env
|
@@ -35,7 +35,8 @@ module ProxES
|
|
35
35
|
|
36
36
|
identity = ::Ditty::Identity.find(username: auth.credentials[0])
|
37
37
|
identity ||= ::Ditty::Identity.find(username: CGI.unescape(auth.credentials[0]))
|
38
|
-
return false unless identity
|
38
|
+
return false unless identity&.authenticate(auth.credentials[1])
|
39
|
+
|
39
40
|
request.env['rack.session'] ||= {}
|
40
41
|
request.env['rack.session']['user_id'] = identity.user_id
|
41
42
|
end
|
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'ditty/models/base'
|
4
4
|
require 'ditty/models/user'
|
5
5
|
require 'ditty/models/role'
|
6
|
+
require 'active_support/core_ext/object/blank'
|
6
7
|
|
7
8
|
module ProxES
|
8
9
|
class Permission < ::Sequel::Model
|
@@ -12,27 +13,57 @@ module ProxES
|
|
12
13
|
many_to_one :user, class: ::Ditty::User
|
13
14
|
|
14
15
|
dataset_module do
|
15
|
-
def for_user(
|
16
|
-
where(
|
16
|
+
def for_user(usr)
|
17
|
+
return where(id: -1) if usr.nil?
|
18
|
+
|
19
|
+
# TODO: Injection of user fields into regex
|
20
|
+
# permission.pattern.gsub(/\{user.(.*)\}/) { |_match| user.send(Regexp.last_match[1].to_sym) }
|
21
|
+
where { Sequel.|({ role: usr.roles }, { user_id: usr.id }) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def for_request(request)
|
25
|
+
where(verb: request.request_method).all.select { |perm| perm.pattern_regex.match request.path }
|
17
26
|
end
|
18
27
|
end
|
19
28
|
|
20
29
|
def validate
|
30
|
+
super
|
21
31
|
validates_presence %i[verb pattern]
|
22
32
|
validates_presence :role_id unless user_id
|
23
33
|
validates_presence :user_id unless role_id
|
24
34
|
validates_includes self.class.verbs, :verb
|
25
35
|
end
|
26
36
|
|
37
|
+
def pattern_regex
|
38
|
+
regex pattern
|
39
|
+
end
|
40
|
+
|
41
|
+
def index_regex
|
42
|
+
regex index
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def regex(str)
|
48
|
+
str ||= ''
|
49
|
+
return Regexp.new(str) if str.blank? || (str[0] == '|' && str[-1] == '|')
|
50
|
+
|
51
|
+
str = str.gsub(/([^.])\*/, '\1.*')
|
52
|
+
str = '.*' if str == '*' # My regex foo is not strong enough to combine the previous line and this one
|
53
|
+
Regexp.new '^' + str
|
54
|
+
end
|
55
|
+
|
27
56
|
class << self
|
28
57
|
def verbs
|
29
|
-
%w[GET POST PUT DELETE HEAD OPTIONS TRACE
|
58
|
+
%w[GET POST PUT DELETE HEAD OPTIONS TRACE]
|
30
59
|
end
|
31
60
|
|
32
61
|
def from_audit_log(audit_log)
|
33
62
|
return {} if audit_log.details.nil?
|
63
|
+
|
34
64
|
match = audit_log.details.match(/^(\w)+ (\S+)/)
|
35
65
|
return {} if match.nil?
|
66
|
+
|
36
67
|
{
|
37
68
|
verb: match[1],
|
38
69
|
path: match[2]
|
@@ -53,3 +84,16 @@ module Ditty
|
|
53
84
|
one_to_many :permissions, class: ::ProxES::Permission
|
54
85
|
end
|
55
86
|
end
|
87
|
+
|
88
|
+
# Table: permissions
|
89
|
+
# Columns:
|
90
|
+
# id | integer | PRIMARY KEY AUTOINCREMENT
|
91
|
+
# verb | varchar(255) |
|
92
|
+
# pattern | varchar(255) |
|
93
|
+
# created_at | timestamp |
|
94
|
+
# role_id | integer |
|
95
|
+
# user_id | integer |
|
96
|
+
# index | varchar(255) | NOT NULL DEFAULT '*'
|
97
|
+
# Foreign key constraints:
|
98
|
+
# (role_id) REFERENCES roles
|
99
|
+
# (user_id) REFERENCES users
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'proxes/services/es'
|
4
|
+
|
5
|
+
module ProxES
|
6
|
+
class StatusCheck < Sequel::Model
|
7
|
+
plugin :single_table_inheritance, :type
|
8
|
+
|
9
|
+
extend ProxES::Services::ES
|
10
|
+
|
11
|
+
SOURCE_CALLS = {
|
12
|
+
health: %i[cluster health],
|
13
|
+
node_stats: %i[nodes stats]
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
def validate
|
17
|
+
super
|
18
|
+
validates_presence %i[name source]
|
19
|
+
end
|
20
|
+
|
21
|
+
def check
|
22
|
+
raise 'Unimplemented'
|
23
|
+
end
|
24
|
+
|
25
|
+
def value
|
26
|
+
raise 'Unimplemented'
|
27
|
+
end
|
28
|
+
|
29
|
+
def passed?
|
30
|
+
return @result if defined? @result
|
31
|
+
|
32
|
+
check
|
33
|
+
end
|
34
|
+
|
35
|
+
def source_result
|
36
|
+
self.class.source_result(source)
|
37
|
+
end
|
38
|
+
|
39
|
+
def children; end
|
40
|
+
|
41
|
+
def formatted(val = nil)
|
42
|
+
val || value
|
43
|
+
end
|
44
|
+
|
45
|
+
def policy_class
|
46
|
+
StatusCheckPolicy
|
47
|
+
end
|
48
|
+
|
49
|
+
class << self
|
50
|
+
def source_result(source)
|
51
|
+
@source_result ||= Hash.new do |h, k|
|
52
|
+
h[k] = client
|
53
|
+
SOURCE_CALLS[source.to_sym].each do |call|
|
54
|
+
h[k] = h[k].send(call)
|
55
|
+
end
|
56
|
+
h[k]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
Dir.glob(File.expand_path('./status_checks', __dir__) + '/*.rb').each { |f| require f }
|
63
|
+
end
|
64
|
+
|
65
|
+
# Table: status_checks
|
66
|
+
# Columns:
|
67
|
+
# id | integer | PRIMARY KEY AUTOINCREMENT
|
68
|
+
# type | varchar(255) |
|
69
|
+
# name | varchar(255) |
|
70
|
+
# source | varchar(255) |
|
71
|
+
# required_value | varchar(255) |
|
72
|
+
# order | integer | DEFAULT 1
|
73
|
+
# created_at | timestamp | DEFAULT datetime(CURRENT_TIMESTAMP, 'localtime')
|
74
|
+
# updated_at | timestamp | DEFAULT datetime(CURRENT_TIMESTAMP, 'localtime')
|
75
|
+
# Indexes:
|
76
|
+
# sqlite_autoindex_status_checks_1 | UNIQUE (name)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ProxES
|
4
|
+
class ClusterHealthStatusCheck < StatusCheck
|
5
|
+
def value
|
6
|
+
source_result['status']['status']
|
7
|
+
end
|
8
|
+
|
9
|
+
def check
|
10
|
+
return true if required_value.blank?
|
11
|
+
|
12
|
+
value == required_value
|
13
|
+
end
|
14
|
+
|
15
|
+
def formatted(val = nil)
|
16
|
+
(val || value).titlecase
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ProxES
|
4
|
+
class CPUStatusCheck < StatusCheck
|
5
|
+
def value
|
6
|
+
children.values.inject(0.0) { |sum, el| sum + el } / children.count
|
7
|
+
end
|
8
|
+
|
9
|
+
def children
|
10
|
+
@children ||= source_result['nodes']['nodes'].values.map do |node|
|
11
|
+
value = node['os']['cpu_percent'] || node['os']['cpu']['percent']
|
12
|
+
[
|
13
|
+
node['name'],
|
14
|
+
value.to_f
|
15
|
+
]
|
16
|
+
end.to_h
|
17
|
+
end
|
18
|
+
|
19
|
+
def check
|
20
|
+
return true if required_value.blank?
|
21
|
+
|
22
|
+
value < required_value.to_f
|
23
|
+
end
|
24
|
+
|
25
|
+
def formatted(val = nil)
|
26
|
+
format('%.4f%% Average Usage', val || value)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ProxES
|
4
|
+
class FileSystemStatusCheck < StatusCheck
|
5
|
+
def value
|
6
|
+
children.values.min
|
7
|
+
end
|
8
|
+
|
9
|
+
def children
|
10
|
+
@children ||= source_result['nodes']['nodes'].values.map do |node|
|
11
|
+
next if node['attributes'] && node['attributes']['data'] == 'false'
|
12
|
+
next if node['roles'] && node['roles'].include?('data') == false
|
13
|
+
|
14
|
+
stats = node['fs']['total']
|
15
|
+
[
|
16
|
+
node['name'],
|
17
|
+
stats['available_in_bytes'] / stats['total_in_bytes'].to_f * 100
|
18
|
+
]
|
19
|
+
end.compact.to_h
|
20
|
+
end
|
21
|
+
|
22
|
+
def check
|
23
|
+
return true if required_value.blank?
|
24
|
+
|
25
|
+
value > required_value.to_f
|
26
|
+
end
|
27
|
+
|
28
|
+
def formatted(val = nil)
|
29
|
+
format('%.4f%% Minimum Free', val || value)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ProxES
|
4
|
+
class JVMHeapStatusCheck < StatusCheck
|
5
|
+
def value
|
6
|
+
children.values.inject(0.0) { |sum, el| sum + el } / children.count
|
7
|
+
end
|
8
|
+
|
9
|
+
def children
|
10
|
+
@children ||= source_result['nodes']['nodes'].values.map do |node|
|
11
|
+
[
|
12
|
+
node['name'],
|
13
|
+
node['jvm']['mem']['heap_used_percent'].to_f
|
14
|
+
]
|
15
|
+
end.to_h
|
16
|
+
end
|
17
|
+
|
18
|
+
def check
|
19
|
+
return true if required_value.blank?
|
20
|
+
|
21
|
+
value < required_value.to_f
|
22
|
+
end
|
23
|
+
|
24
|
+
def formatted(val = nil)
|
25
|
+
format('%.4f%% Average Usage', val || value)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ProxES
|
4
|
+
class MemoryStatusCheck < StatusCheck
|
5
|
+
def value
|
6
|
+
# Currently checks the average. Can change it to check per node too
|
7
|
+
children.values.inject(0.0) { |sum, el| sum + el } / children.count
|
8
|
+
end
|
9
|
+
|
10
|
+
def children
|
11
|
+
@children ||= source_result['nodes']['nodes'].values.map do |node|
|
12
|
+
[
|
13
|
+
node['name'],
|
14
|
+
node['os']['mem']['used_percent'].to_f
|
15
|
+
]
|
16
|
+
end.to_h
|
17
|
+
end
|
18
|
+
|
19
|
+
def check
|
20
|
+
return true if required_value.blank?
|
21
|
+
|
22
|
+
value < required_value.to_f
|
23
|
+
end
|
24
|
+
|
25
|
+
def formatted(val = nil)
|
26
|
+
format('%.4f%% Average Usage', val || value)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ProxES
|
4
|
+
class NodesStatusCheck < StatusCheck
|
5
|
+
def node_type
|
6
|
+
raise 'Unimplemented'
|
7
|
+
end
|
8
|
+
|
9
|
+
def value
|
10
|
+
children.count
|
11
|
+
end
|
12
|
+
|
13
|
+
def check_node(node)
|
14
|
+
node['roles']&.include?(node_type) ||
|
15
|
+
node['attributes'] && node.dig('attributes', node_type) != 'false' ||
|
16
|
+
node.dig('settings', 'node') && node.dig('settings', 'node', node_type) != 'false'
|
17
|
+
end
|
18
|
+
|
19
|
+
def children
|
20
|
+
@children ||= source_result['nodes']['nodes'].map do |_id, node|
|
21
|
+
[node['name'], 1] if check_node(node)
|
22
|
+
end.compact.to_h
|
23
|
+
end
|
24
|
+
|
25
|
+
def check
|
26
|
+
return true if required_value.blank?
|
27
|
+
|
28
|
+
required_value.to_i == value
|
29
|
+
end
|
30
|
+
|
31
|
+
def formatted(val = nil)
|
32
|
+
(val || value).to_i == 1 ? '1 Node' : "#{val || value} Nodes"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class MasterNodesStatusCheck < NodesStatusCheck
|
37
|
+
def node_type
|
38
|
+
'master'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class DataNodesStatusCheck < NodesStatusCheck
|
43
|
+
def node_type
|
44
|
+
'data'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class IngestNodesStatusCheck < NodesStatusCheck
|
49
|
+
def node_type
|
50
|
+
'ingest'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -5,7 +5,7 @@ require 'ditty/policies/application_policy'
|
|
5
5
|
module ProxES
|
6
6
|
class PermissionPolicy < Ditty::ApplicationPolicy
|
7
7
|
def create?
|
8
|
-
user
|
8
|
+
user&.super_admin?
|
9
9
|
end
|
10
10
|
|
11
11
|
def list?
|
@@ -25,12 +25,12 @@ module ProxES
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def permitted_attributes
|
28
|
-
%i[verb pattern role_id user_id]
|
28
|
+
%i[verb pattern index role_id user_id]
|
29
29
|
end
|
30
30
|
|
31
31
|
class Scope < Ditty::ApplicationPolicy::Scope
|
32
32
|
def resolve
|
33
|
-
user
|
33
|
+
user&.super_admin? ? scope : scope.where(id: -1)
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
@@ -1,22 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'active_support'
|
4
|
-
require 'active_support/core_ext/object/blank'
|
5
3
|
require 'proxes/policies/request_policy'
|
6
4
|
|
7
5
|
module ProxES
|
8
6
|
class Request
|
9
7
|
class BulkPolicy < RequestPolicy
|
10
|
-
def post?
|
11
|
-
return false if user.nil? ||
|
12
|
-
(request.index && !index_allowed?) ||
|
13
|
-
(request.bulk_indices == '' || patterns.blank?)
|
14
|
-
|
15
|
-
patterns.find do |pattern|
|
16
|
-
request.bulk_indices.find { |idx| idx !~ /#{pattern}/ }
|
17
|
-
end.nil?
|
18
|
-
end
|
19
|
-
|
20
8
|
class Scope < RequestPolicy::Scope
|
21
9
|
end
|
22
10
|
end
|