授权用户在不使用 Pundit 的情况下为每个控制器执行各种 CRUD 操作; Ruby 在 Rails
Authorize Users to perform various CRUD actions for each controller without using Pundit; Ruby on Rails
我目前正在 Rails 上使用 Ruby 构建一个简单的网络应用程序,允许登录用户对 User
模型执行 CRUD 操作。我想添加一个函数,其中:
- 用户可以select每个控制器可以执行哪些操作;
例如:用户 A 可以在控制器 A 中执行操作 a 和 b,而用户 B 只能在控制器 A 中执行操作 B。这些可以通过视图进行编辑。
- 只有授权用户才能获得其他用户的编辑授权权限。例如,如果用户 A 获得授权,则它可以更改用户 B 可以执行的操作,但未授权的用户 B 将无法更改自己或任何人的可执行操作。
我已经为我的用户控制器设置了视图和模型
class UsersController < ApplicationController
skip_before_action :already_logged_in?
skip_before_action :not_authorized, only: [:index, :show]
def index
@users = User.all
end
def new
@user = User.new
end
def create
@user = User.new(user_params)
if @user.save
redirect_to users_path
else
render :new
end
end
def show
set_user
end
def edit
set_user
end
def update
if set_user.update(user_params)
redirect_to user_path(set_user)
else
render :edit
end
end
def destroy
if current_user.id == set_user.id
set_user.destroy
session[:user_id] = nil
redirect_to root_path
else
set_user.destroy
redirect_to users_path
end
end
private
def user_params
params.require(:user).permit(:email, :password)
end
def set_user
@user = User.find(params[:id])
end
end
My sessions controller:
class SessionsController < ApplicationController
skip_before_action :login?, except: [:destroy]
skip_before_action :already_logged_in?, only: [:destroy]
skip_before_action :not_authorized
def new
end
def create
user = User.find_by(email: params[:email])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
redirect_to user_path(user.id), notice: 'You are now successfully logged in.'
else
flash.now[:alert] = 'Email or Password is Invalid'
render :new
end
end
def destroy
session[:user_id] = nil
redirect_to root_path, notice: 'You have successfully logged out'
end
end
login/logout 功能有效,没有问题。
我首先在主应用程序控制器中实现了一个 not_authorized
方法,如果用户角色不等于 1,该方法默认会阻止用户访问相应的操作。
def not_authorized
return if current_user.nil?
redirect_to users_path, notice: 'Not Authorized' unless current_user.role == 1
end
问题是我想让它可编辑。因此,如果有意义,角色 = 1 的用户可以编辑每个用户的访问权限。
我将如何进一步开发它?我也不想使用宝石,因为这样做的唯一目的是让我学习。
任何见解表示赞赏。谢谢!
授权系统的基础是个例外class:
# app/errors/authorization_error.rb
class AuthorizationError < StandardError; end
以及当您的应用程序引发错误时将捕获的救援:
class ApplicationController < ActionController::Base
rescue_from 'AuthorizationError', with: :deny_access
private
def deny_access
# see
redirect_to '/somewhere', status: :forbidden
end
end
这避免了在整个控制器中重复逻辑,同时您仍然可以覆盖子classes 中的 deny_access
方法来自定义它。
然后您将在您的控制器中执行授权检查:
class ThingsController
before_action :authorize!, only: [:update, :edit, :destroy]
def create
@thing = current_user.things.new(thing_params)
if @thing.save
redirect_to :thing
else
render :new
end
end
# ...
private
def authorize!
@thing.find(params[:id])
raise AuthorizationError unless @thing.user == current_user || current_user.admin?
end
end
在这种非常典型的情况下,任何人都可以创建 Thing,但用户只能编辑他们创建的 thing,除非他们是管理员.随着复杂程度的增加,将诸如此类的所有内容“内联”到您的控制器中很快就会变得笨拙混乱 - 这就是为什么像 Pundit 和 CanCanCan 这样的宝石将其提取到一个单独的层中。
创建一个权限为应用程序用户 editable 的系统在概念化和实施上都困难了好几个数量级,如果您不熟悉授权,这确实超出了您应该尝试的范围(或 Rails)。您需要创建一个单独的 table 来持有权限:
class User < ApplicationRecord
has_many :privileges
end
class Privilege < ApplicationRecord
belongs_to :thing
belongs_to :user
end
class ThingsController
before_action :authorize!, only: [:update, :edit, :destroy]
# ...
private
def authorize!
@thing.find(params[:id])
raise AuthorizationError unless owner? || admin? || privileged?
end
def owner?
@thing.user == current_user
end
def admin?
current_user.admin?
end
def privileged?
current_user.privileges.where(
thing: @thing,
name: params[:action]
)
end
end
我目前正在 Rails 上使用 Ruby 构建一个简单的网络应用程序,允许登录用户对 User
模型执行 CRUD 操作。我想添加一个函数,其中:
- 用户可以select每个控制器可以执行哪些操作; 例如:用户 A 可以在控制器 A 中执行操作 a 和 b,而用户 B 只能在控制器 A 中执行操作 B。这些可以通过视图进行编辑。
- 只有授权用户才能获得其他用户的编辑授权权限。例如,如果用户 A 获得授权,则它可以更改用户 B 可以执行的操作,但未授权的用户 B 将无法更改自己或任何人的可执行操作。
我已经为我的用户控制器设置了视图和模型
class UsersController < ApplicationController
skip_before_action :already_logged_in?
skip_before_action :not_authorized, only: [:index, :show]
def index
@users = User.all
end
def new
@user = User.new
end
def create
@user = User.new(user_params)
if @user.save
redirect_to users_path
else
render :new
end
end
def show
set_user
end
def edit
set_user
end
def update
if set_user.update(user_params)
redirect_to user_path(set_user)
else
render :edit
end
end
def destroy
if current_user.id == set_user.id
set_user.destroy
session[:user_id] = nil
redirect_to root_path
else
set_user.destroy
redirect_to users_path
end
end
private
def user_params
params.require(:user).permit(:email, :password)
end
def set_user
@user = User.find(params[:id])
end
end
My sessions controller:
class SessionsController < ApplicationController
skip_before_action :login?, except: [:destroy]
skip_before_action :already_logged_in?, only: [:destroy]
skip_before_action :not_authorized
def new
end
def create
user = User.find_by(email: params[:email])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
redirect_to user_path(user.id), notice: 'You are now successfully logged in.'
else
flash.now[:alert] = 'Email or Password is Invalid'
render :new
end
end
def destroy
session[:user_id] = nil
redirect_to root_path, notice: 'You have successfully logged out'
end
end
login/logout 功能有效,没有问题。
我首先在主应用程序控制器中实现了一个 not_authorized
方法,如果用户角色不等于 1,该方法默认会阻止用户访问相应的操作。
def not_authorized
return if current_user.nil?
redirect_to users_path, notice: 'Not Authorized' unless current_user.role == 1
end
问题是我想让它可编辑。因此,如果有意义,角色 = 1 的用户可以编辑每个用户的访问权限。
我将如何进一步开发它?我也不想使用宝石,因为这样做的唯一目的是让我学习。 任何见解表示赞赏。谢谢!
授权系统的基础是个例外class:
# app/errors/authorization_error.rb
class AuthorizationError < StandardError; end
以及当您的应用程序引发错误时将捕获的救援:
class ApplicationController < ActionController::Base
rescue_from 'AuthorizationError', with: :deny_access
private
def deny_access
# see
redirect_to '/somewhere', status: :forbidden
end
end
这避免了在整个控制器中重复逻辑,同时您仍然可以覆盖子classes 中的 deny_access
方法来自定义它。
然后您将在您的控制器中执行授权检查:
class ThingsController
before_action :authorize!, only: [:update, :edit, :destroy]
def create
@thing = current_user.things.new(thing_params)
if @thing.save
redirect_to :thing
else
render :new
end
end
# ...
private
def authorize!
@thing.find(params[:id])
raise AuthorizationError unless @thing.user == current_user || current_user.admin?
end
end
在这种非常典型的情况下,任何人都可以创建 Thing,但用户只能编辑他们创建的 thing,除非他们是管理员.随着复杂程度的增加,将诸如此类的所有内容“内联”到您的控制器中很快就会变得笨拙混乱 - 这就是为什么像 Pundit 和 CanCanCan 这样的宝石将其提取到一个单独的层中。
创建一个权限为应用程序用户 editable 的系统在概念化和实施上都困难了好几个数量级,如果您不熟悉授权,这确实超出了您应该尝试的范围(或 Rails)。您需要创建一个单独的 table 来持有权限:
class User < ApplicationRecord
has_many :privileges
end
class Privilege < ApplicationRecord
belongs_to :thing
belongs_to :user
end
class ThingsController
before_action :authorize!, only: [:update, :edit, :destroy]
# ...
private
def authorize!
@thing.find(params[:id])
raise AuthorizationError unless owner? || admin? || privileged?
end
def owner?
@thing.user == current_user
end
def admin?
current_user.admin?
end
def privileged?
current_user.privileges.where(
thing: @thing,
name: params[:action]
)
end
end