How to decode a cookie from the header of a websocket connection handshake? (Ruby) - ruby

I am running a Sinatra app within an EventMachine.run loop and in my ws.onopen method I wish to check the handshake header's cookie to ensure that the incoming request is coming from a registered user of my webapp.
My Sinatra app includes the following:
use Rack::Session::Cookie, :key => COOKIE_KEY,
:path => '/',
:expire_after => 2592000, #30 days
:secret => COOKIE_SECRET
and my ws.onopen method looks like this (trimmed)
ws.onopen { |handshake|
cookie, bakesale = handshake.headers['Cookie'].split('=')
rack_cookie = Rack::Session::Cookie.new(MyApp, {
:key => COOKIE_KEY,
:path => '/',
:expire_after => 2592000, #30 days
:secret => COOKIE_SECRET
})
decoded = rack_cookie.coder.decode(bakesale)
puts "decoded: #{decoded}"
}
The value of cookie matches my COOKIE_KEY just fine, however the value of decoded is nil
How should I decode the incoming cookie data?
-- some time later --
I've changed the above slightly to
ws.onopen { |handshake|
cookie, bakesale = handshake.headers['Cookie'].split('=')
rack_cookie = Rack::Session::Cookie.new(MyApp, {
:key => COOKIE_KEY,
:path => '/',
:expire_after => 2592000, #30 days
:secret => COOKIE_SECRET,
:coder => Rack::Session::Cookie::Base64.new
})
puts rack_cookie.coder.decode(bakesale)
}
and that outputs
?q?[?????ov??????to?Z???294cb6e2b95e9?##v3???#c&F3#SC?CSC#CSs?c3sSCCs?cCm;FI"__FLASH__;F{I" user;FU:Moped::BSO?㣤?&?V7D?B!
which looks like it needs marshalling.
However Marshal.load (rack_cookie.coder.decode(bakesale)) throws an exception, saying dump format error for symbol(0x10)
-- and even more time later --
I also tried rack_cookie.coder.decode(bakesale.split('--').first)
which resulted in
??H?d????=?d:ETI"E7ce599b294cb6e2b95e9?##v3???#c&F3#SC?CSC#CSs?c3sSCCs?cCm;FI"__FLASH__;F{I" user;FU:Moped::BSO?㣤?&?V7D?B!
So as you can see, there is a minor difference, but either way I need to somehow turn that into a valid hash.
Marshal.load(rack_cookie.coder.decode(bakesale.split('--').first)) still results in dump format error for symbol(0x10) either way.
So I feel I'm closer, but no cigar as yet.

The answer is to use Rack::Utils.unencode.
I now have this working
Marshal.load(rack_cookie.coder.decode(Rack::Utils.unescape(bakesale.split('--').first))) decodes perfectly to the hash I need, allowing me to extract the user ID. W00t!
Many thanks to User spastorino over at https://github.com/rack/rack/issues/551 for pointing me in the right direction.

Related

Mailgun::CommunicationError via nginx '301 Moved Permanently' error

I have a Ruby web app that sends email via Mailgun.
My Mailgun account & gem are properly set up and I can send emails manually (via curl, for instance).
The API key and the API base URL (https sandbox domain) are stored in environment variables.
When I attempt to send emails from the app like this:
def initialize(mailer: nil)
#mailer = mailer || Mailgun::Client.new(ENV['MAILGUN_API_KEY'])
end
then:
def call(user)
mailer.send_message(ENV['MAILGUN_SANDBOX'], {from: '...',
to: user.email,
subject: '...',
text: "..."})
end
When I run the app with Sinatra via localhost:xxxx, I get a Mailgun::CommunicationError at /.../... 301 Moved Permanently: ... nginx pointing to this line:
mailer.send_message(ENV['MAILGUN_SANDBOX'], ...
Any idea why that happens? I've researched the issue for hours but couldn't find a clue on what to do next.
Thanks!
I ran into this same issue. If you have already fixed this then hopefully this can help someone else.
I switched over to message builder for ease of use and being able to render my html but I'm pretty sure it will still send with the format you have setup with :text
When I switched over to the proper domain in the .env file I believe it solved my issue. You'll need 2 different domains to use Mailgun. The first is the full domain for your sandbox. ENV['MAILGUN_DOMAIN'] it is the sandbox domain with the full https://api.mailgun.net/v3/sandboxXXXXxxxXXXXXX.mailgun.org to send most of the mail formats.
You'll also need the last half of the full domain to send messages. That's just the sandboxXXXXxxxXXXXXX.mailgun.org which is passed into the MessageBuilder or other message .send_message method. When I had them mixed up or both the same I kept on getting this error. When I switched over to separate the two in my development.rb and some_mailer.rb is when I could send the mail without a problem.
Below is my file setup, for reference. I'm pretty new to all of this but this is how I'm setup and it's working for me so hopefully it helps.
# .env
MAILGUN_DOMAIN='https://api.mailgun.net/v3/sandboxXXXXxxxXXXXXX.mailgun.org'
MAILGUN_SEND_DOMAIN='sandboxXXXXxxxXXXXXX.mailgun.org'
# development.rb
ActionMailer::Base.smtp_settings = {
:authentication => :plain,
:address => "smtp.mailgun.org",
:port => 587,
:domain => "ENV['MAILGUN_DOMAIN']",
:user_name => "ENV['MAILGUN_USERNAME']",
:password => "ENV['MAILGUN_PASSWORD']"
}
# some_mailer.rb
def some_mail_notification(user)
#user = user
mg_client = Mailgun::Client.new ENV['MAILGUN_KEY']
mb_obj = Mailgun::MessageBuilder.new
mb_obj.from "email#testing.com", {'first' => 'Customer', 'last' => 'Support'}
mb_obj.add_recipient :to, #user.email, { 'first' => #user.first_name, 'last' => #user.last_name }
mb_obj.subject "Your Recent Purchase on Some Site"
mb_obj.body_html ("#{render 'some_mail_notification.html.erb'}")
mg_client.send_message("sandboxXXXXxxxXXXXXX.mailgun.org", mb_obj)
end
I left the send_message above to the sandbox domain but you can set that as an environment variable in the .env file.

Set secure session Cookie in Rails 2

I have a Rails 2 application. And I want to set the session cookie to secure. By default it will be http only.To implement the same, I added the :secure=> true in config/initializers/session_store.rb as below:
ActionController::Base.session = {
:key => '_app_session',
:secret => '123.......',
:secure => true
}
But it does not work. However, the same thing works well in Rails 3.
This worked for me in the past. In config/environment.rb:
config.action_controller.session = {
:session_key => '_app_session',
:secret => '123.......',
:secure => true
}

Sending email from simple Sinatra app using Pony

I am building my first portfolio page with Sinatra.
I have a 'textbook' contact page with a straight-forward form containing 'name', 'email' and 'content' fields. When someone submits the form, I want to recieve an email notification.
Pony claims that it can send email via simple 'one-line' of code. I have read the Pony documentation but it is not very detailed in how to set it up.
I don't know if I am not setting it up properly, the code is not right, Pony is not the best tool, or if my development environment is not allowing the mail to be sent.
The code below is supposed to be sending an email from the post method, it is then saving the data to a PostgreSQL database via the save_message method. The data is being persisted correctly.
#server.rb
require 'sinatra'
require 'pony'
require_relative 'model/methods'
get '/contact' do
erb :contact
end
post '/thankyou' do
unless params[:name] == '' || params[:email] == '' || params[:content] == ''
Pony.options = {
:subject => "Portfolio page: Message delivery from #{params[:name]}",
:body => "#{params[:content]}",
:via => :smtp,
:via_options => {
:address => 'smtp.1and1.com',
:port => '587',
:enable_starttls_auto => true,
:user_name => ENV["USER_EMAIL_ADDRESS"],
:password => ENV["SMTP_PASSWORD"],
:authentication => :login,
:domain => 'nterrafranca.com'
}
}
Pony.mail(:to => ENV["DESTINATION_EMAIL_ADDRESS"])
save_message(params[:name], params[:email], params[:content])
end
redirect '/'
end
Pony needs to know how to send the email, not just who it's to, from, what the subject and body are, etc.
From the pony documentation, it will default to use sendmail, otherwise configures SMTP to use localhost. Depending on where this application is running, it's highly likely that sendmail is not available, and that there is no SMTP configured on localhost.
I've used Pony for several applications. Each one, I configure a "noreply#" email address for Pony to use to authenticate for SMTP, therefore using my own domain email (usually Google Apps, or even Gmail) for my SMTP connection. For example:
Pony.options = {
:subject => "Some Subject",
:body => "This is the body.",
:via => :smtp,
:via_options => {
:address => 'smtp.gmail.com',
:port => '587',
:enable_starttls_auto => true,
:user_name => 'noreply#cdubs-awesome-domain.com',
:password => ENV["SMTP_PASSWORD"],
:authentication => :plain, # :plain, :login, :cram_md5, no auth by default
:domain => "localhost.localdomain"
}
}
In the case of a Sinatra app, I perform the exact above code (with the obvious substitutions) right before I call:
Pony.mail(:to => <some_email>)
I've configured Pony multiple times - comment if you still have issues and I'll be glad to help.
If you are using a gmail account with 2-step verification, you must generate an application specific password for the Pony mailer, and NOT use your usual SMTP password.
See https://support.google.com/accounts/answer/185833?hl=en
Insert the application specific password in the place of your usual password.
This is from the Pony project page on Github.

Specify token options for OmniAuth OAuth2 based strategy

I'm creating custom strategy for Nimble.com API. As they're using OAuth, it's pretty simple.
require 'omniauth-oauth2'
module OmniAuth
module Strategies
class Nimble < OmniAuth::Strategies::OAuth2
option :name, "nimble"
option :client_options, {
:site => "https://api.nimble.com",
:authorize_url => '/oauth/authorize',
:token_url => '/oauth/token'
}
# option :access_token_options, {
# :mode => :query,
# :param_name => :access_token
# }
option :provider_ignores_state, true
uid { raw_info['email'] }
info do
{
'uid' => raw_info['email'],
'name' => raw_info['name'],
'email' => raw_info['email']
}
end
extra do
{ 'raw_info' => raw_info }
end
def raw_info
access_token.options[:mode] = :query
access_token.options[:param_name] = :access_token
#raw_info ||= access_token.get('/api/users/myself/', {:parse => :json}).parsed
end
end
end
end
For passing tokens, they need to use access_token parameter in URL. When I specify options in raw_info function directly, as in sample — it's OK.
When I'm trying to specify this options in access_token_options hash (like in commented section) — parameters aren't passing to token. I'm not very good in Ruby, so I didn't figure out from libraries sources — how correctly pass parameters to access_token in OmniAuth OAuth2 descendants.
I'd like to make it "right way", so access_token initialised with correct options, plese someone point me the right way.
Thank you!
I've explored several existing strategies (GitHub, 4SQ), and looks like it's normal practice to directly modify access token options.
So I'll stay with it :)

Getting information from Facebook OmniAuth Authentication Hash

I am a beginner with Ruby and Rails but I managed to use OmniAuth for authentication via Facebook. Everything works fine, I am able to create users and they are able to login with Facebook.
The problem is, I would like to take some of the user data (such as email, profile photo, etc.) and save it.
Going through the README (https://github.com/mkdynamic/omniauth-facebook), I managed to find:
Here's an example Authentication Hash available in request.env['omniauth.auth']:
{
:provider => 'facebook',
:uid => '1234567',
:info => {
:nickname => 'jbloggs',
:email => 'joe#bloggs.com',
:name => 'Joe Bloggs',
:first_name => 'Joe',
:last_name => 'Bloggs',
:image => 'http://graph.facebook.com/1234567/picture?type=square',
:urls => { :Facebook => 'http://www.facebook.com/jbloggs' },
:location => 'Palo Alto, California',
:verified => true
}
}
I tried to do more searching on the Authentication Hash and got this which lists some of the information that can be fetched: https://github.com/intridea/omniauth/wiki/Auth-Hash-Schema
The thing is, I asked for certain permissions. The question is, how do I know what sort of information Facebook is sending? Unfortunately, saying the info is in request.env['omniauth.auth'] does me not much good. How do I fetch the information from here?
I am a real beginner going through the Rails tutorial (http://ruby.railstutorial.org/) but trying to create my own app by trial and error.
request.env['omniauth.auth'] will give you a hash. Check the Ruby docs for what you can do with a hash.
http://ruby-doc.org/core-1.9.3/Hash.html
For any elements that are not always going to be there you can just check for blank? (a Rails convenience method), e.g.
omniauth = request.env['omniauth.auth']
unless omniauth['info']['email'].blank?
send_spam omniauth['info']['email'] # ;)
end
You would probably find the following screencast useful if you haven't seen it already:
http://railscasts.com/episodes/235-omniauth-part-1

Resources