Search code examples
ruby-on-railsauthorizationpundit

Attribute level authorization in views with pundit


I'm using pundit for authorization in a rails app. For some models, I want attribute-level authorization. For example, a normal user is allowed to change his phone number but can't set his status to "administrator".

As recommended in the Pundit docs, I'm using the permitted_attributes for this.

I now want to access these attributes in a view to decide which fields to show or enable in a form. Is there an (elegant) way to do this without essentially repeating the authorized fields there?


Solution

  • First you may want to add some nice shortcuts in your ApplicationPolicy which lets you check if attributes are permitted:

    class ApplicationPolicy 
      #...
    
      def permits_attributes?(*attrs)
        attrs.keep_if({|a| permits_attribute?(a) }).any?
      end
    
      def permits_attribute?(attr)
        permitted_attributes.include?(attr)
      end
    
      # ...
    end
    

    You could then create a custom form builder (or a module that extends a form builder):

    class MyFormBuilder < ActionView::Helpers::FormBuilder
      def can_fill_in?(attr)
        yield if @template.policy(object).permits_attribute?(attr)
      end
    end
    

    <%= form_for(@project, builder: MyFormBuilder) do |f| %>
      <%= f.can_fill_in?(:title) do %>
        <%= f.label :title %>
        <%= f.text_field :title %>
      <% end %>
    <% end %>
    

    You can also configure rails to use your custom form builder by default:

    ActionView::Base.default_form_builder = MyFormBuilder
    

    See: