Emailing Current User in ActionMailer - Rails 5 - ruby-on-rails

I have a Ruby on Rails application with multiple environments (development, staging, production). In my staging environment, I want to be able to override all emails being sent from the system to go to the current_user logged in. Everything I've read is telling me I have to keep passing in current_user into my mailer. However, this doesn't seem logical for me as there are hundreds of mailer code to change.
What I'd ideally like to do is to setup an email intercepter, override the mail.to and always send to current_user. Is there a way to do this? Here's what I have so far in my intializers:
if Rails.env.staging?
class OverrideMailRecipient
def self.delivering_email(mail)
mail.to = ['development#xxx.com']
end
end
ActionMailer::Base.register_interceptor(OverrideMailRecipient)
end
This works, but now I'd like to make it so it goes to current user instead of a hard-coded email.
As a bonus, I'd like add to the body of the email the original recipients of the email, so the current user knows who was supposed to receive it in production.
I hope this makes sense, any help is appreciated! :)
I'm using Rails 5.1.4 and Devise for authentication.

The only way I see is to add the current user to some global variable, then you would be able to access it from the Interceptor. Here is an option: https://stackoverflow.com/a/2513456/740394
To add any information you want to the mailers, use a partial with a condition in your layout like you did for the interceptor.
render('sender_info') if Rails.env.staging?

Related

How to select attributes when devise setting current_user?

At this point, I'm not sure how or where the helper current_user is loaded. I'd like to select only certain properties from the user table, but devise to
select * from users table
I want to do something like select (id, email, additional_stuff) from users
I would like to be able to modify the current_user set by devise so I can optimise my application from a security point.
Using Rails version 7.0.4
RUby Version 3.1.3
Devise is build on top of the Warden gem which handles the grunt work of actually authenticating users.
Fetching the user from the database is done by the Warden::Manager.serialize_from_session method which can be reconfigured.
# app/initializers/devise.rb
config.warden do |manager|
manager.serialize_from_session(:users) do |id|
User.select(:id, :email, :additional_stuff)
.find(id)
end
end
However, I'm very sceptical that this will have any real benefits to security and you'll most likely just end up breaking parts of Devise/the rest of your application. For example authenticating the user for password updates may fail unless you load the password digest.
Make sure you have tests covering your whole Devise implementation before you monkey around with it.
It's hard to know what you actually mean by "Certain class in the application use the current_user object, one such class is template creator" . But that smells like a huge gaping security hole in itself. If this is running code that comes from the user it should not have access to the entire view context (for example the current_user method). If the user has access to the actual object then whats preventing them from querying and getting any missing data?
If you really need this feature you should be sandboxing it in a separate renderer (not using Rails build in render methods) which only has access to the context and data you explicitly pass (like a struct or decorator representing a user) which is deemed safe.

Send Password Reset to a Different Email - Devise

