Skip to content

Conversation

@lodewiges
Copy link
Contributor

@lodewiges lodewiges commented Jan 13, 2026

move Permited Atributes to Policy
Fixes #191

Summary by CodeRabbit

  • New Features

    • Added support for nested form updates where applicable (e.g., invoice rows, order rows, product prices).
    • More precise control over which fields can be created or edited across Activities, Orders, Invoices, Products, Price Lists, Users, Credit Mutations, and Sofia Accounts.
  • Refactor

    • Standardized form handling to use policy-driven validation across the app, improving consistency and safeguarding data entry.
    • Centralized parameter filtering for create/update actions, resulting in clearer, more predictable behavior in forms and submissions.

✏️ Tip: You can customize this high-level summary in your review settings.

Copilot AI review requested due to automatic review settings January 13, 2026 00:04
@coderabbitai
Copy link

coderabbitai bot commented Jan 13, 2026

Warning

Rate limit exceeded

@lodewiges has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 minutes and 53 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 26764d1 and 5af5889.

📒 Files selected for processing (8)
  • app/controllers/activities_controller.rb
  • app/controllers/invoices_controller.rb
  • app/controllers/orders_controller.rb
  • app/controllers/products_controller.rb
  • app/controllers/users_controller.rb
  • app/policies/invoice_policy.rb
  • app/policies/order_policy.rb
  • app/policies/product_policy.rb
📝 Walkthrough

Walkthrough

This PR refactors attribute permission handling across eight controllers and their corresponding policies, centralizing attribute whitelisting from controller methods to policy-driven permitted_attributes methods. Each controller now delegates parameter permitting to its policy instead of managing rules directly.

Changes

Cohort / File(s) Summary
Controller Parameter Helpers
app/controllers/activities_controller.rb, app/controllers/credit_mutations_controller.rb, app/controllers/invoices_controller.rb, app/controllers/orders_controller.rb, app/controllers/price_lists_controller.rb, app/controllers/products_controller.rb, app/controllers/sofia_accounts_controller.rb, app/controllers/users_controller.rb
Replaced direct parameter permitting with policy-driven private helper methods (activity_params, credit_mutation_params, invoice_params, order_params, order_params_for_update, price_list_params, product_params, sofia_account_params, user_params). Each helper delegates to policy(...).permitted_attributes. Removed legacy permitted_attributes methods from controllers.
Application Policy Foundation
app/policies/application_policy.rb
Added default permitted_attributes method returning empty array, establishing base hook for subclass policies.
Model Policies — Attribute Definitions
app/policies/activity_policy.rb, app/policies/credit_mutation_policy.rb, app/policies/invoice_policy.rb, app/policies/order_policy.rb, app/policies/price_list_policy.rb, app/policies/product_policy.rb, app/policies/sofia_account_policy.rb, app/policies/user_policy.rb
Added permitted_attributes and action-specific variants (permitted_attributes_for_create, permitted_attributes_for_update, permitted_attributes_for_update_with_sofia_account) defining allowed attributes for mass assignment per model and action.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Permissions flow through policies now,
No more scattered rules, we know and how!
From controllers freed, to policies they go,
Centralized wisdom helps the whole thing grow! ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description is incomplete. It lacks a proper summary section and does not follow the template structure with checklist, detailed summary, and other information sections. Expand the description to include: completion of the checklist items, a detailed summary of what was changed and why, any UI/screenshot impacts, and other relevant information about the refactor.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'move Permited Atributes to Policy' clearly describes the main change: refactoring parameter whitelisting from controllers to policy classes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @app/policies/invoice_policy.rb:
- Around line 14-19: permitted_attributes currently permits :rows alongside
rows_attributes which allows unintended mass-assignment of the association; edit
the permitted_attributes method to remove the :rows symbol so only
rows_attributes (i.e. { rows_attributes: %i[name amount price] }) is allowed,
leaving the rest of the array (:user_id, :activity_id, :name_override,
:email_override) unchanged.

