Sterling Duchess
Sterling Duchess

Reputation: 2080

Rails no validation errors are returned when empty form is submitted

Short: I have a devise model User I'm adding an option for User to be able to change their password in their profile page without needing to go via "Forgot password" option. Anyhow as this required an additional form with fields: :old_password, :new_password and :new_password_confirmation that are not originally in model i had to create new seperate validations for them,

The form:

<%= form_for(@user, :url => {:controller => :members, :action => :editpass}, :html => {:method => :post}) do |f| %>

    <input name="authenticity_token" type="hidden" value="<%= form_authenticity_token %>">
    <table class="tables no-border">
        <tr>
            <%= f.label :old_password, "Old password" %>
        </tr>
        <tr>
            <%= f.password_field :old_password, :autofocus => :true %>
        </tr>
        <tr>
            <%= f.label :new_password, "New password" %>
        </tr>
        <tr>
            <%= f.password_field :new_password %>
        </tr>
        <tr>
            <%= f.label :new_password_confirmation, "Repeat new password" %>
        </tr>
        <tr>
            <%= f.password_field :new_password_confirmation %>
        </tr>
        <tr>
            <input type="submit" value="Change" class="btn" />
        </tr>
    </table>
</form>

  <%= @user.errors.full_messages %>

<% end %>

The controller:

class MembersController < ApplicationController

  before_filter :authenticate_user!
  skip_before_filter :check_for_main

  def index
    @users = User.all
  end

  def show
    @user = User.find(params[:id])
  end

  def editpass

    current_user.change_password(params[:old_password], params[:new_password])
    redirect_to member_path(current_user.id)

  end

  # User access restriction
  def ban
  end

  def unban
  end



end

The model: LOOK AT: Validation for fields :old_password / :new_password..

class User < ActiveRecord::Base

    # Virtual attributes
    # _password attributes serve in-profile password change and are not part of the model
    attr_accessor :login, :apiid, :vcode, :old_password, :new_password, :new_password_confirmation

    # Include default devise modules. Others available are:
    # :token_authenticatable, :confirmable,
    # :lockable, :timeoutable and :omniauthable
    devise  :database_authenticatable, :registerable, :confirmable,
            :recoverable, :rememberable, :trackable, :validatable, :authentication_keys => [:login]

    # Registration show/edit form validation
    validates :username,    :presence   => true,
                            :length     => { :minimum => 6, :maximum => 255 },
                            :uniqueness => true

    validates :apiid,       :presence   => true,
                            :numericality => { :only_integer => true },
                            :acc_api    => true,
                            :on         => :create

    validates :vcode,       :presence   => true,
                            :length     => { :minimum => 20, :maximum => 255 },
                            :on         => :create

    # In profile password
    validates :old_password,    :presence   => true,
                                :length     => { :minimum => 8, :maximum => 255 },
                                :if         => :password_changed?

    validates :new_password,    :presence   => true,
                                :length     => { :minimum => 8, :maximum => 255 },
                                :if         => :password_changed?,
                                :confirmation   => true

    validates :new_password_confirmation,   
                                :presence   => true


    attr_accessible :login, :username, :group, :apiid, :vcode, :email, :password, :password_confirmation, :remember_me

    # Register character belonging to user
    after_create :register_characters

    # Model association
    has_many :apis
    has_many :characters

    # Allows user to reset password in profile
    # for Forgot Password look at Devise
    def change_password(oldpass, newpass)
        if self.valid_password?(oldpass)
            # User is logged out automatically by devise upon
            # password change
            self.password = newpass
            self.save
        else
            return false
        end
    end

    # Register account characters
    def register_characters
        require 'nokogiri'
        require 'open-uri'

        # Fetch account level xml
        uri = "https://api.eveonline.com/account/Characters.xml.aspx?keyID=#{self.apiid}&vCode=#{self.vcode}"
        xml = Nokogiri::XML(open(uri))
        row = xml.xpath("//row")

        # Create characters bound to user
        row.each do |entry|
            # Register new character
            character = Character.new(
                :charid     =>  entry['characterID'].to_i,
                :user_id    =>  self.id,
                :name       =>  entry['name'],
                :corp       =>  entry['corporationName']
                )
            character.save

            # Register character associated api credentials
            api = Api.new(
                :user_id        =>  self.id,
                :character_id   =>  character.id,
                :apiid          =>  self.apiid,
                :vcode          =>  self.vcode
                )
            api.save

            character.update_character
        end
    end

    # Check if user is banned before login
    def active_for_authentication?
      super && self.banned == false
    end

    # Redefine authentication procedure to allow login with username or email
    def self.find_for_database_authentication(warden_conditions)
      conditions = warden_conditions.dup
      if login = conditions.delete(:login).downcase
        where(conditions).where("username = '#{login}' OR email = '#{login}'").first
      else
        where(conditions).first
      end
    end

end

Upvotes: 1

Views: 686

Answers (2)

aguynamedloren
aguynamedloren

Reputation: 2273

Basically, your password validations aren't being reached.

With the change_password method, you're just passing in values from the controller to that method and using them to set password. The model does not see old_password and new_password being set, so it cannot validate them. Also, since you're not passing in password_confirmation and just setting password directly in the model (self.password = newpass), password_confirmation is not being used either.

As mentioned in another answer, you're better off using update_attributes or whatever is recommended in the Devise docs. Hopefully my explanation adds some insight into why your method is not working.

If you want to stick to your change_password method, you have to pass values to the attributes so they'll be validated by the model. Try something like this:

  #controller

  @user = current_user

  if @user.change_password(params[:old_password], params[:new_password], params[:new_password_confirmation])
    redirect_to member_path(current_user.id)
  else
    @errors = @user.errors.full_messages
    render 'edit_password_view' # not sure what this action is named
    # you now have an array of errors (@errors) that you can render however you see fit in the view
  end


  #model
  def change_password(oldpass, newpass, newpass_conf)
     self.password = newpass
     self.old_password = oldpass
     self.new_password = newpass
     self.new_password_confirmation = newpass_conf
     return self.save
  end

Upvotes: 1

joscas
joscas

Reputation: 7674

I think the right way is to update user attributes instead of using change password. Maybe this guide can be of help:

https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-edit-their-password

Upvotes: 0

Related Questions