4

I'm running Pundit in my Rails app for authorisation. I seem to be getting the hang of it all but want to know how to restrict the edit or update actions to a certain field.

For example, a user can edit their user.first_name, user.mobile or user.birthday etc but can't edit their user.role. Essentially my logic is, let the user edit anything that's cosmetic but not if it is functional.

These fields should only be able to be edited by a user who has a 'super_admin' role (which I have setup on the user.rb with methods such as the below).

  def super_admin?
    role == "super admin"
  end

  def account?
    role == "account"
  end

  def segment?
    role == "segment"
  end

  def sales?
    role == "sale"
  end

  def regional?
    role == "regional"
  end

  def national?
    role == "national"
  end

  def global?
    role == "global"
  end 

I pretty much have a clean slate user_policy.rb file where the update and edit actions are the default

  def update?
    false
  end

  def edit?
    update?
  end

Maybe I am thinking entirely wrong about this and should just wrap a user.super_admin? if statement around the role field on the user show page but this feels wrong if I am only using that tactic for security.

Jay Killeen
  • 2,832
  • 6
  • 39
  • 66

2 Answers2

7

Use Pundit's permitted_attributes helper which is described on the gem's README page: https://github.com/elabs/pundit

# app/policies/post_policy.rb
class PostPolicy < ApplicationPolicy
  def permitted_attributes
    if user.admin? || user.owner_of?(post)
      [:title, :body, :tag_list]
    else
      [:tag_list]
    end
  end
end

# app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def update
    @post = Post.find(params[:id])
    if @post.update_attributes(post_params)
      redirect_to @post
    else
      render :edit
    end
  end

  private

  def post_params
    params.require(:post).permit(policy(@post).permitted_attributes)
  end
end
jpw
  • 18,697
  • 25
  • 111
  • 187
2

In your views, you can limit what users can see based on their role.

User View

- if current_user.super_admin? 
  = f.select(:role, User.roles.keys.map {|role| [role.titleize.role]})
- else
  = user.role

And in the policy you can call the role of the user to make sure they are able to edit.

class UserPolicy
  attr_reader :current_user, :model

  def initialize(current_user, model)
    @current_user = current_user
    @user = model
  end

  def edit?
    @current_user.super_admin || @current_user == @user
  end
end
AGirlThatCodes
  • 575
  • 7
  • 21
  • Wouldn't this approach be susceptible to form injection (where a bad actor can edit the page in the browser, add the html code to add the role and submit their own role? Safer/Better/Cleaner to selectively block the attribute from being added under any circumstances using Pundit's permitted_attributes helper in conjunction with Rails' strong parameters. – jpw Feb 03 '17 at 03:44