【问题标题】:CanCanCan not allowing access to endpoints which returns all recordsCanCanCan 不允许访问返回所有记录的端点
【发布时间】:2019-11-16 00:40:38
【问题描述】:

我正在尝试为我在 Rails 中构建的 API 配置授权。授权似乎有效,但不适用于返回资源所有记录的端点。这些端点也恰好是在 config/routes.rb 的资源块之外创建的(不确定这是否是正确的解释方式)。

例如:当我转到以下端点时,如果有数据,我会收到 200 HTTP 响应,如果记录不存在,我会收到 204 HTTP 响应:

http://localhost:3000/v1/resources/venues/zoom

但是当我转到以下端点时,我得到一个授权错误:

http://localhost:3000/v1/resources/venues/all

这是我的config/routes.rb 文件:

Rails.application.routes.draw do
  # Private Messaging
  resources :conversations, only: [:index, :create]
  resources :messages, only: [:create]
  mount ActionCable.server => '/cable'

  # Admin
  mount RailsAdmin::Engine => '/admin', as: 'rails_admin'

  namespace :v1 do
    namespace :resources do
      # Users
      post '/users/:id/avatar/upload' => 'users#upload_avatar'
      patch '/users/:id/avatar/update' => 'users#update_avatar'
      put '/users/:id/avatar/update' => 'users#update_avatar'
      delete '/users/:id/avatar/delete' => 'users#delete_avatar'
      namespace :users do
        get ':id' => :show
      end

      # Venue Types
      get '/venue_types/all' => 'venue_types#all'
      post '/venues/:name/images/upload' => 'venues#upload_images'
      namespace :venue_types do
        get ':name' => :show
        patch ':name' => :update
        put ':name' => :update
        delete ':name' => :delete
        post 'new'
      end

      # Attributes
      get '/attribute_defaults/all' => 'attribute_defaults#all'
      namespace :attribute_defaults do
        get ':id' => :show
        patch ':id' => :update
        put ':id' => :update
        delete ':id' => :delete
        post 'new'
      end

      # Venues
      get '/venues/all' => 'venues#all'
      namespace :venues do
        get ':name' => :show
        patch ':name' => :update
        put ':name' => :update
        delete ':name' => :delete
        post 'new'
      end

      # Venue Attributes
      get '/venue_attributes/all' => 'venue_attributes#all'
      namespace :venue_attributes do
        get ':id' => :show
        patch ':id' => :update
        put ':id' => :update
        delete ':id' => :delete
        post 'new'
      end

      # Bids
      get '/bids/all' => 'bids#all'
      namespace :bids do
        get ':id' => :show
        patch ':id' => :update
        put ':id' => :update
        delete ':id' => :delete
        post 'new'
      end

      # Bookings
      get '/bookings/all' => 'bookings#all'
      namespace :bookings do
        get ':id' => :show
        patch ':id' => :update
        put ':id' => :update
        delete ':id' => :delete
        post 'new'
      end

      # Venue Ratings
      get '/venue_ratings/all' => 'venue_ratings#all'
      namespace :venue_ratings do
        get ':id' => :show
        patch ':id' => :update
        put ':id' => :update
        delete ':id' => :delete
        post 'new'
      end

      # User Ratings
      get '/user_ratings/all' => 'user_ratings#all'
      namespace :user_ratings do
        get ':id' => :show
        patch ':id' => :update
        put ':id' => :update
        delete ':id' => :delete
        post 'new'
      end
    end
  end

  # Auth
  devise_for :users,
    path: 'auth',
    path_names: {
     sign_in: 'login',
     sign_out: 'logout',
     registration: 'register'
    },
    controllers: {
      sessions: 'sessions',
      registrations: 'registrations',
      confirmations: 'confirmations'
    }
end

这是我的app/models/ability.rb 文件:

# frozen_string_literal: true

