Devise and StrongParams - undefined method permit for nil:NilClass - ruby-on-rails

I current use Rails 4 and I move the app from attr_accessible to the StrongParams. So, I have customised Devise controller:
class UsersController < Devise::RegistrationsController
load_and_authorize_resource
#...
def update
unless #user.userable?
if params[:selected_person_type] == I18n::t('activerecord.attributes.user.individual')
redirect_to new_individual_path and return
elsif params[:selected_person_type] == I18n::t('activerecord.attributes.user.legal_entity')
redirect_to new_legal_entity_path and return
end
end
#avatar = params[:avatar_id].present? ? Avatar.find_by(id: params[:avatar_id]) : #user.avatar
if params[:user][:password].blank?
if #user.update_without_password(user_params)
notice = if #user.unconfirmed_email.present? && Date.today == #user.confirmation_sent_at.to_date
t('devise.confirmations.send_instructions')
else
t('views.messages.notices.personal_data_updated')
end
redirect_to edit_user_path(#user), notice: notice and return
end
else
if #user.valid_password?(params[:user][:current_password])
params[:user].delete("current_password")
if #user.update_attributes(user_params) && #user.save
sign_in(#user, bypass: true)
redirect_to edit_user_path(#user), notice: t('views.messages.notices.personal_data_updated') and return
end
else
#user.errors.add(:current_password, :invalid)
end
end
render action: "edit"
end
def create
if resource.save
SystemMailer.send_mail(to: resource.email, body: resource.temp_password, subject: I18n.t('mailers.password.new')).deliver if resource.generate_password == '1'
if request.xhr?
expire_data_after_sign_in!
respond_to do |format|
format.js
end
else
super
end
else
if request.xhr?
clean_up_passwords(resource)
respond_to do |format|
format.js
end
else
super
end
end
end
private
def user_params
if current_user.present?
params.require(:user).permit(:fullname, :about, :username, :email, :current_password, :password, :password_confirmation)
end
end
end
I got the error before:
Failure/Error: post :create, user: attributes_for(:unconfirmed_user)
ActiveModel::ForbiddenAttributesError:
ActiveModel::ForbiddenAttributesError
This is because CanCan isn't so compatible with StrongParams, so i tried this fix in ApplicationController:
class ApplicationController < ActionController::Base
include SimpleCaptcha::ControllerHelpers
include CaptchaHelper
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_filter :cancan_workaround
rescue_from CanCan::AccessDenied do |e|
redirect_to '/', flash: { error: I18n.t('views.messages.notices.access_denied') }
end
private
def cancan_workaround
resource = controller_name.singularize.to_sym
method = "#{resource}_params"
params[resource] &&= send(method) if respond_to?(method, true)
end
end
After that fix i got that error:
UsersController should successfully create user
Failure/Error: post :create, user: attributes_for(:unconfirmed_user)
NoMethodError:
undefined method `permit' for nil:NilClass
# ./app/controllers/users_controller.rb:75:in 'create'
# ./spec/controllers/users_controller_spec.rb:10:in `block (3 levels) in <top (required)>'
# ./spec/controllers/users_controller_spec.rb:9:in `block (2 levels) in <top (required)>'
Here is ./app/controllers/users_controller.rb:75:in 'create' is super call in the action. Any idea how to fix that?

Devise
Devise doesn't work like other Rails input systems - it uses its own backend functionality to process the params. It seems devise automatically permits the email & password params, and its your job to add the other params its parameter_sanitizer
Devise and Strong Parameters
Bottom line is that, according to the Devise documentation, you'll have to use something like this:
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_filter :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << :username
end
end

Ok, i got it. Problem was not in Devise, it was with my permissions:
private
def user_params
if current_user.present?
params.require(:user).permit(:fullname, :about, :username, :email, :current_password, :password, :password_confirmation)
end
end
Test evaluated for non-signed user, so when cancan's hook tried to execute this method:
params[resource] &&= send(method) if respond_to?(method, true)
It received nil, because user wasn't signed-in, so hook transformed :user => { ... } into the :user => nil. So, i fixed it via removing current_user.present?.
private
def user_params
params.require(:user).permit(:fullname, :about, :username, :email, :current_password, :password, :password_confirmation)
end
Not sure how well this solution is secure.

Related

Difficulty in implementing a one to one relationship

I am trying to update the profile of a user, however, I am getting an ArgumentError (wrong number of arguments (given 6, expected 1)): The relation between the user model and user profile model is one-to-one
application_controllers.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
helper_method :current_user, :logged_in?
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
end
def create
#user_profile = UserProfile.new(user_profile_params)
#user_profile.user_id = current_user.id
if #user_profile.save
format.html {redirect_to home_index_path, notice:"Account created successfully" }
else
flash.now.alert = "Oops, couldn't create account. Please make sure you are using a valid email and password and try again."
format.html { render :new }
end
end
private
def user_profile_params
params.require(:first_name, :other_name, :last_name, :phonemumber, :email, :seller_id)
end
Problem is on this line:
def user_profile_params
params.require(:first_name, :other_name, :last_name, :phonemumber, :email, :seller_id)
end
You should use either:
def user_profile_params
params.require(:user_profile).permit(:first_name, :other_name, :last_name, :phonemumber, :email, :seller_id)
end
or
def user_profile_params
params.permit(:first_name, :other_name, :last_name, :phonemumber, :email, :seller_id)
end
Depends on data you are getting from the form. And btw. there is a typo in :phonemumber should probably be :phonenumber.

Session[:user_return_to] getting nil in redirecting back to current page after sign in

I want to redirect back to current page after sign in I have devise already in my rails application. I have followed this tutorial of devise https://github.com/plataformatec/devise/wiki/How-To:-Redirect-back-to-current-page-after-sign-in,-sign-out,-sign-up,-update#storelocation-to-the-rescue.
What is the problem after implementing this is explained below:
I enter URL directly without logging in /tasks/1 at this point this url get saved in session[:user_return_to]
then I am redirected to /users/sign_in at this point value in session[:user_return_to] gets nil because of this I cant redirect to previous page after logging in the application.
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_action :store_user_location!, if: :storable_location?
before_action :authenticate_user!
before_filter :configure_permitted_parameters, if: :devise_controller?
before_filter :prepare_exception_notifier
rescue_from ActionController::RoutingError, with: :controller_error
rescue_from ActiveRecord::RecordNotFound, with: :active_record_error
rescue_from CanCan::AccessDenied do |exception|
render file: "#{Rails.root}/app/views/pages/not_authorized.html.erb", status: 403
end
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:role_id, :username, :first_name, :last_name, :address, :organization_name , :hours_in_day, :organization_id, :job_title])
devise_parameter_sanitizer.permit(:account_update, keys: [:role_id, :username, :first_name, :last_name,:organization_id, :division_id, :department_id, :email, :password, :password_confirmation, :current_password, :job_title, :avatar, :building_id, :location_id])
end
private
def storable_location?
request.get? && is_navigational_format? && !devise_controller? && !request.xhr?
end
def store_user_location!
store_location_for(:user, request.fullpath)
end
def after_sign_in_path_for(resource)
stored_location_for(resource) || root_path
end
def after_sign_out_path_for(resource_or_scope)
new_user_session_path
end
def prepare_exception_notifier
request.env["exception_notifier.exception_data"] = {
current_user: current_user
}
end
def active_record_error(exception)
respond_to do |f|
f.html{ render file: "errors/404", status: 404 }
f.html{ render file: "errors/500", status: 500 }
f.js{ render partial: "errors/ajax_404", status: 404 }
f.js{ render partial: "errors/ajax_500", status: 500 }
end
end
end
Try the following in the application_controller
class ApplicationController < ActionController::Base
after_filter :store_location
before_action :authenticate_user!
private
def store_location
# store last url as long as it isn't a /users path
session[:previous_url] = request.fullpath unless request.fullpath =~ /\/users/
end
def after_sign_in_path_for(resource)
session[:previous_url] || root_path
end
end
it's working on my hand

"Undefined method `concat'" on "devise_parameter_sanitizer.permit"

I'm writing a custom sign-up devise controller, and I'm having trouble adding permitted params due to this error (this is the output from Rspec, but the same error happens manually):
Failure/Error: devise_parameter_sanitizer.permit(:sign_up, keys: [:nome, :password, :password_confirmation, :cnpj, :razao_social, :nome_fantasia, :email, :tipo_entidade_id])
NoMethodError:
undefined method `concat' for #<Proc:0x0055ca9fb2d850>
Did you mean? concern
The full controller:
class Users::RegistrationsController < Devise::RegistrationsController
before_action :configure_sign_up_params, only: [:create]
# before_action :configure_account_update_params, only: [:update]
# POST /resource
def create
user_params = sign_up_params[:user_params]
entidade_params = sign_up_params[:entidade_params]
if !(User.exists?(email: user_params[:email]) || Entidade.exists?(cnpj: entidade_params[:cnpj]))
#entidade = Entidade.new(entidade_params)
#entidade.data_validade = 30.days.from_now
if #entidade.save
#user = User.new(user_params)
#user.entidade_id = #entidade.id
if #user.save
flash[:notice] = 'Usuario criado com sucesso.'
redirect_to root_path
end
end
end
end
protected
# If you have extra params to permit, append them to the sanitizer.
def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up, keys: [:nome, :password, :password_confirmation, :cnpj, :razao_social, :nome_fantasia, :email, :tipo_entidade_id])
end
end
At first glance it seems like a bug in the gem, but no one seems to have this issue - google returns nothing relevant. Is this an error in my code?
I'm not sure if it was the case, but this error can occur if you duplicate the parameters sanitizer, like using it in users controller but also in application controller.
You can see a more detailed explanation here:
GitHub issue closed

Rails password_reset recordnotfound

I am getting an error when I pass in my url to reset my password for example: localhost:3000/password_reset/SADASIJDSIDJ1231231/edit <---- gives me ActiveRecord::RecordNotFound and its pointing at in my password_reset_controller
def edit
#user = User.find_by_password_reset_token!(params[:id])
end
This is my application controller (notice the "include SessionsHelper")
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
#om du lägger till kod här kmr inte sessions funka när du är inloggad wtf???
#tog bort detta protect_from_forgery with: :exception
include SessionsHelper
protect_from_forgery
private
def current_user
#current_user ||= User.find_by_auth_token(cookies[:auth_token]) if cookies[:auth_token]
end
helper_method :current_user
end
This is my password_reset_controller
class PasswordResetsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:email])
user.send_password_reset if user
redirect_to root_url, :notice => "Email sent with password reset instructions."
end
def edit
#user = User.find_by_password_reset_token!(params[:id])
end
def update
#user = User.find_by_password_reset_token!(params[:id])
if #user.password_reset_sent_at < 2.hours.ago
redirect_to new_password_reset_path, :alert => "Password reset has expired."
elsif #user.update_attributes(params.permit![:user])
redirect_to root_url, :notice => "Password has been reset!"
else
render :edit
end
end
end
HOW I SOLVED IT:
So thanks to jorge I knew password_reset_token wasn't generated in the database. So I went back to my model/user.rb
def send_password_reset
generate_token(:password_reset_token)
self.password_reset_sent_at = Time.zone.now
save!
UserMailer.password_reset(self).deliver
end
I added back "save!" it was "save" before. Then I got another error saying password can't be blank.
So I deleted these two lines
# validates :password, length: { minimum: 6 }, presence: true
#validates :password_confirmation, presence: true
Instead I added this line
validates_presence_of :password, :on => :create
Now everything works. Thanks for your patience jorge. I owe u one big.
Did you check in the database that the a user exists with password reset token "SADASIJDSIDJ1231231"?
Looks like following line is not finding a user:
#user = User.find_by_password_reset_token!(params[:id])
Please include your User class - the send_password_reset method may not be saving the token correctly.

CanCan load_and_authorize_resource triggers Forbidden Attributes

I have a standard RESTful controller that uses strong parameters.
class UsersController < ApplicationController
respond_to :html, :js
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def edit
#user = User.find(params[:id])
end
def create
#user = User.new(safe_params)
if #user.save
redirect_to #user, notice: t('users.controller.create.success')
else
render :new
end
end
def update
#user = User.find(params[:id])
if #user.update_attributes(safe_params)
redirect_to #user, notice: t('users.controller.update.success')
else
render :edit
end
end
def destroy
#user = User.find(params[:id])
if current_user != #user
#user.destroy
else
flash[:error] = t('users.controller.destroy.prevent_self_destroy')
end
redirect_to users_url
end
private
def safe_params
safe_attributes =
[
:first_name,
:last_name,
:email,
:password,
:password_confirmation,
]
if current_user.is?(:admin)
safe_attributes += [:role_ids]
end
params.require(:user).permit(*safe_attributes)
end
end
In my config/initializers I have the file strong_parameters.rb
ActiveRecord::Base.send(:include, ActiveModel::ForbiddenAttributesProtection)
When I add a simple call to CanCan's load_and_authorize_resource I get
1) UsersController POST create with invalid params re-renders the 'new' template
Failure/Error: post :create, user: #attr
ActiveModel::ForbiddenAttributes:
ActiveModel::ForbiddenAttributes
# ./spec/controllers/users_controller_spec.rb:128:in `block (4 levels) in <top (required)>'
Where #attr in the test is defined as
before(:each) do
#attr =
{
first_name: "John",
last_name: "Doe",
email: "user#example.com",
password: "foobar",
password_confirmation: "foobar"
}
end
In the tests I have it all setup properly to login the user and give them the necessary roles for being an administrator so I know it's not that. I don't know why this is causing ForbiddenAttributes to trigger. I'm sure it's something simple I've overlooked. Has anyone else encountered this problem and found a solution to it?
I believe this is because CanCan will use its own getter method for the requested resource if you don't pre-load it with a before_filter. So you could add this to the controller and it should work:
class UsersController < ApplicationController
before_filter :new_user, :only => [:new, :create]
load_and_authorize_resource
def new_user
#user = User.new(safe_params)
end
end
(And then do the same for the edit/update actions.)
before_filter do
params[:user] = safe_params
end
load_and_authorize_resource

Resources