In @app/policies/product_policy.rb:
- Around line 10-15: Remove the top-level :id from the array returned by
permitted_attributes in ProductPolicy; keep the nested :id inside the
product_prices_attributes hash and preserve :_destroy there, so modify the
permitted_attributes method to list only :name, :category, :color, :requires_age
and the nested { product_prices_attributes: %i[id product_id price_list_id price
_destroy] } while ensuring the top-level :id is not included.
🧹 Nitpick comments (2)
app/controllers/credit_mutations_controller.rb (1)

35-45: Consider making helper methods private.

The which_redirect? and model_includes methods (lines 35-41) appear to be internal helpers but are currently public. Consider moving them below the private keyword along with credit_mutation_params for consistency.

♻️ Suggested refactor
   end
   end
 
+  private
+
   def which_redirect?
     request.referer || @mutation.user
   end
 
   def model_includes
     %i[user activity created_by]
   end
 
   def credit_mutation_params
     params.require(:credit_mutation).permit(policy(CreditMutation).permitted_attributes)
   end
 end
app/controllers/users_controller.rb (1)

91-91: Inline permit vs helper method - minor inconsistency.

The update action inlines the params.require(:user).permit(...) call, while create uses the user_params helper. This is functionally correct but slightly inconsistent in style.

Consider extracting to a helper for consistency (optional)
  def update
    @user = User.find(params[:id])
    authorize @user

-    if @user.update(params.require(:user).permit(policy(@user).permitted_attributes_for_update))
+    if @user.update(user_update_params)
      flash[:success] = 'Gebruiker geupdate'
    else
      flash[:error] = "Gebruiker updaten mislukt; #{@user.errors.full_messages.join(', ')}"
    end

    redirect_to @user
  end

Then add a private helper:

def user_update_params
  params.require(:user).permit(policy(@user).permitted_attributes_for_update)
end
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4a331ed and 26764d1.

📒 Files selected for processing (17)
  • app/controllers/activities_controller.rb
  • app/controllers/credit_mutations_controller.rb
  • app/controllers/invoices_controller.rb
  • app/controllers/orders_controller.rb
  • app/controllers/price_lists_controller.rb
  • app/controllers/products_controller.rb
  • app/controllers/sofia_accounts_controller.rb
  • app/controllers/users_controller.rb
  • app/policies/activity_policy.rb
  • app/policies/application_policy.rb
  • app/policies/credit_mutation_policy.rb
  • app/policies/invoice_policy.rb
  • app/policies/order_policy.rb
  • app/policies/price_list_policy.rb
  • app/policies/product_policy.rb
  • app/policies/sofia_account_policy.rb
  • app/policies/user_policy.rb
🧰 Additional context used
🧬 Code graph analysis (11)
app/policies/sofia_account_policy.rb (8)
app/policies/activity_policy.rb (1)
  • permitted_attributes (52-54)
app/policies/application_policy.rb (1)
  • permitted_attributes (41-43)
app/policies/credit_mutation_policy.rb (1)
  • permitted_attributes (20-22)
app/policies/invoice_policy.rb (1)
  • permitted_attributes (14-19)
app/policies/order_policy.rb (1)
  • permitted_attributes (20-22)
app/policies/price_list_policy.rb (1)
  • permitted_attributes (40-42)
app/policies/product_policy.rb (1)
  • permitted_attributes (10-15)
app/policies/user_policy.rb (1)
  • permitted_attributes (26-28)
app/policies/invoice_policy.rb (8)
app/policies/activity_policy.rb (1)
  • permitted_attributes (52-54)
app/policies/application_policy.rb (1)
  • permitted_attributes (41-43)
app/policies/credit_mutation_policy.rb (1)
  • permitted_attributes (20-22)
app/policies/order_policy.rb (1)
  • permitted_attributes (20-22)
app/policies/price_list_policy.rb (1)
  • permitted_attributes (40-42)
app/policies/product_policy.rb (1)
  • permitted_attributes (10-15)
app/policies/sofia_account_policy.rb (1)
  • permitted_attributes (22-24)
app/policies/user_policy.rb (1)
  • permitted_attributes (26-28)
