Devise Override RegistrationController issue with resource.active_for_authentication? - ruby

I want to override this controller, in this moment the create method, I already made a little change on it
class RegistrationsController < Devise::RegistrationsController
def create
unless session[:invitation_token].blank?
super
else
redirect_to new_user_session_path and return
end
end
end
after following the flow of sign up the resource is created in the database like the super class define it
def create
build_resource
if resource.save
if resource.active_for_authentication?
set_flash_message :notice, :signed_up if is_navigational_format?
sign_up(resource_name, resource)
respond_with resource, :location => after_sign_up_path_for(resource)
else
set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_navigational_format?
expire_session_data_after_sign_in!
respond_with resource, :location => after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords resource
respond_with resource
end
end
but in some way there is a problem with the method active_for_authetication?
Is throwing false and I can't figure out why I checked all the gem and it says that the method is overridden by other modules the normal definition is
def active_for_authentication?
true
end
but I don't use any of the modules that override it so for some reason it seems like devise its not being able to succeed that validation but the user is stored in the database and I'm signed in after registration.
This is my configuration for register a user.
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :username
end
This is my Initializer for devise
Devise.setup do |config|
config.mailer_sender = "please-change-me-at-config-initializers-devise#example.com"
require 'devise/orm/active_record'
config.case_insensitive_keys = [ :email ]
config.strip_whitespace_keys = [ :email ]
config.skip_session_storage = [:http_auth]
config.stretches = Rails.env.test? ? 1 : 10
config.password_length = 6..15
config.reset_password_within = 6.hours
config.sign_out_via = :delete
end

Related

Set user_id for nested_attribute in devise registration before user exists

