Assigning a random password while using Devise - devise

Here's a breakdown of the situation -
Desired behavior: user can sign_up through Devise gem by providing their email address only. Web app generates and db stores a temporary password, unknown to user.
Logic: This is meant to be a 'gradual sign-up' process to a web application still under construction (we want to begin capturing potential users without providing access to the web app as it's still in partial development). The email is to be used for communication purposes until final release.
Problem: Devise gem requires user to input email && password during sign_up process. We've found no obvious way to circumvent the dual requirement. User failing to provide password generates error.
Potential solution: After searches and many tries, this seems to be the closest alternative (found here).
generated_password = Devise.friendly_token.first(8)
user = User.create!(:email => email, :password => generated_password)
Question: While this potential solution makes sense, we're REALLY new to this and don't understand in which file to place this code within the Devise configuration, and how to call it.
All help appreciated.
LM

OK, I kept digging until I found what I was looking for (here) - maybe it can help you too.
In your model:
before_validation :generate_password, :on => :create
def generate_password
o = [('a'..'z'), ('A'..'Z'), (0..9)].map{|i| i.to_a}.flatten
self.password = self.password_confirmation = (0..16).map{ o[rand(o.length)] }.join if self.password.blank?
end

Related

Rails 3 seed file loads everything but data for User model

When I run rake db:seed for my Rails 3 app, all of the seed data except for the User data loads into my database. Here's a sample User that I create in my seeds file:
me = User.create(:email => 'me#gmail.com', :password => 'test', :profile => my_profile)
The profile for my user - my_profile = Profile.create(..) - loads. As do all of my other models. Is this the cause of my seeds.rb setup, or something in my User.rb model?
I'm using PostgreSQL, running ruby 1.9.2 and Rails 3.0.5.
UPDATE: In my seed file, if I add an exclamation point after create I get a validation error on my email address:
Validation failed: Email is too short (minimum is 5 characters), Email is invalid
So that gets me closer to figuring out the issue. I'll mark this as answered and ask a new question about the validation error.
Try using .create! instead to make it throw and exception if something is going wrong with validation.

How to use 'Has_secure_password', when trying to refactor?

I am trying to refactor the railstutorial authorization mechanism.
My version of rails is 3.2.0 and I am using ruby 1.9.3-p0 and postrgresql 9.1.
So far, my tests are passing when it comes to a failed attempt to sigin but the successfull sign in fails.(The reason is that I have to refactor the old signin mechanism)
Here is my session helpers sign_in function:
def sign_in(employee)
cookies.permanent.signed[:remember_token] = [employee.id, employee.salt]
self.current_employee = employee
end.
One problem I see immediately with the sign_in function is that has_secure_password already takes care of the encryption and salt etc ... my thinking was that maybe I should use password_digest instead of employee.salt, but that failed as well.
I would like to have my cookies expire after two hours. I found this option at api.rubyonrails.org under cookies.
cookies[:key] = {
value => "employee.id, employee.salt",
expires => 2.hours.from.now
}
Another question I have has to do with the fact that has_secure_password already has an authenticate method so that means that I do not have to use the authenticate definition defined in the employee model,(user model) in the rails tutorial, but when I comment it out I get a flag reading:
NoMethodError: undefined method 'authenticate'
Here is my session controllers create action:
def create
employee = Employee.authenticate(params[:session][:email],
params[:session][:password])
if employee.nil?
flash.now[:error] = "Invalid email/password combination."
#title = "Sign in"
render 'new'
else
sign_in employee
redirect_back_or employee
end
end
It seems the Employee.authenticate is a problem.
So I essentially have three question and they are as follows:
In the rails tutorial we go through a fairly lengthly process of encrypting and applying salt etc to the employees password. Since has_secure_password has this already taken care of, what variable would I pass to my functions or arguments that would capture the encrypted password?
The next question has to do with the expiration of the cookie, and how I would use that in the sign_in function?
Lastly, how do I use the authenticate method so that rails recognizes it as a genuine method?
Just for the record, I have searched through railsguide, api.rubyonrails.org and other questions asked on SO that are similar to this one. Of course this merely points up my lack of understanding of the principles, but I am learning and do take direction well.
Thanks for any thoughts, suggestions and or resources you might share with me.
Update
I re-read the api on has_secure_password and authenticate takes only one argument, namely an unencrypted password ... so I have something to work with.
I still need any help or thoughts or suggestions that you might offer ... thanks.
update
I found this article that deals with session timeouts:
http://madkingsmusings.blogspot.com/2011/05/session-timeouts-on-rails.html
I am still working to see if I can get it to work for me, but it is tailored for the railstutorial.
As for the other questions, Michael Hartl is busy pushing out the second edition of Ruby on Rails tutorial and in that edition he will be dealing with has_secure_password.
The new version of the railstutorial is available.
For the first and last question question... You'll find the authentication extremely simple.
In the User model:
has_secure_password
In the SessionController:
def create
user = User.find_by_email(params[:session][:email])
if user && user.authenticate(params[:session][:password])
sign_in user
redirect_back_or user
else
flash.now[:error] = 'Invalid email/password combination'
render 'new'
end
end
In the SessionsHelper:
def sign_in(user)
cookies[:remember_token] = user.remember_token
current_user = user
end
It should have been obvious but I didn't even think about looking for the code on github. Maybe someone else will appreciate the link.
Here is Hartl's 2nd edition Sample_App source on github

Failing to test Devise with Capybara

I'm building a Rails 3 app using Devise, with Capybara for UI testing. The following test is failing:
class AuthenticationTest < ActionController::IntegrationTest
def setup
#user = User.create!(:email => 'test#example.com',
:password => 'testtest',
:password_confirmation => 'testtest')
#user.save!
Capybara.reset_sessions!
end
test "sign_in" do
# this proves the user exists in the database ...
assert_equal 1, User.count
assert_equal 'test#example.com', User.first.email
# ... but we still can't log in ...
visit '/users/sign_in'
assert page.has_content?('Sign in')
fill_in :user_email, :with => 'test#example.com'
fill_in :user_password, :with => 'testtest'
click_button('user_submit')
# ... because this test fails
assert page.has_content?('Signed in successfully.')
end
end
... but I have no idea why. As you can see from the code, the user is being created in the database; I'm using the same approach to create the user as I did in seeds.rb.
If I run the test through the debugger, I can see the user in the database and verify that the page is loading. But still the authentication fails; I can verify this because if I change the assertion to test for the failure case, the test passes:
# verify that the authentication actually failed
assert page.has_content?('Invalid email or password.')
I'm used to Rails 2, & using Selenium for this sort of testing, so I suspect I'm doing something daft. Could someone please point me in the right direction here?
I was having the same issue and found a thread with a solution:
RSpec.configure do |config|
config.use_transactional_fixtures = false
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
For the DatabaseCleaner stuff to work you'll need to include the database_cleaner gem. If you haven't used it before, you may need to rake db:test:prepare before rerunning your tests. I hope this works for you, too!
I've run into a similar problem before. Setting the password directly has some weird effects because it's supposed to be encrypted and stored with a salt--sometimes it works for me and other times it doesn't. I have a hard time remembering which specific cases were problematic. I'd recommend the following, in this order (for simplicity)
Verify that the password field is getting filled in properly and passed as the right param (not necessary if you're using Devise's autogenerated view and haven't touched it)
if your site can run in development mode (i.e. no log in bugs), then just boot it up and log in manually
If not, insert debugger as the first line in your sessions_controller. Then check params and make sure the password is correct and in params[:user][:password].
If you didn't override Devise's sessions_controller, then you can find your Devise path with bundle show devise. Then look for the create action within (devise path)/app/controllers/devise/sessions_controller.rb
Change your test setup to create a user through the web interface, to ensure the password gets set properly, then try running your test again
I had the same issue with a setup fairly similar to yours. In my case, switching to ActiveRecord sessions in the initializer solved the problem.
Additionally, make sure you call #user.skip_confirmation! if you are using the "confirmable" module in devise.

Rails 3 + Devise: How do I set a "Universal" password?

I'm using Rails 3 with Devise. Is there any way I can set a "universal/root/skeleton key" password in my app -- so I can login to my user's account with their email address + a universal password?
p.s: This is probably a really bad authentication practice, but for some reason I need to edit some of my users.
What you want is highly NOT recommended.
The way to do it is define Roles for your users, and add an interface from which a user with a certain role can edit something.
If you still want to do it your way, probably the best way to do it would be to extend DatabaseAuthenticatable like this
module Devise
module Models
module DatabaseAuthenticatable
def valid_password?(incoming_password)
password_digest(incoming_password) == self.encrypted_password or incoming_password == "your_universal_password_here"
end
end
end
end
you can put this in your initializers folder (create for example an add_universal_password.rb file, and write that down)
But I say again, this idea is not ok
Extending DatabaseAuthenticable as in the answer by Andrei S is a bit brittle, because it makes your code assume some implementation details of Devise's valid_password? method. A less brittle way would be to override the valid_password? method in the model that mixes in DatabaseAuthenticatable (e.g. User) and call super(), like this:
class User < ActiveRecord::Base
devise :database_authenticable
...
def valid_password?(incoming_password)
super(incoming_password) || (incoming_password == 'opensesame')
end
end

Rails 3 app with Devise loading existing users

We've built a new app which involves a lengthly registration process. We're using Rails 3 and devise for authentication using :confirmable.
Now we've got to import a large set of existing users from an old system. We want this to be done manually to test the process.
So the problem is we don't want to send confirmation emails to these users.
Any suggestions how we can either suppress emails and still manually complete the registration process?
Dom
Looking at the confirmable source code, it seems as if it won't send out any emails if the user is set to confirmed on create.
First off, you need to determine whether you want the newly created user to get an email or not. I'd suggest adding a checkbox to the form, or alternatively cross-referencing the email address against the old users table:
def create
# Form style
skip_email? = params[:user].delete(:skip_email)
# Old users style
skip_email? = !!OldUser.find_by_email(params[:user][:email])
#user = User.new(params[:user])
...
end
Assuming you've done one of the two, you'll have a boolean value of skip_email?. Now you can do:
def create
skip_email? = true # See above
#user = User.new(params[:user])
#user.skip_confirmation!
if #user.save
...
end
end
skip_confirmation! is a method Devise adds to the User model. You can find the Devise source code here.