app/controllers/products_controller.rb (2)
app/policies/application_policy.rb (1)
  • permitted_attributes (41-43)
app/policies/product_policy.rb (1)
  • permitted_attributes (10-15)
app/controllers/sofia_accounts_controller.rb (2)
app/policies/application_policy.rb (1)
  • permitted_attributes (41-43)
app/policies/sofia_account_policy.rb (1)
  • permitted_attributes (22-24)
app/controllers/price_lists_controller.rb (2)
app/policies/application_policy.rb (1)
  • permitted_attributes (41-43)
app/policies/price_list_policy.rb (1)
  • permitted_attributes (40-42)
app/policies/application_policy.rb (8)
app/policies/activity_policy.rb (1)
  • permitted_attributes (52-54)
app/policies/credit_mutation_policy.rb (1)
  • permitted_attributes (20-22)
app/policies/invoice_policy.rb (1)
  • permitted_attributes (14-19)
app/policies/order_policy.rb (1)
  • permitted_attributes (20-22)
app/policies/price_list_policy.rb (1)
  • permitted_attributes (40-42)
app/policies/product_policy.rb (1)
  • permitted_attributes (10-15)
app/policies/sofia_account_policy.rb (1)
  • permitted_attributes (22-24)
app/policies/user_policy.rb (1)
  • permitted_attributes (26-28)
app/controllers/orders_controller.rb (3)
app/controllers/users_controller.rb (1)
  • update (87-98)
app/policies/order_policy.rb (2)
  • permitted_attributes_for_create (24-26)
  • permitted_attributes_for_update (28-30)
app/policies/user_policy.rb (1)
  • permitted_attributes_for_update (30-32)
app/controllers/activities_controller.rb (3)
app/controllers/users_controller.rb (1)
  • update (87-98)
app/policies/activity_policy.rb (2)
  • permitted_attributes (52-54)
  • permitted_attributes_for_update (56-58)
app/policies/application_policy.rb (1)
  • permitted_attributes (41-43)
app/controllers/users_controller.rb (3)
app/policies/user_policy.rb (3)
  • permitted_attributes_for_update (30-32)
  • permitted_attributes_for_update_with_sofia_account (34-38)
  • permitted_attributes (26-28)
app/policies/application_policy.rb (1)
  • permitted_attributes (41-43)
app/policies/sofia_account_policy.rb (1)
  • permitted_attributes (22-24)
app/controllers/invoices_controller.rb (3)
app/helpers/application_helper.rb (1)
  • remove_empty (19-27)
app/policies/application_policy.rb (1)
  • permitted_attributes (41-43)
app/policies/invoice_policy.rb (1)
  • permitted_attributes (14-19)
app/controllers/credit_mutations_controller.rb (3)
app/policies/application_policy.rb (1)
  • permitted_attributes (41-43)
app/policies/credit_mutation_policy.rb (1)
  • permitted_attributes (20-22)
app/policies/user_policy.rb (1)
  • permitted_attributes (26-28)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Agent
  • GitHub Check: Build
🔇 Additional comments (21)
app/policies/application_policy.rb (1)

41-43: LGTM!

Good default implementation returning an empty array. This ensures no attributes are permitted unless explicitly whitelisted by subclass policies, following the principle of least privilege.

app/policies/price_list_policy.rb (1)

40-42: LGTM!

Clean implementation following the established pattern. Minimal whitelist with only the :name attribute.

app/policies/credit_mutation_policy.rb (1)

20-22: LGTM!

Clean implementation. The permitted attributes align well with the create? authorization logic that enforces activity presence for main bartenders.

app/controllers/invoices_controller.rb (2)

30-30: LGTM!

The refactoring correctly delegates parameter whitelisting to the policy layer via invoice_params, maintaining the existing remove_empty processing for empty value handling.


83-85: LGTM!

The invoice_params method correctly follows the policy-driven pattern using policy(Invoice).permitted_attributes. Using the class Invoice (rather than an instance) is appropriate here since this is used in the create action before an instance exists.

app/policies/order_policy.rb (1)

19-30: LGTM!

