From 560b65be162f0bf78f14b70e837f3418ce1f3167 Mon Sep 17 00:00:00 2001 From: MAEDA Go Date: Tue, 3 Nov 2026 17:16:13 +0900 Subject: [PATCH] Allow unassigning an issue via the REST API with a null assignee Issue#safe_attributes= skipped assigned_to_id when its value was nil, so sending "assigned_to_id": null in JSON or an empty element in XML was silently ignored and the assignee was kept. Co-Authored-By: Claude Opus 5.5 --- app/models/issue.rb | 5 +++-- test/integration/api_test/issues_test.rb | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/app/models/issue.rb b/app/models/issue.rb index 23ab7532d..868b21831 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -631,8 +631,9 @@ class Issue < ApplicationRecord !attrs.key?('is_private') && safe_attribute?('is_private', user) attrs['is_private'] = '1' end - if (u = attrs.delete('assigned_to_id')) && safe_attribute?('assigned_to_id') - self.assigned_to_id = u + if attrs.key?('assigned_to_id') + assigned_to_id = attrs.delete('assigned_to_id') + self.assigned_to_id = assigned_to_id if safe_attribute?('assigned_to_id') end attrs = delete_unsafe_attributes(attrs, user) return if attrs.empty? diff --git a/test/integration/api_test/issues_test.rb b/test/integration/api_test/issues_test.rb index 4ee007eb9..c723c67e6 100644 --- a/test/integration/api_test/issues_test.rb +++ b/test/integration/api_test/issues_test.rb @@ -1107,6 +1107,24 @@ class Redmine::ApiTest::IssuesTest < Redmine::ApiTest::Base assert_select 'errors error', :text => "Assignee is invalid" end + test "PUT /issues/:id.xml with empty assignee should unassign the issue" do + put( + '/issues/2.xml', + :params => '', + :headers => {'CONTENT_TYPE' => 'application/xml'}.merge(credentials('jsmith'))) + assert_response :no_content + assert_nil Issue.find(2).assigned_to_id + end + + test "PUT /issues/:id.json with null assignee should unassign the issue" do + put( + '/issues/2.json', + :params => {:issue => {:assigned_to_id => nil}}.to_json, + :headers => {'CONTENT_TYPE' => 'application/json'}.merge(credentials('jsmith'))) + assert_response :no_content + assert_nil Issue.find(2).assigned_to_id + end + test "PUT /issues/:id.json" do assert_difference('Journal.count') do put( -- 2.55.0