Conversation attachment uploads now go through the same authentication that every other account-scoped API endpoint uses. Agents continue to attach files exactly as before, and the upload request is now tied to the agent's dashboard session instead of a separately serialized access token. Because the upload request is now authenticated, the dashboard proves the agent's session directly instead of passing `currentUser.access_token`. This keeps uploads working alongside the profile access-token changes in #14973, including on accounts where that token is serialized as empty. ## What changed - `Api::V1::Accounts::Conversations::DirectUploadsController` now runs the standard account auth stack: API access token when the `api_access_token` header is present, dashboard session (devise-token-auth) otherwise, with agent-bot tokens rejected. Previously it inherited `ActiveStorage::DirectUploadsController` directly and did not run any authentication. - `EnsureCurrentAccountHelper#ensure_current_account` now returns `401` when a request has neither an authenticated user nor a bot resource, instead of continuing. This closes the same gap for any controller that relies on the helper. - The dashboard direct-upload paths (`useFileUpload.js` and the legacy `fileUploadMixin.js`) now attach the agent's session headers to the upload request via a new `directUploadsHelper.js`, instead of sending `currentUser.access_token`. ## How to test 1. As a logged-in agent, open a conversation and attach a file. Upload should succeed as before, on installs with direct uploads enabled. 2. Confirm attachments still work for an agent on an account whose profile access token is not serialized (e.g. a Cloud plan without `api_and_webhooks`). 3. Send a `POST` to `/api/v1/accounts/:account_id/conversations/:conversation_id/direct_uploads` with no credentials, an empty `api_access_token`, or an invalid token, and confirm it returns `401`. 4. Confirm a valid agent of the account (via API token or session) gets `200`, while an agent of a different account gets `401`.
62 lines
1.7 KiB
JavaScript
62 lines
1.7 KiB
JavaScript
import { setDirectUploadAuthHeaders } from '../directUploadsHelper';
|
|
import Auth from 'dashboard/api/auth';
|
|
|
|
vi.mock('dashboard/api/auth', () => ({
|
|
default: { getAuthData: vi.fn() },
|
|
}));
|
|
|
|
describe('setDirectUploadAuthHeaders', () => {
|
|
const buildXhr = () => ({ setRequestHeader: vi.fn() });
|
|
|
|
afterEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it('sets the five session auth headers from the auth cookie', () => {
|
|
Auth.getAuthData.mockReturnValue({
|
|
'access-token': 'token-123',
|
|
'token-type': 'Bearer',
|
|
client: 'client-123',
|
|
expiry: '9999',
|
|
uid: 'agent@example.com',
|
|
});
|
|
const xhr = buildXhr();
|
|
|
|
setDirectUploadAuthHeaders(xhr);
|
|
|
|
expect(xhr.setRequestHeader).toHaveBeenCalledTimes(5);
|
|
expect(xhr.setRequestHeader).toHaveBeenCalledWith(
|
|
'access-token',
|
|
'token-123'
|
|
);
|
|
expect(xhr.setRequestHeader).toHaveBeenCalledWith('token-type', 'Bearer');
|
|
expect(xhr.setRequestHeader).toHaveBeenCalledWith('client', 'client-123');
|
|
expect(xhr.setRequestHeader).toHaveBeenCalledWith('expiry', '9999');
|
|
expect(xhr.setRequestHeader).toHaveBeenCalledWith(
|
|
'uid',
|
|
'agent@example.com'
|
|
);
|
|
});
|
|
|
|
it('does not set any header when there is no auth data', () => {
|
|
Auth.getAuthData.mockReturnValue(false);
|
|
const xhr = buildXhr();
|
|
|
|
setDirectUploadAuthHeaders(xhr);
|
|
|
|
expect(xhr.setRequestHeader).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('does not set any header when the access token is missing', () => {
|
|
Auth.getAuthData.mockReturnValue({
|
|
client: 'client-123',
|
|
uid: 'agent@example.com',
|
|
});
|
|
const xhr = buildXhr();
|
|
|
|
setDirectUploadAuthHeaders(xhr);
|
|
|
|
expect(xhr.setRequestHeader).not.toHaveBeenCalled();
|
|
});
|
|
});
|