Finish moving current_workspace tracking to client

GSoC/Meterpreter_Web_Console
James Barnett 2018-03-26 15:58:47 -05:00
parent def0e4d93b
commit cfa03a999c
No known key found for this signature in database
GPG Key ID: 647983861A4EC5EA
8 changed files with 56 additions and 38 deletions

View File

@ -2,7 +2,8 @@ module EventDataProxy
def report_event(opts) def report_event(opts)
begin begin
data_service = self.get_data_service() data_service = self.get_data_service
opts[:workspace] = workspace.name if opts[:workspace].nil?
data_service.report_event(opts) data_service.report_event(opts)
rescue Exception => e rescue Exception => e
self.log_error(e, "Problem reporting event") self.log_error(e, "Problem reporting event")

View File

@ -29,10 +29,13 @@ module WorkspaceDataProxy
def workspace def workspace
begin begin
if @current_workspace_id if @current_workspace
workspaces({ :id => @current_workspace_id }).first @current_workspace
else else
default_workspace # This is mostly a failsafe to prevent bad things from happening. @current_workspace should always be set
# outside of here, but this will save us from crashes/infinite loops if that happens
warn "@current_workspace was not set. Setting to default_workspace"
@current_workspace = default_workspace
end end
rescue Exception => e rescue Exception => e
self.log_error(e, "Problem retrieving workspace") self.log_error(e, "Problem retrieving workspace")
@ -43,7 +46,7 @@ module WorkspaceDataProxy
# See MS-3095 # See MS-3095
def workspace=(workspace) def workspace=(workspace)
begin begin
@current_workspace_id = workspace.id @current_workspace = workspace
rescue Exception => e rescue Exception => e
self.log_error(e, "Problem setting workspace") self.log_error(e, "Problem setting workspace")
end end

View File

@ -24,10 +24,11 @@ class RemoteHTTPDataService
# #
# @param [String] endpoint A valid http or https URL. Cannot be nil # @param [String] endpoint A valid http or https URL. Cannot be nil
# #
def initialize(endpoint, https_opts = {}) def initialize(endpoint, framework, https_opts = {})
validate_endpoint(endpoint) validate_endpoint(endpoint)
@endpoint = URI.parse(endpoint) @endpoint = URI.parse(endpoint)
@https_opts = https_opts @https_opts = https_opts
@framework = framework
build_client_pool(5) build_client_pool(5)
end end
@ -78,8 +79,8 @@ class RemoteHTTPDataService
# #
# @return A wrapped response (ResponseWrapper), see below. # @return A wrapped response (ResponseWrapper), see below.
# #
def get_data(path, data_hash = nil, query = nil) def get_data(path, data_hash = nil, query = nil, add_workspace = true)
make_request(GET_REQUEST, path, data_hash, query) make_request(GET_REQUEST, path, data_hash, query, add_workspace)
end end
# #
@ -118,10 +119,14 @@ class RemoteHTTPDataService
# #
# @return A wrapped response (ResponseWrapper) # @return A wrapped response (ResponseWrapper)
# #
def make_request(request_type, path, data_hash = nil, query = nil) def make_request(request_type, path, data_hash = nil, query = nil, add_workspace = true)
begin begin
# simplify query by removing nil values # simplify query by removing nil values
query_str = (!query.nil? && !query.empty?) ? append_workspace(query).compact.to_query : nil add_workspace = true
query_str = nil
if add_workspace
query_str = (!query.nil? && !query.empty?) ? append_workspace(query).compact.to_query : nil
end
uri = URI::HTTP::build({path: path, query: query_str}) uri = URI::HTTP::build({path: path, query: query_str})
dlog("HTTP #{request_type} request to #{uri.request_uri} with #{data_hash ? data_hash : "nil"}") dlog("HTTP #{request_type} request to #{uri.request_uri} with #{data_hash ? data_hash : "nil"}")
@ -227,12 +232,12 @@ class RemoteHTTPDataService
data_hash[:workspace] = workspace.name data_hash[:workspace] = workspace.name
end end
data_hash[:workspace] = framework.db.workspace.name if workspace.nil? data_hash[:workspace] = @framework.db.workspace.name if workspace.nil?
data_hash data_hash
end end
def build_request(request, data_hash) def build_request(request, data_hash, add_workspace = true)
request.content_type = 'application/json' request.content_type = 'application/json'
if !data_hash.nil? && !data_hash.empty? if !data_hash.nil? && !data_hash.empty?
data_hash.each do |k,v| data_hash.each do |k,v|
@ -244,7 +249,7 @@ class RemoteHTTPDataService
data_hash.delete(k) data_hash.delete(k)
end end
end end
json_body = append_workspace(data_hash).to_json json_body = append_workspace(data_hash).to_json if add_workspace
request.body = json_body request.body = json_body
end end

View File

