Change phoenix endpoint url host at runtime - phoenix-framework

Does anyone know of a way of changing the :host of the Phoenix application endpoint dynamically on every request?
Specifically to support multiple domains on a single phoenix app, i want to change the host in the endpoint based on the host in the connection object.
Am trying something on the lines of
conn = Map.get_and_update(conn.private.phoenix_endpoint[:url], :host, fn (_) -> "ll.com" end)
or
Keyword.put(conn.private.phoenix_endpoint.config(:url), :host, conn.host)
But am not quite correct.

Wouldn't it just be a value you assign the :to keyword in the redirect?
def index(conn, params) do
redirect conn, to: params[:location] # or whatever
end

The master_proxy package offers some useful tools to support multiple sites.

Related

Simplest method of enforcing HTTPS for Heroku Ruby Sinatra app

I have an app I created on Heroku which is written in Ruby (not rails) and Sinatra.
It is hosted on the default herokuapp domain so I can address the app with both HTTP and HTTPS.
The app requests user credentials which I forward on to an HTTPS call so the forwarding part is secure.
I want to ensure my users always connect securely to my app so the credentials aren't passed in clear text.
Despite lots of research, I've not found a solution to this simple requirement.
Is there a simple solution without changing my app to Ruby rails or otherwise?
Thanks,
Alan
I use a helper that looks like this:
def https_required!
if settings.production? && request.scheme == 'http'
headers['Location'] = request.url.sub('http', 'https')
halt 301, "https required\n"
end
end
I can then add it to any single route I want to force to https, or use it in the before filter to force on a set of urls:
before "/admin/*" do
https_required!
end
Redirect in a Before Filter
This is untested, but it should work. If not, or if it needs additional refinement, it should at least give you a reasonable starting point.
before do
redirect request.url.sub('http', 'https') unless request.secure?
end
See Also
Filters
Request Object
RackSsl::Enforcer

HTTParty with Proxy

I am on heroku trying to access an API that requires my apps ip to be whitelisted. So, I used the heroku add-on proximo to get host/ip for the api's whitelist.
A quick test I set up to test connectivity using HTTParty is failing.
class FakeRequest
include HTTParty
http_proxy 'XX.XXX.XX.XX', 80, 'user', 'pass'
def set_defaults
{:api_key=>"BLARG_BLARG",
:login_name=>"user",
:method => "do_something",
:response_format => "json",
:v => "1.0",
:login_password=>"pass"}
end
def make_post
HTTParty.post "https://test.com", :query => set_defaults
end
end
Going this like:
req = FakeRequest.new
req.make_post
Returns an error message from the api complaining that the source IP is not whitelisted. I looked at the source IP and it is not using the proxy. How can I make HTTParty post using the proxy and not my ISP's IP.
This is the module I built to do just this:
module ProximoParty
PROXIMO = URI.parse(ENV['PROXIMO_URL'])
def self.included(base)
base.send(:include, HTTParty)
base.http_proxy(PROXIMO.host, 80, PROXIMO.user, PROXIMO.password)
end
end
This uses the PROXIMO URL as it is added to your heroku app when you install the addon. So you can drop this file into your app and include ProximoParty into your FakeRequest class instead of HTTParty and it should "just work".
It looks like my code is doing the same thing your code is doing though, so what I'm guessing is that you may not be manually carrying over the credentials properly for proximo.
I ran into a similar problem where it wasn't quite working for me right off the bat. I believe the problem was that I was getting tripped up that there looked to be a "proxy:" protocol in the proximo URL but that was just the username part of the URL.
Anyways, this may or may not help, but please let me know if it does!
It blows my mind how many answers you can get on this question that don't work. Here is how I got it working:
HTTParty::Basement.http_proxy('localhost', 8000, nil, nil)
Put this as a global override in your env.rb file. That's it. Done.
With recent versions of HTTParty it's as simple as using the
http_proxy method:
class Foo
include HTTParty
http_proxy 'http://example.com', 80, 'user', 'pass'
end
HTTParty can use a proxy server address using the following proxy options.
[:+http_proxyaddr+:] Address of proxy server to use
[:+http_proxyport+:] Port of proxy server to use.
[:+http_proxyuser+:] User for proxy server authentication
[:+http_proxypass+:] Password for proxy server authentication.
As you configure HTTParty as an inclusion in your module, you have to call HTTParty method through your class, so:
def make_post
self.class.post "https://test.com", :query => set_defaults
end
I included a link because it include lengthy setup steps. But point taken about the link changing and page becoming invalid. We used squid proxy server on a EC2 AMI and called it a day.
I think you need to call the local httparty, like this:
def make_post
self.post "https://test.com", :query => set_defaults
end

