proxes 0.6.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|