I have just added devise to my shiny new Rails 5 app. All's good and dandy until I try to add a username to the Devise user model. Everything worked until I ran it and went to localhost:3000/users/sign_up, where I was greeted by this error:
undefined method `for' for #<Devise::ParameterSanitizer:0x007fa0dc31c1b0> Did you mean? fork
I have searched the wonderful place of Google for any results, only being given outdated, Rails 4 errors and solutions, the same with searching Stack Overflow itself. I cannot get my mind to find a working solution. I would appreciate help very much. Here is how I prepared for this:
Create the migration: rails generate migration add_username_to_users username:string:uniq
Migrate the database rake db:migrate
Add strong parameters to the application controller devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(..) }
Add username parameters to the Devise views
Restart the server
Is there anything I missed or did wrong?
The .for method is deprecated, from devise 4.1+ .permit method is available
Try .permit. It should work.
devise_parameter_sanitizer.permit(:my_action) { |u| u.permit(..) }
Hope this will help you :)
The problem was well mentioned by #Shadow but this syntax worked for me:
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:first_name, :last_name, :email])
devise_parameter_sanitizer.permit(:account_update, keys: [:first_name, :last_name, :phone, :email, bank_attributes:
[:bank_name, :bank_account]])
end
Related
I have a Rails 3.2.20 app which uses Devise 2.1.2. Everything works fine, but Id like to add the :lockable feature to my project. After 5 failed attempts lock the account for 2 hours.
I've been reading over the Devise documentation and various StackOverflow questions and answers but I have a different setup than some.
I handle sessions in my own controller and also use the strategy to login via email or username from the Devise WiKi.
Here is my current setup:
routes.rb
devise_for :users, :controllers => { :sessions => "my_sessions" }
my_sesssions_controller.rb
class MySessionsController < Devise::SessionsController
skip_before_filter :check_concurrent_session
def create
super
set_login_token
end
private
def set_login_token
token = Devise.friendly_token
session[:token] = token
current_user.login_token = token
current_user.save(validate: false)
end
end
user.rb
attr_accessor :login
def self.find_first_by_auth_conditions(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
else
where(conditions).first
end
end
config/initializers/devise.rb (Excerpt)
config.authentication_keys = [ :login ]
config.case_insensitive_keys = [ :username ]
config.strip_whitespace_keys = [ :username ]
So my question is, considering I handle sessions in my own controller and am also passing authentication via either email or username, how can I implement :lockable to lock out the account for 2 hours after 5 failed attempts?
I'm assuming there's a migration that I will need to generate and run, as well as setting the locking and unlocking strategy.
I read this post How to make devise lockable with number of failed attempts but I'm a bit unsure of the unlock_keys and how that works. Also in this post it talks about generating an index for the unlock_token, but I don't think I will need that since I'll be using a :time unlock_strategy.
Basically, I have Devise working well and I don't want to muddy the water or introduce any bugs into my project.
If anyone can help guide me through setting this up, I'd appreciate your assistance. Sorry if the question is redundant or not clear.
Thanks in advance for your help.
I'm attempting to use Devise (2.2.4), which I'm new to, with the Rails 3.2.13/Ruby 2.0.0p195 app I'm building. I turned scoped_views on because I want to have my own separate users and admins views. And I created my own Users::RegistrationsController which seems to be doing what I want it to. I've just added my own Users::SessionsController, which is where I've hit problems.
I straight copied over a couple of action methods from the Devise::SessionsController source as a first step, planning to modify them once they were working (my controller code is at the bottom of this post). But my 'new' method is failing, when called, with a NameError because `sign_in_params' is apparently undefined.
Well, that seems pretty strange because I'm inheriting from Devise::SessionsController, and when I look at the source for that on GitHub, there's the sign_in_params defined in the protected section at the bottom. So I decided to investigate whether my controller is inheriting correctly from Devise::SessionsController - and it certainly seem to be. I can list out all the inherited methods, just not that one missing one. So I ended up running the following piece of code in the Rails Console:
(Devise::SessionsController.new.methods - DeviseController.new.methods).each {|m| puts m}
And it produces the following output:
_one_time_conditions_valid_68?
_one_time_conditions_valid_72?
_callback_before_75
_one_time_conditions_valid_76?
new
create
destroy
serialize_options
auth_options
If I ignore the underscored methods, the remainder are all those methods defined in the Devise::SessionsController source except sign_in_params. I can't see how anything I've written can be deleting that method, and I can't think what else to try. Google is silent on this problem, so I assume I'm doing something uniquely foolish, but I can't work out what. Any suggestions please? And might someone else try running that bit of Rails Console code to see what they get?
class Users::SessionsController < Devise::SessionsController
prepend_before_filter :require_no_authentication, :only => [ :new, :create ]
prepend_before_filter :allow_params_authentication!, :only => :create
prepend_before_filter { request.env["devise.skip_timeout"] = true }
# GET /resource/sign_in
def new
self.resource = resource_class.new(sign_in_params)
clean_up_passwords(resource)
respond_with(resource, serialize_options(resource))
end
# POST /resource/sign_in
def create
self.resource = warden.authenticate!(auth_options)
set_flash_message(:notice, :signed_in) if is_navigational_format?
sign_in(resource_name, resource)
respond_with resource, :location => after_sign_in_path_for(resource)
end
end
I think you are using code from a devise version compatible with Rails 4 on a rails 3 application.
sign_in_params is a method to be used with strong parameters. A gem used in rails 4.
If you check the controller on devise version 2.2. https://github.com/plataformatec/devise/blob/v2.2/app/controllers/devise/sessions_controller.rb
You will see that there is no sign_in_params method.
Check which version of devise you are using and copy the code based on that devise version in your controller, rather than the latest code from github.
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
I'm in the process of upgrading to Rails 3.1.0 from 3.0.10, and I'm using Devise for authentication.
I have a number of functional tests that require me to set a single session variable. Stuff along the lines of:
test "new clears current_lesson_id from session" do
get :new, nil, {'current_lesson_id' => '234234'}
assert_response :success
assert_nil session[:current_lesson_id]
end
This was failing as my session info was clobbering the devise authentication session data, so my previous solution was to merge with the default session:
test "new clears current_lesson_id from session" do
get :new, nil, authenticated_sesion_with({'current_lesson_id' => '234234'})
assert_response :success
assert_nil session[:current_lesson_id]
end
def authenticated_session_with(hash)
session.merge(hash)
end
All of this worked fine with rails 3.0.10 (with warden 1.0.4 and devise 1.4.2), but no longer with rails 3.1.0 (and warden 1.0.5, devise 1.4.4). Now I'm getting the following error:
NoMethodError: private method `stringify_keys' called for <ActionController::TestSession:0x000001060db590>
I gather this is because the 'session' object is an instance of ActionController::TestSession, and in rails 3.1.0 there are a bunch of instance variables that can't and shouldn't be 'stringified'.
How should I properly access the devise user information (preferably dynamically) so I can add 'current_lesson_id', etc.?
Thanks much,
try this to fix your error in Rails 3.1
sign_in user
hashy = session['warden.user.user.key'][2]
get :action, nil, {"warden.user.user.key"=>["User", [user.id],hashy]}, nil
Worked for me. Seems to not like the new way Rails handles TestSessions.
I think you better do:
def valid_session
my_session_values = {:some_key => :some_value}
session.to_hash.merge my_session_values
end
I'm fairly new to rails and TDD (as will no doubt be obvious from my post) and am having a hard time wrapping my brain around Rspec and FactoryGirl.
I'm using Rails 3, rspec and factory girl:
gem 'rails', '3.0.3'
# ...
gem 'rspec-rails', '~>2.4.0'
gem 'factory_girl_rails'
I have a user model that I've been successfully running tests on during development, but then needed to add an attribute to, called "source". It's for determining where the user record originally came from (local vs LDAP).
In my factories.rb file, I have several factories defined, that look something like the following:
# An alumnus account tied to LDAP
Factory.define :alumnus, :class => User do |f|
f.first_name "Mickey"
f.last_name "Mouse"
f.username "mickeymouse"
f.password "strongpassword"
f.source "directory"
end
I have a macro defined (that's been working up until now) that looks like this:
def login(user)
before(:each) do
sign_out :user
sign_in Factory.create(user)
end
end
I'm calling it in multiple specs like so (example from users_controller_spec.rb):
describe "for non-admins or managers" do
login(:alumnus)
it "should deny access" do
get :index
response.should redirect_to(destroy_user_session_path)
end
end
If I don't specify the "source" attribute, everything works OK, but as soon as I do, I get an error like so when running the test
12) UsersController for non-admins or managers should deny access
Failure/Error: Unable to find matching line from backtrace
NoMethodError:
undefined method `source=' for #<User:0x00000100e256c0>
I can access the attribute no problem from the rails console and the app itself, and it's listed in my attr_accessible in the user model. It's almost as though Rspec is seeing an old version of my model and not recognizing that I've added an attribute to it. But if I put the following line into my user model, the error disappears
attr_accessor :source
... which indicates to me that it is actually looking at the correct model.
Help!
How about running this?
rake db:test:load
[If you added a new attribute you'd need to migrate it to the test database.]
if you don't use schema.rb (e.g. you have set config.active_record.schema_format = :sql)
you should run
rake db:test:prepare