proxes 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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