Skip to content

Commit

Permalink
Merge pull request #101 from rapid7/ajax-clean-up
Browse files Browse the repository at this point in the history
AJAX clean up
  • Loading branch information
mdaines-r7 committed Jan 5, 2015
2 parents e3a708e + e1ea3cf commit 8323bed
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 52 deletions.
124 changes: 76 additions & 48 deletions lib/nexpose/ajax.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# encoding: utf-8

module Nexpose

# Accessor to the Nexpose AJAX API.
# These core methods should allow direct access to underlying controllers
# in order to test functionality that is not currently exposed
Expand All @@ -10,6 +8,8 @@ module Nexpose
module AJAX
module_function

# Content type strings acceptect by Nexpose.
#
module CONTENT_TYPE
XML = 'text/xml; charset=UTF-8'
JSON = 'application/json; charset-utf-8'
Expand All @@ -28,7 +28,7 @@ def get(nsc, uri, content_type = CONTENT_TYPE::XML, options = {})
parameterize_uri(uri, options)
get = Net::HTTP::Get.new(uri)
get.set_content_type(content_type)
_request(nsc, get)
request(nsc, get)
end

# PUT call to a Nexpose controller.
Expand All @@ -43,7 +43,7 @@ def put(nsc, uri, payload = nil, content_type = CONTENT_TYPE::XML)
put = Net::HTTP::Put.new(uri)
put.set_content_type(content_type)
put.body = payload.to_s if payload
_request(nsc, put)
request(nsc, put)
end

# POST call to a Nexpose controller.
Expand All @@ -52,13 +52,14 @@ def put(nsc, uri, payload = nil, content_type = CONTENT_TYPE::XML)
# @param [String] uri Controller address relative to https://host:port
# @param [String|REXML::Document] payload XML document required by the call.
# @param [String] content_type Content type to use when issuing the POST.
# @param [Fixnum] timeout Set an explicit timeout for the HTTP request.
# @return [String|REXML::Document|Hash] The response from the call.
#
def post(nsc, uri, payload = nil, content_type = CONTENT_TYPE::XML)
def post(nsc, uri, payload = nil, content_type = CONTENT_TYPE::XML, timeout = nil)
post = Net::HTTP::Post.new(uri)
post.set_content_type(content_type)
post.body = payload.to_s if payload
_request(nsc, post)
request(nsc, post, timeout)
end

# PATCH call to a Nexpose controller.
Expand All @@ -73,7 +74,7 @@ def patch(nsc, uri, payload = nil, content_type = CONTENT_TYPE::XML)
patch = Net::HTTP::Patch.new(uri)
patch.set_content_type(content_type)
patch.body = payload.to_s if payload
_request(nsc, patch)
request(nsc, patch)
end

# POST call to a Nexpose controller that uses a form-post model.
Expand All @@ -90,7 +91,7 @@ def form_post(nsc, uri, parameters, content_type = CONTENT_TYPE::FORM)
post = Net::HTTP::Post.new(uri)
post.set_content_type(content_type)
post.set_form_data(parameters)
_request(nsc, post)
request(nsc, post)
end

# DELETE call to a Nexpose controller.
Expand All @@ -101,9 +102,16 @@ def form_post(nsc, uri, parameters, content_type = CONTENT_TYPE::FORM)
def delete(nsc, uri, content_type = CONTENT_TYPE::XML)
delete = Net::HTTP::Delete.new(uri)
delete.set_content_type(content_type)
_request(nsc, delete)
request(nsc, delete)
end

###
# === Internal helper methods below this line. ===
#
# These are internal utility methods, not subject to backward compatibility
# concerns.
###

# Append the query parameters to given URI.
#
# @param [String] uri Controller address relative to https://host:port
Expand All @@ -114,59 +122,93 @@ def delete(nsc, uri, content_type = CONTENT_TYPE::XML)
def parameterize_uri(uri, parameters)
params = Hash.try_convert(parameters)
unless params.nil? || params.empty?
uri = uri.concat(('?').concat(parameters.map { |k, v| "#{k}=#{CGI.escape(v.to_s)}" }.join('&')))
uri = uri.concat(('?').concat(parameters.map { |k, v| "#{k}=#{CGI.escape(v.to_s)}" }.join('&')))
end
uri
end

def preserving_preference(nsc, pref)
begin
orig = _get_rows(nsc, pref)
yield
ensure
_set_rows(nsc, pref, orig)
end
end

###
# Internal helper methods

# Use the Nexpose::Connection to establish a correct HTTPS object.
def _https(nsc)
def https(nsc, timeout = nil)
http = Net::HTTP.new(nsc.host, nsc.port)
http.read_timeout = timeout if timeout
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
http
end

