Ruby oci8 fails when using dotenv for password - ruby

I'm attempting to use the dotenv gem to securely store a password for an oci8 connection. My .env file looks like this:
# this file is stored in the same location as config.ru
SCOTT_PASS='tiger'
Here's my config.ru file:
require 'dashing'
require 'dotenv'
Dotenv.load
configure do
set :auth_token, 'YOUR_AUTH_TOKEN'
helpers do
def protected!
# Put any authentication code you want in here.
# This method is run before accessing any resource.
end
end
end
map Sinatra::Application.assets_prefix do
run Sinatra::Application.sprockets
end
run Sinatra::Application
Here's the job which is failing. It fails with a null password error (ORA-01005).
SCHEDULER.every '1m', :first_in => 0 do |job|
conn = OCI8.new('scott', ENV['SCOTT_PASS'], 'orcl')
cursor = conn.parse("SELECT COUNT(*) FROM USER_TABLES")
cursor.exec
r = cursor.fetch
send_event('table_count', { current: r })
cursor.close
conn.logoff
end
I was able to confirm Dotenv.load is working properly as I was able to set other variables successfully, so there seems to be something unique about the oci8 connection.
I'm new to both Ruby and programming, so I might be overlooking something simple. Thanks!

This was a fairly obscure issue, but the root cause was due to a complex password being used (alpha numeric and special characters). The same password worked correctly in a small ruby script, but fails when placed under the rufus scheduler. Switching to a 20+ character password without numbers or special characters resolved the issue. Guessing there might some type of escaping required.

Related

Altering params in Sinatra before blocks

I am trying to alter the request params in a before block in Sinatra.
It seems that when using this syntax, it works:
before do
#params['global-before'] = 'yes'
end
But when using this syntax, it does not work:
before '/:id/test' do
#params['route-before'] = 'yes'
end
Here is a full example:
# test.rb
require 'sinatra'
require 'sinatra/reloader'
set :bind, '0.0.0.0'
set :port, 3000
before do
#params['global-before'] = 'yes'
end
before '/:id/test' do
#params['route-before'] = 'yes'
end
get '/' do
params.to_json
end
get '/:id/test' do
params.to_json
end
Then running:
$ ruby test.rb
$ curl localhost:3000
{"global-before":"yes"}
$ curl localhost:3000/123/test
{"global-before":"yes","id":"123"}
I was expecting to see the params['route-before'] populated as well.
I have tried using request.params instead of #params but that did not work at all.
Can anyone shed some light on this?
Update:
Opened an issue in Sinatra's issue tracker
The route filter goes first, and it has route parameters: {"id"=>"123"}, so this happens:
original, #params = #params, #params.merge(params) if params.any?
where original ends up as {} and #params as {"id"=>"123"}. When the global filter runs, there are no route parameters, so original remains unassigned (nil) and #params is the {} that was originally there.
After the filter processes, in the ensure clause, there is this:
#params = original if original
So global filter skips it, because original is nil, because there were no route parameters. The route filter resets the #params to whatever it was before the filter ran, because original is preserved, because there were route parameters.
I can't say whether this is a bug or an intended behaviour, but it's a "how" at least, if not a "why". It may make sense asking the Sinatra team (and reporting back here with the verdict).
tl;dr: #params are reset to pre-filter state if there are parameters in the filter's path pattern.
Note: you can hack around it by making your own instance variable:
before '/:id/test' do
#route_before = 'yes'
end
get '/:id/test' do
"Did we run route before? #{#route_before}"
end

How to use login credentials from yaml in ruby

