Class: ProxES::Security

Inherits:
Object
  • Object
show all
Includes:
Ditty::Helpers::Authentication, Ditty::Helpers::Pundit, Ditty::Helpers::Wisper, Wisper::Publisher
Defined in:
lib/proxes/security.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app, logger = nil) ⇒ Security

Returns a new instance of Security.



19
20
21
22
# File 'lib/proxes/security.rb', line 19

def initialize(app, logger = nil)
  @app = app
  @logger = logger || ::Ditty::Services::Logger.instance
end

Instance Attribute Details

#envObject (readonly)

Returns the value of attribute env.



12
13
14
# File 'lib/proxes/security.rb', line 12

def env
  @env
end

#loggerObject (readonly)

Returns the value of attribute logger.



12
13
14
# File 'lib/proxes/security.rb', line 12

def logger
  @logger
end

Instance Method Details

#call(env) ⇒ Object



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/proxes/security.rb', line 71

def call(env)
  @env = env

  request = Request.from_env(env)
  broadcast(:call_started, request)

  log(request, 'BEFORE')
  unless env['PROXES_PASSTHROUGH']
    result = check(request)
    return result if result.is_a?(Array) # Rack Response

    request.index = policy_scope(request) if request.indices?
    log(request, 'AFTER')
  end

  forward request
end

#check(request) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/proxes/security.rb', line 34

def check(request)
  check_basic request
  authorize request, request.request_method.downcase
rescue Pundit::NotAuthorizedError
  return redirect '/_proxes/' if request.get_header('HTTP_ACCEPT') && request.get_header('HTTP_ACCEPT').include?('text/html')

  log_action(:es_request_denied, details: "#{request.request_method.upcase} #{request.fullpath} (#{request.class.name})")
  logger.debug "Access denied for #{current_user ? current_user.email : 'Anonymous User'} by security layer: #{request.request_method.upcase} #{request.fullpath} (#{request.class.name})"
  error 'Not Authorized', 401
rescue ::Ditty::Helpers::NotAuthenticated
  logger.warn "Access denied for unauthenticated request by security layer: #{request.request_method.upcase} #{request.fullpath} (#{request.class.name})"
  error 'Not Authenticated', 401
rescue StandardError => e
  raise e if env['RACK_ENV'] != 'production'
  logger.error "Access denied for #{current_user ? current_user.email : 'Anonymous User'} by security exception: #{request.request_method.upcase} #{request.fullpath} (#{request.class.name})"
  logger.error e
  error 'Forbidden', 403
end

#error(message, code = 500) ⇒ Object



24
25
26
27
28
# File 'lib/proxes/security.rb', line 24

def error(message, code = 500)
  headers = { 'Content-Type' => 'application/json' }
  headers['WWW-Authenticate'] = 'Basic realm="security"' if code == 401
  [code, headers, ['{"error":"' + message + '"}']]
end

#forward(request) ⇒ Object



53
54
55
56
57
58
59
60
61
62
# File 'lib/proxes/security.rb', line 53

def forward(request)
  start = Time.now.to_f
  result = @app.call request.env
  broadcast(:call_completed, request, Time.now.to_f - start) if result[0].to_i >= 200 && result[0].to_i < 300
  result
rescue Errno::EHOSTUNREACH
  error 'Could not reach Elasticsearch at ' + ENV['ELASTICSEARCH_URL']
rescue Errno::ECONNREFUSED
  error 'Elasticsearch not listening at ' + ENV['ELASTICSEARCH_URL']
end

#log(request, stage) ⇒ Object



64
65
66
67
68
69
# File 'lib/proxes/security.rb', line 64

def log(request, stage)
  logger.debug '============' + stage.ljust(56) + '============'
  logger.debug '= ' + "Request: #{request.request_method} #{request.fullpath} (#{request.class.name})".ljust(76) + ' ='
  logger.debug '= ' + "Endpoint: #{request.endpoint}".ljust(76) + ' ='
  logger.debug '================================================================================'
end

#redirect(destination, code = 302) ⇒ Object



30
31
32
# File 'lib/proxes/security.rb', line 30

def redirect(destination, code = 302)
  [code, { 'Location' => destination}, []]
end