How do I set HTTParty configuration parameters on my class dynamically? - ruby

The simple_client.rb file below works perfectly fine against my emulation cas server; however, the casport.rb file (main file of oa-casport OmniAuth strategy) is not setting or passing the headers / format properly. It needs to be dynamically assigned to the class to allow initializer options to be able to create them, but I'm not sure how else to do it besides how I've attempted to do it here. I was fairly certain I had this working at some point, but I can't see any other explanation for why this wouldn't be working given the simplicity of the client file.
Any help is greatly appreciated on figuring out how to best set the format and headers settings for HTTParty within my Casport class dynamically. As it is it just keeps returning the HTML view for that particular user.
simple_client.rb:
### simple_client.rb - works properly w/ parsed XML response
### The cas.dev project is coming from this Github repo:
### https://github.com/stevenhaddox/oa-casport-server
require 'rubygems'
require 'httparty'
require 'awesome_print'
class Casport
include HTTParty
base_uri 'cas.dev/users'
format :xml
headers 'Accept' => 'application/xml'
def self.find_user(id)
get("/#{id}").parsed_response
end
end
user = Casport.find_user(1)
ap user
casport.rb:
# lib/omniauth/strategies/casport.rb
require 'omniauth/core'
require 'httparty'
require 'redis'
require 'uri'
module OmniAuth
module Strategies
#
# Authentication to CASPORT
#
# #example Basic Usage
#
# use OmniAuth::Strategies::Casport, {
# :setup => true
# }
# #example Full Options Usage
#
# use OmniAuth::Strategies::Casport, {
# :setup => true,
# :cas_server => 'http://cas.slkdemos.com/users/',
# :format => 'xml',
# :format_header => 'application/xml',
# :ssl_ca_file => 'path/to/ca_file.crt',
# :pem_cert => '/path/to/cert.pem',
# :pem_cert_pass => 'keep it secret, keep it safe.'
# }
class Casport
include OmniAuth::Strategy
include HTTParty
def initialize(app, options)
super(app, :casport)
#options = options
#options[:cas_server] ||= 'http://cas.dev/users'
#options[:format] ||= 'xml'
#options[:format_header] ||= 'application/xml'
end
def request_phase
Casport.setup_httparty(#options)
redirect(callback_path)
end
def callback_phase
begin
raise 'We seemed to have misplaced your credentials... O_o' if user.nil?
super
rescue => e
redirect(request_path)
# fail!(:invalid_credentials, e)
end
call_app!
end
def auth_hash
# store user in a local var to avoid new method calls for each attribute
# convert all Java camelCase keys to Ruby snake_case, it just feels right!
user_obj = user.inject({}){|memo, (k,v)| memo[k.gsub(/[A-Z]/){|c| '_'+c.downcase}] = v; memo}
begin
user_obj = user_obj['userinfo']
rescue => e
fail!(:invalid_user, e)
end
OmniAuth::Utils.deep_merge(super, {
'uid' => user_obj['uid'],
'user_info' => {
'name' => user_obj['full_name'],
'email' => user_obj['email']
},
'extra' => {'user_hash' => user_obj}
})
end
# Set HTTParty params that we need to set after initialize is called
# These params come from #options within initialize and include the following:
# :ssl_ca_file - SSL CA File for SSL connections
# :format - 'json', 'xml', 'html', etc. || Defaults to 'xml'
# :format_header - :format Header string || Defaults to 'application/xml'
# :pem_cert - /path/to/a/pem_formatted_certificate.pem for SSL connections
# :pem_cert_pass - plaintext password, not recommended!
def self.setup_httparty(opts)
format opts[:format].to_sym
headers 'Accept' => opts[:format_header]
if opts[:ssl_ca_file]
ssl_ca_file opts[:ssl_ca_file]
if opts[:pem_cert_pass]
pem File.read(opts[:pem_cert]), opts[:pem_cert_pass]
else
pem File.read(opts[:pem_cert])
end
end
end
def user
# Can't get user data without a UID from the application
begin
raise "No UID set in request.env['omniauth.strategy'].options[:uid]" if #options[:uid].nil?
#options[:uid] = #options[:uid].to_s
rescue => e
fail!(:uid_not_found, e)
end
url = URI.escape(#options[:cas_server] + '/' + #options[:uid])
# It appears the headers aren't going through properly to HTTParty...
# The URL + .xml works in the application & the url w/out .xml works in standalone file
# Which means somehow the setup with self.setup_httparty isn't kicking in properly :(
ap Casport.get(url+'.xml').parsed_response
begin
cache = #options[:redis_options].nil? ? Redis.new : Redis.new(#options[:redis_options])
unless #user = (cache.get #options[:uid])
# User is not in the cache
# Retrieving the user data from CASPORT
# {'userinfo' => {{'uid' => UID}, {'fullName' => NAME},...}},
#user = Casport.get(url).parsed_response
cache.set #options[:uid], #user
# CASPORT expiration time for user (24 hours => 1440 seconds)
cache.expire #options[:uid], 1440
end
# If we can't connect to Redis...
rescue Errno::ECONNREFUSED => e
#user ||= Casport.get(url).parsed_response
end
#user = nil if user_empty?
#user
end
# Investigate user_obj to see if it's empty (or anti-pattern data)
def user_empty?
is_empty = false
is_empty = true if #user.nil?
is_empty = true if #user.empty?
# If it isn't empty yet, let's convert it into a Hash object for easy parsing via eval
unless #user.class == Hash
is_empty = true
raise "String returned when a Hash was expected."
end
is_empty == true ? true : nil
end
end
end
end

This was apparently working properly, what I failed to do was to provide the header for Content-Type:
...
def self.setup_httparty(opts)
format opts[:format].to_sym
headers 'Accept' => opts[:format_header]
headers 'Content-Type' => opts[:format_header]
...
Once I added that additional line everything kicked in properly.

Related

Facing issues while calling method of module(having object of class , contained in another ruby file)

I am newbie in ruby and working on making performance automation framework using using 'ruby-jmeter' gem (provided by flood.io)
I have made following files structure
Where payload.rb and request.rb contains my common utility methods . Which I am calling from test.rb
(test.rb would be going to be written by QA ppl)
Performance Automation Framework Structure
request.rb (lying under 'common' folder)
require 'ruby-jmeter' #(Consider any 3rd party gem )
require 'rubygems'
module RubyJmeter
class ExtendedDSL < DSL #'ruby-jmeter' class extending
def get_common_headers()
commonHeaderHashMap = {
'tns' => 'urn:',
'Content-Type' => 'text/xml; charset=utf-8',
'Accept-Language' => 'en-US,en;q=0.5',
'Accept-Encoding' => 'gzip, deflate'
}
return commonHeaderHashMap
end
end
end
class Request
def initialize(dsl)
#dsl = dsl
end
def soap_post_web_request(name,rawBody)
endPoint = '/SoapEndPoint'
post name: name, url: endPoint , raw_body: rawBody do
tempHeaderHashMap = get_common_headers.merge( {'SOAPAction' =>
'urn:'+name} )
finalHeaderArray = []
tempHeaderHashMap.each {|key, value|
localHashMap = Hash.new
localHashMap = {:name => key, :value => value}
finalHeaderArray << localHashMap
}
header finalHeaderArray
end # End of soapCall
end #end of soap_post_web_request
# Passes method calls through to the underlying DSL (ruby-jmeter).
def method_missing method, *args, &block
#dsl.__send__ method, *args, &block
end
end
wrappingclasses_under_single_module.rb (lying under 'common' folder)
require 'rubygems'
require 'ruby-jmeter'
require 'require_all'
require_all './'
module PerformanceAutomation
def self.MyRequest
Request.new(self)
end
end
test.rb (lying under 'testflow->simpleflow' folder)
require 'ruby-jmeter' #(Consider any 3rd party gem )
require 'rubygems'
require 'require_all' #(another 3rd party gem)
require_all '../../common/'
include PerformanceAutomation
test name:'JiraAnalyticsPerformanceFlow' do
threads name: 'NoOfUsers',scheduler: false,continue_forever: false,
count: 1 do
PerformanceAutomation.MyRequest.soap_post_web_request('soapRequestmethodName',rawdata)# End of Soap request 'soapRequestmethodName'
end # End of TestPlan
view_results_tree
puts "JMX FILE IS GONNA SAVED # "+Dir.pwd+"/CreatedJMeter_DB2Navigation.jmx"
end.jmx(file: Dir.pwd+"/CreatedJMeter_DB2Navigation.jmx")
When running test.rb , I am getting following error
`<top (required)>': uninitialized constant PerformanceAutomation (NameError)
Edit (It is working fine now)
Updated common utility files by using
request.rb
module RubyJmeter
class ExtendedDSL < DSL
def get_admin_common_headers()
commonHeaderHashMap = {
'X-XSRF-TOKEN' => '${COOKIE_XSRF-TOKEN}',
'Content-Type' => 'text/xml; charset=utf-8',
'Accept-Language' => 'en-US,en;q=0.5',
'Accept-Encoding' => 'gzip, deflate'
}
return commonHeaderHashMap
end
end
end
module API
class AdminWebService
def initialize(dsl)
#dsl = dsl
end
## Admin request for 'get_space_properties'
def get_space_properties(rawBody)
endPoint = "/AdminService.asmx"
post name: "admin_GetSpaceProperties", url: endPoint ,
raw_body:rawBody do
tempHeaderHashMap = get_admin_common_headers.merge( {'SOAPAction' =>
'http://example.com/GetSpaceProperties'} )
finalHeaderArray = []
tempHeaderHashMap.each {|key, value|
localHashMap = Hash.new
localHashMap = {:name => key, :value => value}
finalHeaderArray << localHashMap
}
header finalHeaderArray
end # End get_space_properties
end
test.rb
require 'rubygems'
require 'ruby-jmeter'
require 'require_all'
require_all '../../../common/'
defaults domain: 'example.com', protocol: 'http', connect_timeout:
'2000', response_timeout: '3000'
cookies policy: 'compatibility', clear_each_iteration: true
#'cookies' is method defined under 'ruby-jmeter'
cache clear_each_iteration: true # 'cache' is method defined under
'ruby-jmeter'
# starting testPlan 'JiraAnalyticsPerformanceFlow'
test name:"testFlowName" do
adminwebservice = API::AdminWebService.new(self)
adminwebservice.get_space_properties(Payload.get_local_payload("get_space_properties.xml"))
#Payload is another common utility class for fetching payload stuff
end
puts "JMX FILE IS GONNA SAVED #
"+Dir.pwd+"/CreatedJMeter_DB2Navigation.jmx"
end.jmx(file: Dir.pwd+"/CreatedJMeter_DB2Navigation.jmx")

Google API Client secrets failing to load issued_at

I'm getting the following error message when I try to do an OAuth2 connection to google.
.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/google-api-client-0.7.1/lib/google/api_client/auth/file_storage.rb:49:in `at': can't convert nil into an exact number (TypeError)
Looking at the source this is trying to read a cached credentials file and is failing to parse an attribute called issued_at.
I initially set up my app with the wrong port in the google developer console. Now I've updated the client_secrets.json but I'm continually getting this error.
My code is trying doing the calendar example from the google site, but converted to use the admin directory API, but it isn't getting beyond the auth step.
Where is this cached value coming from?
require 'rubygems'
require 'google/api_client'
require 'google/api_client/client_secrets'
require 'google/api_client/auth/file_storage'
require 'sinatra'
require 'logger'
enable :sessions
CREDENTIAL_STORE_FILE = "client_secrets.json"
def logger; settings.logger end
def api_client; settings.api_client; end
def directory_api; settings.directory; end
def user_credentials
# Build a per-request oauth credential based on token stored in session
# which allows us to use a shared API client.
#authorization ||= (
auth = api_client.authorization.dup
auth.redirect_uri = to('/oauth2callback')
auth.update_token!(session)
auth
)
end
configure do
log_file = File.open('directory.log', 'a+')
log_file.sync = true
logger = Logger.new(log_file)
logger.level = Logger::DEBUG
client = Google::APIClient.new(
:application_name => 'Ruby Directory sample',
:application_version => '1.0.0')
puts "store file : #{CREDENTIAL_STORE_FILE}"
file_storage = Google::APIClient::FileStorage.new(CREDENTIAL_STORE_FILE)
if file_storage.authorization.nil?
client_secrets = Google::APIClient::ClientSecrets.load
client.authorization = client_secrets.to_authorization
client.authorization.scope = 'https://www.googleapis.com/auth/admin.directory.user'
else
client.authorization = file_storage.authorization
end
# Since we're saving the API definition to the settings, we're only retrieving
# it once (on server start) and saving it between requests.
# If this is still an issue, you could serialize the object and load it on
# subsequent runs.
directory = client.discovered_api('admin', "directory_v1")
set :logger, logger
set :api_client, client
set :directory, directory
end
before do
# Ensure user has authorized the app
unless user_credentials.access_token || request.path_info =~ /\A\/oauth2/
redirect to('/oauth2authorize')
end
end
after do
# Serialize the access/refresh token to the session and credential store.
session[:access_token] = user_credentials.access_token
session[:refresh_token] = user_credentials.refresh_token
session[:expires_in] = user_credentials.expires_in
session[:issued_at] = user_credentials.issued_at
file_storage = Google::APIClient::FileStorage.new(CREDENTIAL_STORE_FILE)
file_storage.write_credentials(user_credentials)
end
get '/oauth2authorize' do
# Request authorization
redirect user_credentials.authorization_uri.to_s, 303
end
get '/oauth2callback' do
# Exchange token
user_credentials.code = params[:code] if params[:code]
user_credentials.fetch_access_token!
redirect to('/')
end
get '/' do
result = api_client.execute(:api_method => directory.users.list)
# # Fetch list of events on the user's default calandar
# result = api_client.execute(:api_method => calendar_api.events.list,
# :parameters => {'calendarId' => 'primary'},
# :authorization => user_credentials)
[result.status, {'Content-Type' => 'application/json'}, result.data.to_json]
end
Change the line to CREDENTIAL_STORE_FILE = "#{$0}-oauth2.json" and then rename the json you downloaded from the Google dashboard to client_secrets.json per convention. CREDENTIAL_STORE_FILE is where your OAuth tokens get stored and is created by the FileStorage instance.

How do I use omniauth in rspec for sinatra?

Shortened version:
Using the omniauth gem for sinatra, I can't get rspec log in to work and keep my session for subsequent requests.
Based on suggestions from http://benprew.posterous.com/testing-sessions-with-sinatra, and turning off sessions, I've isolated the problem to this:
app.send(:set, :sessions, false) # From http://benprew.posterous.com/testing-sessions-with-sinatra
get '/auth/google_oauth2/callback', nil, {"omniauth.auth" => OmniAuth.config.mock_auth[:google_oauth2] }
# last_request.session => {"uid"=>"222222222222222222222", :flash=>{:success=>"Welcome"}}
# last_response.body => ""
follow_redirect!
# last_request.session => {:flash=>{}}
# last_response.body => Html for the homepage, which is what I want
How do I get rspec to follow the redirect and retain the session variables? Is this possible in Sinatra?
From http://benprew.posterous.com/testing-sessions-with-sinatra, it seems like I'd have to send the session variables on each get/post request that I require login for, but this wouldn't work in the case of redirects.
The details:
I'm trying to use the omniauth gem in sinatra with the following setup:
spec_helper.rb
ENV['RACK_ENV'] = 'test'
# Include web.rb file
require_relative '../web'
# Include factories.rb file
require_relative '../test/factories.rb'
require 'rspec'
require 'rack/test'
require 'factory_girl'
require 'ruby-debug'
# Include Rack::Test in all rspec tests
RSpec.configure do |conf|
conf.include Rack::Test::Methods
conf.mock_with :rspec
end
web_spec.rb
describe "Authentication:" do
before do
OmniAuth.config.test_mode = true
OmniAuth.config.add_mock(:google_oauth2, {
:uid => '222222222222222222222',
:info => {
:email => "someone#example.com",
:name => 'Someone'
}
})
end
describe "Logging in as a new user" do
it "should work" do
get '/auth/google_oauth2/'
last_response.body.should include("Welcome")
end
end
end
When trying to authenticate, I get a <h1>Not Found</h1> response. What am I missing?
On the Integration testing page of the omniauth docs, it mentions adding two environment variables:
before do
request.env["devise.mapping"] = Devise.mappings[:user]
request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:twitter]
end
But seems to be for rails only, as I added
request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:google_oauth2]
to my before block in my spec and I get this error:
Failure/Error: request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:google_oauth2]
ArgumentError:
wrong number of arguments (0 for 1)
Edit:
Calling get with
get '/auth/google_oauth2/', nil, {"omniauth.auth" => OmniAuth.config.mock_auth[:google_oauth2]}
seems to give me last_request.env["omniauth.auth"] equal to
{"provider"=>"google_oauth2", "uid"=>"222222222222222222222", "info"=>{"email"=>"someone#example.com", "name"=>"Someone"}}
which seems right, but last_response.body still returns
<h1>Not Found</h1>
A partial answer...
The callback url works better, with the added request environment variables:
get '/auth/google_oauth2/callback', nil, {"omniauth.auth" => OmniAuth.config.mock_auth[:google_oauth2]}
follow_redirect!
last_response.body.should include("Welcome")
However, this doesn't work with sessions after the redirect, which is required for my app to know someone is logged in. Updated the question to reflect this.
Using this gist (originating from https://stackoverflow.com/a/3892401/111884) to store session data, I got my tests to store the session, allowing me to pass the session to further requests.
There might be an easier way though.
Setup code:
# Omniauth settings
OmniAuth.config.test_mode = true
OmniAuth.config.add_mock(:google_oauth2, {
:uid => '222222222222222222222',
:info => {
:email => "someone#example.com",
:name => 'Someone'
}
})
# Based on https://gist.github.com/375973 (from https://stackoverflow.com/a/3892401/111884)
class SessionData
def initialize(cookies)
#cookies = cookies
#data = cookies['rack.session']
if #data
#data = #data.unpack("m*").first
#data = Marshal.load(#data)
else
#data = {}
end
end
def [](key)
#data[key]
end
def []=(key, value)
#data[key] = value
session_data = Marshal.dump(#data)
session_data = [session_data].pack("m*")
#cookies.merge("rack.session=#{Rack::Utils.escape(session_data)}", URI.parse("//example.org//"))
raise "session variable not set" unless #cookies['rack.session'] == session_data
end
end
def login!(session)
get '/auth/google_oauth2/callback', nil, { "omniauth.auth" => OmniAuth.config.mock_auth[:google_oauth2] }
session['uid'] = last_request.session['uid']
# Logged in user should have the same uid as login credentials
session['uid'].should == OmniAuth.config.mock_auth[:google_oauth2]['uid']
end
# Based on Rack::Test::Session::follow_redirect!
def follow_redirect_with_session_login!(session)
unless last_response.redirect?
raise Error.new("Last response was not a redirect. Cannot follow_redirect!")
end
get(last_response["Location"], {}, { "HTTP_REFERER" => last_request.url, "rack.session" => {"uid" => session['uid']} })
end
def get_with_session_login(path)
get path, nil, {"rack.session" => {"uid" => session['uid']}}
end
Sample rspec code:
describe "Authentication:" do
def session
SessionData.new(rack_test_session.instance_variable_get(:#rack_mock_session).cookie_jar)
end
describe "Logging in as a new user" do
it "should create a new account with the user's name" do
login!(session)
last_request.session[:flash][:success].should include("Welcome")
get_with_session_login "/"
follow_redirect_with_session_login!(session)
last_response.body.should include("Someone")
end
end
end

Receiving errors when saving Tweets to a database using Sinatra

I'm using Sinatra, EventMachine, DataMapper, SQLite3 and the Twitter Stream API to capture and save tweets. When I run the application from my command line, it seems to continually fail at tweet 50. If I'm not saving the tweets, it can run seemingly forever.
Below is the app code to capture tweets with 'oscar' in them, which provided a very quick stream. Just enter your twitter username and password and run at the command line.
require 'rubygems'
require 'sinatra'
require 'em-http'
require 'json'
require 'dm-core'
require 'dm-migrations'
USERNAME = '<your twitter username>'
PASSWORD = '<your secret password>'
STREAMING_URL = 'http://stream.twitter.com/1/statuses/filter.json'
DataMapper.setup(:default, ENV['DATABASE_URL'] || "sqlite3://#{Dir.pwd}/db/development.db")
class Tweet
include DataMapper::Resource
property :id, Serial
property :tweet_id, String
property :username, String
property :avatar_url, String
property :text, Text
end
DataMapper.auto_upgrade!
get '/' do
#tweets = Tweet.all
erb :index
end
def rip_tweet(line)
#count += 1
tweet = Tweet.new :tweet_id => line['id'],
:username => line['user']['screen_name'],
:avatar_url => line['user']['profile_image_url'],
:text => line['text']
if tweet.save
puts #count
else
puts "F"
end
end
EM.schedule do
#count = 0
http = EM::HttpRequest.new(STREAMING_URL).get({
:head => {
'Authorization' => [ USERNAME, PASSWORD]
},
:query => {
'track' => 'oscars'
}
})
buffer = ""
http.stream do |chunk|
buffer += chunk
while line = buffer.slice!(/.+\r?\n/)
rip_tweet JSON.parse(line)
end
end
end
helpers do
alias_method :h, :escape_html
end
I'm not sure you can safely mix EM and Sinatra in the same process. You might want to try splitting the Sinatra viewer and the EventMachine downloader into separate programs and processes.

merb_auth_password_slice does not maintain the session

Having integrated merb_auth_password_slice as per the README, I can successfully login as redirect_after_login is being triggered, although session.authenticated? returns false.
Just trying the basic auth strategy for now (password form), can't seem to get it working, any ideas?
My init file:
require 'dm-validations'
dependencies "merb-more", "merb_helpers", "merb-slices", "merb_auth_password_slice"
Merb::BootLoader.before_app_loads do
DataMapper.setup(:default, "sqlite3://config/dev.db")
end
Merb::BootLoader.after_app_loads do
# have already done this
# raise "You must specify a valid openid in Merb.root/config/open_id to use this example app" unless File.exists?(Merb.root / "config" / "open_id")
# # DataMapper.auto_migrate!
# User.create(:login => "admin",
# :password => "password", :password_confirmation => "password",
# :email => "admin#example.com",
# :identity_url => File.read(Merb.root / "config" / "open_id"))
end
Merb::Config.use do |c|
c[:session_secret_key] = 'my key'
c[:session_store] = 'cookie'
end
Setup.rb
class Authentication
def store_user(user)
return nil unless user
user.id
end
def fetch_user(session_info)
User.get(session_info)
end
end # Authentication
# before(nil, :only => [:update, :destroy]) { session.abandon! }
This is the culprit in the slice's session controller

Resources