Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

oracle_session and mssql_session improvement #1857

Merged
merged 9 commits into from
Jun 29, 2017
Merged

Conversation

chris-rock
Copy link
Contributor

@chris-rock chris-rock commented May 29, 2017

TODO:

  • update docs
  • unit tests

@aaronlippold
Copy link
Collaborator

no love for postgres_session? :)

@aaronlippold
Copy link
Collaborator

@chris-rock did you cover the case where the SID and SERVICE_NAME change? With one you need the port/host with the other you don't.

I don't see a service_name param in the current push.

    def db_conn
      if @sid
        "#{@user}/#{@password}@#{@host}:#{@port}/#{@sid}"
      else # using a 'service_name'
        "#{@user}/#{@password}@/#{@service_name}"
      end
    end

With a call like:

 escaped_query = Shellwords.escape(@query)
      cmd = inspec.command("echo -ne \"set heading off\nset echo off\n#{escaped_query}\" | #{@sqlplus_bin} -s #{db_conn_string}")

code:

require 'shellwords'

module Inspec::Resources
  class OracledbSession < Inspec.resource(1)
    name 'oracledb_session'
    desc 'Use the oracledb_session InSpec resource to test commands against an Oracle database'
    example "
      describe oracledb_session(user: 'my_user',
                                password: 'password',
                                host: '192.168.0.10',
                                service_name: 'TESTDB',
                                query: 'SELECT NAME FROM v$database;') do
        it { should be_successful }
        its('output') { should_not match /test/ }
      end

      Required values:
       - user
       - pass
       - sid or service_name
       - query

      Optional values:
       - host (default: localhost)
       - port (default: 1526)
    "

    attr_reader :user, :password, :host, :sid, :sqlplus_bin, :service_name, :port

    def initialize(opts = {})
      @user = opts[:user]
      @host = opts[:host] || 'localhost'
      @port = opts[:port] || '1526'
      @sid = opts[:sid]
      @query = opts[:query]
      @service_name = opts[:service_name]
      @sqlplus_bin = opts[:sqlplus_bin] || 'sqlplus'

      # legacy option was "pass" but we prefer "password" for clarity
      if opts[:pass]
        warn '[DEPRECATED] use `password` option to supply password instead of `pass`'
        @password = opts[:pass]
      else
        @password = opts[:password]
      end

      return skip_resource 'The `oracledb_session` resource does not support Windows yet' if inspec.os.windows?
      return skip_resource 'Username and password are required' if @user.nil? || @password.nil?
      return skip_resource 'You must provide at least an SID and/or a Service Name for the session' if @sid.nil? && @service_name.nil?
    end

    def output
      run_query
      @output
    end

    def errors
      run_query
      @errors
    end

    def successful?
      run_query
      @errors.nil? || @errors.empty?
    end

    def query(q)
      warn '[DEPRECATED] pass query in as an option when creating oracledb_session resource'
      @query = q
      run_query
      self
    end

    def run_query
      return if @query_already_run

      db_conn_string = db_conn
      # escaped_query = @query.gsub(/\\/, '\\\\').gsub(/"/, '\\"')
      escaped_query = Shellwords.escape(@query)
      cmd = inspec.command("echo -ne \"set heading off\nset echo off\n#{escaped_query}\" | #{@sqlplus_bin} -s #{db_conn_string}")
      @output = cmd.stdout
      @errors = cmd.stderr
      @query_already_run = true
    end

    def stdout
      warn '[DEPRECATED] use the `output` method to get the output of the oracle query'
      @output
    end

    def stderr
      warn '[DEPRECATED] use the `errors` method to get the errors from the oracle query'
      @errors
    end

    def db_conn
      if @sid
        "#{@user}/#{@password}@#{@host}:#{@port}/#{@sid}"
      else # using a 'service_name'
        "#{@user}/#{@password}@/#{@service_name}"
      end
    end

    def to_s
      'Oracle Session'
    end
  end
end

@chris-rock
Copy link
Contributor Author

@aaronlippold postgres is another iteration

@chris-rock
Copy link
Contributor Author

@aaronlippold we always use a service name, no sid

@aaronlippold
Copy link
Collaborator

aaronlippold commented May 30, 2017 via email

Copy link
Contributor

@adamleff adamleff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chris-rock I know this is a WIP, but I have some feedback I'm hoping you'll take into consideration during your next iteration.

Gemfile Outdated
@@ -8,6 +8,7 @@ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.2.2')
end

gem 'ffi', '>= 1.9.14'
gem 'htmlentities'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should go in the gemspec since the InSpec core (and therefore users who install the gem) is relying on it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad. Thank you for the reminder!

end
cmd
SQLQueryResult.new(cmd, parse_result(cmd))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parse_result doesn't exist... did you mean parse_csv_result which is below in line 68?

@sqlplus_bin = opts[:sqlplus_bin] || 'sqlplus'
return skip_resource("Can't run Oracle checks without authentication") if @user.nil? or @pass.nil?

return skip_resource "Can't run Oracle checks without authentication" if @user.nil? or @password.nil?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use || instead of or

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

end

