proxes 0.6.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Vagrantfile +0 -4
- data/lib/proxes/controllers/app.rb +27 -19
- data/lib/proxes/controllers/application.rb +1 -1
- data/lib/proxes/controllers/audit_logs.rb +5 -0
- data/lib/proxes/controllers/component.rb +16 -9
- data/lib/proxes/controllers/permissions.rb +5 -0
- data/lib/proxes/controllers/roles.rb +5 -0
- data/lib/proxes/controllers/users.rb +5 -0
- data/lib/proxes/db.rb +2 -0
- data/lib/proxes/forwarder.rb +12 -4
- data/lib/proxes/helpers/authentication.rb +19 -9
- data/lib/proxes/helpers/indices.rb +1 -1
- data/lib/proxes/models/user.rb +1 -1
- data/lib/proxes/policies/request/cat_policy.rb +4 -0
- data/lib/proxes/policies/request/create_policy.rb +20 -0
- data/lib/proxes/policies/request/index_policy.rb +24 -0
- data/lib/proxes/policies/request/search_policy.rb +4 -0
- data/lib/proxes/policies/request/snapshot_policy.rb +2 -0
- data/lib/proxes/policies/request/stats_policy.rb +4 -0
- data/lib/proxes/policies/request_policy.rb +13 -11
- data/lib/proxes/proxes.rb +29 -3
- data/lib/proxes/request.rb +4 -5
- data/lib/proxes/request/cat.rb +3 -3
- data/lib/proxes/request/create.rb +33 -0
- data/lib/proxes/request/index.rb +29 -0
- data/lib/proxes/request/root.rb +1 -1
- data/lib/proxes/request/search.rb +7 -7
- data/lib/proxes/request/snapshot.rb +1 -1
- data/lib/proxes/request/stats.rb +4 -4
- data/lib/proxes/security.rb +41 -21
- data/lib/proxes/version.rb +1 -1
- data/views/partials/navbar.haml +1 -1
- data/views/partials/sidebar.haml +2 -2
- data/views/users/display.haml +16 -15
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b182884ae9246085097e7ff89d204d225b050b8
|
4
|
+
data.tar.gz: 5a22e671d4ec358da74d05525bdad867a95b810a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 77202113968d066c205ebb136ab95a6ede2264f0633f33574f2b9f26dd32da272ea2479c471e4a8781eddf0248a99f4648f33a21fb18d67e55e85447803fa4fa
|
7
|
+
data.tar.gz: a46f43c312ad5fa68d794fe642bdef660e20c1970824e7393ec227a068904fda447b896d761d3d6972f38a0b78617a55afce8772cdf317c44cec12a8639dcd99
|
data/Vagrantfile
CHANGED
@@ -4,37 +4,41 @@ require 'proxes/controllers/application'
|
|
4
4
|
|
5
5
|
module ProxES
|
6
6
|
class App < Application
|
7
|
+
def find_template(views, name, engine, &block)
|
8
|
+
super(views, name, engine, &block) # Root
|
9
|
+
super(::ProxES::ProxES.view_folder, name, engine, &block) # Basic Plugin
|
10
|
+
end
|
11
|
+
|
7
12
|
# Home Page
|
8
13
|
get '/' do
|
9
14
|
authenticate!
|
10
|
-
|
11
|
-
redirect "/_proxes/dashboards/#{cluster_health['status']}"
|
15
|
+
haml :index, locals: { title: 'Home' }
|
12
16
|
end
|
13
17
|
|
14
18
|
# OmniAuth Identity Stuff
|
15
19
|
# Log in Page
|
16
|
-
get '/
|
20
|
+
get '/auth/identity' do
|
17
21
|
haml :'identity/login', locals: { title: 'Log In' }
|
18
22
|
end
|
19
23
|
|
20
|
-
# Successful Login
|
21
24
|
post '/auth/identity/callback' do
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
25
|
+
if env['omniauth.auth']
|
26
|
+
# Successful Login
|
27
|
+
user = User.find(email: env['omniauth.auth']['info']['email'])
|
28
|
+
self.current_user = user
|
29
|
+
log_action(:identity_login, user: user)
|
30
|
+
flash[:success] = 'Logged In'
|
31
|
+
redirect '/_proxes'
|
32
|
+
else
|
33
|
+
# Failed Login
|
34
|
+
broadcast(:identity_failed_login)
|
35
|
+
flash[:warning] = 'Invalid credentials. Please try again.'
|
36
|
+
redirect '/_proxes/auth/identity'
|
37
|
+
end
|
34
38
|
end
|
35
39
|
|
36
40
|
# Register Page
|
37
|
-
get '/
|
41
|
+
get '/auth/identity/register' do
|
38
42
|
identity = Identity.new
|
39
43
|
haml :'identity/register', locals: { title: 'Register', identity: identity }
|
40
44
|
end
|
@@ -46,6 +50,10 @@ module ProxES
|
|
46
50
|
user = User.find_or_create(email: identity.username)
|
47
51
|
user.add_identity identity
|
48
52
|
|
53
|
+
# Create the SA user if none is present
|
54
|
+
sa = Role.find_or_create(name: 'super_admin')
|
55
|
+
user.add_role sa if User.where(roles: sa).count == 0
|
56
|
+
|
49
57
|
log_action(:identity_register, user: user)
|
50
58
|
flash[:info] = 'Successfully Registered. Please log in'
|
51
59
|
redirect '/_proxes/auth/identity'
|
@@ -56,7 +64,7 @@ module ProxES
|
|
56
64
|
end
|
57
65
|
|
58
66
|
# Logout Action
|
59
|
-
delete '/
|
67
|
+
delete '/auth/identity' do
|
60
68
|
log_action(:identity_logout)
|
61
69
|
logout
|
62
70
|
flash[:info] = 'Logged Out'
|
@@ -65,7 +73,7 @@ module ProxES
|
|
65
73
|
end
|
66
74
|
|
67
75
|
# Unauthenticated
|
68
|
-
get '/
|
76
|
+
get '/unauthenticated' do
|
69
77
|
redirect '/_proxes/auth/identity'
|
70
78
|
end
|
71
79
|
end
|
@@ -14,7 +14,7 @@ require 'elasticsearch'
|
|
14
14
|
|
15
15
|
module ProxES
|
16
16
|
class Application < Sinatra::Base
|
17
|
-
set :root, ::File.expand_path(::File.dirname(__FILE__) + '/../../../')
|
17
|
+
set :root, ENV['APP_ROOT'] || ::File.expand_path(::File.dirname(__FILE__) + '/../../../')
|
18
18
|
set :view_location, nil
|
19
19
|
set :model_class, nil
|
20
20
|
# The order here is important, since Wisper has a deprecated method respond_with method
|
@@ -8,6 +8,11 @@ module ProxES
|
|
8
8
|
class AuditLogs < Component
|
9
9
|
set model_class: AuditLog
|
10
10
|
|
11
|
+
def find_template(views, name, engine, &block)
|
12
|
+
super(views, name, engine, &block) # Root
|
13
|
+
super(::ProxES::ProxES.view_folder, name, engine, &block) # Basic Plugin
|
14
|
+
end
|
15
|
+
|
11
16
|
def list
|
12
17
|
super.order(:created_at).reverse
|
13
18
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'proxes/controllers/application'
|
4
4
|
require 'proxes/helpers/component'
|
5
|
+
require 'sinatra/json'
|
5
6
|
|
6
7
|
module ProxES
|
7
8
|
class Component < Application
|
@@ -12,8 +13,9 @@ module ProxES
|
|
12
13
|
set view_location: nil
|
13
14
|
set track_actions: false
|
14
15
|
|
15
|
-
|
16
|
-
|
16
|
+
def find_template(views, name, engine, &block)
|
17
|
+
super(views, name, engine, &block) # Root
|
18
|
+
super(::ProxES::ProxES.view_folder, name, engine, &block) # Basic Plugin
|
17
19
|
end
|
18
20
|
|
19
21
|
# List
|
@@ -31,12 +33,12 @@ module ProxES
|
|
31
33
|
end
|
32
34
|
format.json do
|
33
35
|
# TODO: Add links defined by actions (New #{heading})
|
34
|
-
|
36
|
+
json(
|
35
37
|
'items' => list.map(&:for_json),
|
36
38
|
'page' => params[:page],
|
37
39
|
'count' => list.count,
|
38
40
|
'total' => dataset.count
|
39
|
-
|
41
|
+
)
|
40
42
|
end
|
41
43
|
end
|
42
44
|
end
|
@@ -68,7 +70,11 @@ module ProxES
|
|
68
70
|
end
|
69
71
|
format.json do
|
70
72
|
headers 'Content-Type' => 'application/json'
|
71
|
-
|
73
|
+
if success
|
74
|
+
redirect "#{base_path}/#{entity.id}", 201
|
75
|
+
else
|
76
|
+
400
|
77
|
+
end
|
72
78
|
end
|
73
79
|
end
|
74
80
|
end
|
@@ -90,7 +96,7 @@ module ProxES
|
|
90
96
|
end
|
91
97
|
format.json do
|
92
98
|
# TODO: Add links defined by actions (Edit #{heading})
|
93
|
-
entity.for_json
|
99
|
+
json entity.for_json
|
94
100
|
end
|
95
101
|
end
|
96
102
|
end
|
@@ -121,10 +127,8 @@ module ProxES
|
|
121
127
|
redirect "#{base_path}/#{entity.id}"
|
122
128
|
end
|
123
129
|
format.json do
|
124
|
-
content_type 'application/json'
|
125
130
|
headers 'Location' => "#{base_path}/#{entity.id}"
|
126
|
-
body entity.
|
127
|
-
status 200
|
131
|
+
json body entity.for_json
|
128
132
|
end
|
129
133
|
end
|
130
134
|
else
|
@@ -132,6 +136,9 @@ module ProxES
|
|
132
136
|
format.html do
|
133
137
|
haml :"#{view_location}/edit", locals: { entity: entity, title: heading(:edit) }
|
134
138
|
end
|
139
|
+
format.json do
|
140
|
+
400
|
141
|
+
end
|
135
142
|
end
|
136
143
|
end
|
137
144
|
end
|
@@ -7,5 +7,10 @@ require 'proxes/policies/permission_policy'
|
|
7
7
|
module ProxES
|
8
8
|
class Permissions < Component
|
9
9
|
set model_class: Permission
|
10
|
+
|
11
|
+
def find_template(views, name, engine, &block)
|
12
|
+
super(views, name, engine, &block) # Root
|
13
|
+
super(::ProxES::ProxES.view_folder, name, engine, &block) # Basic Plugin
|
14
|
+
end
|
10
15
|
end
|
11
16
|
end
|
@@ -7,5 +7,10 @@ require 'proxes/policies/role_policy'
|
|
7
7
|
module ProxES
|
8
8
|
class Roles < Component
|
9
9
|
set model_class: Role
|
10
|
+
|
11
|
+
def find_template(views, name, engine, &block)
|
12
|
+
super(views, name, engine, &block) # Root
|
13
|
+
super(::ProxES::ProxES.view_folder, name, engine, &block) # Basic Plugin
|
14
|
+
end
|
10
15
|
end
|
11
16
|
end
|
@@ -11,6 +11,11 @@ module ProxES
|
|
11
11
|
set model_class: User
|
12
12
|
set track_actions: true
|
13
13
|
|
14
|
+
def find_template(views, name, engine, &block)
|
15
|
+
super(views, name, engine, &block) # Root
|
16
|
+
super(::ProxES::ProxES.view_folder, name, engine, &block) # Basic Plugin
|
17
|
+
end
|
18
|
+
|
14
19
|
# New
|
15
20
|
get '/new' do
|
16
21
|
authorize settings.model_class, :create
|
data/lib/proxes/db.rb
CHANGED
@@ -7,6 +7,8 @@ require 'proxes/services/logger'
|
|
7
7
|
# passed to subprocesses. DATABASE_URL may contain passwords.
|
8
8
|
DB = Sequel.connect(ENV['RACK_ENV'] == 'production' ? ENV.delete('DATABASE_URL') : ENV['DATABASE_URL'])
|
9
9
|
|
10
|
+
log_level = (ENV['SEQUEL_LOGGING_LEVEL'] || :debug).to_sym
|
11
|
+
DB.sql_log_level = log_level
|
10
12
|
DB.loggers << ProxES::Services::Logger.instance
|
11
13
|
|
12
14
|
DB.extension(:pagination)
|
data/lib/proxes/forwarder.rb
CHANGED
@@ -10,16 +10,24 @@ module ProxES
|
|
10
10
|
@backend = URI(opts[:backend]) if opts[:backend]
|
11
11
|
end
|
12
12
|
|
13
|
+
def body(request)
|
14
|
+
return nil unless request.body
|
15
|
+
return nil if request.body.is_a? Puma::NullIO
|
16
|
+
return request.body.string if request.body.is_a? StringIO
|
17
|
+
return request.body.read if request.body.is_a? Tempfile
|
18
|
+
request.body
|
19
|
+
end
|
20
|
+
|
13
21
|
def call(env)
|
14
22
|
source_request = Rack::Request.new(env)
|
15
23
|
full_path = source_request.fullpath == '' ? URI.parse(env['REQUEST_URI']).request_uri : source_request.fullpath
|
16
24
|
target_request = Net::HTTP.const_get(source_request.request_method.capitalize).new(full_path)
|
17
25
|
|
18
|
-
|
19
|
-
|
20
|
-
target_request.
|
26
|
+
request_body = body(source_request)
|
27
|
+
if request_body
|
28
|
+
target_request.body = request_body
|
29
|
+
target_request.content_length = request_body.length
|
21
30
|
target_request.content_type = source_request.content_type if source_request.content_type
|
22
|
-
target_request.body_stream.rewind
|
23
31
|
end
|
24
32
|
|
25
33
|
http = Net::HTTP.new(backend.host, backend.port)
|
@@ -4,13 +4,16 @@ module ProxES
|
|
4
4
|
module Helpers
|
5
5
|
module Authentication
|
6
6
|
def current_user
|
7
|
-
|
7
|
+
if env['rack.session'].nil? || env['rack.session']['user_id'].nil?
|
8
|
+
self.current_user = anonymous_user
|
9
|
+
end
|
8
10
|
@users ||= Hash.new { |h, k| h[k] = User[k] }
|
9
11
|
@users[env['rack.session']['user_id']]
|
10
12
|
end
|
11
13
|
|
12
14
|
def current_user=(user)
|
13
|
-
env['rack.session']['
|
15
|
+
env['rack.session'] = {} if env['rack.session'].nil?
|
16
|
+
env['rack.session']['user_id'] = user.id if user
|
14
17
|
end
|
15
18
|
|
16
19
|
def authenticate
|
@@ -18,11 +21,11 @@ module ProxES
|
|
18
21
|
end
|
19
22
|
|
20
23
|
def authenticated?
|
21
|
-
current_user.
|
24
|
+
current_user && !current_user.role?('anonymous')
|
22
25
|
end
|
23
26
|
|
24
27
|
def authenticate!
|
25
|
-
raise NotAuthenticated unless
|
28
|
+
raise NotAuthenticated unless authenticated?
|
26
29
|
true
|
27
30
|
end
|
28
31
|
|
@@ -30,16 +33,23 @@ module ProxES
|
|
30
33
|
env['rack.session'].delete('user_id')
|
31
34
|
end
|
32
35
|
|
33
|
-
def check_basic
|
34
|
-
auth = Rack::Auth::Basic::Request.new(env)
|
35
|
-
return unless auth.provided?
|
36
|
-
return unless auth.basic?
|
36
|
+
def check_basic(request)
|
37
|
+
auth = Rack::Auth::Basic::Request.new(request.env)
|
38
|
+
return false unless auth.provided? && auth.basic?
|
37
39
|
|
38
40
|
identity = ::ProxES::Identity.find(username: auth.credentials[0])
|
39
41
|
identity = ::ProxES::Identity.find(username: URI.unescape(auth.credentials[0])) unless identity
|
40
|
-
|
42
|
+
return false unless identity
|
41
43
|
self.current_user = identity.user if identity.authenticate(auth.credentials[1])
|
42
44
|
end
|
45
|
+
|
46
|
+
def anonymous_user
|
47
|
+
return @anonymous_user if defined? @anonymous_user
|
48
|
+
@anonymous_user ||= begin
|
49
|
+
role = ::ProxES::Role.where(name: 'anonymous').first
|
50
|
+
::ProxES::User.where(roles: role).first unless role.nil?
|
51
|
+
end
|
52
|
+
end
|
43
53
|
end
|
44
54
|
|
45
55
|
class NotAuthenticated < StandardError
|
data/lib/proxes/models/user.rb
CHANGED
@@ -49,8 +49,8 @@ module ProxES
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def check_roles
|
52
|
+
return if role?('anonymous')
|
52
53
|
add_role Role.find_or_create(name: 'user') unless role?('user')
|
53
|
-
add_role Role.find_or_create(name: 'super_admin') if id == 1 && ENV['RACK_ENV'] != 'test' && !role?('super_admin')
|
54
54
|
end
|
55
55
|
|
56
56
|
def index_prefix
|
@@ -1,10 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'proxes/policies/request_policy'
|
4
|
+
|
3
5
|
module ProxES
|
4
6
|
class Request
|
5
7
|
class CatPolicy < RequestPolicy
|
6
8
|
class Scope < RequestPolicy::Scope
|
7
9
|
def resolve
|
10
|
+
return [] if user.nil?
|
11
|
+
|
8
12
|
patterns = Permission.for_user(user, 'INDEX').map do |permission|
|
9
13
|
permission.pattern.gsub(/\{user.(.*)\}/) { |_match| user.send(Regexp.last_match[1].to_sym) }
|
10
14
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'proxes/policies/request_policy'
|
4
|
+
|
5
|
+
module ProxES
|
6
|
+
class Request
|
7
|
+
class CreatePolicy < RequestPolicy
|
8
|
+
class Scope < RequestPolicy::Scope
|
9
|
+
def resolve
|
10
|
+
return [] if user.nil?
|
11
|
+
|
12
|
+
patterns = Permission.for_user(user, 'INDEX').map do |permission|
|
13
|
+
permission.pattern.gsub(/\{user.(.*)\}/) { |_match| user.send(Regexp.last_match[1].to_sym) }
|
14
|
+
end
|
15
|
+
filter(scope.index, patterns).count > 0 ? scope.index : []
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'proxes/db'
|
4
|
+
require 'proxes/models/permission'
|
5
|
+
require 'proxes/policies/request_policy'
|
6
|
+
|
7
|
+
module ProxES
|
8
|
+
class Request
|
9
|
+
class IndexPolicy < RequestPolicy
|
10
|
+
class Scope < RequestPolicy::Scope
|
11
|
+
def resolve
|
12
|
+
return [] if user.nil?
|
13
|
+
|
14
|
+
patterns = Permission.for_user(user, 'INDEX').map do |permission|
|
15
|
+
permission.pattern.gsub(/\{user.(.*)\}/) { |_match| user.send(Regexp.last_match[1].to_sym) }
|
16
|
+
end
|
17
|
+
result = filter(scope.index, patterns)
|
18
|
+
return [] unless result.count > 0
|
19
|
+
%w[POST PUT].include?(scope.request_method) ? scope.index : result
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -1,10 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'proxes/policies/request_policy'
|
4
|
+
|
3
5
|
module ProxES
|
4
6
|
class Request
|
5
7
|
class SearchPolicy < RequestPolicy
|
6
8
|
class Scope < RequestPolicy::Scope
|
7
9
|
def resolve
|
10
|
+
return false if user.nil?
|
11
|
+
|
8
12
|
patterns = Permission.for_user(user, 'INDEX').map do |permission|
|
9
13
|
permission.pattern.gsub(/\{user.(.*)\}/) { |_match| user.send(Regexp.last_match[1].to_sym) }
|
10
14
|
end
|
@@ -1,10 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'proxes/policies/request_policy'
|
4
|
+
|
3
5
|
module ProxES
|
4
6
|
class Request
|
5
7
|
class StatsPolicy < RequestPolicy
|
6
8
|
class Scope < RequestPolicy::Scope
|
7
9
|
def resolve
|
10
|
+
return [] if user.nil?
|
11
|
+
|
8
12
|
patterns = Permission.for_user(user, 'INDEX').map do |permission|
|
9
13
|
permission.pattern.gsub(/\{user.(.*)\}/) { |_match| user.send(Regexp.last_match[1].to_sym) }
|
10
14
|
end
|
@@ -19,13 +19,16 @@ module ProxES
|
|
19
19
|
def method_missing(method_sym, *arguments, &block)
|
20
20
|
return super if method_sym.to_s[-1] != '?'
|
21
21
|
|
22
|
-
return false if user.nil?
|
23
22
|
return true if record.indices? && index_allowed?
|
24
23
|
action_allowed? method_sym[0..-2].upcase
|
25
24
|
end
|
26
25
|
|
26
|
+
def respond_to_missing?(name, _include_private = false)
|
27
|
+
name[-1] == '?'
|
28
|
+
end
|
29
|
+
|
27
30
|
def index_allowed?
|
28
|
-
patterns =
|
31
|
+
patterns = patterns_for('INDEX').map do |permission|
|
29
32
|
permission.pattern.gsub(/\{user.(.*)\}/) { |_match| user.send(Regexp.last_match[1].to_sym) }
|
30
33
|
end
|
31
34
|
filter(record.index, patterns).count > 0
|
@@ -33,14 +36,15 @@ module ProxES
|
|
33
36
|
|
34
37
|
def action_allowed?(action)
|
35
38
|
# Give me all the user's permissions that match the verb
|
36
|
-
|
39
|
+
patterns_for(action).each do |permission|
|
37
40
|
return true if record.path =~ /#{permission.pattern}/
|
38
41
|
end
|
39
42
|
false
|
40
43
|
end
|
41
44
|
|
42
|
-
def
|
43
|
-
|
45
|
+
def patterns_for(action)
|
46
|
+
return Permission.for_user(user, action) if user
|
47
|
+
[]
|
44
48
|
end
|
45
49
|
|
46
50
|
def logger
|
@@ -60,12 +64,10 @@ module ProxES
|
|
60
64
|
def logger
|
61
65
|
@logger ||= ProxES::Services::Logger.instance
|
62
66
|
end
|
67
|
+
|
68
|
+
def resolve
|
69
|
+
scope
|
70
|
+
end
|
63
71
|
end
|
64
72
|
end
|
65
73
|
end
|
66
|
-
|
67
|
-
require 'proxes/policies/request/cat_policy'
|
68
|
-
require 'proxes/policies/request/root_policy'
|
69
|
-
require 'proxes/policies/request/stats_policy'
|
70
|
-
require 'proxes/policies/request/search_policy'
|
71
|
-
require 'proxes/policies/request/snapshot_policy'
|
data/lib/proxes/proxes.rb
CHANGED
@@ -6,12 +6,15 @@ module ProxES
|
|
6
6
|
File.expand_path('../../../migrate', __FILE__)
|
7
7
|
end
|
8
8
|
|
9
|
+
def self.view_folder
|
10
|
+
File.expand_path('../../../views', __FILE__)
|
11
|
+
end
|
12
|
+
|
9
13
|
def self.route_mappings
|
10
14
|
controllers = File.expand_path('../controllers', __FILE__)
|
11
15
|
Dir.glob("#{controllers}/*.rb").each { |f| require f }
|
12
16
|
{
|
13
17
|
'/' => ::ProxES::App,
|
14
|
-
'/dashboards' => ::ProxES::Dashboards,
|
15
18
|
'/users' => ::ProxES::Users,
|
16
19
|
'/roles' => ::ProxES::Roles,
|
17
20
|
'/permissions' => ::ProxES::Permissions,
|
@@ -24,17 +27,40 @@ module ProxES
|
|
24
27
|
{ order: 0, link: '/users/', text: 'Users', target: User, icon: 'user' },
|
25
28
|
{ order: 1, link: '/roles/', text: 'Roles', target: Role, icon: 'group' },
|
26
29
|
{ order: 2, link: '/permissions/', text: 'Permissions', target: Permission, icon: 'check-square' },
|
27
|
-
{ order: 3, link: '/audit-logs/', text: 'Audit Logs', target: AuditLog, icon: 'history' }
|
30
|
+
{ order: 3, link: '/audit-logs/', text: 'Audit Logs', target: AuditLog, icon: 'history' }
|
28
31
|
]
|
29
32
|
end
|
30
33
|
|
31
34
|
def self.seeder
|
32
35
|
proc do
|
33
|
-
|
36
|
+
require 'proxes/models/user'
|
37
|
+
require 'proxes/models/role'
|
38
|
+
|
34
39
|
sa = ::ProxES::Role.find_or_create(name: 'super_admin')
|
35
40
|
%w[GET POST PUT DELETE HEAD OPTIONS INDEX].each do |verb|
|
36
41
|
::ProxES::Permission.find_or_create(role: sa, verb: verb, pattern: '.*')
|
37
42
|
end
|
43
|
+
::ProxES::Role.find_or_create(name: 'admin')
|
44
|
+
user_role = ::ProxES::Role.find_or_create(name: 'user')
|
45
|
+
|
46
|
+
# Kibana Specific
|
47
|
+
anon = ::ProxES::User.find_or_create(email: 'anonymous@proxes.io')
|
48
|
+
anon.remove_role user_role
|
49
|
+
anon_role = ::ProxES::Role.find_or_create(name: 'anonymous')
|
50
|
+
anon.add_role anon_role unless anon.role?('anonymous')
|
51
|
+
::ProxES::Permission.find_or_create(role: anon_role, verb: 'GET', pattern: '/.kibana/config/*')
|
52
|
+
::ProxES::Permission.find_or_create(role: anon_role, verb: 'INDEX', pattern: '.kibana')
|
53
|
+
|
54
|
+
kibana = ::ProxES::Role.find_or_create(name: 'kibana')
|
55
|
+
::ProxES::Permission.find_or_create(role: kibana, verb: 'INDEX', pattern: '.kibana')
|
56
|
+
::ProxES::Permission.find_or_create(role: kibana, verb: 'HEAD', pattern: '/')
|
57
|
+
::ProxES::Permission.find_or_create(role: kibana, verb: 'GET', pattern: '/_nodes*')
|
58
|
+
::ProxES::Permission.find_or_create(role: kibana, verb: 'GET', pattern: '/_cluster/health*')
|
59
|
+
::ProxES::Permission.find_or_create(role: kibana, verb: 'GET', pattern: '/_cluster/settings*')
|
60
|
+
::ProxES::Permission.find_or_create(role: kibana, verb: 'POST', pattern: '/_mget')
|
61
|
+
::ProxES::Permission.find_or_create(role: kibana, verb: 'POST', pattern: '/_search')
|
62
|
+
::ProxES::Permission.find_or_create(role: kibana, verb: 'POST', pattern: '/_msearch')
|
63
|
+
::ProxES::Permission.find_or_create(role: kibana, verb: 'POST', pattern: '/_refresh')
|
38
64
|
end
|
39
65
|
end
|
40
66
|
end
|
data/lib/proxes/request.rb
CHANGED
@@ -4,12 +4,12 @@ require 'rack'
|
|
4
4
|
|
5
5
|
module ProxES
|
6
6
|
class Request < Rack::Request
|
7
|
-
ID_ENDPOINTS = %w[_create _explain _mlt _percolate _source _termvector _update]
|
7
|
+
ID_ENDPOINTS = %w[_create _explain _mlt _percolate _source _termvector _update].freeze
|
8
|
+
WRITE_METHODS = %w[POST PUT DELETE].freeze
|
8
9
|
|
9
10
|
def self.from_env(env)
|
10
11
|
endpoint = path_endpoint(env['REQUEST_PATH'])
|
11
|
-
|
12
|
-
endpoint_class = endpoint[1..-1]
|
12
|
+
endpoint_class = endpoint.nil? ? 'index' : endpoint[1..-1]
|
13
13
|
begin
|
14
14
|
require 'proxes/request/' + endpoint_class.downcase
|
15
15
|
Request.const_get(endpoint_class.titlecase).new(env)
|
@@ -36,7 +36,6 @@ module ProxES
|
|
36
36
|
path_parts[0]
|
37
37
|
end
|
38
38
|
|
39
|
-
|
40
39
|
def parse
|
41
40
|
path_parts
|
42
41
|
end
|
@@ -53,7 +52,7 @@ module ProxES
|
|
53
52
|
|
54
53
|
def check_part(val)
|
55
54
|
return val if val.nil?
|
56
|
-
return [] if [endpoint, '_all'].include?
|
55
|
+
return [] if ([endpoint, '_all'].include?(val) && !WRITE_METHODS.include?(request_method))
|
57
56
|
val.split(',')
|
58
57
|
end
|
59
58
|
end
|
data/lib/proxes/request/cat.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'rack'
|
4
3
|
require 'proxes/request'
|
4
|
+
require 'proxes/policies/request/cat_policy'
|
5
5
|
|
6
6
|
module ProxES
|
7
7
|
class Request
|
8
8
|
class Cat < Request
|
9
|
-
attr_reader :index, :type
|
9
|
+
attr_reader :index, :type
|
10
10
|
|
11
11
|
def index=(idx)
|
12
12
|
@index = idx
|
13
|
-
self.path_info = '/' + [endpoint, type, index]
|
13
|
+
self.path_info = '/' + [endpoint, type, index].compact
|
14
14
|
.map { |v| v.is_a?(Array) ? v.join(',') : v }
|
15
15
|
.select { |v| !v.nil? && v != '' }.join('/')
|
16
16
|
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,29 @@
|
|
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 parse
|
19
|
+
@index ||= check_part(path_parts[0])
|
20
|
+
@type ||= check_part(path_parts[1])
|
21
|
+
@id ||= check_part(path_parts[2])
|
22
|
+
end
|
23
|
+
|
24
|
+
def indices?
|
25
|
+
true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/proxes/request/root.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'rack'
|
4
3
|
require 'proxes/request'
|
4
|
+
require 'proxes/policies/request/search_policy'
|
5
5
|
|
6
6
|
module ProxES
|
7
7
|
class Request
|
@@ -10,9 +10,9 @@ module ProxES
|
|
10
10
|
|
11
11
|
def index=(idx)
|
12
12
|
@index = idx
|
13
|
-
self.path_info = '/' + [index, type, id, endpoint]
|
14
|
-
|
15
|
-
|
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
16
|
end
|
17
17
|
|
18
18
|
def endpoint
|
@@ -20,9 +20,9 @@ module ProxES
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def parse
|
23
|
-
@index ||= check_part(path_parts[0])
|
24
|
-
@type ||= check_part(path_parts[1])
|
25
|
-
@id ||= check_part(path_parts[2])
|
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
26
|
end
|
27
27
|
|
28
28
|
def id
|
data/lib/proxes/request/stats.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'rack'
|
4
3
|
require 'proxes/request'
|
4
|
+
require 'proxes/policies/request/stats_policy'
|
5
5
|
|
6
6
|
module ProxES
|
7
7
|
class Request
|
@@ -10,9 +10,9 @@ module ProxES
|
|
10
10
|
|
11
11
|
def index=(idx)
|
12
12
|
@index = idx
|
13
|
-
self.path_info = '/' + [index, endpoint]
|
14
|
-
|
15
|
-
|
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
16
|
end
|
17
17
|
|
18
18
|
def endpoint
|
data/lib/proxes/security.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'proxes/models/identity'
|
3
4
|
require 'proxes/services/logger'
|
4
5
|
require 'proxes/request'
|
5
6
|
require 'proxes/policies/request_policy'
|
@@ -23,44 +24,63 @@ module ProxES
|
|
23
24
|
end
|
24
25
|
|
25
26
|
def error(message, code = 500)
|
26
|
-
|
27
|
+
headers = { 'Content-Type' => 'application/json' }
|
28
|
+
headers['WWW-Authenticate'] = 'Basic realm="security"' if code == 401
|
29
|
+
[code, headers, ['{"error":"' + message + '"}']]
|
30
|
+
end
|
31
|
+
|
32
|
+
def check(request)
|
33
|
+
check_basic request
|
34
|
+
authorize request
|
35
|
+
rescue Pundit::NotAuthorizedError
|
36
|
+
log_action(:es_request_denied, details: "#{request.request_method.upcase} #{request.fullpath} (#{request.class.name})")
|
37
|
+
logger.debug "Access denied for #{current_user ? current_user.email : 'Anonymous User'} by security layer: #{request.request_method.upcase} #{request.fullpath} (#{request.class.name})"
|
38
|
+
error 'Not Authorized', 401
|
39
|
+
rescue ::ProxES::Helpers::NotAuthenticated
|
40
|
+
logger.warn "Access denied for unauthenticated request by security layer: #{request.request_method.upcase} #{request.fullpath} (#{request.class.name})"
|
41
|
+
error 'Not Authenticated', 401
|
42
|
+
rescue StandardError => e
|
43
|
+
raise e if env['RACK_ENV'] != 'production'
|
44
|
+
logger.error "Access denied for #{current_user ? current_user.email : 'Anonymous User'} by security exception: #{request.request_method.upcase} #{request.fullpath} (#{request.class.name})"
|
45
|
+
logger.error e
|
46
|
+
error 'Forbidden', 403
|
47
|
+
end
|
48
|
+
|
49
|
+
def forward(request)
|
50
|
+
start = Time.now.to_f
|
51
|
+
result = @app.call request.env
|
52
|
+
broadcast(:call_completed, endpoint: request.endpoint, duration: Time.now.to_f - start)
|
53
|
+
result
|
54
|
+
rescue Errno::EHOSTUNREACH
|
55
|
+
error 'Could not reach Elasticsearch at ' + env['ELASTICSEARCH_URL']
|
56
|
+
rescue Errno::ECONNREFUSED
|
57
|
+
error 'Elasticsearch not listening at ' + env['ELASTICSEARCH_URL']
|
27
58
|
end
|
28
59
|
|
29
60
|
def call(env)
|
30
61
|
@env = env
|
31
62
|
|
32
63
|
request = Request.from_env(env)
|
64
|
+
broadcast(:call_started, request)
|
33
65
|
|
34
66
|
logger.debug '==========================BEFORE================================================'
|
35
|
-
logger.debug '= ' + "Request: #{request.request_method} #{request.fullpath}".ljust(76) + ' ='
|
67
|
+
logger.debug '= ' + "Request: #{request.request_method} #{request.fullpath} (#{request.class.name})".ljust(76) + ' ='
|
36
68
|
logger.debug '= ' + "Endpoint: #{request.endpoint}".ljust(76) + ' ='
|
37
69
|
logger.debug '================================================================================'
|
38
70
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
logger.debug "Access denied for #{current_user ? current_user.email : 'Anonymous User'} by security layer: #{request.request_method.upcase} #{request.fullpath} (#{request.class.name})"
|
45
|
-
return error 'Forbidden', 403
|
71
|
+
unless env['PROXES_PASSTHROUGH']
|
72
|
+
result = check(request)
|
73
|
+
return result if result.is_a?(Array) # Rack Response
|
74
|
+
|
75
|
+
request.index = policy_scope(request) if request.indices?
|
46
76
|
end
|
47
|
-
request.index = policy_scope(request) if request.indices?
|
48
77
|
|
49
78
|
logger.debug '==========================AFTER================================================='
|
50
|
-
logger.debug '= ' + "Request: #{request.request_method} #{request.fullpath}".ljust(76) + ' ='
|
79
|
+
logger.debug '= ' + "Request: #{request.request_method} #{request.fullpath} (#{request.class.name})".ljust(76) + ' ='
|
51
80
|
logger.debug '= ' + "Endpoint: #{request.endpoint}".ljust(76) + ' ='
|
52
81
|
logger.debug '================================================================================'
|
53
82
|
|
54
|
-
|
55
|
-
start = Time.now.to_f
|
56
|
-
result = @app.call request.env
|
57
|
-
broadcast(:call_completed, endpoint: request.endpoint, duration: Time.now.to_f - start)
|
58
|
-
result
|
59
|
-
rescue Errno::EHOSTUNREACH
|
60
|
-
error 'Could not reach Elasticsearch at ' + ENV['ELASTICSEARCH_URL']
|
61
|
-
rescue Errno::ECONNREFUSED
|
62
|
-
error 'Elasticsearch not listening at ' + ENV['ELASTICSEARCH_URL']
|
63
|
-
end
|
83
|
+
forward request
|
64
84
|
end
|
65
85
|
end
|
66
86
|
end
|
data/lib/proxes/version.rb
CHANGED
data/views/partials/navbar.haml
CHANGED
@@ -9,7 +9,7 @@
|
|
9
9
|
%span.icon-bar.bar1
|
10
10
|
%span.icon-bar.bar2
|
11
11
|
%span.icon-bar.bar3
|
12
|
-
-if
|
12
|
+
-if authenticated?
|
13
13
|
%form.nav.navbar-top-links.navbar-form.navbar-right{ action: '/_proxes/auth/identity', method: 'post' }
|
14
14
|
%a.btn.btn-default{ href: "/_proxes/users/profile" } My Account
|
15
15
|
%input{ name: '_method', value: 'DELETE', type: 'hidden' }
|
data/views/partials/sidebar.haml
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
%ul.nav.nav-pills.nav-stacked
|
2
|
-
- if
|
2
|
+
- if authenticated?
|
3
3
|
%li
|
4
4
|
%a{ href: '/_proxes' }
|
5
5
|
%i.fa.fa-dashboard.fa-fw
|
@@ -20,7 +20,7 @@
|
|
20
20
|
%i.fa.fa-pencil-square-o.fa-fw
|
21
21
|
Register
|
22
22
|
|
23
|
-
- if
|
23
|
+
- if authenticated?
|
24
24
|
- if cluster_health
|
25
25
|
%table.table
|
26
26
|
%tbody
|
data/views/users/display.haml
CHANGED
@@ -20,7 +20,7 @@
|
|
20
20
|
%p.description
|
21
21
|
%label Signed up:
|
22
22
|
= entity.created_at.strftime('%Y-%m-%d %H:%M:%S')
|
23
|
-
|
23
|
+
|
24
24
|
.row
|
25
25
|
.col-md-6
|
26
26
|
%a.btn.btn-default{ href: "#{base_path}/#{entity.id}/edit" } Edit
|
@@ -50,19 +50,20 @@
|
|
50
50
|
%td{ colspan: 2 } No Permissions
|
51
51
|
.col-md-2
|
52
52
|
|
53
|
-
.
|
54
|
-
.
|
55
|
-
|
56
|
-
.
|
57
|
-
.panel-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
%
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
53
|
+
- if entity.identity.first
|
54
|
+
.row
|
55
|
+
.col-md-2
|
56
|
+
.col-md-8
|
57
|
+
.panel.panel-default
|
58
|
+
.panel-heading
|
59
|
+
%h4 Change Password
|
60
|
+
.panel-body
|
61
|
+
%form.form-horizontal{ method: 'post', action: "#{base_path}/#{entity.id}/identity" }
|
62
|
+
%input{ name: '_method', value: 'PUT', type: 'hidden' }
|
63
|
+
= form_control(:password, entity.identity.first, type: 'password', placeholder: 'Your password', group: 'identity')
|
64
|
+
= form_control(:password_confirmation, entity.identity.first, type: 'password', label: 'Confirm Password', placeholder: 'Confirm your password', group: 'identity')
|
65
|
+
%button.btn.btn-primary{ type: 'submit' }
|
66
|
+
Change Password
|
67
|
+
.col-md-2
|
67
68
|
|
68
69
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: proxes
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jurgens du Toit
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-07-
|
11
|
+
date: 2017-07-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -419,6 +419,8 @@ files:
|
|
419
419
|
- lib/proxes/policies/identity_policy.rb
|
420
420
|
- lib/proxes/policies/permission_policy.rb
|
421
421
|
- lib/proxes/policies/request/cat_policy.rb
|
422
|
+
- lib/proxes/policies/request/create_policy.rb
|
423
|
+
- lib/proxes/policies/request/index_policy.rb
|
422
424
|
- lib/proxes/policies/request/root_policy.rb
|
423
425
|
- lib/proxes/policies/request/search_policy.rb
|
424
426
|
- lib/proxes/policies/request/snapshot_policy.rb
|
@@ -431,6 +433,8 @@ files:
|
|
431
433
|
- lib/proxes/rake_tasks.rb
|
432
434
|
- lib/proxes/request.rb
|
433
435
|
- lib/proxes/request/cat.rb
|
436
|
+
- lib/proxes/request/create.rb
|
437
|
+
- lib/proxes/request/index.rb
|
434
438
|
- lib/proxes/request/root.rb
|
435
439
|
- lib/proxes/request/search.rb
|
436
440
|
- lib/proxes/request/snapshot.rb
|