简体   繁体   中英

Ruby on Rails - pundit - unknown action for controller

this question is referring to this specific question.

I use pundit as my authorization gem and I want that only the user info that belongs to user X can be downloaded by user X. Right now i have http://localhost:3000/download.csv as my link to recieve the user info. But if I am logged in in to another user, for example user/2, I can still enter the url and download user/3 data.

What I have right now:

user_policy.rb

class UserPolicy < ApplicationPolicy

  def profile?
    true
  end

  def download?

  end

  private

  def user_or_admin
    user.id == record.id || user.admin?
  end


end

application_policy.rb

class ApplicationPolicy
  attr_reader :user, :record

  def initialize(user, record)
    @user = user
    @record = record
  end

  def index?
    false
  end

  def show?
    scope.where(:id => record.id).exists?
  end

  def create?
    create?
  end

  def new?
    create?
  end

  def update?
    false
  end

  def edit?
    update?
  end

  def destroy?
    false
  end

  class Scope
    attr_reader :user, :scope

    def initialize(user, scope)
      @user = user
      @scope = scope
    end

    def resolve
      scope.all
    end
  end
end

This is my user_controller.rb # this is not the users_controller, those are different views

    class UserController < ApplicationController
  prepend_view_path(File.join(Rails.root, 'app/views/user/'))


  layout 'application'


  def index

  end

  def billing
  end

  def plan
  end

  def profile
    @user = User.find(params[:id])
    @user_posts = @user.posts.order('created_at DESC')
  end
end

def download
  @user = User.find(params[:id])

  respond_to do |format|
    format.html
    format.csv { send_data @user.csv, filename: "userinfo-#{Date.today}.csv" }
  end
end

  def support
    @user = User.find(params[:id])
    @user_posts = @user.posts.order('created_at DESC')
  end

  def notifications
  end

  def show
    @user = User.find(params[:id])
    @posts = current_user.posts.order('created_at DESC')
    @user_posts = @user.posts.order('created_at DESC')
  end

Update: As suggested I tried implementing a new download action in my user_controller and tried to use this code in my view:

<p><%= link_to("Export data as CSV", download_path(@user, format: :csv), { :controller => :user, :action => :download }, class: "btn btn-success") %></p> 

but this throws the following error:

Recieve Unknown action - The action 'download' could not be found for UserController

routes.rb

        Rails.application.routes.draw do

  get 'legal/privacy'

  get :datenschutz, to: 'legal#terms_of_service'


  devise_for :users, path_names: { sign_in: 'login', sign_out: 'logout', sign_up: 'registrieren', edit: 'bearbeiten' }, :controllers => { registrations: 'registrations' }

  get '/users/mitteilungen/:id' => 'user#notifications'
  get '/users/:id/artikel/', :to => 'user#support', :as => :artikel
  get '/users/plan' => 'user#plan'
  get '/users/billing' => 'user#billing'
  get '/users/:id', :to => 'user#profile', :as => :user
  get 'download', :to => 'user#download', :controller => :user, action: :download, :as => :download


  resources :posts, path: :news do
    resources :comments, path: :kommentare do
    end
  end


  devise_scope :user do
    resources :posts, path: :news do
      resources :comments do

      end
    end
  end
  root to: "posts#index", as: :root

end

Your condition is wrong @user.id = user.id .

user IS @user . I think you meant user.id = record.id

record is an instance that you are authorizing, for example:

def show
  @user = User.find(params[:id])
  authorize @user
  ...
end

authorize @user is basically shortcut for: UserPolicy.new(@user, current_user).show?

BTW I am not sure but I think you can safely omit .id : user == record

UPD. I re-read your policy. ApplicationPolicy generally looks like this:

class ApplicationPolicy
  attr_reader :user, :record

  def initialize(user, record)
    @user = user
    @record = record
  end
end

You overrided constructor. Why? Let it stay the way Pundit intends it to be

UPD2. This is what I expect your UserPolicy to look like:

class UserPolicy < ApplicationPolicy
  def profile? #this is the action I have the button in to export data
    return true if user_or_admin?
  end

  private

  def user_or_admin?
    user.id == record.id || user.admin?
  end
end

We discussed too much in comments to my original answer. I wanna clarify my point.

You wrote:

I use pundit as my authorization gem and I want that only the userdata that belongs to user X can be downloaded by user X. Right now i have http://localhost:3000/download/x.csv as my link to recieve the user data. But if I am logged in in to another user, for example user/:id 2, I can still enter the url and download users/3 data.

So the problem is that authorization doesnt work. Am I right?

After that you wrote:

Update: As suggested I tried implementing a new download action in my user_controller and tried to use this code in my view: ...

but this throws the following error: ...

So problem with your routing is outside of original question and should be solved in different one. Anyway, you can check out this question .

So. If your problem is authorization - just write your policies right and use them in your controller. This would be all.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM