fix(security): gate copilot on Captain feature and guard enterprise agent seat race

This commit is contained in:
Tanmay Deep Sharma
2026-07-22 17:36:08 +05:30
parent 746abd8b28
commit 33913d9585
5 changed files with 10 additions and 2 deletions
@@ -1,4 +1,4 @@
class Api::V1::Accounts::Captain::CopilotMessagesController < Api::V1::Accounts::BaseController
class Api::V1::Accounts::Captain::CopilotMessagesController < Api::V1::Accounts::Captain::BaseController
before_action :set_copilot_thread
def index
@@ -1,4 +1,4 @@
class Api::V1::Accounts::Captain::CopilotThreadsController < Api::V1::Accounts::BaseController
class Api::V1::Accounts::Captain::CopilotThreadsController < Api::V1::Accounts::Captain::BaseController
before_action :ensure_message, only: :create
def index
@@ -12,6 +12,10 @@ module Enterprise::Api::V1::Accounts::AgentsController
private
def associate_agent_with_custom_role
# `super` may render payment-required without creating an agent (seat limit lost in the locked check);
# skip the association so that response is preserved instead of raising on a nil agent.
return if @agent.blank?
@agent.current_account_user.update!(custom_role_id: params[:custom_role_id])
end
end
@@ -6,6 +6,8 @@ RSpec.describe 'Api::V1::Accounts::Captain::CopilotMessagesController', type: :r
let(:copilot_thread) { create(:captain_copilot_thread, account: account, user: user) }
let!(:copilot_message) { create(:captain_copilot_message, copilot_thread: copilot_thread, account: account) }
before { account.enable_features!('captain_integration') }
describe 'GET /api/v1/accounts/{account.id}/captain/copilot_threads/{thread.id}/copilot_messages' do
context 'when it is an authenticated user' do
it 'returns all messages' do
@@ -6,6 +6,8 @@ RSpec.describe 'Api::V1::Accounts::Captain::CopilotThreads', type: :request do
let(:agent) { create(:user, account: account, role: :agent) }
let(:conversation) { create(:conversation, account: account) }
before { account.enable_features!('captain_integration') }
def json_response
JSON.parse(response.body, symbolize_names: true)
end