How to pass query parameters through force_ssl

In Rails 3.1 I am using force_ssl when authenticating. This seems to cause a problem when I have a redirect uri appended as a query string because it is stripped in the http -> https protocol switch. If I start on https protocol the query string stays intact and the redirect happens just fine.
Any ideas on how to preserve query strings through the force_ssl protocol switch?
The latest version of rails has it fixed https://github.com/rmm5t/rails/commit/391e6a47dbd46fdce0a472819e8d27792cc91984
If you have to use Rails 3.1 you will have to create your own force ssl before filter
before_filter :check_ssl
def check_ssl
redirect_to({:protocol => 'https'}.merge(params), :flash => flash) unless request.ssl?
end

Different Rails route if IP not in list?

I have a Rails 3.2.2 application which is a simple company intranet, however although there isn't any private information on there it's probably best if it was fairly secure from the outside world.
We do however have people working from home on fairly regular occasions that don't have a VPN setup.
Currently I have a firewall rule that blocks everyone except a list of our teams/branches static IP addresses. The problem with this is when a team member visits the site from home the site never loads because the firewall rejects them. What I would like to do is serve a simple page within the application explaining why they don't have "full" access.
The firewall is serving multiple applications, so I can't put the access denied page on there
I have read a few questions on SO such as Get real IP address in local Rails development environment which show how to get their IP address, but I'm unsure how to alter a default route based on that.
Dae raises a good point in the comments, but just so you know:
http://guides.rubyonrails.org/routing.html#advanced-constraints
class BlacklistConstraint
def initialize
#ips = Blacklist.retrieve_ips
end
def matches?(request)
#ips.include?(request.remote_ip)
end
end
YourApp::Application.routes.draw do
match "*path" => "blacklist#index", :constraints => BlacklistConstraint.new
end
To expand on Robin's Whitelist method, here is my solution using multiple partial whitelisted ip's
class WhitelistConstraint
def initialize
#ips = ["127.0", "10.0.0.0/1"]
end
def matches?(request)
!#ips.select{|req| request.remote_ip.include?(req) }.empty?
end
end

POSTing an HTML form to remote.cgi - written in Ruby?

I am working on a website hosted on microsoft's office live service. It has a contact form enabling visitors to get in touch with the owner. I want to write a Ruby script that sits on a seperate sever and which the form will POST to. It will parse the form data and email the details to a preset address. The script should then redirect the browser to a confirmation page.
I have an ubuntu hardy machine running nginx and postfix. Ruby is installed and we shall see about using Thin and it's Rack functionality to handle the script. Now it's come to writing the script and i've drawn a blank.
It's been a long time and if i remember rightly the process is something like;
read HTTP header
parse parameters
send email
send redirect header
Broadly speaking, the question has been answered. Figuring out how to use the answer was more complicated than expected and I thought worth sharing.
First Steps:
I learnt rather abruptly that nginx doesn't directly support cgi scripts. You have to use some other process to run the script and get nginx to proxy requests over. If I was doing this in php (which in hind sight i think would have been a more natural choice) i could use something like php-fcgi and expect life would be pretty straight forward.
Ruby and fcgi felt pretty daunting. But if we are abandoning the ideal of loading these things at runtime then Rack is probably the most straight forward solution and Thin includes all we need. Learning how to make basic little apps with them has been profoundly beneficial to a relative Rails newcomer like me. The foundations of a Rails app can seem hidden for a long time and Rack has helped me lift the curtain that little bit further.
Nonetheless, following Yehuda's advice and looking up sinatra has been another surprise. I now have a basic sinatra app running in a Thin instance. It communicates with nginx over a unix socket in what i gather is the standard way. Sinatra enables a really elegant way to handle different requests and routes into the app. All you need is a get '/' {} to start handling requests to the virtual host. To add more (in a clean fashion) we just include a routes/script.rb into the main file.
# cgi-bin.rb
# main file loaded as a sinatra app
require 'sinatra'
# load cgi routes
require 'routes/default'
require 'routes/contact'
# 404 behaviour
not_found do
"Sorry, this CGI host does not recognize that request."
end
These route files will call on functionality stored in a separate library of classes:
# routes/contact.rb
# contact controller
require 'lib/contact/contactTarget'
require 'lib/contact/contactPost'
post '/contact/:target/?' do |target|
# the target for the message is taken from the URL
msg = ContactPost.new(request, target)
redirect msg.action, 302
end
The sheer horror of figuring out such a simple thing will stay with me for a while. I was expecting to calmly let nginx know that .rb files were to be executed and to just get on with it. Now that this little sinatra app is up and running, I'll be able to dive straight in if I want to add extra functionality in the future.
Implementation:
The ContactPost class handles the messaging aspect. All it needs to know are the parameters in the request and the target for the email. ContactPost::action kicks everything off and returns an address for the controller to redirect to.
There is a separate ContactTarget class that does some authentication to make sure the specified target accepts messages from the URL given in request.referrer. This is handled in ContactTarget::accept? as we can guess from the ContactPost::action method;
# lib/contact/contactPost.rb
class ContactPost
# ...
def action
return failed unless #target.accept? #request.referer
if send?
successful
else
failed
end
end
# ...
end
ContactPost::successful and ContactPost::failed each return a redirect address by combining paths supplied with the HTML form with the request.referer URI. All the behaviour is thus specified in the HTML form. Future websites that use this script just need to be listed in the user's own ~/cgi/contact.conf and they'll be away. This is because ContactTarget looks in /home/:target/cgi/contact.conf for the details. Maybe oneday this will be inappropriate, but for now it's just fine for my purposes.
The send method is simple enough, it creates an instance of a simple Email class and ships it out. The Email class is pretty much based on the standard usage example given in the Ruby net/smtp documentation;
# lib/email/email.rb
require 'net/smtp'
class Email
def initialize(from_alias, to, reply, subject, body)
#from_alias = from_alias
#from = "cgi_user#host.domain.com"
#to = to
#reply = reply
#subject = subject
#body = body
end
def send
Net::SMTP.start('localhost', 25) do |smtp|
smtp.send_message to_s, #from, #to
end
end
def to_s
<<END_OF_MESSAGE
From: #{#from_alias}
To: #{#to}
Reply-To: #{#from_alias}
Subject: #{#subject}
Date: #{DateTime::now().to_s}
#{#body}
END_OF_MESSAGE
end
end
All I need to do is rack up the application, let nginx know which socket to talk to and we're away.
Thank you everyone for your helpful pointers in the right direction! Long live sinatra!
It's all in the Net module, here's an example:
#net = Net::HTTP.new 'http://www.foo.com', 80
#params = {:name => 'doris', :email => 'doris#foo.com'}
# Create HTTP request
req = Net::HTTP::Post.new( 'script.cgi', {} )
req.set_form_data #params
# Send request
response = #net.start do |http|
http.read_timeout = 5600
http.request req
end
Probably the best way to do this would be to use an existing Ruby library like Sinatra:
require "rubygems"
require "sinatra"
get "/myurl" do
# params hash available here
# send email
end
You'll probably want to use MailFactory to send the actual email, but you definitely don't need to be mucking about with headers or parsing parameters.
CGI class of Ruby can be used for writing CGI scripts. Please check: http://www.ruby-doc.org/stdlib/libdoc/cgi/rdoc/index.html
By the way, there is no need to read the HTTP header. Parsing parametres will be easy using CGI class. Then, send the e-mail and redirect.

Resources