@ -13,19 +13,25 @@ module RemoteWorkspaceDataService
end end
def default_workspace def default_workspace
json_to_mdm_object(self.get_data(WORKSPACE_API_PATH, nil, {:name => default}), WORKSPACE_MDM_CLASS, []) json_to_mdm_object(self.get_data(WORKSPACE_API_PATH, nil, {:name => default}, false), WORKSPACE_MDM_CLASS, [])
end end
def workspace def workspace
find_workspace(current_workspace_name) # The @current_workspace is tracked on the client side, so attempting to call it directly from the RemoteDataService
# will not return the correct results. Run it back through the proxy.
wlog "[DEPRECATION] Calling workspace from within the RemoteDataService is no longer supported. Please call from WorkspaceDataProxy instead."
framework.db.workspace
end end
def workspace=(workspace) def workspace=(workspace)
@current_workspace_name = workspace.name # The @current_workspace is tracked on the client side, so attempting to call it directly from the RemoteDataService
# will not return the correct results. Run it back through the proxy.
wlog "[DEPRECATION] Setting the current workspace from the RemoteDataService is no longer supported. Please call from WorkspaceDataProxy instead."
framework.db.workspace = workspace
end end
def workspaces(opts) def workspaces(opts)
json_to_mdm_object(self.get_data(WORKSPACE_API_PATH, nil, opts), WORKSPACE_MDM_CLASS, []) json_to_mdm_object(self.get_data(WORKSPACE_API_PATH, nil, opts, false), WORKSPACE_MDM_CLASS, [])
end end
def delete_workspaces(opts) def delete_workspaces(opts)

View File

@ -9,38 +9,38 @@ module Msf::DBManager::Workspace
end end
def default_workspace def default_workspace
puts "default_workspace is being called directly from dbmanager" # Workspace tracking is handled on the client side, so attempting to call it directly from the DbManager
caller.each { |line| puts "#{line}\n"} # will not return the correct results. Run it back through the proxy.
# ::ActiveRecord::Base.connection_pool.with_connection { wlog "[DEPRECATION] Setting the workspace from within DbManager is no longer supported. Please call from WorkspaceDataProxy instead."
# ::Mdm::Workspace.default raise NotImplementedError
# }
end end
def find_workspace(name) def find_workspace(name)
puts "find_workspace is being called directly from dbmanager" ::ActiveRecord::Base.connection_pool.with_connection {
caller.each { |line| puts "#{line}\n"} ::Mdm::Workspace.find_by_name(name)
# ::ActiveRecord::Base.connection_pool.with_connection { }
# ::Mdm::Workspace.find_by_name(name)
# }
end end
def workspace def workspace
puts "workspace is being called directly from dbmanager" # The @current_workspace is tracked on the client side, so attempting to call it directly from the DbManager
caller.each { |line| puts "#{line}\n"} # will not return the correct results. Run it back through the proxy.
# ::ActiveRecord::Base.connection_pool.with_connection { wlog "[DEPRECATION] Calling workspace from within DbManager is no longer supported. Please call from WorkspaceDataProxy instead."
# ::Mdm::Workspace.find(@workspace_id) raise NotImplementedError
# }
end end
def workspace=(workspace) def workspace=(workspace)
#@workspace_id = workspace.id # The @current_workspace is tracked on the client side, so attempting to call it directly from the DbManager
puts "workspace= is being called directly from dbmanager" # will not return the correct results. Run it back through the proxy.
caller.each { |line| puts "#{line}\n"} wlog "[DEPRECATION] Setting the workspace from within DbManager is no longer supported. Please call from WorkspaceDataProxy instead."
raise NotImplementedError
end end
def workspaces(opts = {}) def workspaces(opts = {})
::ActiveRecord::Base.connection_pool.with_connection { ::ActiveRecord::Base.connection_pool.with_connection {
search_term = opts.delete(:search_term) search_term = opts.delete(:search_term)
# Passing these values to the search will cause exceptions, so remove them if they accidentally got passed in.
opts.delete(:workspace)
opts.delete(:wspace)
::ActiveRecord::Base.connection_pool.with_connection { ::ActiveRecord::Base.connection_pool.with_connection {
if search_term && !search_term.empty? if search_term && !search_term.empty?
@ -58,7 +58,7 @@ module Msf::DBManager::Workspace
::ActiveRecord::Base.connection_pool.with_connection { ::ActiveRecord::Base.connection_pool.with_connection {
deleted = [] deleted = []
default_deleted = false default_deleted = false q
opts[:ids].each do |ws_id| opts[:ids].each do |ws_id|
ws = Mdm::Workspace.find(ws_id) ws = Mdm::Workspace.find(ws_id)
default_deleted = true if ws.default? default_deleted = true if ws.default?

View File

@ -1963,7 +1963,7 @@ class Db
end end
endpoint = "#{protocol}://#{host}:#{port}" endpoint = "#{protocol}://#{host}:#{port}"
remote_data_service = Metasploit::Framework::DataService::RemoteHTTPDataService.new(endpoint, https_opts) remote_data_service = Metasploit::Framework::DataService::RemoteHTTPDataService.new(endpoint, framework, https_opts)
begin begin
framework.db.register_data_service(remote_data_service) framework.db.register_data_service(remote_data_service)
print_line "Registered data service: #{remote_data_service.name}" print_line "Registered data service: #{remote_data_service.name}"

View File

@ -418,6 +418,7 @@ class Driver < Msf::Ui::Driver
print_warning("\t#{path}: #{error}") print_warning("\t#{path}: #{error}")
end end
end end
framework.db.workspace = framework.db.default_workspace
framework.events.on_ui_start(Msf::Framework::Revision) framework.events.on_ui_start(Msf::Framework::Revision)

View File

@ -16,7 +16,9 @@ module DBManager
end end
def self.process_opts_workspace(opts, framework) def self.process_opts_workspace(opts, framework)
wspace = opts.delete(:wspace) || opts.delete(:workspace) || framework.db.workspace wspace = opts.delete(:wspace) || opts.delete(:workspace)
raise ArgumentError.new("opts must include a valid :workspace or :wspace value.") if wspace.nil? || ((wspace.kind_of? String) && wspace.empty?)
if wspace.kind_of? String if wspace.kind_of? String
wspace = framework.db.find_workspace(wspace) wspace = framework.db.find_workspace(wspace)
end end