使用Devise和Omniauth编辑用户

Sam*_*Sam 13 ruby-on-rails devise omniauth ruby-on-rails-3.2

我正在通过Railscast实施Devise和OmniAuth(以及Devise 文档) - 目前,我有一个网站,访问者可以使用他们的Facebook帐户或填写表格进行注册.

但是,当通过OmniAuth注册的用户尝试编辑他们的个人资料时,我遇到了麻烦.当用户提交对其个人资料的更改时,Devise会查找用户的当前密码,但是使用facebook登录的用户不知道他们的密码(他们是在用户模型中自动设置的):

  def self.find_for_facebook_oauth(auth, signed_in_resource=nil)
    user = User.where(:provider => auth.provider, :uid => auth.uid).first
    unless user
      user = User.create(first_name:auth.extra.raw_info.first_name,
                         last_name:auth.extra.raw_info.last_name,
                           provider:auth.provider,
                           uid:auth.uid,
                           email:auth.info.email,
                           password:Devise.friendly_token[0,20]
                           )
    end
    user
  end
Run Code Online (Sandbox Code Playgroud)

当用户编辑他的信息时,如果他通过OmniAuth设置了他的帐户,则该应用程序不应要求密码确认.教程建议方便的password_required?方法将帮助我实现这一结果.具体来说,将此方法添加到用户模型意味着它应该仅在用户未通过OmniAuth注册时返回true(在这种情况下提供者属性将为nil):

def password_required?
  super && provider.blank?
end
Run Code Online (Sandbox Code Playgroud)

因此,一段代码如:

<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %>
  <%= devise_error_messages! %>
    <%= render :partial => "essential_user_info_inputs", :locals => { :f => f } %>
    <%= render :partial => "inessential_user_info_inputs", :locals => { :f => f } %>
    <% if f.object.password_required? %> 
        <%= render :partial => "password_inputs", :locals => { :f => f } %>
        <%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
        <%= f.password_field :current_password %>
    <% end %>
  <%= f.submit "Update" %>
<% end %>
Run Code Online (Sandbox Code Playgroud)

理论上只在需要时显示密码输入.它还表明Devise内置了逻辑,称OmniAuth用户不需要使用密码来编辑他们的帐户.我不知道这是否属实,但是这种教程使它看起来像那样.但是当OmniAuth用户尝试编辑他的帐户时,我会收到"当前密码不能为空".非OmniAuth用户也是如此(这是有道理的,因为密码字段也不会出现在这些用户的编辑页面上).

有些戳了一下确认了password_required?当用户通过OmniAuth注册并通过网站的常规用户注册时,方法返回false.即使我将其更改为只运行超类方法,它也会返回false.

有关password_required方法发生了什么的任何想法?我在任何地方都找不到任何关于它的东西,但我觉得这就是现在绊倒的东西.

更新:

这现在有效,但是没有使用Railscast中概述的方法,它依赖于requires_password?方法,一个我仍然一无所知的话题.相反,我实现了此处概述的解决方案,如此处所示.所以我现在只要求密码用代码更新非OmniAuth帐户:

class Users::RegistrationsController < Devise::RegistrationsController
def update
    @user = User.find(current_user.id)
    email_changed = @user.email != params[:user][:email]
    is_facebook_account = !@user.provider.blank?

    successfully_updated = if !is_facebook_account
      @user.update_with_password(params[:user])
    else
      @user.update_without_password(params[:user])
    end

    if successfully_updated
      # Sign in the user bypassing validation in case his password changed
      sign_in @user, :bypass => true
      redirect_to root_path
    else
      render "edit"
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

小智 21

最简单的方法是覆盖你的update_resource方法RegistrationsController.这是建议他们自己实现的控制器:

  # By default we want to require a password checks on update.
  # You can overwrite this method in your own RegistrationsController.
  def update_resource(resource, params)
    resource.update_with_password(params)
  end
Run Code Online (Sandbox Code Playgroud)

因此解决方案是在您自己的控制器中覆盖此方法,如下所示:

class Users::RegistrationsController < Devise::RegistrationsController

  # Overwrite update_resource to let users to update their user without giving their password
  def update_resource(resource, params)
    if current_user.provider == "facebook"
      params.delete("current_password")
      resource.update_without_password(params)
    else
      resource.update_with_password(params)
    end
  end

end
Run Code Online (Sandbox Code Playgroud)

  • 简单直接!完美的工作. (2认同)
  • 我还在`update_resource`方法上面添加了`protected`并更新了我的路由以使用控制器`devise_for:users,controllers:{omniauth_callbacks:"users/omniauth_callbacks",注册:'users/registrations'}`按照https:/ /github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-edit-their-account-without-providing-a-password (2认同)
  • 我认为 `current_user.provider == "facebook"` 应该是 `resource.provider == "facebook"` current_user 并不总是可用。例如,如果用户名为“current_login”或“current_user_login”。`resource` 方法被解析为当前的 `@request.env['devise.mapping']`。也许值得改变答案 (2认同)

pja*_*mer 0

我看到这个在某处使用过。


def update
    params[:user].delete(:current_password)
    params[:user].delete(:password)
    params[:user].delete(:password_confirmation)
    if current_user.update_without_password(params[:user])
      redirect_to somewhere_wicked_path, notice => "You rock"
    else
      render 'edit', :alert => 'you roll'
    end
end
Run Code Online (Sandbox Code Playgroud)

在控制器的更新方法中使用类似的东西。很确定这个方法也在 Devise 中。