【问题标题】:How to add this specific authorization feature to my rails app?如何将此特定授权功能添加到我的 Rails 应用程序?
【发布时间】:2014-11-26 08:33:02
【问题描述】:

我的 rails 应用程序有几个出租车司机,他们有几个出租车与之关联,它们之间的关系如下:

class Operator < ActiveRecord::Base
    has_many :cabs
end

我使用 Devise 作为我的身份验证 gem。它对我的应用程序中的用户、管理员和超级管理员进行身份验证。我为用户、管理员和超级管理员创建了单独的模型(并且没有为用户本身分配角色)。

我现在希望将授权功能添加到应用程序中,以便管理员(在我的情况下基本上是出租车司机)可以只对自己的出租车进行 CRUD。例如,属于 operator#2 的管理员只能访问链接:http://localhost:3000/operators/2/cabs,而不能访问链接:http://localhost:3000/operators/3/cabs

我的管理员模型已经有一个 operator_id,当管理员注册时,它将它与操作员相关联。我尝试通过 CanCan 添加授权功能,但我无法配置 CanCan 以提供上述示例的限制。

我还尝试在 cabs_controller 中扩展我的身份验证功能,如下:

class CabsController < ApplicationController
  before_action :authenticate_admin!

def index
    if current_admin.operator_id != params[:operator_id]
       redirect_to new_admin_session_path, notice: "Unauthorized access!"
    else
      @operator = Operator.find(params[:operator_id])
      @cabs = Operator.find(params[:operator_id]).cabs
    end
end 

但这会将我重定向到 root_path,即使 current_admin 的 operator_id 等于 params[:operator_id]。我应该如何进行?

编辑: 以下是我的 routes.rb 文件:

Rails.application.routes.draw do


  devise_for :super_admins
  devise_for :users
resources :operators do
    resources :cabs
  end

scope "operators/:operator_id" do 
    devise_for :admins
  end
end

我有三个表:users、admins 和 super_admins。我创建了这些,因为我希望我的管理员持有 operator_ids,以便可以识别与操作员相对应的管理员。另外,我希望管理员登录路径的类型为 /operators/:operator_id/admins/sign_in,因此在路由文件中进行了调整。

【问题讨论】:

  • authenticate_admin!是身份验证 gem 还是您的自定义函数的一部分?
  • 设计提供authenticate_admin!顺便说一句,我已将我的路线定义为: scope "operators/:operator_id" do devise_for :admins end
  • 您是否考虑过将 Rollify 添加到您的应用程序中?它与 CanCan 配合得很好,有助于您进行角色管理。我还建议使用 CanCanCan 而不是 CanCan,因为后者不再维护。
  • @ThomasO:你能通过一个例子来说明我如何在这种情况下使用 Rollify 吗?
  • 如果需要我可以,但我认为本教程比我可以为您编写的任何内容都更清晰:github.com/RolifyCommunity/rolify/wiki/…

标签: ruby-on-rails ruby-on-rails-4 devise authorization cancan


【解决方案1】:

不幸的是,最初我不明白您实际上有 3 个不同的表供用户和(超级)管理员使用...不确定 Pundit 能否在这种情况下为您提供帮助,但我会为未来的访问者保留旧答案. 回到您的问题,让我们尝试仅修复意外重定向。 路线似乎很好,所以问题可能是这样的:

  • 您正在被重定向,因为您当前没有以管理员身份登录,因此您没有通过 :authenticate_admin! before_action。
  • 您说“即使 current_admin 的 operator_id 等于 params[:operator_id]”,但这个条件可能不成立。你能在某处调试或打印current_admin.operator_idparams[:operator_id] 的值,看看它们是否真的相等吗?

另一件有趣的事情是,你的代码中有一个new_admin_session_path 的重定向,但是你说“这会将我重定向到root_path”。你能再检查一下吗?

老答案

如果你想设置一个好的授权逻辑层,我建议你使用pundit。 您可能听说过cancan,但它不再受支持... 让Devise 只管理身份验证部分并试一试;)

权威示例

首先,按照 pundit installation 的步骤创建 app/policies 文件夹和基础 ApplicationPolicy 类。

然后,根据您的情况,您需要在该文件夹中创建一个 CabPolicy 类:

class CabPolicy < ApplicationPolicy
  def update?
    user.is_super_admin? or user.cabs.include?(record)
  end
end

这是update 操作的示例。如果user 有权更新驾驶室,update? 函数必须返回 true(稍后您将看到 WHICH cab),否则返回 false。所以,我在这里说的是“如果用户是超级管理员(is_super_admin? 是一个占位符函数,使用你自己的)就足以返回 true,否则检查记录(这是你检查的 cab)是否包括在内在您的用户的cabs 关联中”。 您也可以使用 record.operator_id == record.id,但我不确定 cab 的关联是 belongs_to :operator。请记住,在CabPolicy 中,record 是一个 Cab 对象,user 是设计current_user,因此请实施您喜欢的检查。

接下来,在您的控制器中,您只需在更新函数中添加一行:

def update
  @cab = Cab.find(params[:id]) # this will change based on your implementation
  authorize @cab # this will call CabPolicy#update? passing current_user and @cab as user and record
  @cab.update(cab_params)
end

如果你想让事情变得更好,我建议你使用 before_action

class CabsController < ApplicationController
  before_action :set_cab, only: [:show, :update, :delete]

  def update
    @cab.update(cab_params)
  end

  #def delete and show...

  private
  def set_cab
    @cab = Cab.find(params[:id])
    authorize @cab
  end

当然,还要记住在 CabPolicy 中定义 show?delete? 方法。

【讨论】:

  • 您能否提供一些关于在这种情况下如何使用 Pundit 的提示?谢谢!
  • 是的!让我换个答案,它的格式比评论好
  • 我听从了您的建议,但问题仍然存在。我的 Operator 1 管理员仍然可以访问链接 localhost:3000/operators/2/cabs。此外,当我尝试访问编辑操作时,它向我显示此错误:无法找到 #<0x5428de8>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-07
  • 2015-11-21
  • 2015-06-30
相关资源
最近更新 更多