From 7b42a0c974cdf89ddd3349f15045fae94a4fa9a3 Mon Sep 17 00:00:00 2001 From: Juliet Shin Date: Tue, 1 Sep 2026 15:04:23 -0700 Subject: [PATCH 1/3] fix(policy): harden authorization for plan resources Harden Pundit policies for contributor and research output endpoints to enforce the expected access controls. Reference: CDLUC3#803 - NOTE: The referenced PR also fixes app/controllers/paginable/research_outputs_controller.rb. However, that file does not exist within this codebase. Co-Authored-By: Juliet Shin <8452761+jupiter007@users.noreply.github.com> --- app/controllers/contributors_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/contributors_controller.rb b/app/controllers/contributors_controller.rb index e260dbd731..1d4e2719bc 100644 --- a/app/controllers/contributors_controller.rb +++ b/app/controllers/contributors_controller.rb @@ -12,13 +12,13 @@ class ContributorsController < ApplicationController # GET /plans/:plan_id/contributors def index - authorize @plan + authorize @plan, :show? @contributors = @plan.contributors end # GET /plans/:plan_id/contributors/new def new - authorize @plan + authorize @plan, :edit? default_org = @plan.org.present? ? @plan.org : current_user.org @contributor = Contributor.new(plan: @plan, org: default_org) end From 22c392de31605794d7d0008910a562833a76e24d Mon Sep 17 00:00:00 2001 From: aaronskiba <71047780+aaronskiba@users.noreply.github.com> Date: Thu, 3 Sep 2026 10:32:35 -0600 Subject: [PATCH 2/3] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f402b2e254..a344d9bbec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ # Changelog ## v5.0.3 -- Patch API token auth to check user's status +- Patch API token auth to check user's status [#3606](https://github.com/DMPRoadmap/roadmap/pull/3606) +- fix(policy): harden authorization for plan resources [#3613](https://github.com/DMPRoadmap/roadmap/pull/3613) ## v5.0.2 - Bump Ruby to v3.1.4 and use `.ruby-version` in CI From 5504508e2a4fab97350396fc403243f8ae7af5e1 Mon Sep 17 00:00:00 2001 From: aaronskiba <71047780+aaronskiba@users.noreply.github.com> Date: Wed, 9 Sep 2026 11:03:59 -0600 Subject: [PATCH 3/3] test(contributors_controller): cover unauthorized plan access Add specs for the contributors index and new endpoints to ensure unauthorized plans are denied access. --- .../contributors_controller_spec.rb | 51 ++++++++++++++----- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/spec/controllers/contributors_controller_spec.rb b/spec/controllers/contributors_controller_spec.rb index 1545c6a901..234625a910 100644 --- a/spec/controllers/contributors_controller_spec.rb +++ b/spec/controllers/contributors_controller_spec.rb @@ -7,6 +7,7 @@ @scheme = create(:identifier_scheme, name: 'orcid') @org = create(:org, managed: true) @plan = create(:plan, :creator, org: @org) + @unauthorized_plan = create(:plan) @user = @plan.owner @contributor = create(:contributor, plan: @plan, org: @org) @@ -36,20 +37,46 @@ sign_in(@user) end - it 'GET plans/:plan_id/contributors (:index)' do - get :index, params: { plan_id: @plan.id } - expect(response).to render_template(:index) - expect(assigns(:plan)).to eql(@plan) - expect(assigns(:contributors).length).to eql(1) - expect(assigns(:contributors).first).to eql(@contributor) + describe 'GET plans/:plan_id/contributors (:index)' do + it 'renders the index' do + get :index, params: { plan_id: @plan.id } + expect(response).to render_template(:index) + expect(assigns(:plan)).to eql(@plan) + expect(assigns(:contributors).length).to eql(1) + expect(assigns(:contributors).first).to eql(@contributor) + end + + it 'denies access to an unauthorized plan' do + get :index, params: { plan_id: @unauthorized_plan.id } + + expect(response).not_to render_template(:index) + expect(assigns(:contributors)).to eql(nil) + + expect(response).to have_http_status(:redirect) + expect(response).to redirect_to(plans_url) + expect(flash[:alert]).to eq('You are not authorized to perform this action.') + end end - it 'GET plans/:plan_id/contributors/new (:new)' do - get :new, params: { plan_id: @plan.id } - expect(response).to render_template(:new) - expect(assigns(:plan)).to eql(@plan) - expect(assigns(:contributor).new_record?).to eql(true) - expect(assigns(:contributor).plan).to eql(@plan) + describe 'GET plans/:plan_id/contributors/new (:new)' do + it 'renders the new form' do + get :new, params: { plan_id: @plan.id } + expect(response).to render_template(:new) + expect(assigns(:plan)).to eql(@plan) + expect(assigns(:contributor).new_record?).to eql(true) + expect(assigns(:contributor).plan).to eql(@plan) + end + + it 'denies access to an unauthorized plan' do + get :new, params: { plan_id: @unauthorized_plan.id } + + expect(response).not_to render_template(:new) + expect(assigns(:contributor)).to eql(nil) + + expect(response).to have_http_status(:redirect) + expect(response).to redirect_to(plans_url) + expect(flash[:alert]).to eq('You are not authorized to perform this action.') + end end it 'GET plans/:plan_id/contributors/:id/edit (:edit)' do