I'm new in ruby and I can't move forward from using login cred. from a yml file for a ruby project .I have a basic yml file
login:
urls:
gmail: "https://accounts.google.com/signin"
users:
username: something
password: something_new
I've created a yml.rb with require yml ,and access the yml path & loading the file .
But I don't know how to go through users/username ... in my test.rb :( .I've added in the "it " a variable to store the yml class and at the end i'm trying with
expect data['valid_user']
expect data['login']['urls']['gmail']
expect data['login']['users']['username']
but in the terminal I receive th error "NoMethodError: undefined method `[]' for nil:NilClass "
Update
Here is my yml.rb
require 'rspec'
require 'yaml'
class YamlHelper
#env = {}
def initialize
file = "#{Dir.pwd}path of yml file"
#env = YAML.load_file(file)
end
def get_variables
#env
end
end
Here is my test.rb file
describe 'My behaviour' do
before(:each) do
#browser = Selenium::WebDriver.for :firefox
end
it 'verifies yml login' do
yaml_helper = YamlHelper.new
data = yaml_helper.get_variables
expect data['valid_user']
expect test_data['login']['urls']['gmail']
expect test_data['login']['users']['username']
expect test_data['login']['users']['password']
end
after(:each) do #browser.quit
end
Can anyone take a look ?
thanks in advance
Have a lovely day
It looks like the code is almost there. When I'm debugging this sort of thing I'll often try to distil it down to the most basic test first
Something like:
require 'yaml'
file = "#{Dir.pwd}/data.yml"
data = YAML.load_file(file)
data['valid_user']
#=> nil
data['login']['urls']['gmail']
#=> "https://accounts.google.com/signin"
data['login']['users']['username']
#=> "something"
From the above you can see there's probably a typo in your test.rb file: test_data should most likely be data. Also, your YAML file doesn't contain the valid_user key, so you probably want to remove it from the test, at least for now.
The other two keys load fine.
The error you're seeing NoMethodError: undefined method '[]' for nil:NilClass means that one of the hashes you're variables you're treating like a hash is actually nil. This sort of bug is fairly common when you're diving into nested hashes. It means one of two things:
You've correctly descended into the hash, but the data is not present in the YAML.
The data is present in the YAML, but you're not getting to it correctly.
One change you can make that will make this code a bit more resilient is to replace:
test_data['login']['users']['username']
with:
test_data.dig('login', 'users', 'username')
The latter uses dig, which delves into the data structure and tries to return the value you're after, but if it gets a nil back at any point it'll just return nil, rather than throwing an exception.
Finally, for the test you've pasted here, you don't need the before(:each) or after(:each) blocks – Selenium is only necessary for browser testing.

Customising IRB console for gem

