proxes 0.9.8 → 0.9.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/ditty/components/proxes.rb +89 -0
- data/lib/proxes/controllers/permissions.rb +41 -0
- data/lib/proxes/controllers/search.rb +55 -0
- data/lib/proxes/controllers/status.rb +115 -0
- data/lib/proxes/forwarder.rb +49 -0
- data/lib/proxes/helpers/indices.rb +33 -0
- data/lib/proxes/loggers/elasticsearch.rb +10 -0
- data/lib/proxes/middleware/error_handling.rb +64 -0
- data/lib/proxes/middleware/metrics.rb +25 -0
- data/lib/proxes/middleware/security.rb +59 -0
- data/lib/proxes/models/permission.rb +55 -0
- data/lib/proxes/policies/permission_policy.rb +37 -0
- data/lib/proxes/policies/request/bulk_policy.rb +24 -0
- data/lib/proxes/policies/request/cat_policy.rb +12 -0
- data/lib/proxes/policies/request/create_policy.rb +15 -0
- data/lib/proxes/policies/request/index_policy.rb +19 -0
- data/lib/proxes/policies/request/root_policy.rb +13 -0
- data/lib/proxes/policies/request/search_policy.rb +14 -0
- data/lib/proxes/policies/request/snapshot_policy.rb +15 -0
- data/lib/proxes/policies/request/stats_policy.rb +12 -0
- data/lib/proxes/policies/request_policy.rb +62 -0
- data/lib/proxes/policies/search_policy.rb +29 -0
- data/lib/proxes/policies/status_policy.rb +21 -0
- data/lib/proxes/request.rb +84 -0
- data/lib/proxes/request/bulk.rb +40 -0
- data/lib/proxes/request/cat.rb +32 -0
- data/lib/proxes/request/create.rb +33 -0
- data/lib/proxes/request/index.rb +33 -0
- data/lib/proxes/request/root.rb +11 -0
- data/lib/proxes/request/search.rb +37 -0
- data/lib/proxes/request/snapshot.rb +17 -0
- data/lib/proxes/request/stats.rb +35 -0
- data/lib/proxes/services/es.rb +34 -0
- data/lib/proxes/services/listener.rb +29 -0
- data/lib/proxes/services/search.rb +45 -0
- data/lib/proxes/version.rb +5 -0
- data/migrate/20170209_permissions.rb +13 -0
- data/migrate/20170416_user_specific_permissions.rb +9 -0
- data/public/browserconfig.xml +9 -0
- data/public/manifest.json +25 -0
- data/views/index.haml +1 -0
- data/views/layout.haml +60 -0
- 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,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,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
|
+
}
|
data/views/index.haml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
#react-dashboard{ 'data-elasticsearch-url' => '..'}
|
data/views/layout.haml
ADDED
@@ -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
|
+
© <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
|
+
|