I was wondering what rails offers to obfuscate e-mail addresses to protect it from crawlers, spambots and mail harvesters, gathering addresses to send spam.
May be I used wrong keywords, but wasn’t really able to find a gem.
I found a statistic comparing different methods to mask the mail address:
http://techblog.tilllate.com/2008/07/20/ten-methods-to-obfuscate-e-mail-addresses-compared/
I wrote a snippet that combines the top two methods.
The snipped isn’t mature yet, but I like to share it anyway, it might be a starting point for others facing the same issue.
(One next step would be to replace already linked addresses with obscured plain text.)
Before heading on I would like to know what is best practice in rails. This is a common problem and I must have missed a gem dealing with it!?
If I use my approach, what is the best way to integrate/trigger it in my app?
Any kind of before_filter? Before rendering??? Something like that?
Or like I do it currently, calling it in the view as a helper_methode?
It could even be added to string class…
In my application_helper.rb
def obfuscate_emails(content, domain_prefix = 'nirvana', clss = 'maildecode')
# This shall protect emails from spam spiders/crawlers gathering emails from webpages
# Add the following SASS to your Stylesheets
#
# span.maildecode
# direction: rtl
# unicode-bidi: bidi-override
#
# Further more you might want to use Javascript(.erb) to add links to the email addresses like this
#
# $(document).ready(function() {
# function link_emails(subdomain){
# console.log("Find an replace reverse emails, fake subdomain is "+subdomain);
# $(".maildecode").each(function() {
# email = $(this).text().replace('.'+subdomain,'').split("").reverse().join("");
# console.log("- clean email is "+email);
# // $(this).html($(this).text().replace('.'+subdomain,'')); // uncomment if you like to clean up the html a bit
# $(this).wrap('<a href="mailto:'+email+'">');
# });
# }
#
# link_emails('<%= ENV['OBFUSCATE_EMAIL_SUBDOMAIN'] %>');
# });
#
# Thanks to
# http://techblog.tilllate.com/2008/07/20/ten-methods-to-obfuscate-e-mail-addresses-compared/
email_re = /[\w.!#\$%+-]+#[\w-]+(?:\.[\w-]+)+/
content.scan(email_re).each do |mail|
obfuscate_mail = "<span class='#{clss}'>#{mail.reverse.split('#')[0]}<span style='display: none;'>.#{domain_prefix}</span>##{mail.reverse.split('#')[1]}</span>"
content = content.sub(mail, obfuscate_mail)
end
content # use raw(obfuscate_emails(content)) otherwise rails will escape the html
end
Just use the built in mail_to helper that Rails has...
http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-mail_to
mail_to 'email#here.com', 'click to email', :encode => .... # couple of encoding options
NOTE: This does not work in Rails 4 anymore. From the docs: Prior to Rails 4.0, mail_to provided options for encoding the address in order to hinder email harvesters. To take advantage of these options, install the actionview-encoded_mail_to gem. (Thanks to #zwippie)
You could simply replace the #-sign as a simple solution:
"example#example.com".sub("#","-at-") #=> example-at-example.com
"example#example.org".sub("#","{at}") #=> example{at}example.org
see obfuscate emails with ruby to protect against harvesters
Related
Many simmilar Q/A on this topic here and there, but I was unable to find exact solution for my problem. Using Rails 3.0.9 now, and trying to upgrade existing older application(not Rails).
The goal is to send simple email to new clients created by admins.
Have been following this oficial guide (and many others), but with no success.
The issue is, that method(s) defined in this controller, from class 'UserMailer', aren`t recognised from another controller, while class 'UserMailer' itself recognised is(how do I know this, will be explained below):
/app/mailers/user_mailer.rb
class UserMailer < ActionMailer::Base
default :from => "info#xxxxx.sk"
def kokotina # << this is just a dummy method for testing
caf = "ssss"
end
def regMailUsr(nazov, priezvisko, email, pass)
#nazov = nazov
#priezvisko = priezvisko
#email = email
#pass = pass
#url = "http://loyalty2.xxxx.sk"
mail(to: email, subject: 'Vaša registrácia bola dokončená.')
end
end
I have also created View for this mail controller but that is not important right now.
The fragments from clients controller are here:
/app/controllers/clients_controller.rb
# encoding: UTF-8
class ClientsController < ApplicationController
load_and_authorize_resource
.......
def new
#noveHeslo = genHeslo(10) # << I defined this in application_controller.rb and it works
UserMailer.kokotina # << just a dummy method from UserMailer
#client = Client.new(params[:client])
.......
end
.......
def create
.......
if #client.save
#send email to new client:
UserMailer.regMailUsr(params[:client][:fname], params[:client][:lname], params[:client][:email], params[:client][:password]).deliver
.....
end ......
Now how do I know that my class is loaded? If in client controller, I change 'UserMailer' to 'xUserMailer', I will get 'no class or method in ...' error, but without 'x', I get only:
'undefined method `kokotina' for UserMailer:Class'
I also tried to define my methods in UserMailer:Class like this:
def self.kokotina # << this is just a dummy method for testing
caf = "ssss"
end
#or even like this
def self <<
def kokotina # << this is just a dummy method for testing
caf = "ssss"
end
end
#and then tried to invoke this method(s) like this:
UserMailer.new.kokotina
#or simply
kokotina
Strange is, that when I put contents of file '/app/mailers/user_mailer.rb' at the end of 'application_helper.rb' file, just after the end of 'module ApplicationHelper', I get no errors but of course, it won`t work.
Please keep in mind that I have no problem coding in another languages, but this mystic/kryptic rules of Ruby on Rails are still a complete mistery to me and unfortunatelly, I don`t have time or even motivation to read time extensive quides or even books for RoR beginners. I have been coding much more difficult applications and implementations, but this heavily discriminating system is driving me nuts.
Thank you all!
Problem solved!
The trick was, that in '/app/mailers/user_mailer.rb', I had multibyte characters. In mail subject.
So I added:
# encoding: UTF-8
at the very first line of '/app/mailers/user_mailer.rb'
I found this by total accident: later my rails app could not start, and server was simply throwing HTTP 500 error. So no trace, error defining etc.
I found out that multibyte string in:
mail(to: email, subject: 'Vaša registrácia bola dokončená.')
Was responsible for crash. When I removed that string, I noticed one important side effect: my methods became magicaly available for another controller!!!!
So if someone could give me at least one reason to lowe Rails...
I'm trying to connect MoinMoin with my ldap server, however it doesn't work. Am I doing the setting in a proper way?
I'm using MoinMoin from the Ubuntu's repository.
Here I show you my farmconfig.py:
from farmconfig import FarmConfig
# now we subclass that config (inherit from it) and change what's different:
class Config(FarmConfig):
# basic options (you normally need to change these)
sitename = u'MyWiki' # [Unicode]
interwikiname = u'MyWiki' # [Unicode]
# name of entry page / front page [Unicode], choose one of those:
# a) if most wiki content is in a single language
#page_front_page = u"MyStartingPage"
# b) if wiki content is maintained in many languages
page_front_page = u"FrontPage"
data_dir = '/usr/share/moin/data'
data_underlay_dir = '/usr/share/moin/underlay'
from MoinMoin.auth.ldap_login import LDAPAuth
ldap_authenticator1 = LDAPAuth(
server_uri='ldap://192.168.1.196',
bind_dn='cn=admin,ou=People,dc=company,dc=com',
bind_pw='secret',
scope=2,
referrals=0,
search_filter='(uid=%(username)s)',
givenname_attribute='givenName',
surname_attribute='sn',
aliasname_attribute='displayName',
email_attribute='mailRoutingAddress',
email_callback=None,
coding='utf-8',
timeout=10,
start_tls=0,
tls_cacertdir=None,
tls_cacertfile=None,
tls_certfile=None,
tls_keyfile=None,
tls_require_cert=0,
bind_once=True,
autocreate=True,
)
auth = [ldap_authenticator1, ]
cookie_lifetime = 1
This is an indentation issue, auth and cookie_lifetime must be within class Config (so just indent all that by 4 spaces).
I am implementing background email processing with Resque using the resque_mailer gem (https://github.com/zapnap/resque_mailer). I was able to get it to work for all my emails except the ones sent by Devise.
I went through a bunch of SO questions, and blog posts (for instance http://teeparham.posterous.com/send-devise-emails-with-resque) but could not find a way to get it to work.
What are the precise steps to follow to get resque_mailer to work with Devise?
I went through tee's answer and several resources online, but couldn't find a working solution.
After a few days of reading through resque-mailer and devise code, a solution that worked for me. Thanks to tee for gist which put me in right direction.
Assuming your app/mailers/application_mailer.rb looks similar to
class ApplicationMailer < ActionMailer::Base
include Resque::Mailer # This will add a `self.perform` class method, which we will overwrite in DeviseResqueMailer
end
In config/initializers/devise.rb
Devise.parent_mailer = "ApplicationMailer"
Devise.setup do |config|
config.mailer = 'DeviseResqueMailer'
end
In the resource class which uses devise, overwrite the send_devise_notification method to send resource class and id instead of object to prevent marshalling
# app/models/user.rb
protected
def send_devise_notification(notification, *args)
# Based on https://github.com/zapnap/resque_mailer/blob/64d2be9687e320de4295c1bd1b645f42bd547743/lib/resque_mailer.rb#L81
# Mailer may completely skip Resque::Mailer in certain cases - and will fail as we write custom handle in DeviseResqueMailer assuming mails are handled via resque
# So in those cases, don't retain original devise_mailer so things work properly
if ActionMailer::Base.perform_deliveries && Resque::Mailer.excluded_environments.exclude?(Rails.env.to_sym)
# Originally devise_mailer.send(notification, self, *args).deliver
# Modified to ensure devise mails are safely sent via resque
resource_id, resource_class = self.id, self.class.name
devise_mailer.send(notification, {resource_id: resource_id, resource_class: resource_class}, *args).deliver
else
super
end
end
Finally, in app/mailers/devise_resque_mailer.rb, fetch the record again from the database and continue
class DeviseResqueMailer < Devise::Mailer
def self.perform(action, *args)
# Hack to prevent RuntimeError - Could not find a valid mapping for admin.attributes
record_hash = args.shift
record = record_hash["resource_class"].constantize.find(record_hash["resource_id"])
args.unshift(record)
super # From resque-mailer
end
end
I feel this approach is a better than using devise-async as all the mails go through same code path. Its easier to control and overwrite if needed.
I'd take a look at devise-async. Looks like it fits your use case. Devise Async
The readme for Capybara (see Using Capybara with MiniTest::Spec) says that I can do this if I include the module correctly, but it doesn't give any illustrative examples of how... I've tried including the module like this:
class MiniTest::Spec
include Capybara::DSL
end
... to no avail. I keep getting this error:
<main>': undefined methodfeature' for main:Object (NoMethodError)
How can I get it to work as it's written in the commented-out code?
spec/acceptance/api/reward_terms_spec.rb:
require "#{Dir.pwd}/spec/acceptance/acceptance_helper"
# this syntax works...
describe 'reward terms acceptance test' do
include Capybara::DSL
describe '#index' do
specify {
visit '/reward_terms'
# ...
}
end
end
# this syntax doesn't work...
# feature 'RewardTerms', %q{
# In order to get all reward terms available to me
# As an API client
# I want to list all active RewardTerms
# } do
# background do
# set_api_headers
# end
# scenario 'RewardTerm index' do
# visit '/reward_terms'
# ...
# end
# end
spec/acceptance/acceptance_helper.rb:
ENV["RAILS_ENV"] = "test"
require "#{Dir.pwd}/config/environment"
require 'minitest/autorun'
require 'capybara/rails'
def set_api_headers(device_id = 'abcd1234')
header 'Accept', 'application/json'
header 'X-Device-Id', device_id
end
There is a nice description in this post for how you should make MinitTest::Spec run with capybara. There he basically includes the Capybara::DSL into the base class of all the specs as in
class RequestSpec < MiniTest::Spec
include Rails.application.routes.url_helpers
include Capybara::DSL
end
this works rather nicely in our setup, but of course it does not reopen the MiniTest::Spec.
Here's a simple test_helper rig to run functional & integration tests in Rails using spec syntax. Based on a gist by tenderlove, the article mentioned above re: MiniTest with Capybara, as well as a lot of tinkering & source-poring.
https://gist.github.com/1607879
You should add minitest-rails-capybara gem to the Gemfile and add a word "feature" to the end of description as follows:
feature 'RewardTerms feature', %q{
In order to get all reward terms available to me
As an API client
I want to list all active RewardTerms
} do
background do
set_api_headers
end
scenario 'RewardTerm index' do
visit '/reward_terms'
#...
end
end
The special word "feature" is case incensitive, and can be "browser". You can customize it by adding a line to the test_helper.rb:
MiniTest::Spec.register_spec_type(/FooBar\z/i, Capybara::Rails::TestCase)
I would like to alter the Message-ID header that is in the header portion of an email sent from a Ruby on Rails v3 application using ActionMailer.
I am using Sendmail on localhost for mail delivery.
Do I configure this in Sendmail or ActionMailer?
Where do I configure this (if it is ActionMailer): a file in config/ folder or a file in app/mailers/ folder?
Teddy's answer is good, except that if you actually want each message to have a different ID, you need to make the default a lambda. In the first block of code in his answer, it calculates the message-ID once, at init, and uses the same one for every message.
Here's how I'm doing this in my app:
default "Message-ID" => lambda {"<#{SecureRandom.uuid}##{Rails.application.config.mailgun_domain}>"}
... with the domain taken from a custom app config variable (and using SecureRandom.uuid, which is a little more straightforward than a SHA-2 based on the timestamp IMO.)
I usually prefer generating the message-id with a UUID. Assuming you have the uuid gem:
headers['Message-ID'] = "<#{ UUID.generate }#example.com>"
Also you should note that according to RFC 2822 the message-id should be placed inside "<" and ">"
In Rails 4+ (or just Ruby 2.0+) the folowing syntax is working correctly:
default "Message-ID" => ->(v){"<#{Digest::SHA2.hexdigest(Time.now.to_i.to_s)}#yourdomain.com>"}
Tested this with MailCatcher.
I figured this out. The easiest way to do is to use the default method at the top of the mailer class file.
Example:
require 'digest/sha2'
class UserMailer < ActionMailer::Base
default "Message-ID"=>"#{Digest::SHA2.hexdigest(Time.now.to_i.to_s)}#yourdomain.com"
# ... the rest of your mailer class
end
However, I found this difficult to test, so I wrote a private method and used the sent_at time instead of Time.now:
def message_id_in_header(sent_at=Time.now)
headers["Message-ID"] = "#{Digest::SHA2.hexdigest(sent_at.to_i.to_s)}#yourdomain.com"
end
And I simply called that method before calling the mail method. This made it easy to pass a sent_at parameter from my test and verify a match in email.encoded.
#jasoncrawford is almost right. The problem is that the mailgun_domain attribute may not be able at development environment, so it is better to access the ActionMailer configs.
default "Message-ID" => lambda {"<#{SecureRandom.uuid}##{ActionMailer::Base.smtp_settings[:domain]}>"}