# Attach necessary header fields.
def _headers(nsc, request)
def headers(nsc, request)
request.add_field('nexposeCCSessionID', nsc.session_id)
request.add_field('Cookie', "nexposeCCSessionID=#{nsc.session_id}")
end

def _request(nsc, request)
http = _https(nsc)
_headers(nsc, request)
def request(nsc, request, timeout = nil)
http = https(nsc, timeout)
headers(nsc, request)

# Return response body if request is successful. Brittle.
response = http.request(request)
case response
when Net::HTTPOK
response.body
when Net::HTTPCreated
when Net::HTTPOK, Net::HTTPCreated
response.body
when Net::HTTPForbidden
raise Nexpose::PermissionError.new(response)
when Net::HTTPUnauthorized
raise Nexpose::PermissionError.new(response)
when Net::HTTPFound
if response.header['location'] =~ /login/
raise Nexpose::AuthenticationFailed.new(response)
else
req_type = request.class.name.split('::').last.upcase
raise Nexpose::APIError.new(response, "#{req_type} request to #{request.path} failed. #{request.body}", response.code)
end
else
req_type = request.class.name.split('::').last.upcase
raise Nexpose::APIError.new(response, "#{req_type} request to #{request.path} failed. #{request.body}", response.code)
end
end

def _get_rows(nsc, pref)
# Execute a block of code while presenving the preferences for any
# underlying table being accessed. Use this method when accessing data
# tables which are present in the UI to prevent existing row preferences
# from being set to 500.
#
# This is an internal utility method, not subject to backward compatibility
# concerns.
#
# @param [Connection] nsc Live connection to a Nepose console.
# @param [String] pref Preference key value to preserve.
#
def preserving_preference(nsc, pref)
begin
orig = get_rows(nsc, pref)
yield
ensure
set_rows(nsc, pref, orig)
end
end

# Get a valid row preference value.
#
# This is an internal utility method, not subject to backward compatibility
# concerns.
#
# @param [Fixnum] val Value to get inclusive row preference for.
# @return [Fixnum] Valid row preference.
#
def row_pref_of(val)
if val.nil? || val > 100
500
elsif val > 50
100
elsif val > 25
50
elsif val > 10
25
else
10
end
end

def get_rows(nsc, pref)
uri = '/ajax/user_pref_get.txml'
resp = get(nsc, uri, CONTENT_TYPE::XML, 'name' => "#{pref}.rows")
xml = REXML::Document.new(resp)
Expand All @@ -178,7 +220,7 @@ def _get_rows(nsc, pref)
end
end

def _set_rows(nsc, pref, value)
def set_rows(nsc, pref, value)
uri = '/ajax/user_pref_set.txml'
params = { 'name' => "#{pref}.rows",
'value' => value }
Expand All @@ -188,19 +230,5 @@ def _set_rows(nsc, pref, value)
attr.value == '1'
end
end

def _row_pref_of(val)
if val.nil? || val > 100
500
elsif val > 50
100
elsif val > 25
50
elsif val > 10
25
else
10
end
end
end
end
8 changes: 4 additions & 4 deletions lib/nexpose/scan.rb
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ def scan_statistics(scan_id)
#
def past_scans(limit = nil)
uri = '/data/scan/global/scan-history'
rows = AJAX._row_pref_of(limit)
rows = AJAX.row_pref_of(limit)
params = { 'sort' => 'endTime', 'dir' => 'DESC', 'startIndex' => 0 }
AJAX.preserving_preference(self, 'global-completed-scans') do
data = DataTable._get_json_table(self, uri, params, rows, limit)
Expand All @@ -255,7 +255,7 @@ def past_scans(limit = nil)
# zip_file, if provided. Otherwise, returns raw ZIP binary data.
#
def export_scan(scan_id, zip_file = nil)
http = AJAX._https(self)
http = AJAX.https(self)
headers = { 'Cookie' => "nexposeCCSessionID=#{@session_id}",
'Accept-Encoding' => 'identity' }
resp = http.get("/data/scan/#{scan_id}/export", headers)
Expand Down Expand Up @@ -305,8 +305,8 @@ def import_scan(site_id, zip_file)
post.set_content_type('multipart/form-data', boundary: data.bound)

# Avoiding AJAX#request, because the data can cause binary dump on error.
http = AJAX._https(self)
AJAX._headers(self, post)
http = AJAX.https(self)
AJAX.headers(self, post)
response = http.request(post)
case response
when Net::HTTPOK
Expand Down

0 comments on commit 8323bed

Please sign in to comment.