I'm using ruby on rails 5 with devise and I need to send a password reset email to a different email than the one stored in my User table. How can this be achieved?
Please note: it is pretty unrecommended way to implement things.It is not within scope of the best practices.
It is dirty and fragile.
But if you really need to achieve it no matter how dirty are the measures, this is it.
Well, the requirement to send the reset instruction to other email is already weird enough. Is it really a last resort?
Anyway,
You've not specified the Devise version but that behaviour was unlikely changed too much so lets take the current master and look how it sends emails:
https://github.com/plataformatec/devise/blob/f39c6fd92774cb66f96f546d8d5e8281542b4e78/lib/devise/mailers/helpers.rb#L31
def headers_for(action, opts)
headers = {
subject: subject_for(action),
to: resource.email,
So, the getter is somewhat hardcoded.
Though, it is possible to create a token and set it as Devise does:
https://github.com/plataformatec/devise/blob/d1948b79d3e933253baa753bd033c92171c0a7d0/lib/devise/models/recoverable.rb#L89
def set_reset_password_token
raw, enc = Devise.token_generator.generate(self.class, :reset_password_token)
self.reset_password_token = enc
self.reset_password_sent_at = Time.now.utc
save(validate: false)
raw
end
And when find in sources how Devise sends it and try to somehow replicate it but using your custom email.
I think the less evil in this case would be just implementing your own mailer for that kind of reset instructions which would use the same URL as Devise does.
Otherwise you would have too much coupling with a current version of Devise.

How can Rails Mailer view update an ActiveRecord record? (update_attribute not persisting)

In my Foo model I have a method
def support_token_create_it_if_none_exists()
self.my_support_token || self.create_a_support_token
end
In all my web code when I want the support token, I call foobar.support_token_create_it_if_none_exists and it returns a token (creating it first if needed).
If, however, in my Mailer view I want to do the same thing, eg, display the support token (and create it on the flyer if it does not yet exist)
...
Your token is #{#foobar.support_token_create_it_if_none_exists}
...
and in the email it displays a support token BUT if a token does not already exists it does NOT persist the token in Activerecord.
I assume there is some "Mailer view local copy" thing going on? If so, is there a way to have the update_attribute work even when in a mailer view?
(The easy workaround is ensure I always have the token created when I invoke the Mailer, but I'd like to understand what is happening so I don't make the same mistake.)
What about checking for the token and creating it before the mailer is called?

ActionMailer workflow

I am newbie in Rails, therefore sorry for silly question. For sending emails I use ActionMailer with Rails 2.3.5. The syntax is following
deliver_maintest1
deliver_maintest2
in model of instance of ActionMailer I have
def maintest1
end
def maintest2
end
Inside definitions I set recipient, subject, headers,...As I understand there is no any explicity defined method mail which is actually sent email. Emails are sent from def maintest1 and maintest2. The problem is before sending email I need to define few counters how many emails were sent thought maintest1 and maintest2. Now take into account I have tens defs like maintest. So I need a common place for all those defs. In your opinion what's the best solution?
Thanks!
On rails 3 and above you could use an observer. These get called after every mail delivery, passing through the message object. You just need to implement a delivered_email class method and register it.
class EmailObserver
def self.delivered_email(message)
# do something with message
end
end
Then, hook it into mail with
Mail.register_observer(EmailObserver)
This doesn't work on rails 2.x, which doesn't use the mail gem (it uses tmail from the ruby standard library.)
On 2.3.x I would try something like
class MyMailer < ActionMailer::Base
def deliver!(mail=#mail)
super
# do your logging here
end
end
You would be calling "Mailer.deliver_maintest" to send the mails out to anyone, to count the nos of times you sent a particular email you just need to keep track of it each time you call "Mailer.deliver_maintest" .
You can store that counter either the database or somewhere. something like this.
// Some lines of code to Update the counter for the mailer
Mailer.deliver_maintest
You can also use a third party email tool like PostMark to send your email ( with them you can associate each email with tags, and I just generally use those tags to keep track of emails sent out ).

RoR testing controllers

I use RoR 3 and i guess something changed in controller's tests.
There is no
def test_should_create_post
but
test "should create user" do
...
end
Is there any decription how is that mapping etc? Because i dont get it.
And second thing. How to program (what assertion) use to test login?
so the test "something here" style is rails way of helping us out. It is fundamentally the same as def test_as_you_want but they helped us out by taking away those nasty '_(underscores)' and wrapping the actual test wording in a string. This change came back, phew... maybe 2.3.x. that fact has to be checked but at least a year and a half ago.
Your second thing is a little more harder to answer man. What plugin are you using, or are you one of those guys who are writing their own auth system?
Either way, check out how the 'famous' auth plugins do it. from Restful Auth to Devise, basically you want test that you can:
Signup for the User account
all of your confirmation emails are sent etc..
Most of these 'cheat' or take the easy way out by passing a helper called signed_in users(:one) for instance. Assuming you are cool and using fixtures.
Basically here is what a helper method looks like if your Auth plugin/gem doesn't have one, like Clearance which didn't have it when i was first writing my tests... not sure if it has it now but it sheds light on how it should look. Notice I've commented out Restful Auth and how he/they did it:
#login user
def login_user(user = users(:one))
#Restful Auth Example
# #request.session[:user_id] = user ? users(user).id : nil
# Clearance
#controller.class_eval { attr_accessor :current_user }
#controller.current_user = user
return user
end
Actually i think i stole this from their shoulda login helper... that's probably what i did. Either way it shows you how to fake login a user.
Now when you are testing, just pass this login_user method to your test when you need a user logged in and start testing the rest of the method without worrying about them actually signing in. That is what the plugin is supposed to do and the 1000 people following it on github would scream if it didn't at least LOG that guy in.
cheers

Resources