Send Password Reset to a Different Email - Devise - ruby-on-rails

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.

Related

Emailing Current User in ActionMailer - Rails 5

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?

Should I accept a crypted password with AuthLogic?

Basically as the question asks.
The AuthLogic documentation for verify_password_method states:
The name of the method in your model used to verify the password. This should be an instance method. It should also be prepared to accept a raw password and a crytped password.
I'd like to support this, because it allows me to maintain our system's current masquerading behaviour without storing plain-text passwords like we have been in the past. However, it seems to me like allowing a simple string comparison to the crypted_password in the database is just as bad as storing the regular plain-text passwords.
Am I missing something here, or is there some other way to accept a crypted password in AuthLogic?
For reference, this is how I was going to write the valid_password? method:
def valid_password?(password, check_from_database = nil)
if password == self.crypted_password
true
else
super(password, check_from_database)
end
end
Ok, turns out that there's a much easier way to do this (although it seems horribly undocumented, and didn't turn up with a Google search of how to achieve this).
Authlogic::Session::Base.new(#user, true)
That line allows session creation without checking credentials. Obviously you should be careful with this since it assumes that the user has already identified themselves correctly - for my usage, since there is a check to ensure the current user is the admin user, it's safe.

How to enforce providing password for devise to delete account

how can I make devise enforce getting correct password before canceling registration (deleting account)
You can either:
Do something along the lines of pst's answer: have a text box for :canceled in a form that when saved, cancels the account. Since it would be part of the user model, devise would force the password check upon the update action.
Do it yourself via a button that warns (similar to the delete buttons often in Rails). The controller that receives the request would simply do something like the following (I seem to remember that Devise uses MD5, maybe it's SHA1, SHA2, unsure- see documentation; the key is to use the same type):
if params[:password] == Digest::MD5.hexdigest(params[:password])
cancel_account(…)
…
end
Yeah, the key here is knowing how to encrypt params[:password] to be able to compare it to the current_user.encrypted_password
Older versions of Devise use a password_salt as well. My advice to you would be to look at how devise does this on sign in, and use the same method in your destroy action, or whatever user-facing page you have for that.

My cookie token is strong enough in order to use that for user authentication purposes?

I am running Ruby on Rails 3 and I would know if the code that I am using in order to set the cookie value for user authentication purposes is strong enough.
In my model I have:
require 'digest'
class User < ActiveRecord::Base
...
def make_cookie_id_salt(string)
secure_hash("#{self.id}--#{string}")
end
def secure_hash(string)
Digest::SHA2.hexdigest(string)
end
end
In my controller I have:
cookies.signed[:current_user_id] = { :value => [#user.id, #user.make_cookie_id_salt(#user.id)], :expires => 15.days.from_now }
Is it strong enough? If no, how I can improve that (make an example!)?
Everything that gets put into cookies is stored as plain text.
If you set a cookie, and then check the cookies in your browser you will notice (in your case the cookie name would be current_user_id) that it is represented by a string of characters like: G8gcm9sbCB5b3VyIG93biBhdXRoIHRvIGt... (Not quite plain text, right? It is actually Base64 encoded, but you can easily read it - require('base64'); Base64.decode64(string)).
Rails stores a special _yourapp_session cookie that is used to check the cookies validity. If for example someone/something was trying to modify it, it would get rejected.
Now in your case it doesn't really matter if you try to hash something in the cookie or not.
It is just used for authentication (to look up a user in the database by his id) and you are not storing any unique secret data (Which you should not place in a cookie anyway, but it would be the only reason to hash something)
Of course someone could steal the cookie of a user (if he used a public computer and hasn't cleared his cache, etc.) and log in, but there's no way to prevent that (No matter what kind of hashing was used to obfsucate it)
In conclusion you should be fine with what you have.
Rather than try to create your own, I suggest using the Authlogic gem. In a few minutes of configuration you get a complete authentication solution, including cookies and much more. If you really want to roll your own, install the Authlogic gem and take a look at how they do it.
Devise is another option. It's extremely configurable, pretty DRY, with exhausting wiki.
For now-days I prefer it over Authlogic.

Use Authlogic with Typus

I'm switching to Typus because I prefer its UI over ActiveScaffold and I love the way you can set roles for the admin section. We need that.
However, where ActiveScaffold worked flawlessly with Authlogic, Typus doesn't. I'd like to combine the two anyway, but can't seem to find out how. Typus has very basic password encryption, but I can't write a crypto_provider for it, because it depends on a very simple Sha1-encryption of the salt and the password. Authlogic doesn't support that, because it doesn't send along the actual password.
I'd hate it if we had to use two User models for the front- and backend. I don't need Authlogic to be the authentication method for Typus, but they should both at least be able to compare the password with the crypted one.
Is there anyone out there who has worked around this issue?
Thank you.
I'm not entirely happy with it, but I think I've found an answer to my own question.
I've let Typus create the AdminUser, added a user_id to it and added this method to it and I call it in a before_save:
def sync_user
self.user ||= User.find_by_email(self.email)
if user = self.user
user.email = self.email
user.password = self.password
user.password_confirmation = self.password_confirmation
user.save
end
end
This seems to do the trick for me. I'd love to do it differently, but it works for now.

Resources