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