class Ability
  include CanCan::Ability

  def initialize(user)
    # Admin
    if user.present? && user.is_admin?
      can :manage, :all
    # Host
    elsif user.present? && user.is_host?
      can :manage, User, id: user.id
      can [:read, :write, :update], [Venue, VenueAttribute, Bid, UserRating, VenueRating]
      can [:read], User
      can :destroy, [Venue, VenueAttribute]
    # Guest
    elsif user.present? && user.is_guest?
      can :manage, User, id: user.id
      can [:write, :update], [Bid, VenueRating], id: user.id
      can [:write, :update], [UserRating], rated_by: user.id
      can :read, Bid, id: user.id
      can :read, [Venue, User, VenueRating, UserRating]
    # No role
    else
      can :read, [Venue, User, UserRating, VenueRating]
    end
  end
end

这是我的app/controllers/v1/resources/venues_controller.rb 文件:

class V1::Resources::VenuesController < ActionController::API
  load_and_authorize_resource param_method: :venue_params
  respond_to :json
  before_action :authenticate_user!, except: [:all, :show]
  before_action :set_venue, except: [:all, :new]

  # GET /v1/resources/venues/all
  def all
    @venues = Venue.all.where(is_published: true, is_expired: false)
    if !@venues.empty?
      render json: @venues, methods: :image_urls, status: :ok
    else
      head :no_content
    end
  end

  # POST /v1/resources/venues/new
  def new
    @venue = Venue.new(venue_params)
    if @venue.save
      render json: @venue, status: :created
    else
      render json: @venue.errors, status: :unprocessable_entity
    end
  end

  # GET /v1/resources/venues/:name
  def show
    if @venue
      render json: @venue, methods: :image_urls, status: :ok
    else
      head :no_content
    end
  end

  # PATCH/PUT /v1/resources/venues/:name
  def update
    if @venue.update(venue_params)
      render json: @venue, status: :ok
    else
      render json: @venue.errors, status: :unprocessable_entity
    end
  end

  # DELETE /v1/resources/venues/:name
  def delete
    @venue.destroy
    head :no_content
  end

  # POST /v1/resources/venues/:name/images/upload
  def upload_images
    @venue.images.attach(params[:images])
    render json: @venue, methods: :image_urls, status: :ok
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_venue
      @venue = Venue.find_by(name: params[:name])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def venue_params
      params.require(:venue).permit(:name, :price, :minimum_bid, :is_published, :currency, :is_expired, :location, :venue_type_id, :user_id, images: [])
    end
end

app/controllers/application_controller.rb 文件中没有额外的代码。

知道如何调试这个问题吗?另外,我如何授予未经身份验证的用户访问权限?我希望没有帐户的用户能够浏览场地。

【问题讨论】:

    标签: ruby-on-rails cancancan rolify


    【解决方案1】:

    首先,将load_and_authorize_resource 更改为authorize_resource,因为您是手动加载它。在all 操作中手动授权。

    class V1::Resources::VenuesController < ActionController::API
      authorize_resource param_method: :venue_params
      skip_authorize_resource only: :all
      respond_to :json
      before_action :authenticate_user!, except: [:all, :show]
      before_action :set_venue, except: [:all, :new]
    
      # GET /v1/resources/venues/all
      def all
        @venues = Venue.all.where(is_published: true, is_expired: false)
        authorize! :read, Venue
        if !@venues.empty?
          render json: @venues, methods: :image_urls, status: :ok
        else
          head :no_content
        end
      end
    end
    

    添加到能力.rb

    if user.nil?
      can :read, Venue
    

    让它为未经过身份验证的用户工作

    【讨论】:

    • :manage, :all 会向未经过身份验证的用户打开所有路由。
    • 我最终将我的负载和授权行更改为:load_and_authorize_resource param_method: :venue_params, except: [:all],它似乎有效,即使对于未经身份验证的用户也是如此。
    • @SteveTurczyn 你是对的,这是一个例子,更新了答案
    • @DerekC 是的,如果您不需要任何all 操作的授权,这是一个不错的选择。我仍然建议您将其更改为简单的authorize_resource,以免对 DB 提出额外请求
    • 当我更改为authorize_resource 时,我仍然在终端中看到相同的数据库查询。
    猜你喜欢
    • 2019-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-26
    • 1970-01-01
    • 2019-11-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多