proxes 0.9.7 → 0.9.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. metadata +4 -92
  3. data/.codeclimate.yml +0 -24
  4. data/.gitignore +0 -20
  5. data/.pryrc +0 -6
  6. data/.rspec +0 -2
  7. data/.rubocop.yml +0 -15
  8. data/.ruby-version +0 -1
  9. data/.travis.yml +0 -46
  10. data/Dockerfile +0 -42
  11. data/Gemfile +0 -4
  12. data/Gemfile.ci +0 -23
  13. data/Gemfile.deploy +0 -8
  14. data/Gemfile.deploy.lock +0 -147
  15. data/Gemfile.dev +0 -13
  16. data/LICENSE.txt +0 -8
  17. data/README.md +0 -98
  18. data/Rakefile +0 -23
  19. data/Vagrantfile +0 -47
  20. data/config.ru +0 -71
  21. data/config/puma.rb +0 -15
  22. data/config/settings.yml +0 -7
  23. data/deploy_docker.sh +0 -8
  24. data/docker-compose.yml +0 -20
  25. data/gulpfile.js +0 -80
  26. data/lib/ditty/components/proxes.rb +0 -80
  27. data/lib/proxes/controllers/permissions.rb +0 -41
  28. data/lib/proxes/controllers/search.rb +0 -45
  29. data/lib/proxes/controllers/status.rb +0 -115
  30. data/lib/proxes/forwarder.rb +0 -49
  31. data/lib/proxes/helpers/indices.rb +0 -33
  32. data/lib/proxes/loggers/elasticsearch.rb +0 -10
  33. data/lib/proxes/middleware/error_handling.rb +0 -62
  34. data/lib/proxes/middleware/metrics.rb +0 -25
  35. data/lib/proxes/middleware/security.rb +0 -59
  36. data/lib/proxes/models/permission.rb +0 -53
  37. data/lib/proxes/policies/permission_policy.rb +0 -37
  38. data/lib/proxes/policies/request/bulk_policy.rb +0 -24
  39. data/lib/proxes/policies/request/cat_policy.rb +0 -12
  40. data/lib/proxes/policies/request/create_policy.rb +0 -15
  41. data/lib/proxes/policies/request/index_policy.rb +0 -19
  42. data/lib/proxes/policies/request/root_policy.rb +0 -13
  43. data/lib/proxes/policies/request/search_policy.rb +0 -14
  44. data/lib/proxes/policies/request/snapshot_policy.rb +0 -15
  45. data/lib/proxes/policies/request/stats_policy.rb +0 -12
  46. data/lib/proxes/policies/request_policy.rb +0 -65
  47. data/lib/proxes/policies/status_policy.rb +0 -21
  48. data/lib/proxes/request.rb +0 -84
  49. data/lib/proxes/request/bulk.rb +0 -40
  50. data/lib/proxes/request/cat.rb +0 -32
  51. data/lib/proxes/request/create.rb +0 -33
  52. data/lib/proxes/request/index.rb +0 -33
  53. data/lib/proxes/request/root.rb +0 -11
  54. data/lib/proxes/request/search.rb +0 -37
  55. data/lib/proxes/request/snapshot.rb +0 -17
  56. data/lib/proxes/request/stats.rb +0 -35
  57. data/lib/proxes/services/es.rb +0 -34
  58. data/lib/proxes/services/listener.rb +0 -29
  59. data/lib/proxes/services/search.rb +0 -43
  60. data/lib/proxes/version.rb +0 -5
  61. data/migrate/20170209_permissions.rb +0 -13
  62. data/migrate/20170416_user_specific_permissions.rb +0 -9
  63. data/package.json +0 -35
  64. data/proxes.gemspec +0 -53
  65. data/public/browserconfig.xml +0 -9
  66. data/public/css/typeahead.css +0 -94
  67. data/public/images/apple-icon.png +0 -0
  68. data/public/images/favicon-16x16.png +0 -0
  69. data/public/images/favicon-32x32.png +0 -0
  70. data/public/images/launcher-icon-1x.png +0 -0
  71. data/public/images/launcher-icon-2x.png +0 -0
  72. data/public/images/launcher-icon-4x.png +0 -0
  73. data/public/images/mstile-150x150.png +0 -0
  74. data/public/images/safari-pinned-tab.svg +0 -43
  75. data/public/js/bundle.js +0 -43636
  76. data/public/js/typeahead.bundle.min.js +0 -8
  77. data/public/js/vendors.js +0 -1
  78. data/public/manifest.json +0 -25
  79. data/src/scripts/app.js +0 -12
  80. data/startup.sh +0 -19
  81. data/views/index.haml +0 -1
  82. data/views/layout.haml +0 -60
  83. data/views/partials/navbar.haml +0 -25
  84. data/views/permissions/display.haml +0 -27
  85. data/views/permissions/edit.haml +0 -11
  86. data/views/permissions/form.haml +0 -4
  87. data/views/permissions/index.haml +0 -32
  88. data/views/permissions/new.haml +0 -10
  89. data/views/search/index.haml +0 -95
  90. data/views/status/check.haml +0 -12
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'ditty/controllers/component'
4
- require 'proxes/models/permission'
5
- require 'ditty/policies/user_policy'
6
- require 'ditty/policies/role_policy'
7
- require 'proxes/policies/permission_policy'
8
-
9
- module ProxES
10
- class Permissions < Ditty::Component
11
- set model_class: Permission
12
-
13
- FILTERS = [
14
- { name: :user, field: 'user.email' },
15
- { name: :role, field: 'role.name' },
16
- { name: :verb }
17
- ].freeze
18
-
19
- SEARCHABLE = %i[pattern].freeze
20
-
21
- helpers do
22
- def user_options
23
- policy_scope(::Ditty::User).as_hash(:email, :email)
24
- end
25
-
26
- def role_options
27
- policy_scope(::Ditty::Role).as_hash(:name, :name)
28
- end
29
-
30
- def verb_options
31
- ProxES::Permission.verbs
32
- end
33
- 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
- end
41
- end
@@ -1,45 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'active_support/core_ext/object/blank'
4
- require 'ditty/controllers/application'
5
- require 'proxes/services/search'
6
-
7
- module ProxES
8
- class Search < Ditty::Application
9
- set base_path: "#{settings.map_path}/search"
10
-
11
- get '/' do
12
- page = (params['page'] || 1).to_i
13
- size = (params['count'] || 25).to_i
14
- from = ((page - 1) * size)
15
- params['q'] = '*' if params['q'].blank?
16
- result = ProxES::Services::Search.search(params['q'], index: params['indices'], from: from, size: size)
17
- haml :"#{view_location}/index",
18
- locals: {
19
- title: 'Search',
20
- indices: ProxES::Services::Search.indices,
21
- fields: ProxES::Services::Search.fields(params['indices']),
22
- result: result
23
- }
24
- end
25
-
26
- get '/fields/?:indices?/?' do
27
- json ProxES::Services::Search.fields params['indices']
28
- end
29
-
30
- get '/indices/?' do
31
- json ProxES::Services::Search.indices
32
- end
33
-
34
- get '/values/:field/?:indices?/?' do |field|
35
- size = params['size'] || 25
36
- json ProxES::Services::Search.values(field, size: size, index: params['indices'])
37
- end
38
-
39
- def find_template(views, name, engine, &block)
40
- super(views, name, engine, &block) # Root
41
- super(::Ditty::ProxES.view_folder, name, engine, &block) # This Component
42
- super(::Ditty::App.view_folder, name, engine, &block) # Ditty
43
- end
44
- end
45
- end
@@ -1,115 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'ditty/controllers/application'
4
- require 'proxes/policies/status_policy'
5
- require 'proxes/services/es'
6
-
7
- module ProxES
8
- class Status < Ditty::Application
9
- helpers ProxES::Services::ES
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
16
-
17
- # This provides a URL that can be polled by a monitoring system. It will return
18
- # 200 OK if all the checks pass, or 500 if any of the checks fail.
19
- get '/check' do
20
- checks = []
21
- begin
22
- health = client.cluster.health level: 'cluster'
23
- checks << { text: 'Cluster Reachable', passed: true, value: health['cluster_name'] }
24
- checks << { text: 'Cluster Health', passed: health['status'] == 'green', value: health['status'] }
25
-
26
- node_stats = client.nodes.stats
27
-
28
- master_nodes = []
29
- data_nodes = []
30
- ingestion_nodes = []
31
- node_stats['nodes'].each_value do |node|
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
67
- end
68
- checks << { text: 'Node JVM Heap', passed: jvm_passed, value: jvm_values.sort }
69
-
70
- fs_values = []
71
- fs_passed = true
72
- node_stats['nodes'].each_value do |node|
73
- next if node['attributes'] && node['attributes']['data'] == 'false'
74
- next if node['roles'] && node['roles'].include?('data') == false
75
- stats = node['fs']['total']
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 }
99
- rescue Faraday::Error => e
100
- checks << { text: 'Cluster Reachable', passed: false, value: e.message }
101
- end
102
-
103
- status checks.find { |c| c[:passed] == false } ? 500 : 200
104
-
105
- respond_to do |format|
106
- format.html do
107
- haml :'status/check', locals: { title: 'Status Check', checks: checks }
108
- end
109
- format.json do
110
- json checks
111
- end
112
- end
113
- end
114
- end
115
- end
@@ -1,49 +0,0 @@
1
- require 'proxes/services/es'
2
- require 'net/http/persistent'
3
- require 'singleton'
4
- require 'rack'
5
-
6
- module ProxES
7
- # A lot of code in this comes from Rack::Proxy
8
- class Forwarder
9
- include Singleton
10
- include ProxES::Services::ES
11
-
12
- def call(env)
13
- source = Rack::Request.new(env)
14
- response = conn.send(source.request_method.downcase) do |req|
15
- source_body = body_from(source)
16
- req.body = source_body if source_body
17
- req.url source.fullpath == '' ? URI.parse(env['REQUEST_URI']).request_uri : source.fullpath
18
- end
19
- mangle response
20
- end
21
-
22
- def mangle(response)
23
- headers = (response.respond_to?(:headers) && response.headers) || self.class.normalize_headers(response.to_hash)
24
- body = response.body || ['']
25
- body = [body] unless body.respond_to?(:each)
26
-
27
- # Not sure where this is coming from, but it causes timeouts on the client
28
- headers.delete('transfer-encoding')
29
- # Ensure that the content length rack middleware kicks in
30
- headers.delete('content-length')
31
-
32
- [response.status, headers, body]
33
- end
34
-
35
- def body_from(request)
36
- return nil if request.body.nil? || (Kernel.const_defined?('::Puma::NullIO') && request.body.is_a?(Puma::NullIO))
37
- request.body.read.tap { |_r| request.body.rewind }
38
- end
39
-
40
- class << self
41
- def normalize_headers(headers)
42
- mapped = headers.map do |k, v|
43
- [k, v.is_a?(Array) ? v.join("\n") : v]
44
- end
45
- Rack::Utils::HeaderHash.new Hash[mapped]
46
- end
47
- end
48
- end
49
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'active_support'
4
- require 'active_support/core_ext/object/blank'
5
-
6
- module ProxES
7
- module Helpers
8
- module Indices
9
- def filter(asked, against)
10
- return against.map { |a| a.gsub(/\.\*/, '*') } if asked == ['*'] || asked.blank?
11
-
12
- answer = []
13
- against.each do |pattern|
14
- answer.concat(asked.select { |idx| idx =~ /#{pattern}/ })
15
- end
16
- answer
17
- end
18
-
19
- def patterns
20
- return [] if user.nil?
21
- patterns_for('INDEX').map do |permission|
22
- return nil if permission.pattern.blank?
23
- permission.pattern.gsub(/\{user.(.*)\}/) { |_match| user.send(Regexp.last_match[1].to_sym) }
24
- end.compact
25
- end
26
-
27
- def patterns_for(action)
28
- return [] if user.nil?
29
- Permission.for_user(user, action)
30
- end
31
- end
32
- end
33
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'logger'
4
-
5
- module ProxES
6
- module Loggers
7
- class Elasticsearch < ::Logger
8
- end
9
- end
10
- end
@@ -1,62 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'wisper'
4
- require 'proxes/request'
5
- require 'ditty/services/logger'
6
-
7
- module ProxES
8
- module Middleware
9
- class ErrorHandling
10
- attr_reader :logger
11
-
12
- include Wisper::Publisher
13
-
14
- def initialize(app, logger = nil)
15
- @app = app
16
- @logger = logger || ::Ditty::Services::Logger.instance
17
- end
18
-
19
- def call(env)
20
- request = ProxES::Request.from_env(env)
21
- response = @app.call env
22
- broadcast(:es_request_failed, request, response) unless (200..299).cover?(response[0])
23
- response
24
- rescue Errno::EHOSTUNREACH
25
- error 'Could not reach Elasticsearch at ' + ENV['ELASTICSEARCH_URL']
26
- rescue Errno::ECONNREFUSED, Faraday::ConnectionFailed, SocketError
27
- error 'Elasticsearch not listening at ' + ENV['ELASTICSEARCH_URL']
28
- rescue Pundit::NotAuthorizedError, Ditty::Helpers::NotAuthenticated => e
29
- broadcast(:es_request_denied, request, e)
30
- log_not_authorized request
31
- raise e if env['APP_ENV'] == 'development'
32
- request.html? && request.user.nil? ? login_and_redirect(request) : error('Not Authorized', 401)
33
- rescue StandardError => e
34
- broadcast(:es_request_denied, request, e)
35
- log_not_authorized request
36
- raise e if env['APP_ENV'] == 'development'
37
- error 'Forbidden', 403
38
- end
39
-
40
- def log_not_authorized(request)
41
- user = request.user ? request.user.email : 'unauthenticated request'
42
- logger.error "Access denied for #{user} by security layer: #{request.detail}"
43
- end
44
-
45
- # Response Helpers
46
- def error(message, code = 500)
47
- headers = { 'Content-Type' => 'application/json' }
48
- headers['WWW-Authenticate'] = 'Basic realm="security"' if code == 401
49
- [code, headers, ['{"error":"' + message + '"}']]
50
- end
51
-
52
- def login_and_redirect(request)
53
- request.session['omniauth.origin'] = request.url unless request.url == '/_proxes/auth/identity'
54
- redirect '/_proxes/auth/identity'
55
- end
56
-
57
- def redirect(destination, code = 302)
58
- [code, { 'Location' => destination }, []]
59
- end
60
- end
61
- end
62
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'wisper'
4
-
5
- module ProxES
6
- module Middleware
7
- class Metrics
8
- include Wisper::Publisher
9
-
10
- def initialize(app)
11
- @app = app
12
- end
13
-
14
- def call(env)
15
- request = Request.from_env(env)
16
- broadcast(:call_started, request)
17
-
18
- result = @app.call request.env
19
-
20
- broadcast(:call_completed, request) if result[0].to_i >= 200 && result[0].to_i < 300
21
- result
22
- end
23
- end
24
- end
25
- end
@@ -1,59 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'proxes/request'
4
- require 'proxes/policies/request_policy'
5
- require 'ditty/services/logger'
6
- require 'ditty/helpers/pundit'
7
- require 'ditty/helpers/authentication'
8
-
9
- module ProxES
10
- module Middleware
11
- class Security
12
- attr_reader :env, :logger
13
-
14
- include Ditty::Helpers::Authentication
15
- include Ditty::Helpers::Pundit
16
-
17
- def initialize(app, logger = nil)
18
- @app = app
19
- @logger = logger || ::Ditty::Services::Logger.instance
20
- end
21
-
22
- def call(env)
23
- @env = env
24
- request = ProxES::Request.from_env(env)
25
- log(request, 'BEFORE')
26
-
27
- check_basic request
28
- authorize request
29
-
30
- request.index = policy_scope(request) if request.indices?
31
- log(request, 'AFTER')
32
-
33
- @app.call env
34
- end
35
-
36
- def check_basic(request)
37
- auth = Rack::Auth::Basic::Request.new(request.env)
38
- return false unless auth.provided? && auth.basic?
39
-
40
- identity = ::Ditty::Identity.find(username: auth.credentials[0])
41
- identity ||= ::Ditty::Identity.find(username: CGI.unescape(auth.credentials[0]))
42
- return false unless identity && identity.authenticate(auth.credentials[1])
43
- request.env['rack.session'] ||= {}
44
- request.env['rack.session']['user_id'] = identity.user_id
45
- end
46
-
47
- def authorize(request)
48
- Pundit.authorize(request.user, request, request.request_method.downcase + '?')
49
- end
50
-
51
- def log(request, stage)
52
- logger.debug '============' + stage.ljust(56) + '============'
53
- logger.debug '= ' + "Request: #{request.detail}".ljust(76) + ' ='
54
- logger.debug '= ' + "Endpoint: #{request.endpoint}".ljust(76) + ' ='
55
- logger.debug '================================================================================'
56
- end
57
- end
58
- end
59
- end