Setup
基本的酒店设置,users
是hotel
的团队成员。它使用Cancancan,并使用Rails 5.2进行设计
rails g scaffold User name
rails g scaffold Hotel name
rails g scaffold TeamMembership user:references hotel:references
rails g scaffold Reservation starts_on:date ends_on:date hotel:references
rails g scaffold CheckIn hotel:references reservation:references
hotels
通过has_many :users, through: :team_memberships
连接到users
。反之亦然,users
hotels
.
config/routes.rb
resources :hotels do
resources :reservations
resources :check_ins
end
app/controllers/check_ins_controller.rb
class CheckInsController < ApplicationController
before_action :authenticate_user!
load_and_authorize_resource :hotel
load_and_authorize_resource :check_in, :through => :hotel
[...]
app/models/ability.rb
[...]
can [:read, :destroy], CheckIn, hotel_id: user.hotel_ids
can [:create], CheckIn
[...]
问题/疑问
在视图中的某个地方,我有以下代码:
<% if can? :create, CheckIn %>
<%= link_to 'Create Check-In', new_hotel_check_in_path(@hotel) %>
<% end %>
它应该只对@hotel
的团队成员可见。
ability.rb
的第一行工作正常,但第二行不起作用,因为任何人都可以创建一个新check_in
但只有team_memberships
应该能够为他们的酒店创建一个新check_in
。
解决这个问题的最佳方法是什么?显然,不应显示链接,但非团队成员的任何人都不应访问/hotels/:hotel_id/check_ins/new
URL。
这是一个常见问题,这就是business
逻辑与authorization
逻辑相交的地方。
关于这个问题有很多意见。
1(许多人认为这种交集是不可接受的。他们会建议您以这种方式做您需要的事情(分离业务和授权逻辑(
<% if can?(:create, CheckIn) && current_user.member_of?(@hotel) %>
<%= link_to 'Create Check-In', new_hotel_check_in_path(@hotel) %>
<% end %>
2(如果你确定你需要这个,你可以这样做:
向Hotel
模型添加新权限:
can [:check_in], Hotel do |hotel|
user.member_of?(hotel)
end
然后在视图中:
<% if can?(:create, CheckIn) && can?(:check_in, @hotel) %>
<%= link_to 'Create Check-In', new_hotel_check_in_path(@hotel) %>
<% end %>
在控制器中:
class CheckInsController < ApplicationController
# ...
def new
authorize! :check_in, @hotel
# ...
end
end
试试这个:
can [:create], CheckIn if user.team_memberships.present?
或
can [:create], CheckIn if user.hotels.present?
希望这有帮助。