如何限制对 UI 功能的访问?
How can I limit access to UI features?
我过去曾使用 cancan
和 cancancan
等声明式授权 gem 来控制对数据的访问。但是,在这个新应用程序中,我试图控制对实际功能的访问。
这是典型的 SaaS 模型,根据用户的订阅级别,可以使用的功能会逐渐增多。
大多数功能都可以通过单击整个应用程序中的不同图标或菜单项来使用。
我写信是想询问在我自己动手之前是否有一个我不知道的众所周知的实现模式。
以下是根据订阅级别限制的不同类型的内容:
- 可通过图标访问的功能
- 可通过菜单项访问的功能
- 某些报表(每个报表都有一个 ReportDefinition 定义。)
- 某些上传(每个上传都有一个文件类型定义。)
- 某些 BackgroundProcesses(每个都有一个定义它的 ProcessType。)
每个订阅都有一个计划。将该计划与上面的第 3、4 和 5 项联系起来并使用 cancancan
以实现 current_user
的可访问性非常简单。但是,功能 1 和 2 是另一回事。我可以看到将它们的可访问性包装在检查 Feature/Plan 列表的视图助手中。但这只处理视图。如果用户知道 URL,他们仍然可以通过直接键入 URL 来访问该功能。在这种情况下,我是否需要在控制器操作级别再次处理授权,或者是否可以放入某种中间件来限制对功能的访问?
非常感谢。
https://github.com/jnunemaker/flipper 是一个很好的解决方案,可以满足您的需求。
否则,就像你说的,cancancan
是一个天真的解决方案。
我会简单地使用枚举来设置不同的访问或订阅级别。然后只需写一个 before_action
称为 can_access?
来阻止整个操作。然后我会在视图中设置一些条件或 view_helpers 以阻止对某些 UI 元素的访问。
如果它是一个简单的应用程序,我只是在 User
上添加一个 admin
列。如果有超过 2 种用户类型 (admin/non-admin/author/editor/etc),那么我会将其设为 Enum
字段而不是布尔值。
然后,在user.rb
里面我添加了几个方法...
def is_admin?
admin?
end
def is_author?
!admin?
end
从那里,我还在 application_controller.rb
中添加了一些引发异常的代码:
def unauthorized
head(:unauthorized)
end
def unprocessable
head(:unprocessable_entity)
end
我还在application_controller.rb
中添加了一个current_user
方法:
helper_method :current_user
def current_user
@user ||= User.find(session[:user_id])
end
然后在我看来,那是我处理 "hiding" 事情或禁用按钮的地方。
<%= if current_user.is_admin? %>
<button>Admin button</button>
<% else %>
<button>Author button</button>
<% end %>
这当然不是安全性(仅视图层发生变化),这就是为什么我也 return 使用我之前列出的方法从控制器中提早
def destroy
return unauthorized unless current_user.is_admin?
# ... delete code here
end
对于上面的例子不要忘记使用return否则代码将继续执行。
对于比较简单的事情,我使用Pundit。
我过去曾使用 cancan
和 cancancan
等声明式授权 gem 来控制对数据的访问。但是,在这个新应用程序中,我试图控制对实际功能的访问。
这是典型的 SaaS 模型,根据用户的订阅级别,可以使用的功能会逐渐增多。
大多数功能都可以通过单击整个应用程序中的不同图标或菜单项来使用。
我写信是想询问在我自己动手之前是否有一个我不知道的众所周知的实现模式。
以下是根据订阅级别限制的不同类型的内容:
- 可通过图标访问的功能
- 可通过菜单项访问的功能
- 某些报表(每个报表都有一个 ReportDefinition 定义。)
- 某些上传(每个上传都有一个文件类型定义。)
- 某些 BackgroundProcesses(每个都有一个定义它的 ProcessType。)
每个订阅都有一个计划。将该计划与上面的第 3、4 和 5 项联系起来并使用 cancancan
以实现 current_user
的可访问性非常简单。但是,功能 1 和 2 是另一回事。我可以看到将它们的可访问性包装在检查 Feature/Plan 列表的视图助手中。但这只处理视图。如果用户知道 URL,他们仍然可以通过直接键入 URL 来访问该功能。在这种情况下,我是否需要在控制器操作级别再次处理授权,或者是否可以放入某种中间件来限制对功能的访问?
非常感谢。
https://github.com/jnunemaker/flipper 是一个很好的解决方案,可以满足您的需求。
否则,就像你说的,cancancan
是一个天真的解决方案。
我会简单地使用枚举来设置不同的访问或订阅级别。然后只需写一个 before_action
称为 can_access?
来阻止整个操作。然后我会在视图中设置一些条件或 view_helpers 以阻止对某些 UI 元素的访问。
如果它是一个简单的应用程序,我只是在 User
上添加一个 admin
列。如果有超过 2 种用户类型 (admin/non-admin/author/editor/etc),那么我会将其设为 Enum
字段而不是布尔值。
然后,在user.rb
里面我添加了几个方法...
def is_admin?
admin?
end
def is_author?
!admin?
end
从那里,我还在 application_controller.rb
中添加了一些引发异常的代码:
def unauthorized
head(:unauthorized)
end
def unprocessable
head(:unprocessable_entity)
end
我还在application_controller.rb
中添加了一个current_user
方法:
helper_method :current_user
def current_user
@user ||= User.find(session[:user_id])
end
然后在我看来,那是我处理 "hiding" 事情或禁用按钮的地方。
<%= if current_user.is_admin? %>
<button>Admin button</button>
<% else %>
<button>Author button</button>
<% end %>
这当然不是安全性(仅视图层发生变化),这就是为什么我也 return 使用我之前列出的方法从控制器中提早
def destroy
return unauthorized unless current_user.is_admin?
# ... delete code here
end
对于上面的例子不要忘记使用return否则代码将继续执行。
对于比较简单的事情,我使用Pundit。