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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/Vagrantfile +0 -4
  3. data/lib/proxes/controllers/app.rb +27 -19
  4. data/lib/proxes/controllers/application.rb +1 -1
  5. data/lib/proxes/controllers/audit_logs.rb +5 -0
  6. data/lib/proxes/controllers/component.rb +16 -9
  7. data/lib/proxes/controllers/permissions.rb +5 -0
  8. data/lib/proxes/controllers/roles.rb +5 -0
  9. data/lib/proxes/controllers/users.rb +5 -0
  10. data/lib/proxes/db.rb +2 -0
  11. data/lib/proxes/forwarder.rb +12 -4
  12. data/lib/proxes/helpers/authentication.rb +19 -9
  13. data/lib/proxes/helpers/indices.rb +1 -1
  14. data/lib/proxes/models/user.rb +1 -1
  15. data/lib/proxes/policies/request/cat_policy.rb +4 -0
  16. data/lib/proxes/policies/request/create_policy.rb +20 -0
  17. data/lib/proxes/policies/request/index_policy.rb +24 -0
  18. data/lib/proxes/policies/request/search_policy.rb +4 -0
  19. data/lib/proxes/policies/request/snapshot_policy.rb +2 -0
  20. data/lib/proxes/policies/request/stats_policy.rb +4 -0
  21. data/lib/proxes/policies/request_policy.rb +13 -11
  22. data/lib/proxes/proxes.rb +29 -3
  23. data/lib/proxes/request.rb +4 -5
  24. data/lib/proxes/request/cat.rb +3 -3
  25. data/lib/proxes/request/create.rb +33 -0
  26. data/lib/proxes/request/index.rb +29 -0
  27. data/lib/proxes/request/root.rb +1 -1
  28. data/lib/proxes/request/search.rb +7 -7
  29. data/lib/proxes/request/snapshot.rb +1 -1
  30. data/lib/proxes/request/stats.rb +4 -4
  31. data/lib/proxes/security.rb +41 -21
  32. data/lib/proxes/version.rb +1 -1
  33. data/views/partials/navbar.haml +1 -1
  34. data/views/partials/sidebar.haml +2 -2
  35. data/views/users/display.haml +16 -15
  36. metadata +6 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4785a97b631eeb52cc78cba8244574f786bceb47
4
- data.tar.gz: 16c51704657319e518ab24bbecfd55cb3c91217d
3
+ metadata.gz: 3b182884ae9246085097e7ff89d204d225b050b8
4
+ data.tar.gz: 5a22e671d4ec358da74d05525bdad867a95b810a
5
5
  SHA512:
6
- metadata.gz: c7202970ccb85ff8780f269423115508545f31bb353c98d5b2cd672bff2ee0116a55536d52a1e698278fef43dfdc21eaa6f4b07c3269b0d9bb8286fe6b78e696
7
- data.tar.gz: eb3141d296c473bcd49f49c941b5050d5e04ac4b5375dfe55b337284c2f9e6f748c824c724589fa49d0674ae64e5f79135d34e8b541a87914e8257bdcbb2d295
6
+ metadata.gz: 77202113968d066c205ebb136ab95a6ede2264f0633f33574f2b9f26dd32da272ea2479c471e4a8781eddf0248a99f4648f33a21fb18d67e55e85447803fa4fa
7
+ data.tar.gz: a46f43c312ad5fa68d794fe642bdef660e20c1970824e7393ec227a068904fda447b896d761d3d6972f38a0b78617a55afce8772cdf317c44cec12a8639dcd99
@@ -44,7 +44,3 @@ Vagrant.configure(2) do |config|
44
44
  # npm install --no-bin-links
45
45
  SHELL
46
46
  end
