fix: harden article author updates (#15229)
## Description Article edits now ignore an `author_id` that does not belong to the current account, retain the existing author, and still apply other valid article changes. Article creation continues to reject cross-account authors with a generic validation error and without creating a record. The authenticated article serializers still omit authors without a current-account membership so existing forged or stale records cannot expose agent profile fields. The guard now checks `current_account_user` directly to make that intent explicit. ## Closes Follow-up to [CW-7665](https://linear.app/chatwoot/issue/CW-7665) and [#15191](https://github.com/chatwoot/chatwoot/pull/15191). ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? - Creating an article with a cross-account author returns `422` and does not create an article. - Updating an article with a cross-account author retains the previous author while applying other valid attributes. - Articles whose previous author is no longer an account member can still be edited without exposing that author's agent profile. - Existing OSS and Enterprise article request coverage passes locally. ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] Any dependent changes have been merged and published in downstream modules Co-authored-by: Vishnu Narayanan <iamwishnu@gmail.com>
This commit is contained in:
@@ -52,14 +52,15 @@ RSpec.describe 'Api::V1::Accounts::Articles', type: :request do
|
||||
author_id: foreign_user.id
|
||||
}
|
||||
}
|
||||
post "/api/v1/accounts/#{account.id}/portals/#{portal.slug}/articles",
|
||||
params: article_params,
|
||||
headers: admin.create_new_auth_token
|
||||
expect do
|
||||
post "/api/v1/accounts/#{account.id}/portals/#{portal.slug}/articles",
|
||||
params: article_params,
|
||||
headers: admin.create_new_auth_token
|
||||
end.not_to(change { portal.articles.count })
|
||||
|
||||
expect(response).to have_http_status(:unprocessable_entity)
|
||||
expect(response.parsed_body).to eq('error' => 'Invalid author ID')
|
||||
expect(response.body).not_to include(foreign_user.email)
|
||||
expect(portal.articles.where(author_id: foreign_user.id)).to be_empty
|
||||
end
|
||||
|
||||
it 'rejects a malformed author_id without raising' do
|
||||
@@ -205,16 +206,18 @@ RSpec.describe 'Api::V1::Accounts::Articles', type: :request do
|
||||
expect(json_response['payload']['position']).to eql(article_params[:article][:position])
|
||||
end
|
||||
|
||||
it 'does not expose a cross-account author set through update' do
|
||||
it 'ignores a cross-account author while updating other attributes' do
|
||||
foreign_user = create(:user, account: create(:account), role: :agent)
|
||||
|
||||
put "/api/v1/accounts/#{account.id}/portals/#{portal.slug}/articles/#{article.id}",
|
||||
params: { article: { author_id: foreign_user.id } },
|
||||
params: { article: { title: 'Updated title', author_id: foreign_user.id } },
|
||||
headers: admin.create_new_auth_token
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(response.parsed_body.dig('payload', 'title')).to eq('Updated title')
|
||||
expect(response.parsed_body.dig('payload', 'author', 'id')).to eq(agent.id)
|
||||
expect(response.body).not_to include(foreign_user.email)
|
||||
expect(response.parsed_body['payload']).not_to have_key('author')
|
||||
expect(article.reload.author_id).to eq(agent.id)
|
||||
end
|
||||
|
||||
it 'allows editing an article whose author is no longer an account member' do
|
||||
|
||||
Reference in New Issue
Block a user