class Inspec::Resources::OracledbSession


This resource needs further testing and refinement
STABILITY: Experimental

def initialize(opts = {})

rubocop:disable Metrics/PerceivedComplexity,Metrics/CyclomaticComplexity
def initialize(opts = {})
  @user = opts[:user]
  @password = opts[:password] || opts[:pass]
  if opts[:pass]
    Inspec.deprecate(:oracledb_session_pass_option, 'The oracledb_session `pass` option is deprecated. Please use `password`.')
  end
  @host = opts[:host] || 'localhost'
  @port = opts[:port] || '1521'
  @service = opts[:service]
  # connection as sysdba stuff
  return skip_resource "Option 'as_os_user' not available in Windows" if inspec.os.windows? && opts[:as_os_user]
  @su_user = opts[:as_os_user]
  @db_role = opts[:as_db_role]
  # we prefer sqlci although it is way slower than sqlplus, but it understands csv properly
  @sqlcl_bin = 'sql' unless opts.key?(:sqlplus_bin) # don't use it if user specified sqlplus_bin option
  @sqlplus_bin = opts[:sqlplus_bin] || 'sqlplus'
  return fail_resource "Can't run Oracle checks without authentication" if @su_user.nil? && (@user.nil? || @password.nil?)
  return fail_resource 'You must provide a service name for the session' if @service.nil?
end

def parse_csv_result(stdout)

def parse_csv_result(stdout)
  output = stdout.delete(/\r/)
  table = CSV.parse(output, { headers: true })
  # convert to hash
  headers = table.headers
  results = table.map { |row|
    res = {}
    headers.each { |header|
      res[header.downcase] = row[header]
    }
    Hashie::Mash.new(res)
  }
  results
end

def parse_html_result(stdout) # rubocop:disable Metrics/AbcSize

rubocop:disable Metrics/AbcSize
def parse_html_result(stdout) # rubocop:disable Metrics/AbcSize
  result = stdout
  # make oracle html valid html by removing the p tag, it does not include a closing tag
  result = result.gsub('<p>', '').gsub('</p>', '').gsub('<br>', '')
  doc = REXML::Document.new result
  table = doc.elements['table']
  hash = []
  if !table.nil?
    rows = table.elements.to_a
    headers = rows[0].elements.to_a('th').map { |entry| entry.text.strip }
    rows.delete_at(0)
    # iterate over each row, first row is header
    hash = []
    if !rows.nil? && !rows.empty?
      hash = rows.map { |row|
        res = {}
        entries = row.elements.to_a('td')
        # ignore if we have empty entries, oracle is adding th rows in between
        return nil if entries.empty?
        headers.each_with_index { |header, index|
          # we need htmlentities since we do not have nokogiri
          coder = HTMLEntities.new
          val = coder.decode(entries[index].text).strip
          res[header.downcase] = val
        }
        Hashie::Mash.new(res)
      }.compact
    end
  end
  hash
end

def query(q)

def query(q)
  escaped_query = q.gsub(/\\/, '\\\\').gsub(/"/, '\\"')
  # escape tables with $
  escaped_query = escaped_query.gsub('$', '\\$')
  p = nil
  # use sqlplus if sqlcl is not available
  if @sqlcl_bin and inspec.command(@sqlcl_bin).exist?
    bin = @sqlcl_bin
    opts = "set sqlformat csv\nSET FEEDBACK OFF"
    p = :parse_csv_result
  else
    bin = @sqlplus_bin
    opts = "SET MARKUP HTML ON\nSET PAGESIZE 32000\nSET FEEDBACK OFF"
    p = :parse_html_result
  end
  query = verify_query(escaped_query)
  query += ';' unless query.end_with?(';')
  if @db_role.nil?
    command = %{#{bin} "#{@user}"/"#{@password}"@#{@host}:#{@port}/#{@service} <<EOC\n#{opts}\n#{query}\nEXIT\nEOC}
  elsif @su_user.nil?
    command = %{#{bin} "#{@user}"/"#{@password}"@#{@host}:#{@port}/#{@service} as #{@db_role} <<EOC\n#{opts}\n#{query}\nEXIT\nEOC}
  else
    command = %{su - #{@su_user} -c "env ORACLE_SID=#{@service} #{bin} / as #{@db_role} <<EOC\n#{opts}\n#{query}\nEXIT\nEOC"}
  end
  cmd = inspec.command(command)
  out = cmd.stdout + "\n" + cmd.stderr
  if out.downcase =~ /^error/
    # TODO: we need to throw an exception here
    # change once https://github.com/chef/inspec/issues/1205 is in
    warn "Could not execute the sql query #{out}"
    DatabaseHelper::SQLQueryResult.new(cmd, Hashie::Mash.new({}))
  end
  DatabaseHelper::SQLQueryResult.new(cmd, send(p, cmd.stdout))
end

def to_s

def to_s
  'Oracle Session'
end

def verify_query(query)

def verify_query(query)
  # ensure we have a ; at the end
  query + ';' if !query.strip.end_with?(';')
  query
end