I'd like to extend the default console application that is built as standard with bundle gem by applying some of the IRB config options.
Looking at the documentation, I can see that it should be possible for instance to change the prompt, and this works fine on an interactive session. For example I can play with the displayed prompt like this:
2.1.4 :001 > conf.prompt_mode=:SIMPLE
=> :SIMPLE
>>
?> conf.prompt_mode=:DEFAULT
=> :DEFAULT
irb(main):004:0>
However, I cannot find how to translate this into syntax for use in the console app. For example this script:
require 'irb'
IRB.conf[:PROMPT_MODE] = :SIMPLE
IRB.start
Just starts with the generic configured prompt:
2.1.4 :001 >
I have spent some time trying to find an example use of IRB for a custom repl without loading global defaults, but not found anything I can copy from.
I can see that the undocumented method IRB.setup has something to do with this, it is setting all the config somehow. Is my only option to write my own version of IRB.start that applies my desired config after calling IRB.setup, or is there support for what I want to do built-in but not documented in standard location?
E.g. the following works, but I feel it's a bit heavy handed extending IRB module this way (and also prone to failing if IRB internals change).
require 'irb'
def IRB.custom_start custom_conf = {}
STDOUT.sync = true
IRB.setup(nil)
custom_conf.each do |k,v|
IRB.conf[k] = v
end
if #CONF[:SCRIPT]
irb = IRB::Irb.new(nil, #CONF[:SCRIPT])
else
irb = IRB::Irb.new
end
#CONF[:IRB_RC].call(irb.context) if #CONF[:IRB_RC]
#CONF[:MAIN_CONTEXT] = irb.context
trap("SIGINT") do
irb.signal_handle
end
begin
catch(:IRB_EXIT) do
irb.eval_input
end
ensure
irb_at_exit
end
end
IRB.custom_start :PROMPT_MODE => :SIMPLE
You can apply custom configurations in two ways.
The first one is to use irbrc file. It may be tricky in building console application (calling IRB.start from the ruby file instead of irb from the console).
The second one is the approach that you have described in the post. You can write your own IRB::start method based on the original one. There are exactly the same potential issues as in using undocumented API - it can break in the future with newer versions of irb.
You should think if you really need to build a console application on the top of irb. For example you can solve this problem using Pry. It allows to define configuration before starting interactive session.
require 'irb'
IRB.conf[:PROMPT_MODE] = :SIMPLE
IRB.start
The approach above doesn't work because conf[:PROMPT_MODE] gets over-riden in a method called IRB.init_config here
When IRB.start is called, it calls IRB.setup which in turn calls the method IRB.init_config -- which over-rides conf[:PROMPT_MODE] setting.
Here is one approach which solves the problem (relies on internal knowledge of the implementation).
require 'irb'
module IRB
singleton_class.send(:alias_method, :old_setup, :setup)
def IRB.setup(ap_path)
IRB.old_setup(ap_path)
conf[:PROMPT_MODE] = :SIMPLE
end
end
IRB.start

include file to ruby testScript

I have multiple ruby test cases for selenium-webdriver and all the files are sharing the same user name and password to login to my account. Is there a way to create a global file and include that file in these test cases instead of typing them over and over again, something like #include?
Here is the part of the code that needs to be shared between other test cases:
def setup
#driver = Selenium::WebDriver.for :firefox
#base_url = "http://localhost:3000/"
#driver.manage.timeouts.implicit_wait = 30
#verification_errors = []
#facebook_ID = "xxxxxxxxxx#xxx.xxx"
#facebook_password = "xxxxxxx"
#facebook_receiver_friend = "John Smith"
end
There are multiple ways to do it.
You could use
require 'setup'
where setup.rb is the common file that has all the setting up variables/functions.
You could also use a YAML file. More information here. Where all your config attributes can be defined.
Also put this is at the top of the file $:.unshift '.' . This is so that your test file can "discover" your setup.rb file which is routable from the home directory.
Note - Even though you will save the file as setup.rb you will require as only setup.
I got my answer:
eval File.open('setup.dat').read
This will insert what I have in setup.dat as a text wherever I need it.

Ruby net-ssh-multi: passing a password as a parameter at runtime

I am trying to use net-ssh-multi to run a command on a group of servers. For this taks, ssh-key authentication is not an option; a password has to be passed to each server defined in the session.use lines. Here's the problem, 'net/ssh' can take a password parameter, but 'net/ssh/multi' cannot. What I would like to do is somehting like this:
require 'net/ssh'
require 'net/ssh/multi'
#The necessary data is contained in a Ticket object
my_ticket = Ticket.new
Net::SSH::Multi.start (:password => 'xxxx') do |session|
# define the servers we want to use
my_ticket.servers.each do |serv_id|
session.use "#{my_ticket.user_name}##{serv_id}"
end
# execute commands on all servers
session.exec "uptime"
# run the aggregated event loop
session.loop
end
However, this get me:
file.rb:35:in `start': wrong number of arguments (1 for 2) (ArgumentError) from file.rb:35
I know this is a bit of a n00b question, but I would really appreciate some help.
(http://rubydoc.info/gems/net-ssh-multi/1.1/Net/SSH/Multi)
Turns out the answer is far simpler than I thought it would be. Poring over the documentation, I noticed this in the Class: Net::SSH::Multi::Server docs:
Class: Net::SSH::Multi::Server
Overview:
Encapsulates the connection
information for a single remote
server, as well as the Net::SSH
session corresponding to that
information. You'll rarely need to
instantiate one of these directly:
instead, you should use
Net::SSH::Multi::Session#use.'
So, no class extending or calls to super-classes are necessary. The above can be accomplished with:
require 'net/ssh'
require 'net/ssh/multi'
#The necessary data is contained in a Ticket object
my_ticket = Ticket.new
Net::SSH::Multi.start do |session|
# define the servers we want to use
my_ticket.servers.each do |session_server|
session.use session_server , :user => my_ticket.user_name , \
:password => my_ticket.user_pass
end
# execute commands on all servers
session.exec my_ticket.command_to_do
# run the aggregated event loop
session.loop
end

Resources