Good separation of concerns with action-specific permitted attributes:

  • Base permitted_attributes for common fields
  • permitted_attributes_for_create extends base with nested order_rows_attributes including product_id
  • permitted_attributes_for_update restricts to only product_count changes, appropriately preventing product_id modification on updates

The composition pattern using permitted_attributes + [...] in permitted_attributes_for_create is clean and maintainable.

app/controllers/products_controller.rb (2)

8-8: LGTM!

Both create and update actions correctly use the refactored product_params method.

Also applies to: 21-21


34-36: LGTM!

The product_params method correctly uses policy(Product).permitted_attributes. Since ProductPolicy has a single permitted_attributes method (without action-specific variants), using the class-based policy lookup is appropriate for both create and update actions.

app/controllers/orders_controller.rb (3)

19-19: LGTM!

The create action correctly uses order_params with the created_by merge, delegating attribute whitelisting to the policy layer.


46-46: LGTM!

The update action correctly uses the dedicated order_params_for_update helper.


86-92: LGTM!

Good implementation of action-specific parameter helpers:

  • order_params uses class-based policy(Order) with permitted_attributes_for_create (appropriate before instance exists)
  • order_params_for_update uses instance-based policy(@order) with permitted_attributes_for_update (proper Pundit pattern for existing records)
app/controllers/credit_mutations_controller.rb (1)

17-17: LGTM!

The create action correctly uses credit_mutation_params with the created_by merge, consistent with the pattern used in orders_controller.rb.

app/controllers/price_lists_controller.rb (1)

79-81: LGTM!

The refactor correctly delegates attribute whitelisting to the policy. Using policy(PriceList) with the class is appropriate here since permitted_attributes in PriceListPolicy doesn't depend on the record's state.

app/policies/user_policy.rb (1)

26-38: LGTM!