The idea is simple. I require a nested attribute tag for user registration. tag requires a user_id.
the view
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: {role: :form}) do |f| %>
<%= f.fields_for :tags, resource.tags.build do |a| %>
<%= a.text_field :tagname %>
<% end %>
<% end %>
tag migration
class CreateTags < ActiveRecord::Migration
def change
create_table :tags do |t|
t.string :tagname
t.references :user, index: true
t.timestamps
end
add_index :tags, :tagname, unique: true
end
end
the tag model
class Tag < ActiveRecord::Base
belongs_to :user
validates_presence_of :user_id
validates_uniqueness_of :tagname
end
the user model
class User < ActiveRecord::Base
has_many :tags, autosave: true, dependent: :destroy
accepts_nested_attributes_for :tags
end
strong parameters
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(
:tags_attributes => [:id, :user_id, :tagname]
) }
The save feature is untouched at the moment. The form passes the nested attribute :tagname just fine. But I've been unable to get the "would be" user_id from resource.
I've already looked for hours online for any answer to this. None has appeared, but the idea that the nested attribute should be saved after the initial user object is saved sounds like a workable solution. But then it's no longer handled as a nested attribute.
Help is appreciated! Thanks!
Solution
Alright I solved it. I bastardised my devise registrations controller a bit, but it works.
First I removed user_id verification from my tag model.
class Tag < ActiveRecord::Base
belongs_to :user
validates_uniqueness_of :tagname
end
I then proceeded with modifying the devise registration controller.
def create
build_resource(sign_up_params)
# added code begins here
#tag = nil
#tag.define_singleton_method(:valid?) {false}
if params["user"].has_key? "tags_attributes"
#tag = Tag.new(params["user"].delete("tags_attributes").values.first)
if #tag.valid?
resource_saved = resource.save # original line of code
else
resource.errors.add(*#tag.errors.first)
flash.delete :tagname_error
set_flash_message :alert, :tag_taken if is_flashing_format?
end
else
set_flash_message :alert, :need_tag if is_flashing_format?
end
# end added code
yield resource if block_given?
if resource_saved
if resource.active_for_authentication?
set_flash_message :notice, :signed_up if is_flashing_format?
sign_up(resource_name, resource)
# This is where current_user has been created and resource.id == current_user.id
# begin added code
#tag.user_id= resource.id
# end added code
respond_with resource, location: after_sign_up_path_for(resource)
else
set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_flashing_format?
expire_data_after_sign_in!
respond_with resource, location: after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords resource
#validatable = devise_mapping.validatable?
if #validatable
#minimum_password_length = resource_class.password_length.min
end
respond_with resource
end
end
I've defined :tag_taken and :need_tag in my devise locales file for translations of the error string.
And everything works!
For the record this is in addition to the existing code in the question.

Using devise current_user

I have a user and an address for example and the relation is as follows:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :address
end
class Address< ActiveRecord::Base
belongs_to :user
end
Now when I create an address object, I want to be able to pass through the current_user id and save it to the address model, so in my controller I have
before_filter :authenticate_user!
def new
#address= current_user.address.new
end
def create
#address= current_user.address.new(address_params)
if #address.save
redirect_to root_path, notice: 'Address Successfully Created'
else
render action: 'new'
end
end
private
def address_params
params.require(:address).permit(:id, :user_id, :add_1, :add_2, :add_3)
end
I can't access the new action at the moment, as the error I am getting is
undefined method `new' for nil:NilClass
What am I missing?
I think you should do it like this:
def new
#address = current_user.build_address
end
And same in create action
More info here

undefined method `password' for #<User:1123123123>

I'm following this video tutorial and learning how to create authentication from scratch:
http://www.youtube.com/watch?v=O5RDisWr_7Q
Here is my migration file for User:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :email
t.string :password_hash
t.string :password_salt
t.timestamps
end
end
end
And my controller:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(params[:users])
if #user.save
redirect_to root_url, :notice => "Signed up!"
else
render "new"
end
end
end
And finally my Model:
class User < ActiveRecord::Base
attr_accessible :email, :password_hash, :password_salt
before_save :encrypt_password
validates_confirmation_of :password
validates :password, presence: true
validates :email, presence: true
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
end
Now, I think I know why this error is firing; obviously the #user.save call is trying to save the value in password to the password field in the User table, but that field doesn't exist in the database. In the video he mentions that to fix this bug I should just add: attr_accessible :password to my model and it should work, but I get the following error:
NoMethodError in UsersController#create
undefined method `password' for #
app/controllers/users_controller.rb:8:in `create'
Any suggestions? I just would like to take advantage of the validations that come with using a strongly typed model instead of loose html fields.
You have attr_accessible :password_hash, :password_salt, but I think it should be attr_accessible :password together with attr_accessor :password since you need a virtual attribute password on which you work in your encrypt_password method. So:
class User < ActiveRecord::Base
attr_accessible :email, :password
attr_accessor :password
end
attr_accessor creates the virtual attribute which is not available as a database field (therefore virtual).
attr_accessible is a security mechanism to white-list attributes which are allowed to be set through mass-assignment like you do with User.new(params[:users])

Rails 3 - Devise: users route

I am using devise with CanCan. I am using my user model.
My user index page is localhost:3000/users (It is enabled just for :administrator role).
The problem is that CanCan (or Devise) is not checking for authorization on this route. All other routes (i.e. localhost:3000/tasks) are being checked. i.e. If I logout of the system and type the users index page it displays its content. If I type the tasks route it redirects me to the login screen (correct behaviour).
I think that this is happening because of Devise's routes.
My simplified User model is:
class User < ActiveRecord::Base
has_and_belongs_to_many :roles
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :name, :role_ids, :role, :email, :password, :password_confirmation, :remember_me
def role?(role_check)
self.roles.each do |role|
return true if (role.name.eql? role_check.to_s.humanize )
end
return false
end
def role=(role_id)
self.roles.clear
self.roles << Role.find(role_id)
end
def role
self.roles.first unless self.roles.length == 0
end
end
My Routes are the following:
devise_for :users
resources :users
devise_for :users, :controllers => { :registrations => "users/registrations" }
My ability.rb is the following (I havenĀ“t defined yet, it is allowing all):
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user
if user.role? :administrator
can :manage, :all
elsif user.role? :department_header
can :manage, :all
elsif user.role? :staff
can :manage, :all
end
end
end
How can I fix this?
Thanks!
My problem was on the following line:
load_and_authorize_resource :only => [:show,:new,:destroy,:edit,:update]
I have to include :index

"undefined method" for a rails model

I am using Devise with rails and i want to add a method "getAllComments", so i write this :
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :city, :newsletter_register, :birthday, :postal_code,
:address_complement, :address, :lastname, :firstname, :civility
has_many :hotels_comments
class << self # Class methods
def getAllComments
true
end
end
end
And in my controller :
def dashboard
#user = current_user
#comments = #user.getAllComments();
end
And when i go to my url, i got
undefined method `getAllComments' for #<User:0x00000008759718>
What i am doing wrong?
Thank you
Because getAllComments is a class method and you are attempting to access it as an instance method.
You either need to access it as:
User.getAllComments
or redefine it as an instance method:
class User < ActiveRecord::Base
#...
def getAllComments
true
end
end
def dashboard
#user = current_user
#comments = #user.getAllComments
end
As I can see, you make getAllComments as class method through addition it to eigenclass. And you try to call this method from instance.
Content of class << self means class method. It could be shortened as def self.getAllComments
You should call it User.getAllComments and not #user.getAllComments
The getAllComments() method that you wrote is a class method
So the correct way to call the methods is
#comments = User.getAllComments
But if you really want to scope the getAllComments to the current user, I recommend you write an instance method
class User < ActiveRecord::Base
..
def getAllComments
// comments implementation
end
So that way you can access the getAllComments method like so:
#user = current_user
#comments = #user.getAllComments

Resources