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.
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
@@ -8,11 +8,6 @@ module ProxES
8
8
  class Request
9
9
  class IndexPolicy < RequestPolicy
10
10
  class Scope < RequestPolicy::Scope
11
- def resolve
12
- result = super
13
- return [] unless result.count > 0
14
- %w[POST PUT].include?(request.request_method) ? request.index : result
15
- end
16
11
  end
17
12
  end
18
13
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'proxes/policies/request_policy'
4
+
5
+ module ProxES
6
+ class Request
7
+ class MgetPolicy < RequestPolicy
8
+ class Scope < RequestPolicy::Scope
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'proxes/policies/request_policy'
4
+
5
+ module ProxES
6
+ class Request
7
+ class MsearchPolicy < RequestPolicy
8
+ class Scope < RequestPolicy::Scope
9
+ end
10
+ end
11
+ end
12
+ end
@@ -4,9 +4,6 @@ module ProxES
4
4
  class Request
5
5
  class RootPolicy < RequestPolicy
6
6
  class Scope < RequestPolicy::Scope
7
- def resolve
8
- request
9
- end
10
7
  end
11
8
  end
12
9
  end
@@ -6,9 +6,6 @@ module ProxES
6
6
  class Request
7
7
  class SnapshotPolicy < RequestPolicy
8
8
  class Scope < RequestPolicy::Scope
9
- def resolve
10
- request
11
- end
12
9
  end
13
10
  end
14
11
  end
@@ -4,60 +4,59 @@ require 'active_support'
4
4
  require 'active_support/core_ext/object/blank'
5
5
  require 'ditty/services/logger'
6
6
  require 'proxes/models/permission'
7
- require 'proxes/helpers/indices'
8
7
 
9
8
  module ProxES
10
9
  class RequestPolicy
11
- include Helpers::Indices
12
-
13
10
  attr_reader :user, :record
14
11
  alias request record
15
12
 
16
13
  def initialize(user, record)
17
- @user = user
14
+ @user = user || Ditty::User.anonymous_user
18
15
  @record = record
19
16
  end
20
17
 
21
18
  def method_missing(method_sym, *arguments, &block)
22
- return super if method_sym.to_s[-1] != '?'
19
+ return super unless respond_to_missing? method_sym
20
+
21
+ return false if permissions.empty?
22
+
23
+ return permissions.count.positive? unless request.indices?
23
24
 
24
- return true if user && user.super_admin?
25
- return false if request.indices? && !index_allowed?
26
- action_allowed? method_sym[0..-2].upcase
25
+ # Only allow if all the indices match the given permissions
26
+ request.indices.find do |idx|
27
+ idx = idx[1..-1] if idx[0] == '-'
28
+ permissions.find { |perm| perm.index_regex.match idx }.nil?
29
+ end.nil?
27
30
  end
28
31
 
29
32
  def respond_to_missing?(name, _include_private = false)
30
33
  name[-1] == '?'
31
34
  end
32
35
 
