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.