For a more recent discussion about a similar topic check this question out.
What\'s the best way to validate whether a particular user has ownership of a website?
Thanks a lot guys. I used a blend of Steve's and Amiel's suggestions to find a solution that works:
In the website model:
class Website < ActiveRecord::Base
require 'net/http'
belongs_to :user
before_create :generate_unique_id
# Validate unless already validated
def verify!
verify_unique_id unless self.verified == true
end
protected
# Generate a random string for unique_id
def generate_unique_id
self.unique_id = ActiveSupport::SecureRandom.hex(10)
end
def verify_unique_id
response = Net::HTTP.start(self.domain, 80) {|http| http.head("/# {unique_id}.html") }
self.verified = true if response.code == "200"
end
end
The controller is nearly unchanged from Steve's suggestion except for the part where the app finds the id of the website:
def verify
@website = Website.find_by_id(params[:website])
@website.verify!
respond_to do |format|
if @website.save && @website.verified == true
flash[:notice] = 'Site verified!'
format.html { redirect_to(websites_path) }
format.xml { head :ok }
else
flash[:notice] = 'Site verification failed!'
format.html { redirect_to(websites_path) }
format.xml { render :status => :unprocessable_entity }
end
end
end
Finally I have an index view of the user's websites (It's ugly but it does the job):
<% for website in @websites %>
<%= link_to "#{website.domain}", website %> |
<% if website.verified? %> VERIFIED |
<% else %> NOT VERIFIED
<%= link_to "Verify your website", verify_website_path(:website => website.id), :id => website.id %>
Verification key: <%= website.unique_id %>
<% end %><% end %>
<%= link_to "Add a website", new_website_path %>
Anyway, I put this through a few manual tests with one of my existing websites and it works without a problem. I still of course have to implement other validations but this was the one I really needed help on. Thanks guys!
Kenji
Special code embedded on the given page (like Google Analytics).
You may also request that user have to provide a URL within the given domain with the specified content. For example user says that under the URL /vali/date/me on domain www.foo.com you can find page generating "VALIDATED" text.
This is is how you could validate a domain using the google subdomain approach in a RESTful style. You will allow a user to create a Site record, which will remain unverified until the user later clicks a link/button to verify the domain at a later time (to allow DNS propagation).
This code is untested but will get you started.
Model:
class Site < ActiveRecord::Base
# schema
# create_table "sites", :force => true do |t|
# t.string "domain"
# t.string "cname"
# t.integer "user_id"
# t.boolean "verified"
# t.datetime "created_at"
# t.datetime "updated_at"
# end
require "resolv"
YOUR_DOMAIN = "example.com"
belongs_to :user
before_create :generate_cname
attr_accessible :domain
…
# Validate unless already validated
def validate!
validate_cname unless self.verifed == true
end
protected
# Generate a random string for cname
def generate_cname
chars = ('a'..'z').to_a
self.cname = 10.times.collect { chars[rand(chars.length)] }.join
end
# Sets verifed to true if there is a CNAME record matching the cname attr and it points to this site.
def validate_cname
Resolv::DNS.open do |domain|
@dns = domain.getresources("#{cname}.#{domain}", Resolv::DNS::Resource::IN::CNAME)
self.verified = !@dns.empty? && @dns.first.name.to_s == YOUR_DOMAIN
end
end
end
Controller
class SitesController < ActionController::Base
# Usual RESTful controller actions
# …
def validate
@site = current_user.sites.find(params[:id])
@site.validate!
respond_to do |format|
if @site.save && @site.verified
flash[:notice] = 'Site verified!'
format.html { redirect_to(@site) }
format.xml { head :ok }
else
flash[:Error] = 'Site verification failed!'
format.html { redirect_to(@site) }
format.xml { render :status => :unprocessable_entity }
end
end
end
end
Put this in routes.rb:
map.resources :sites, :member => { :validate => :put }
I'll leave implementing the views as an exercise for you.
You could confirm it in a manner similar to what google analytics does by having them place some javascript/code on their site that you could use to confirm. Alternatively you could have them create a DNS record that you could check for - both of those would show actual ownership of the site rather than just being part of the domain.
Google Website tools allows you to just upload a file with a very specific name. so, you could require that the user create a file in order for you to verify that they own the website
require 'digest/sha1'
require 'net/http'
# you may want to store a unique value in the website table instead
unique_id = Digest::SHA1.hexdigest("#{website.id}/#{website.created_at}")
response = Net::HTTP.start(website.url, 80) {|http| http.head("/verify_#{unique_id}.html") }
website.verified = true if response.code == "200"