Rails、Devise、Pundit - 授权从 Devise 注册控制器创建的配置文件

Rails, Devise, Pundit - authorise Profile created from Devise registration controller

如果您认为有什么不对的地方,请随时说出来。 我扩展了 Devise Registration 控制器来为每个新用户创建一个 Profile 对象:

class Users::RegistrationsController < Devise::RegistrationsController

  def new
    resource = build_resource({})
    resource.profile = Profile.new
    resource.profile.user_id = @user.id
    respond_with resource 
  end

它们都是 has_one - has_one 相关并且在数据库中:

create_table :profiles do |t|
  t.belongs_to :user, index: { unique: true }, foreign_key: true
end

所以要获得当前用户的正确配置文件,我必须:

private 
  def set_profile
    @profile = Profile.where(user_id: current_user.id).first 
  end

这有点解决问题 - 似乎其他用户无法绕过此查询并访问其他配置文件(或者他们可以吗?),但对于其他资源我使用 Pundit 来控制授权,所以现在感觉有点乱。

所以这是一个问题。其他 - 我仍然不知道在没有用户登录时如何操作,因为如果访问任何受限资源,这:

private
 def set_some_resource
 end
end

抛出 - “nil:NilClass) 的未定义方法 `id' - 如何最好地避免这种情况?

感谢您的任何建议。

您可能想先阅读 Rails guides on assocations

要创建一对一关联,您在外键列的一侧使用 belongs_to,在另一侧使用 has_one。

class User
  has_one :profile
end

class Profile
  belongs_to :user
end

ActiveRecord 然后自动将记录链接在一起。通常,您应该避免显式设置 ID(或通过 ID 获取关联记录),而是使用关联:

class Users::RegistrationsController < Devise::RegistrationsController
  # ...
  def new
    # calls Devise::RegistrationsController#new 
    super do |user|
      user.profile.new
    end
  end
end

Devise 非常漂亮,让您可以通过一个块来进入流程,而不是复制粘贴整个操作。

同样地,您将获取当前用户的个人资料:

private 
  def set_profile
    @profile = current_user.profile
  end

您可以设置是否应使用 if: 选项调用回调。

before_action :set_profile, if: :user_signed_in?

但是如果操作需要身份验证,您应该确保它在 :authenticate_user! 之后,无论如何这将停止过滤器链。

And this kinda solves the problem - seems other users cant go around this query and access other profiles (or CAN THEY?), but for other resources I use Pundit to control authorisation, so now it feels a bit messy.

您不需要使用 Pundit 来授权创建个人资料或获取当前用户个人资料。由于配置文件是通过用户获取的,因此其他用户无法访问它(没有黑客攻击)。

如果您创建 ProfilesController,您可能想要授权的是显示、索引、编辑等操作。