【问题标题】:Rails roles and permissions - how do I do it right?Rails 角色和权限 - 我该怎么做?
【发布时间】:2015-09-29 05:37:48
【问题描述】:

我已经成功推出了自己的基于角色和权限的授权系统:

  • 角色和权限都是数据库中的模型,它们通过另一个表具有多对多关系。
  • 用户属于_一个角色并通过该角色拥有_许多权限
  • 管理员用户可以创建新角色并定义哪些权限与哪些角色对应。
  • 控制器和视图会在执行或呈现某些内容之前检查当前用户是否具有特定权限。

这一切都非常好。这是包含在application_controller,rb 中的代码的关键部分:

def permitted_action (permission_names)
  # if the users permissions do not instersect with those given then redirect to root
  redirect_to(root_url) if (permission_names & current_user.permissions.map(&:name)).empty?
end

这是一个验证用户权限的控制器:

before_action only: [:new, :create] do
  permitted_action ['create_user'] 
end

我的问题是权限是具有唯一字符串名称的数据库行,当我想识别权限时我会使用这些名称。因此,我使用我需要的所有权限为数据库播种,然后当视图或控制器需要检查权限时,我需要在此时获取权限名称。如果我检查权限“add_user”,但在数据库中权限名称是“add_users”,就会出错。

然后在测试中,我必须再次将所有权限作为固定装置放入,并重新获得所有名称。

当我添加需要更多权限的功能时,我必须在至少 3 个不同的地方使用相同的字符串添加这些权限。这对我来说似乎是错误的。

理想情况下,控制器会定义他们使用的权限,db/seeds.rb 文件会获取这些权限并为 db 提供种子,测试装置也会获取它们,但我不确定是否这是可能的。

我应该如何构建它?

编辑:

我认为对我有帮助的是提供 db/seeds.rb 文件的夹具解析器。从permissions.yml 获取类似的东西:

archive_tally:
  name: archive_tally
  description: Archive or make active any tally
  category: 3

然后吐出这样的东西:

archive_tally = Permission.find_or_initiate_by_name("archive_tally")
archive_tally.description = "Archive or make active any tally"
archive_tally.category = 3
archive_tally.save!

我认为这种东西还不存在。

【问题讨论】:

  • 我认为我需要做的是在控制器中使用特殊格式的 cmets 来声明它们正在使用的权限,然后有一个 rake 任务来扫描控制器中的这些声明,然后填充 db/seed.rb具有尚不存在的权限。另一个 rake 任务将执行相同的操作来填充测试夹具。但是,由于我不知道编辑 rake 文件的第一件事,也没有时间学习,所以我只需要手动放入种子和固定装置就可以了。
  • 我刚刚注意到this question 的公认答案应该让我大有帮助。

标签: ruby-on-rails ruby-on-rails-4 permissions authorization


【解决方案1】:

实际上,您可以通过添加 is_adminis_general_user 等字段将您的用户分组到权限组中,从而将其缩减为仅作为用户表(或角色也取决于有多少)。那么权限只是用户模型上的方法问题,例如:

def can_create_new_roles?
  self.is_admin
end

所以现在你可以做

before_action only: [:create, :new] do
  redirect_to root_path unless current_user.can_create_new_roles?
end

读起来更好看。另外,由于这一切都发生在 User 模型上而不是在其自己的 DB 表上,因此对所有这些 UserPermissions 的测试将变得更加容易。

【讨论】:

  • 此解决方案不允许管理员用户在应用程序中创建和编辑角色。我已经实现了该功能,不想失去它。
  • 另外,我已经使用method_missing 获得了current_user.can_permission_name? 的功能,正如Ernie Miller 所展示的那样,我正在视图中使用这种方法调用。
  • 但是你是对的,使用这些方法比我的设置更好,所以我也会把它们放在控制器中。
【解决方案2】:

这是我想出的,它让我满意,虽然它不是我在问题中寻找的理想解决方案:

事实证明,我希望在生产数据库中植入固定装置,这些固定装置是我希望出现在测试数据库中的固定装置的子集。

  1. 我创建了一个目录db/seed_fixtures,其中包含roles.ymlpermissions.yml 等文件。在这些文件中,我放置了我想用来作为生产数据库种子的固定装置。

  2. tests/fixtures/roles.yml 中,我已经在文件顶部包含了带有这一行的其他灯具: <%= ERB.new(IO.read(Rails.root.join "db/seed_fixtures/roles.yml")).result %> 同样适用于 permissions.yml 和其他 YAML 文件。我将所有仅用于测试的固定装置放在该包含行的下方。

  3. db/seeds.rb 中,我从用于生产的固定装置生成种子代码,如下所示:ActiveRecord::Fixtures.create_fixtures("#{Rails.root}/db/seed_fixtures", "roles")

现在我将所有的fixture 编写一次,其中一些用作生产数据库中的种子,另一些仅用于测试。要将种子加载到数据库中,我运行 rake db:seed(我在生产服务器上执行此操作)并在数据库中加载所有测试装置,我运行 rake db:fixtures:load

更新:

我刚开始使用 Cucumber,我意识到我可以在 Cucumber Step 中使用上面步骤 3 中的代码将种子数据导入 Cucumber 的测试数据库:

Given(/^seed data is loaded into the database$/) do
  ActiveRecord::FixtureSet.create_fixtures("#{Rails.root}/db/seed_fixtures", "roles")
  _(Role.count).wont_equal 0
end

Cucumber 还告诉我,ActiveRecord::Fixtures 已弃用,取而代之的是 ActiveRecord::FixtureSet

【讨论】:

  • 我刚刚意识到ActiveRecord::FixtureSet.create_fixtures 在添加数据之前会从数据库中删除数据。这与db/seeds.rb 通常的行为方式不同。这意味着您必须谨慎使用它,尤其是在生产服务器上。
  • 我不接受这个答案,因为我发现 create_fixtures 方法会删除现有数据,这对于 db/seeds.rb 来说是不正常的行为,并且在生产数据库中使用时可能会导致各种问题。
【解决方案3】:

我想出了一个完全不同的解决方案:我创建了一个夹具解析器。

我的大多数第一个解决方案仍然适用(请参阅我的其他答案),但现在,我制作了一个解析器,而不是该解决方案中的第 3 步,它读取我想用于种子的固定装置并将它们添加到数据库中,检查它们是否已经存在。我从db/seeds.rb运行解析器

我已将code for the parser 作为要点放在 github 上

【讨论】:

  • 如果这样的东西尚不存在,我会感到惊讶。似乎其他人会想要这种功能 - 但如果每个人都从固定装置转向 factory_girl 可能不会。我很想看看有什么其他现有的解决方案可以做这种事情。
猜你喜欢
  • 1970-01-01
  • 2018-01-16
  • 2012-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-15
相关资源
最近更新 更多