33
- def index_allowed?
34
- patterns = patterns_for('INDEX').map do |permission|
35
- return nil if permission.pattern.blank?
36
- permission.pattern.gsub(/\{user.(.*)\}/) { |_match| user.send(Regexp.last_match[1].to_sym) }
37
- end.compact
38
- filter(request.index, patterns).count > 0
39
- end
40
-
41
- def action_allowed?(action)
42
- # Give me all the user's permissions that match the verb
43
- !!patterns_for(action).find { |permission| (request.path =~ /#{permission.pattern}/) }
36
+ def permissions
37
+ @permissions ||= Permission.for_user(user).for_request(request)
44
38
  end
45
39
 
46
40
  class Scope
47
- include Helpers::Indices
48
-
49
41
  attr_reader :user, :scope
50
42
  alias request scope
51
43
 
52
44
  def initialize(user, scope)
53
- @user = user
45
+ @user = user || Ditty::User.anonymous_user
54
46
  @scope = scope
55
47
  end
56
48
 
57
49
  def resolve
58
- current_user = user || Ditty::User.anonymous_user
59
- return [] if current_user.nil?
60
- filter request.index, patterns
50
+ return permissions.map(&:index) if request.indices == ['*'] || request.indices == ['_all'] || request.indices.blank?
51
+
52
+ request.indices.select do |idx|
53
+ idx = idx[1..-1] if idx[0] == '-'
54
+ permissions.find { |perm| perm.index_regex.match idx }
55
+ end
56
+ end
57
+
58
+ def permissions
59
+ @permissions ||= Permission.for_user(user).for_request(request)
61
60
  end
62
61
  end
63
62
  end
@@ -4,8 +4,12 @@ require 'ditty/policies/application_policy'
4
4
 
5
5
  module ProxES
6
6
  class SearchPolicy < Ditty::ApplicationPolicy
7
+ def list?
8
+ search?
9
+ end
10
+
7
11
  def search?
8
- user && user.super_admin?
12
+ user
9
13
  end
10
14
 
11
15
  def fields?
@@ -22,7 +26,7 @@ module ProxES
22
26
 
23
27
  class Scope < Ditty::ApplicationPolicy::Scope
24
28
  def resolve
25
- user && user.super_admin? ? scope : scope.where(id: -1)
29
+ user&.super_admin? ? scope : scope.where(id: -1)
26
30
  end
27
31
  end
28
32
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ditty/policies/application_policy'
4
+
5
+ module ProxES
6
+ class StatusCheckPolicy < Ditty::ApplicationPolicy
7
+ def create?
8
+ user && (user.super_admin? || user.admin?)
9
+ end
10
+
11
+ def list?
12
+ user && (user.super_admin? || user.admin?)
13
+ end
14
+
15
+ def read?
16
+ create?
17
+ end
18
+
19
+ def update?
20
+ read?
21
+ end
22
+
23
+ def delete?
24
+ create?
25
+ end
26
+
27
+ def permitted_attributes
28
+ %i[type name source required_value order]
29
+ end
30
+
31
+ class Scope < Ditty::ApplicationPolicy::Scope
32
+ def resolve
33
+ user&.super_admin? ? scope : scope.where(id: -1)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -21,12 +21,18 @@ module ProxES
21
21
  path_parts
22
22
  end
23
23
 
24
+ # Indicate whether or not the request is index specific
24
25
  def indices?
25
26
  false
26
27
  end
27
28
 
29
+ # Return the indices associated with the request as an array. [] if `#indices?` is false
30
+ def indices
31
+ []
32
+ end
33
+
28
34
  def html?
29
- get_header('HTTP_ACCEPT') && get_header('HTTP_ACCEPT').include?('text/html')
35
+ get_header('HTTP_ACCEPT')&.include?('text/html')
30
36
  end
31
37
 
32
38
  def duration
@@ -34,17 +40,22 @@ module ProxES
34
40
  end
35
41
 
36
42
  def user_id
37
- return env['rack.session']['user_id'] if env['rack.session']
38
- env['omniauth.auth'].uid if env['omniauth.auth']
43
+ return session['user_id'] if session['user_id']
44
+
45
+ env['omniauth.auth']&.uid
39
46
  end
40
47
 
41
48
  def user
42
49
  return nil if user_id.nil?
50
+
43
51
  @user ||= Ditty::User[user_id]
44
52
  end
45
53
 
46
54
  def detail
47
- "#{request_method.upcase} #{fullpath} (#{self.class.name})"
55
+ detail = "#{request_method.upcase} #{fullpath} (#{self.class.name})"
56
+ return detail unless indices?
57
+
58
+ "#{detail} #{indices.join(',')}"
48
59
  end
49
60
 
50
61
  private
@@ -56,6 +67,7 @@ module ProxES
56
67
  def check_part(val)
57
68
  return val if val.nil?
58
69
  return [] if [endpoint, '_all'].include?(val) && !WRITE_METHODS.include?(request_method)
70
+
59
71
  val.split(',')
60
72
  end
61
73
 
@@ -73,11 +85,13 @@ module ProxES
73
85
 
74
86
  def path_endpoint(path)
75
87
  return '_root' if ['', nil, '/'].include? path
88
+
76
89
  path_parts = path[1..-1].split('/')
77
90
  return path_parts[-1] if ID_ENDPOINTS.include? path_parts[-1]
78
91
  return path_parts[-2] if path_parts[-1] == 'count' && path_parts[-2] == '_percolate'
79
92
  return path_parts[-2] if path_parts[-1] == 'scroll' && path_parts[-2] == '_search'
80
- path_parts.find { |part| part[0] == '_' }
93
+
94
+ path_parts.find { |part| part[0] == '_' && part != '_all' }
81
95
  end
82
96
  end
83
97
  end
@@ -1,39 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'proxes/request'
3
+ require 'proxes/request/multi'
4
4
  require 'proxes/policies/request/bulk_policy'
5
5
 
6
6
  module ProxES
7
7
  class Request
8
- class Bulk < Request
9
- attr_reader :index, :type
10
-
11
- REGEX = /"(index|delete|create|update)".*"_index"\s*:\s*"(.*?)"/
12
-
13
- def bulk_indices
14
- @bulk_indices ||= begin
15
- body.read.scan(REGEX).tap { |_r| body.rewind }
16
- end.map { |e| e[1] }.uniq
17
- end
8
+ class Bulk < Multi
9
+ INDICES_REGEX = /"(index|delete|create|update)".*"_index"\s*:\s*"(.*?)"/
18
10
 
19
- def index=(idx)
20
- @index = idx
21
- self.path_info = '/' + [index, type, endpoint].compact
22
- .map { |v| v.is_a?(Array) ? v.join(',') : v }
23
- .select { |v| !v.nil? && v != '' }.join('/')
24
- end
11
+ attr_reader :index, :type
25
12
 
26
13
  def endpoint
27
14
  '_bulk'
28
15
  end
29
16
 
30
- def parse
31
- @index ||= check_part(path_parts[0]) unless path_parts[0] == endpoint
32
- @type ||= check_part(path_parts[1]) unless path_parts[1] == endpoint
33
- end
34
-
35
- def indices?
36
- !@index.nil?
17
+ class << self
18
+ def indices_regex
19
+ INDICES_REGEX
20
+ end
37
21
  end
38
22
  end
39
23
  end
@@ -25,8 +25,14 @@ module ProxES
25
25
  end
26
26
 
27
27
  def indices?
28
+ return false if type.nil?
29
+
28
30
  %w[shards indices segments count recovery].include? type.first
29
31
  end
32
+
33
+ def indices
34
+ @index || []
35
+ end
30
36
  end
31
37
  end
32
38
  end
@@ -28,6 +28,10 @@ module ProxES
28
28
  def indices?
29
29
  true
30
30
  end
31
+
32
+ def indices
33
+ @index || []
34
+ end
31
35
  end
32
36
  end
33
37
  end
@@ -28,6 +28,10 @@ module ProxES
28
28
  def indices?
29
29
  true
30
30
  end
31
+
32
+ def indices
33
+ @index || []
34
+ end
31
35
  end
32
36
  end
33
37
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'proxes/request/multi'
4
+ require 'proxes/policies/request/mget_policy'
5
+
6
+ module ProxES
7
+ class Request
8
+ class Mget < Multi
9
+ INDICES_REGEX = /"(_index)"\s*:\s*"(.*?)"/
10
+
11
+ attr_reader :index, :type
12
+
13
+ def endpoint
14
+ '_mget'
15
+ end
16
+
17
+ class << self
18
+ def indices_regex
19
+ INDICES_REGEX
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'proxes/request/multi'
4
+ require 'proxes/policies/request/msearch_policy'
5
+
6
+ module ProxES
7
+ class Request
8
+ class Msearch < Multi
9
+ INDICES_REGEX = /"(index)"\s*:\s*"(.*?)"/
10
+
11
+ attr_reader :index, :type
12
+
13
+ def endpoint
14
+ '_msearch'
15
+ end
16
+
17
+ class << self
18
+ def indices_regex
19
+ INDICES_REGEX
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'proxes/request'
4
+
5
+ module ProxES
6
+ class Request
7
+ class Multi < Request
8
+ # Read up on URL-based access control - https://www.elastic.co/guide/en/elasticsearch/reference/current/url-access-control.html
9
+ # Setting `rest.action.multi.allow_explicit_index` to false will not allow the user to send an index in the request body
10
+ # which negates this. Depending on your needs, you can enable / disable this
11
+ def body_indices
12
+ # TODO: Do / Don't do this depending on rest.action.multi.allow_explicit_index
13
+ @body_indices ||= begin
14
+ body.read.scan(self.class.indices_regex).tap { |_r| body.rewind }
15
+ end.map { |e| e[1] }.compact.uniq
16
+ end
17
+
18
+ def index=(idx)
19
+ @body_indices = []
20
+ @index = idx
21
+ self.path_info = '/' + [index, type, endpoint].compact
22
+ .map { |v| v.is_a?(Array) ? v.join(',') : v }
23
+ .select { |v| !v.nil? && v != '' }.join('/')
24
+ end
25
+
26
+ def parse
27
+ @index ||= check_part(path_parts[0]) unless path_parts[0] == endpoint
28
+ @type ||= check_part(path_parts[1]) unless path_parts[1] == endpoint
29
+ end
30
+
31
+ def indices?
32
+ indices.blank? == false
33
+ end
34
+
35
+ def indices
36
+ body_indices + (@index || [])
37
+ end
38
+ end
39
+ end
40
+ end