The three permission methods appropriately separate concerns:

  • permitted_attributes for creation includes provider/sub_provider
  • permitted_attributes_for_update excludes provider fields (correct - shouldn't change after creation)
  • permitted_attributes_for_update_with_sofia_account correctly uses conditional logic for treasurer-only fields

The nested sofia_account_attributes syntax is correct for Rails strong parameters.

app/controllers/users_controller.rb (2)

75-76: LGTM!

Correctly uses user_params which delegates to policy-based whitelisting for user creation.


168-170: LGTM!

The user_params helper correctly delegates to the policy for attribute whitelisting during user creation.

app/policies/sofia_account_policy.rb (1)

22-24: LGTM!

The permitted_attributes method correctly defines the whitelist for direct Sofia account updates, including password management attributes. This is consistent with the policy-driven approach used across the codebase.

app/policies/activity_policy.rb (1)

52-58: LGTM!

The separation between create and update permissions is appropriate:

  • permitted_attributes allows full configuration on creation
  • permitted_attributes_for_update restricts to only title, preventing changes to start_time, end_time, and price_list_id after creation

This restrictive update policy appears intentional to maintain data integrity for activities that may have associated orders and invoices.

app/controllers/sofia_accounts_controller.rb (1)

18-18: LGTM! Clean delegation to policy for permitted attributes.

The implementation correctly uses policy(SofiaAccount) (class-based) since there's no instance available at create time, and the method naming follows Rails conventions.

Also applies to: 259-261

app/controllers/activities_controller.rb (2)

48-48: LGTM! Correct class-based policy delegation for create action.

Using policy(Activity) (class-based) is appropriate here since the instance doesn't exist yet at parameter parsing time.

Also applies to: 180-182


64-64: LGTM! Correct instance-based policy delegation for update action.

Using policy(@activity) allows the policy to make context-aware decisions about permitted attributes based on the specific record being updated. This pattern correctly differs from the create action approach.

Also applies to: 184-186

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the application to move permitted attributes from controller private methods to policy classes, centralizing authorization logic. This change follows a common Rails pattern where Pundit policies not only handle authorization but also define which attributes are permitted for different operations.

Changes:

  • Added permitted_attributes methods to all policy classes (ApplicationPolicy, UserPolicy, SofiaAccountPolicy, ProductPolicy, PriceListPolicy, OrderPolicy, InvoicePolicy, CreditMutationPolicy, and ActivityPolicy)
  • Updated all controller methods to call policy permitted_attributes methods instead of locally defined private methods
  • Renamed controller private methods from permitted_attributes to more descriptive names like user_params, product_params, etc.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 16 comments.

Show a summary per file
File Description
app/policies/application_policy.rb Added base permitted_attributes method returning empty array
app/policies/user_policy.rb Added three permitted attributes methods for different update scenarios
app/policies/sofia_account_policy.rb Added permitted_attributes method for sofia account creation
app/policies/product_policy.rb Added permitted_attributes method including nested product_prices_attributes
app/policies/price_list_policy.rb Added simple permitted_attributes method for name field
app/policies/order_policy.rb Added three permitted attributes methods for base, create, and update operations
app/policies/invoice_policy.rb Added permitted_attributes method including nested rows_attributes
app/policies/credit_mutation_policy.rb Added permitted_attributes method for credit mutation fields
app/policies/activity_policy.rb Added two permitted attributes methods for create and update scenarios
app/controllers/users_controller.rb Updated to use policy permitted_attributes; renamed method to user_params
app/controllers/sofia_accounts_controller.rb Updated to use policy permitted_attributes; renamed method to sofia_account_params
app/controllers/products_controller.rb Updated to use policy permitted_attributes; renamed method to product_params
app/controllers/price_lists_controller.rb Updated to use policy permitted_attributes; renamed method to price_list_params
app/controllers/orders_controller.rb Updated to use policy permitted_attributes; renamed methods to order_params and order_params_for_update
app/controllers/invoices_controller.rb Updated to use policy permitted_attributes; renamed method to invoice_params
app/controllers/credit_mutations_controller.rb Updated to use policy permitted_attributes; renamed method to credit_mutation_params
app/controllers/activities_controller.rb Updated to use policy permitted_attributes; added activity_params and activity_params_for_update methods

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

params.require(:order).permit(%i[user_id paid_with_cash paid_with_pin activity_id],
order_rows_attributes: %i[id product_id product_count])
def order_params
params.require(:order).permit(policy(Order).permitted_attributes_for_create)
Copy link

Copilot AI Jan 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The policy is called with the Order class instead of an instance. This will cause an error when trying to access permitted_attributes_for_create from the policy, as Pundit expects to initialize policies with a user and a record. You should pass an instance like Order.new here.

Suggested change
params.require(:order).permit(policy(Order).permitted_attributes_for_create)
params.require(:order).permit(policy(Order.new).permitted_attributes_for_create)

Copilot uses AI. Check for mistakes.
Comment on lines +40 to +42
def permitted_attributes
%i[name]
end
Copy link

Copilot AI Jan 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The newly added permitted_attributes method lacks test coverage. Since this repository has comprehensive controller tests, consider adding tests to verify that the permitted_attributes method returns the correct attributes.

Copilot uses AI. Check for mistakes.
Comment on lines +20 to +22
def permitted_attributes
%i[description amount user_id activity_id]
end
Copy link

Copilot AI Jan 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The newly added permitted_attributes method lacks test coverage. Since this repository has comprehensive controller tests, consider adding tests to verify that the permitted_attributes method returns the correct attributes.

Copilot uses AI. Check for mistakes.
@codecov
Copy link

codecov bot commented Jan 13, 2026

Codecov Report

❌ Patch coverage is 98.50746% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 78.18%. Comparing base (4a331ed) to head (5af5889).
⚠️ Report is 3 commits behind head on staging.

Files with missing lines Patch % Lines
app/policies/application_policy.rb 50.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           staging    #1200      +/-   ##
===========================================
+ Coverage    77.68%   78.18%   +0.49%     
===========================================
  Files           54       54              
  Lines         1340     1375      +35     
===========================================
+ Hits          1041     1075      +34     
- Misses         299      300       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@lodewiges lodewiges added the prio label Jan 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Permitted Attributes naar policy verplaatsen

2 participants