def query(q)
escaped_query = q.gsub(/\\/, '\\\\').gsub(/"/, '\\"')
cmd = inspec.command("echo \"#{escaped_query}\" | #{@sqlplus_bin} -s #{@user}/#{@pass}@#{@host}/#{@sid}")
# escape tables with $
escaped_query = escaped_query.gsub('$', "\\$")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use Shellwords.shellescape instead of these gsubs?

if inspec.command(@sqlcl_bin).exist?
bin = @sqlcl_bin
opts = "set sqlformat csv\nSET FEEDBACK OFF"
p = 'parse_csv_result'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use a better variable name here, such as parse_method, and since we're already going to convert it to a symbols, let's set it as a symbol here and avoid the indirection.

parse_method = :parse_csv_result

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I like that

elsif inspec.command(@sqlplus_bin).exist?
bin = @sqlplus_bin
opts = "SET MARKUP HTML ON\nSET FEEDBACK OFF"
p = 'parse_html_result'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment here as on line 50.

entries = row.elements.to_a('/td')

headers.each_with_index { |header, index|
# TODO: once nokogiri is back, we can remove html entities
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there plans to bring nokogiri back in? It has only caused us pain on multiple projects, especially since we support Windows. Can we just remove this TODO and continue to use htmlentities if we need it?

I don't suppose there's a non-HTML markup we can use with Oracle to avoid this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still try to find a way to circumvent htmlentities


headers.each_with_index { |header, index|
# TODO: once nokogiri is back, we can remove html entities
require 'htmlentities'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move this to the top of the file and treat it as first class? I'd rather throw a compile-time error on this when the resource is loaded/eval'd rather than at runtime.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

@results.empty?
end

def stdout
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm nitpicking, but this and stderr really bug me because it feels like we're exposing implementation-level details to the user. They shouldn't care that we're making command calls underneath to get the data.

Can we call these output and errors? That way if we ever change the implementation, the method names will still be truthful.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I agree on that. I am going to remove that since there is no need to expose this right now

@YarNhoj
Copy link

YarNhoj commented Jun 8, 2017

Since all we are doing is shelling out to sqlplus does this resource require that the stand alone sqlplus client be installed, or are we assuming that the user running the scans has all of the environment variables set up that sqlplus requires to run correctly?

As a non Oracle DB user I would have to set LD_LIBRARY_PATH, ORACLE_HOME, and a couple of other env variables before the sqlplus client would function correctly.

Here's a link to the sqlplus documentation from Oracle http://docs.oracle.com/database/121/SQPUG/ch_two.htm#SQPUG012

In my current use case I have an audit user that checks that seed data was populated correctly after install. This user isn't a DB user and won't have all of the env variables set.

@chris-rock
Copy link
Contributor Author

@YarNhoj Right now, this expects that the sqlplus client is installed and configured. Do you see any way around this?

@chris-rock chris-rock force-pushed the chris-rock/database branch 2 times, most recently from 76da1b5 to 8e5de65 Compare June 26, 2017 05:09
Signed-off-by: Christoph Hartmann <chris@lollyrock.com>
Signed-off-by: Christoph Hartmann <chris@lollyrock.com>
Signed-off-by: Christoph Hartmann <chris@lollyrock.com>
Signed-off-by: Christoph Hartmann <chris@lollyrock.com>
Signed-off-by: Christoph Hartmann <chris@lollyrock.com>
@chris-rock
Copy link
Contributor Author

As discussed with @adamleff in #1857 (comment) we are going to remove stdout. Therefore this is a breaking change now.

Signed-off-by: Christoph Hartmann <chris@lollyrock.com>
@adamleff
Copy link
Contributor

It doesn't have to be... why don't we deprecate stdout with a warning now, and we can add another item to our 2.0 depredations issue to remove it then. What do you think?

@chris-rock
Copy link
Contributor Author

@adamleff great idea

Signed-off-by: Christoph Hartmann <chris@lollyrock.com>
@chris-rock chris-rock force-pushed the chris-rock/database branch 2 times, most recently from 91dff67 to 4f92019 Compare June 29, 2017 09:52
@chris-rock chris-rock changed the title WIP: oracle_session and mssql_session improvement oracle_session and mssql_session improvement Jun 29, 2017
Copy link
Contributor

@adamleff adamleff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few last cleanup things that I'm happy to do myself.

module Inspec::Resources
# STABILITY: Experimental
# This resouce needs further testing and refinement
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/resouce/resource

if cmd.exit_status != 0 || out =~ /Sqlcmd: Error/
# TODO: we need to throw an exception here
# change once https://github.com/chef/inspec/issues/1205 is in
# require "pry"; binding.pry
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should remove this line.


module Inspec::Resources
# STABILITY: Experimental
# This resouce needs further testing and refinement
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/resouce/resource

return skip_resource("Can't run Oracle checks without authentication") if @user.nil? or @pass.nil?

return skip_resource "Can't run Oracle checks without authentication" if @user.nil? || @password.nil?
return skip_resource 'You must provide at least an SID and/or a Service Name for the session' if @service.nil?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should eliminate the reference to "SID" from this skip message since we only support service name.

@adamleff adamleff added the Type: Enhancement Improves an existing feature label Jun 29, 2017
Signed-off-by: Christoph Hartmann <chris@lollyrock.com>
Copy link
Contributor

@adamleff adamleff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sweet update, @chris-rock!

@adamleff adamleff merged commit 0839be5 into master Jun 29, 2017
@adamleff adamleff deleted the chris-rock/database branch June 29, 2017 15:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Enhancement Improves an existing feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Inspec oracledb_session resource not connecting to the database.
4 participants