proxes 0.9.8 → 0.9.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/lib/ditty/components/proxes.rb +89 -0
  3. data/lib/proxes/controllers/permissions.rb +41 -0
  4. data/lib/proxes/controllers/search.rb +55 -0
  5. data/lib/proxes/controllers/status.rb +115 -0
  6. data/lib/proxes/forwarder.rb +49 -0
  7. data/lib/proxes/helpers/indices.rb +33 -0
  8. data/lib/proxes/loggers/elasticsearch.rb +10 -0
  9. data/lib/proxes/middleware/error_handling.rb +64 -0
  10. data/lib/proxes/middleware/metrics.rb +25 -0
  11. data/lib/proxes/middleware/security.rb +59 -0
  12. data/lib/proxes/models/permission.rb +55 -0
  13. data/lib/proxes/policies/permission_policy.rb +37 -0
  14. data/lib/proxes/policies/request/bulk_policy.rb +24 -0
  15. data/lib/proxes/policies/request/cat_policy.rb +12 -0
  16. data/lib/proxes/policies/request/create_policy.rb +15 -0
  17. data/lib/proxes/policies/request/index_policy.rb +19 -0
  18. data/lib/proxes/policies/request/root_policy.rb +13 -0
  19. data/lib/proxes/policies/request/search_policy.rb +14 -0
  20. data/lib/proxes/policies/request/snapshot_policy.rb +15 -0
  21. data/lib/proxes/policies/request/stats_policy.rb +12 -0
  22. data/lib/proxes/policies/request_policy.rb +62 -0
  23. data/lib/proxes/policies/search_policy.rb +29 -0
  24. data/lib/proxes/policies/status_policy.rb +21 -0
  25. data/lib/proxes/request.rb +84 -0
  26. data/lib/proxes/request/bulk.rb +40 -0
  27. data/lib/proxes/request/cat.rb +32 -0
  28. data/lib/proxes/request/create.rb +33 -0
  29. data/lib/proxes/request/index.rb +33 -0
  30. data/lib/proxes/request/root.rb +11 -0
  31. data/lib/proxes/request/search.rb +37 -0
  32. data/lib/proxes/request/snapshot.rb +17 -0
  33. data/lib/proxes/request/stats.rb +35 -0
  34. data/lib/proxes/services/es.rb +34 -0
  35. data/lib/proxes/services/listener.rb +29 -0
  36. data/lib/proxes/services/search.rb +45 -0
  37. data/lib/proxes/version.rb +5 -0
  38. data/migrate/20170209_permissions.rb +13 -0
  39. data/migrate/20170416_user_specific_permissions.rb +9 -0
  40. data/public/browserconfig.xml +9 -0
  41. data/public/manifest.json +25 -0
  42. data/views/index.haml +1 -0
  43. data/views/layout.haml +60 -0
  44. metadata +44 -2
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'proxes/request'
4
+ require 'proxes/policies/request/bulk_policy'
5
+
6
+ module ProxES
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
18
+
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
25
+
26
+ def endpoint
27
+ '_bulk'
28
+ end
29
+
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?
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'proxes/request'
4
+ require 'proxes/policies/request/cat_policy'
5
+
6
+ module ProxES
7
+ class Request
8
+ class Cat < Request
9
+ attr_reader :index, :type
10
+
11
+ def index=(idx)
12
+ @index = idx
13
+ self.path_info = '/' + [endpoint, type, index].compact
14
+ .map { |v| v.is_a?(Array) ? v.join(',') : v }
15
+ .select { |v| !v.nil? && v != '' }.join('/')
16
+ end
17
+
18
+ def endpoint
19
+ '_cat'
20
+ end
21
+
22
+ def parse
23
+ @type ||= check_part(path_parts[1])
24
+ @index ||= check_part(path_parts[2])
25
+ end
26
+
27
+ def indices?
28
+ %w[shards indices segments count recovery].include? type.first
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'proxes/request'
4
+ require 'proxes/policies/request/create_policy'
5
+
6
+ module ProxES
7
+ class Request
8
+ class Create < Request
9
+ attr_reader :index, :type, :id
10
+
11
+ def index=(idx)
12
+ @index = idx
13
+ self.path_info = '/' + [index, type, id, endpoint].compact
14
+ .map { |v| v.is_a?(Array) ? v.join(',') : v }
15
+ .select { |v| !v.nil? && v != '' }.join('/')
16
+ end
17
+
18
+ def endpoint
19
+ '_create'
20
+ end
21
+
22
+ def parse
23
+ @index ||= check_part(path_parts[0])
24
+ @type ||= check_part(path_parts[1])
25
+ @id ||= check_part(path_parts[2])
26
+ end
27
+
28
+ def indices?
29
+ true
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'proxes/request'
4
+ require 'proxes/policies/request/index_policy'
5
+
6
+ module ProxES
7
+ class Request
8
+ class Index < Request
9
+ attr_reader :index, :type, :id
10
+
11
+ def index=(idx)
12
+ @index = idx
13
+ self.path_info = '/' + [index, type, id].compact
14
+ .map { |v| v.is_a?(Array) ? v.join(',') : v }
15
+ .select { |v| !v.nil? && v != '' }.join('/')
16
+ end
17
+
18
+ def endpoint
19
+ nil
20
+ end
21
+
22
+ def parse
23
+ @index ||= check_part(path_parts[0])
24
+ @type ||= check_part(path_parts[1])
25
+ @id ||= check_part(path_parts[2])
26
+ end
27
+
28
+ def indices?
29
+ true
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'proxes/request'
4
+ require 'proxes/policies/request/root_policy'
5
+
6
+ module ProxES
7
+ class Request
8
+ class Root < Request
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'proxes/request'
4
+ require 'proxes/policies/request/search_policy'
5
+
6
+ module ProxES
7
+ class Request
8
+ class Search < Request
9
+ attr_reader :index, :type
10
+
11
+ def index=(idx)
12
+ @index = idx
13
+ self.path_info = '/' + [index, type, id, endpoint].compact
14
+ .map { |v| v.is_a?(Array) ? v.join(',') : v }
15
+ .select { |v| !v.nil? && v != '' }.join('/')
16
+ end
17
+
18
+ def endpoint
19
+ '_search'
20
+ end
21
+
22
+ def parse
23
+ @index ||= check_part(path_parts[0]) unless path_parts[0] == endpoint
24
+ @type ||= check_part(path_parts[1]) unless path_parts[1] == endpoint
25
+ @id ||= check_part(path_parts[2]) unless path_parts[2] == endpoint
26
+ end
27
+
28
+ def id
29
+ @id == [] ? nil : @id
30
+ end
31
+
32
+ def indices?
33
+ type != ['scroll']
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'proxes/request'
4
+ require 'proxes/policies/request/snapshot_policy'
5
+
6
+ module ProxES
7
+ class Request
8
+ class Snapshot < Request
9
+ attr_reader :repository
10
+
11
+ def parse
12
+ @repository ||= check_part(path_parts[1])
13
+ @repository = [] if repository.nil?
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'proxes/request'
4
+ require 'proxes/policies/request/stats_policy'
5
+
6
+ module ProxES
7
+ class Request
8
+ class Stats < Request
9
+ attr_reader :index
10
+
11
+ def index=(idx)
12
+ @index = idx
13
+ self.path_info = '/' + [index, endpoint].compact
14
+ .map { |v| v.is_a?(Array) ? v.join(',') : v }
15
+ .select { |v| !v.nil? && v != '' }.join('/')
16
+ end
17
+
18
+ def endpoint
19
+ '_stats'
20
+ end
21
+
22
+ def parse
23
+ @index ||= check_part(path_parts[0])
24
+ end
25
+
26
+ def stats
27
+ @stats ||= check_part(path_parts[2])
28
+ end
29
+
30
+ def indices?
31
+ true
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openssl'
4
+ require 'elasticsearch'
5
+ require 'ditty/services/logger'
6
+
7
+ module ProxES
8
+ module Services
9
+ module ES
10
+ def client
11
+ @client ||= Elasticsearch::Client.new(
12
+ url: ENV['ELASTICSEARCH_URL'],
13
+ transport_options: {
14
+ ssl: {
15
+ verify: ENV['SSL_VERIFY_NONE'].to_i != 1,
16
+ cert_store: ssl_store
17
+ }
18
+ },
19
+ logger: Ditty::Services::Logger.instance
20
+ )
21
+ end
22
+
23
+ def ssl_store
24
+ store = OpenSSL::X509::Store.new
25
+ store.set_default_paths
26
+ store
27
+ end
28
+
29
+ def conn
30
+ client.transport.connections.get_connection.connection
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'wisper'
4
+ require 'ditty/models/audit_log'
5
+ require 'ditty/services/logger'
6
+
7
+ module ProxES
8
+ class Listener
9
+ def es_request_failed(request, response)
10
+ Ditty::AuditLog.create(
11
+ action: :es_request_failed,
12
+ user: request.user,
13
+ details: "#{request.detail} > #{response[0]}"
14
+ )
15
+ end
16
+
17
+ def es_request_denied(request, exception = nil)
18
+ detail = request.detail
19
+ detail = "#{detail} - #{exception.class}" if exception
20
+ Ditty::AuditLog.create(
21
+ action: :es_request_denied,
22
+ user: request.user,
23
+ details: detail
24
+ )
25
+ end
26
+ end
27
+ end
28
+
29
+ Wisper.subscribe(ProxES::Listener.new) unless ENV['RACK_ENV'] == 'test'
@@ -0,0 +1,45 @@
1
+ require 'proxes/services/es'
2
+
3
+ # TODO: This needs to be filtered.
4
+
5
+ module ProxES
6
+ module Services
7
+ module Search
8
+ class << self
9
+ include ES
10
+
11
+ def indices
12
+ client.indices.get_mapping(index: '_all').keys
13
+ end
14
+
15
+ def fields(index: '_all', names_only: false)
16
+ fields = {}
17
+ client.indices.get_mapping(index: index).each do |_idx, index_map|
18
+ index_map['mappings'].each do |_type, type_map|
19
+ next if type_map['properties'].nil?
20
+ type_map['properties'].each do |name, details|
21
+ if details['type'] != 'keyword' && details['fields'] && (names_only == false)
22
+ keyword = details['fields'].find do |v|
23
+ v[1]['type'] == 'keyword'
24
+ end
25
+ fields["#{name}.#{keyword[0]}"] ||= keyword[1]['type'] if keyword
26
+ end
27
+ fields[name] ||= details['type'] unless details['type'].nil?
28
+ end
29
+ end.to_h
30
+ end
31
+ fields
32
+ end
33
+
34
+ def values(field, index = '_all', size = 25)
35
+ result = client.search index: index, body: { size: 0, aggs: { values: { terms: { field: field, size: size } } } }
36
+ result['aggregations']['values']['buckets'].map { |e| e['key'] }
37
+ end
38
+
39
+ def search(qs, options = {})
40
+ client.search options.merge(q: qs) # , explain: true
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ProxES
4
+ VERSION = '0.9.9'.freeze
5
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ change do
5
+ create_table :permissions do
6
+ primary_key :id
7
+ String :verb
8
+ String :pattern
9
+ DateTime :created_at
10
+ foreign_key :role_id, :roles
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ change do
5
+ alter_table :permissions do
6
+ add_foreign_key :user_id, :users
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <browserconfig>
3
+ <msapplication>
4
+ <tile>
5
+ <square150x150logo src="/_proxes/images/mstile-150x150.png"/>
6
+ <TileColor>#da532c</TileColor>
7
+ </tile>
8
+ </msapplication>
9
+ </browserconfig>
@@ -0,0 +1,25 @@
1
+ {
2
+ "short_name": "ProxES",
3
+ "name": "ProxES",
4
+ "icons": [
5
+ {
6
+ "src": "_proxes/images/launcher-icon-1x.png",
7
+ "type": "image/png",
8
+ "sizes": "48x48"
9
+ },
10
+ {
11
+ "src": "_proxes/images/launcher-icon-2x.png",
12
+ "type": "image/png",
13
+ "sizes": "96x96"
14
+ },
15
+ {
16
+ "src": "_proxes/images/launcher-icon-4x.png",
17
+ "type": "image/png",
18
+ "sizes": "192x192"
19
+ }
20
+ ],
21
+ "start_url": "_proxes/auth/login",
22
+ "theme_color": "#bebebe",
23
+ "background_color": "#bebebe",
24
+ "display": "fullscreen"
25
+ }
@@ -0,0 +1 @@
1
+ #react-dashboard{ 'data-elasticsearch-url' => '..'}
@@ -0,0 +1,60 @@
1
+ !!! 5
2
+ %html{ lang: 'en' }
3
+ %head
4
+ %meta{ charset: 'utf-8' }
5
+ %meta{ 'http-equiv' => 'X-UA-Compatible', 'content' => 'IE=edge,chrome=1' }
6
+ %meta{ name: 'viewport', content: 'width=device-width, initial-scale=1' }
7
+ %meta{ name: 'theme-color', content: '#ffffff' }
8
+ %link{ rel: 'manifest', href: '/_proxes/manifest.json' }
9
+ %link{ rel: 'icon', type: 'image/png', sizes: '32x32', href: '/_proxes/images/favicon-32x32.png' }
10
+ %link{ rel: 'icon', type: 'image/png', sizes: '16x16', href: '/_proxes/images/favicon-16x16.png' }
11
+ %link{ rel: 'apple-touch-icon', sizes: '76x76', href: '/_proxes/images/apple-icon.png' }
12
+ %link{ rel: 'mask-icon', href: '/_proxes/images/safari-pinned-tab.svg', color: '#5bbad5' }
13
+
14
+ %title
15
+ ProxES
16
+ - if defined? title
17
+ = "- #{title}"
18
+
19
+ %meta{ name: 'description', content: '' }
20
+ %meta{ name: 'author', content: '' }
21
+
22
+ / Le styles
23
+ %link{ rel: 'stylesheet', href: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css', media: 'screen' }
24
+ %link{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/startbootstrap-sb-admin-2/3.3.7+1/css/sb-admin-2.min.css', media: 'screen' }
25
+ %link{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/metisMenu/2.5.2/metisMenu.min.css', media: 'screen' }
26
+ %link{ rel: 'stylesheet', href: 'https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css', media: 'screen' }
27
+ /[if lt IE 9] <script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
28
+ /[if lt IE 9] <script src="https://cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.min.js"></script>
29
+
30
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js' }
31
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/startbootstrap-sb-admin-2/3.3.7+1/js/sb-admin-2.min.js' }
32
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/metisMenu/2.5.2/metisMenu.min.js' }
33
+ %body
34
+ #wrapper
35
+ = haml :'partials/navbar', locals: { title: (defined?(title) ? title : 'ProxES') }
36
+ #page-wrapper{ style: 'opacity: 0.8' }
37
+ .row
38
+ .col-md-12
39
+ -if defined? title
40
+ %h1.page-header= title
41
+ = haml :'partials/notifications'
42
+
43
+ = yield
44
+ %footer.footer.text-muted.text-center
45
+ %hr
46
+ .copyright
47
+ :plain
48
+ &copy; <script>document.write(new Date().getFullYear())</script>, DataTools.io
49
+
50
+
51
+ / Placed at the end of the document so the pages load faster
52
+ %script{ type: 'text/javascript', src: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js' }
53
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react.min.js' }
54
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react-dom.min.js' }
55
+ %script{ type: 'text/javascript', src: '/_proxes/js/bundle.js' }
56
+ :javascript
57
+ $(function() {
58
+ $('.sidebar-nav').metisMenu();
59
+ });
60
+