47
-
48
- # create user proxes with password 'somethingrandom';
49
- # create database proxes;
50
- # grant all privileges on database proxes to proxes;
@@ -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
- redirect '/_proxes/dashboards/offline' if cluster_health.nil?
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 '/_proxes/auth/identity' do
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
- user = User.find(email: env['omniauth.auth']['info']['email'])
23
- self.current_user = user
24
- log_action(:identity_login, user: user)
25
- flash[:success] = 'Logged In'
26
- redirect '/_proxes'
27
- end
28
-
29
- # Failed Login
30
- post '/_proxes/auth/identity/callback' do
31
- broadcast(:identity_failed_login)
32
- flash[:warning] = 'Invalid credentials. Please try again.'
33
- redirect '/_proxes/auth/identity'
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 '/_proxes/auth/identity/register' do
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 '/_proxes/auth/identity' do
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 '/_proxes/unauthenticated' do
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
- before do
16
- check_basic
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
- }.to_json
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
- redirect "#{base_path}/#{entity.id}", 201 if success
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.to_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.to_hash.to_json
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
@@ -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)
@@ -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
- if source_request.body
19
- target_request.body_stream = source_request.body
20
- target_request.content_length = source_request.content_length.to_i
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
- return nil unless env['rack.session'] && env['rack.session']['user_id']
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']['user_id'] = user.id
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.nil?
24
+ current_user && !current_user.role?('anonymous')
22
25
  end
23
26
 
24
27
  def authenticate!
25
- raise NotAuthenticated unless current_user
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
- raise NotAuthenticated unless identity
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
@@ -10,7 +10,7 @@ module ProxES
10
10
  against.each do |pattern|
11
11
  answer.concat(asked.select { |idx| idx =~ /#{pattern}/ })
12
12
  end
13
- answer.count > 0 ? answer : against
13
+ answer
14
14
  end
15
15
  end
16
16
  end
@@ -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,5 +1,7 @@
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 SnapshotPolicy < RequestPolicy
@@ -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 = Permission.for_user(user, 'INDEX').map do |permission|
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
- Permission.for_user(user, action).each do |permission|
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 respond_to_missing?(name, _include_private = false)
43
- name[-1] == '?'
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'
@@ -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
- ::ProxES::Role.find_or_create(name: 'user')
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
@@ -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
- return new(env) if endpoint.nil?
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? val
55
+ return [] if ([endpoint, '_all'].include?(val) && !WRITE_METHODS.include?(request_method))
57
56
  val.split(',')
58
57
  end
59
58
  end
@@ -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, :id
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
@@ -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/root_policy'
5
5
 
6
6
  module ProxES
7
7
  class Request
@@ -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
- .map { |v| v.is_a?(Array) ? v.join(',') : v }
15
- .select { |v| !v.nil? && v != '' }.join('/')
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
@@ -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/snapshot_policy'
5
5
 
6
6
  module ProxES
7
7
  class Request
@@ -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
- .map { |v| v.is_a?(Array) ? v.join(',') : v }
15
- .select { |v| !v.nil? && v != '' }.join('/')
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
@@ -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
- [code, { 'Content-Type' => 'application/json' }, ['{"error":"' + message + '"}']]
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
- begin
40
- check_basic
41
- authorize request
42
- rescue StandardError
43
- log_action(:es_request_denied, details: "#{request.request_method.upcase} #{request.fullpath} (#{request.class.name})")
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
- begin
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ProxES
4
- VERSION = '0.6.1'.freeze
4
+ VERSION = '0.7.0'.freeze
5
5
  end
@@ -9,7 +9,7 @@
9
9
  %span.icon-bar.bar1
10
10
  %span.icon-bar.bar2
11
11
  %span.icon-bar.bar3
12
- -if current_user
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' }
@@ -1,5 +1,5 @@
1
1
  %ul.nav.nav-pills.nav-stacked
2
- - if defined?(current_user) && current_user
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 defined?(current_user) && current_user
23
+ - if authenticated?
24
24
  - if cluster_health
25
25
  %table.table
26
26
  %tbody
@@ -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
- .row
54
- .col-md-2
55
- .col-md-8
56
- .panel.panel-default
57
- .panel-heading
58
- %h4 Change Password
59
- .panel-body
60
- %form.form-horizontal{ method: 'post', action: "#{base_path}/#{entity.id}/identity" }
61
- %input{ name: '_method', value: 'PUT', type: 'hidden' }
62
- = form_control(:password, entity.identity.first, type: 'password', placeholder: 'Your password', group: 'identity')
63
- = form_control(:password_confirmation, entity.identity.first, type: 'password', label: 'Confirm Password', placeholder: 'Confirm your password', group: 'identity')
64
- %button.btn.btn-primary{ type: 'submit' }
65
- Change Password
66
- .col-md-2
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.6.1
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-09 00:00:00.000000000 Z
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