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
@@ -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)
@@ -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 && identity.authenticate(auth.credentials[1])
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(a_user, action)
16
- where(verb: action).where { Sequel.|({ role: a_user.roles }, { user_id: a_user.id }) }
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 INDEX]
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 && user.super_admin?
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 && user.super_admin? ? scope : scope.where(id: -1)
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
@@ -6,9 +6,6 @@ module ProxES
6
6
  class Request
7
7
  class CreatePolicy < RequestPolicy
8
8
  class Scope < RequestPolicy::Scope
9
- def resolve
10
- super.count > 0 ? request.index : []
11
- end
12
9
  end
13
10
  end
14
11
  end