Page MenuHomePhabricator

D10913.id36668.diff
No OneTemporary

D10913.id36668.diff

diff --git a/lib/actions/upload-actions.js b/lib/actions/upload-actions.js
--- a/lib/actions/upload-actions.js
+++ b/lib/actions/upload-actions.js
@@ -5,6 +5,7 @@
import blobService from '../facts/blob-service.js';
import { extractKeyserverIDFromID } from '../keyserver-conn/keyserver-call-utils.js';
import type { CallKeyserverEndpoint } from '../keyserver-conn/keyserver-conn-types.js';
+import type { AuthMetadata } from '../shared/identity-client-context.js';
import type { UploadMultimediaResult, Dimensions } from '../types/media-types';
import { toBase64URL } from '../utils/base64.js';
import {
@@ -15,7 +16,10 @@
import type { CallSingleKeyserverEndpoint } from '../utils/call-single-keyserver-endpoint.js';
import { getMessageForException } from '../utils/errors.js';
import { useKeyserverCall } from '../utils/keyserver-call.js';
-import { handleHTTPResponseError } from '../utils/services-utils.js';
+import {
+ handleHTTPResponseError,
+ createDefaultHTTPRequestHeaders,
+} from '../utils/services-utils.js';
import { type UploadBlob } from '../utils/upload-blob.js';
export type MultimediaUploadCallbacks = Partial<{
@@ -140,17 +144,19 @@
export type BlobServiceUploadAction = (input: {
+uploadInput: BlobServiceUploadInput,
+keyserverOrThreadID: string,
+ +authMetadata: AuthMetadata,
+callbacks?: MultimediaUploadCallbacks,
}) => Promise<BlobServiceUploadResult>;
const blobServiceUpload =
(callKeyserverEndpoint: CallKeyserverEndpoint): BlobServiceUploadAction =>
async input => {
- const { uploadInput, callbacks, keyserverOrThreadID } = input;
+ const { uploadInput, callbacks, keyserverOrThreadID, authMetadata } = input;
const { encryptionKey, loop, dimensions, thumbHash, blobInput } =
uploadInput;
const blobHolder = uuid.v4();
const blobHash = toBase64URL(uploadInput.blobHash);
+ const defaultHeaders = createDefaultHTTPRequestHeaders(authMetadata);
// 1. Assign new holder for blob with given blobHash
let blobAlreadyExists: boolean;
@@ -165,6 +171,7 @@
blob_hash: blobHash,
}),
headers: {
+ ...defaultHeaders,
'content-type': 'application/json',
},
},
@@ -196,6 +203,7 @@
blobHash,
blobInput,
},
+ authMetadata,
{ ...callbacks },
);
} catch (e) {
diff --git a/lib/utils/blob-service-upload.js b/lib/utils/blob-service-upload.js
--- a/lib/utils/blob-service-upload.js
+++ b/lib/utils/blob-service-upload.js
@@ -3,10 +3,12 @@
import invariant from 'invariant';
import _throttle from 'lodash/throttle.js';
+import { createHTTPAuthorizationHeader } from './services-utils.js';
import type {
MultimediaUploadCallbacks,
BlobServiceUploadFile,
} from '../actions/upload-actions.js';
+import type { AuthMetadata } from '../shared/identity-client-context.js';
function blobServiceUploadHandler(
url: string,
@@ -15,6 +17,7 @@
blobHash: string,
blobInput: BlobServiceUploadFile,
},
+ authMetadata: AuthMetadata,
options?: ?MultimediaUploadCallbacks,
): Promise<void> {
if (input.blobInput.type !== 'file') {
@@ -28,6 +31,9 @@
const xhr = new XMLHttpRequest();
xhr.open(method, url);
+ const authHeader = createHTTPAuthorizationHeader(authMetadata);
+ xhr.setRequestHeader('Authorization', authHeader);
+
const { timeout, onProgress, abortHandler } = options ?? {};
if (timeout) {
diff --git a/native/avatars/avatar-hooks.js b/native/avatars/avatar-hooks.js
--- a/native/avatars/avatar-hooks.js
+++ b/native/avatars/avatar-hooks.js
@@ -18,6 +18,7 @@
extensionFromFilename,
filenameFromPathOrURI,
} from 'lib/media/file-utils.js';
+import { IdentityClientContext } from 'lib/shared/identity-client-context.js';
import type {
AvatarDBContent,
UpdateUserAvatarRequest,
@@ -55,6 +56,10 @@
}
function useUploadProcessedMedia(): MediaResult => Promise<?AvatarDBContent> {
+ const identityContext = React.useContext(IdentityClientContext);
+ invariant(identityContext, 'Identity context should be set');
+ const { getAuthMetadata } = identityContext;
+
const callUploadMultimedia = useLegacyAshoatKeyserverCall(uploadMultimedia);
const callBlobServiceUpload = useBlobServiceUpload();
const uploadProcessedMultimedia: MediaResult => Promise<?AvatarDBContent> =
@@ -94,6 +99,7 @@
dimensions,
thumbHash,
} = encryptionResult;
+ const authMetadata = await getAuthMetadata();
const { id } = await callBlobServiceUpload({
uploadInput: {
blobInput: {
@@ -108,6 +114,7 @@
thumbHash,
loop: false,
},
+ authMetadata,
keyserverOrThreadID: ashoatKeyserverID,
callbacks: { blobServiceUploadHandler },
});
@@ -116,7 +123,7 @@
}
return { type: 'encrypted_image', uploadID: id };
},
- [callUploadMultimedia, callBlobServiceUpload],
+ [callUploadMultimedia, callBlobServiceUpload, getAuthMetadata],
);
return uploadProcessedMultimedia;
}
diff --git a/native/input/input-state-container.react.js b/native/input/input-state-container.react.js
--- a/native/input/input-state-container.react.js
+++ b/native/input/input-state-container.react.js
@@ -39,6 +39,8 @@
combineLoadingStatuses,
createLoadingStatusSelector,
} from 'lib/selectors/loading-selectors.js';
+import { IdentityClientContext } from 'lib/shared/identity-client-context.js';
+import type { IdentityClientContextType } from 'lib/shared/identity-client-context.js';
import {
createMediaMessageInfo,
useMessageCreationSideEffectsFunc,
@@ -158,6 +160,7 @@
+sendTextMessage: (input: SendTextMessageInput) => Promise<SendMessageResult>,
+newThread: (request: ClientNewThreadRequest) => Promise<NewThreadResult>,
+textMessageCreationSideEffectsFunc: CreationSideEffectsFunc<RawTextMessageInfo>,
+ +identityContext: ?IdentityClientContextType,
};
type State = {
+pendingUploads: PendingMultimediaUploads,
@@ -820,7 +823,7 @@
const { uploadURI, filename, mime } = processedMedia;
- const { hasWiFi } = this.props;
+ const { hasWiFi, identityContext } = this.props;
const uploadStart = Date.now();
let uploadExceptionMessage,
@@ -833,41 +836,52 @@
(processedMedia.mediaType === 'encrypted_photo' ||
processedMedia.mediaType === 'encrypted_video')
) {
- const uploadPromise = this.props.blobServiceUpload({
- uploadInput: {
- blobInput: {
- type: 'uri',
- uri: uploadURI,
- filename: filename,
- mimeType: mime,
+ invariant(identityContext, 'Identity context should be set');
+ const uploadPromise = (async () => {
+ invariant(
+ processedMedia.mediaType === 'encrypted_photo' ||
+ processedMedia.mediaType === 'encrypted_video',
+ 'expected encrypted media',
+ );
+ const authMetadata = await identityContext.getAuthMetadata();
+ return this.props.blobServiceUpload({
+ uploadInput: {
+ blobInput: {
+ type: 'uri',
+ uri: uploadURI,
+ filename: filename,
+ mimeType: mime,
+ },
+ blobHash: processedMedia.blobHash,
+ encryptionKey: processedMedia.encryptionKey,
+ dimensions: processedMedia.dimensions,
+ thumbHash:
+ processedMedia.mediaType === 'encrypted_photo'
+ ? processedMedia.thumbHash
+ : null,
},
- blobHash: processedMedia.blobHash,
- encryptionKey: processedMedia.encryptionKey,
- dimensions: processedMedia.dimensions,
- thumbHash:
- processedMedia.mediaType === 'encrypted_photo'
- ? processedMedia.thumbHash
- : null,
- },
- keyserverOrThreadID: threadInfo.id,
- callbacks: {
- blobServiceUploadHandler,
- onProgress: (percent: number) => {
- this.setProgress(
- localMessageID,
- localMediaID,
- 'uploading',
- percent,
- );
+ authMetadata,
+ keyserverOrThreadID: threadInfo.id,
+ callbacks: {
+ blobServiceUploadHandler,
+ onProgress: (percent: number) => {
+ this.setProgress(
+ localMessageID,
+ localMediaID,
+ 'uploading',
+ percent,
+ );
+ },
},
- },
- });
+ });
+ })();
const uploadThumbnailPromise: Promise<?BlobServiceUploadResult> =
(async () => {
if (processedMedia.mediaType !== 'encrypted_video') {
return undefined;
}
+ const authMetadata = await identityContext.getAuthMetadata();
return await this.props.blobServiceUpload({
uploadInput: {
blobInput: {
@@ -882,6 +896,7 @@
dimensions: processedMedia.dimensions,
thumbHash: processedMedia.thumbHash,
},
+ authMetadata,
keyserverOrThreadID: threadInfo.id,
callbacks: {
blobServiceUploadHandler,
@@ -1720,6 +1735,7 @@
const staffCanSee = useStaffCanSee();
const textMessageCreationSideEffectsFunc =
useMessageCreationSideEffectsFunc<RawTextMessageInfo>(messageTypes.TEXT);
+ const identityContext = React.useContext(IdentityClientContext);
return (
<InputStateContainer
@@ -1740,6 +1756,7 @@
dispatch={dispatch}
staffCanSee={staffCanSee}
textMessageCreationSideEffectsFunc={textMessageCreationSideEffectsFunc}
+ identityContext={identityContext}
/>
);
});
diff --git a/native/utils/blob-service-upload.js b/native/utils/blob-service-upload.js
--- a/native/utils/blob-service-upload.js
+++ b/native/utils/blob-service-upload.js
@@ -6,11 +6,13 @@
import { pathFromURI } from 'lib/media/file-utils.js';
import type { BlobServiceUploadHandler } from 'lib/utils/blob-service-upload.js';
import { getMessageForException } from 'lib/utils/errors.js';
+import { createDefaultHTTPRequestHeaders } from 'lib/utils/services-utils.js';
const blobServiceUploadHandler: BlobServiceUploadHandler = async (
url,
method,
input,
+ authMetadata,
options,
) => {
if (input.blobInput.type !== 'uri') {
@@ -23,6 +25,7 @@
path = resolvedPath;
}
}
+ const headers = authMetadata && createDefaultHTTPRequestHeaders(authMetadata);
const uploadTask = FileSystem.createUploadTask(
url,
path,
@@ -31,6 +34,7 @@
fieldName: 'blob_data',
httpMethod: method,
parameters: { blob_hash: input.blobHash },
+ headers,
},
uploadProgress => {
if (options?.onProgress) {
diff --git a/web/avatars/avatar-hooks.react.js b/web/avatars/avatar-hooks.react.js
--- a/web/avatars/avatar-hooks.react.js
+++ b/web/avatars/avatar-hooks.react.js
@@ -1,11 +1,13 @@
// @flow
+import invariant from 'invariant';
import * as React from 'react';
import {
uploadMultimedia,
useBlobServiceUpload,
} from 'lib/actions/upload-actions.js';
+import { IdentityClientContext } from 'lib/shared/identity-client-context.js';
import type { UpdateUserAvatarRequest } from 'lib/types/avatar-types.js';
import { useLegacyAshoatKeyserverCall } from 'lib/utils/action-utils.js';
import { ashoatKeyserverID } from 'lib/utils/validation-utils.js';
@@ -18,6 +20,10 @@
const useBlobServiceUploads = false;
function useUploadAvatarMedia(): File => Promise<UpdateUserAvatarRequest> {
+ const identityContext = React.useContext(IdentityClientContext);
+ invariant(identityContext, 'Identity context should be set');
+ const { getAuthMetadata } = identityContext;
+
const callUploadMultimedia = useLegacyAshoatKeyserverCall(uploadMultimedia);
const callBlobServiceUpload = useBlobServiceUpload();
const uploadAvatarMedia = React.useCallback(
@@ -56,6 +62,7 @@
? thumbHashResult.thumbHash
: null;
+ const authMetadata = await getAuthMetadata();
const { id } = await callBlobServiceUpload({
uploadInput: {
blobInput: {
@@ -68,13 +75,14 @@
loop: false,
thumbHash,
},
+ authMetadata,
keyserverOrThreadID: ashoatKeyserverID,
callbacks: {},
});
return { type: 'encrypted_image', uploadID: id };
},
- [callBlobServiceUpload, callUploadMultimedia],
+ [callBlobServiceUpload, callUploadMultimedia, getAuthMetadata],
);
return uploadAvatarMedia;
}
diff --git a/web/input/input-state-container.react.js b/web/input/input-state-container.react.js
--- a/web/input/input-state-container.react.js
+++ b/web/input/input-state-container.react.js
@@ -41,6 +41,8 @@
import commStaffCommunity from 'lib/facts/comm-staff-community.js';
import { getNextLocalUploadID } from 'lib/media/media-utils.js';
import { pendingToRealizedThreadIDsSelector } from 'lib/selectors/thread-selectors.js';
+import { IdentityClientContext } from 'lib/shared/identity-client-context.js';
+import type { IdentityClientContextType } from 'lib/shared/identity-client-context.js';
import {
createMediaMessageInfo,
localIDPrefix,
@@ -95,6 +97,7 @@
} from 'lib/utils/redux-promise-utils.js';
import { useDispatch } from 'lib/utils/redux-utils.js';
import { generateReportID } from 'lib/utils/report-utils.js';
+import { createDefaultHTTPRequestHeaders } from 'lib/utils/services-utils.js';
import {
type BaseInputState,
@@ -151,6 +154,7 @@
+registerSendCallback: (() => mixed) => void,
+unregisterSendCallback: (() => mixed) => void,
+textMessageCreationSideEffectsFunc: CreationSideEffectsFunc<RawTextMessageInfo>,
+ +identityContext: ?IdentityClientContextType,
};
type WritableState = {
pendingUploads: {
@@ -867,6 +871,9 @@
const steps = [...upload.steps];
let userTime;
+ const { identityContext } = this.props;
+ invariant(identityContext, 'Identity context should be set');
+
const sendReport = (missionResult: MediaMissionResult) => {
const newThreadID = this.getRealizedOrPendingThreadID(threadID);
const latestUpload = this.state.pendingUploads[newThreadID][localID];
@@ -907,6 +914,8 @@
encryptionKey && blobHash && dimensions,
'incomplete encrypted upload',
);
+
+ const authMetadata = await identityContext.getAuthMetadata();
uploadResult = await this.props.blobServiceUpload({
uploadInput: {
blobInput: {
@@ -920,6 +929,7 @@
thumbHash,
},
keyserverOrThreadID: threadID,
+ authMetadata,
callbacks,
});
} else {
@@ -1011,7 +1021,10 @@
});
if (encryptionKey) {
- const { steps: preloadSteps } = await preloadMediaResource(result.uri);
+ const { steps: preloadSteps } = await preloadMediaResource(
+ result.uri,
+ authMetadata,
+ );
steps.push(...preloadSteps);
} else {
const { steps: preloadSteps } = await preloadImage(result.uri);
@@ -1202,6 +1215,8 @@
keyserverOrThreadID: threadID,
});
if (isBlobServiceURI(pendingUpload.uri)) {
+ const identityContext = this.props.identityContext;
+ invariant(identityContext, 'Identity context should be set');
invariant(
pendingUpload.blobHolder,
'blob service upload has no holder',
@@ -1209,16 +1224,22 @@
const endpoint = blobService.httpEndpoints.DELETE_BLOB;
const holder = pendingUpload.blobHolder;
const blobHash = blobHashFromBlobServiceURI(pendingUpload.uri);
- void fetch(makeBlobServiceEndpointURL(endpoint), {
- method: endpoint.method,
- body: JSON.stringify({
- holder,
- blob_hash: blobHash,
- }),
- headers: {
- 'content-type': 'application/json',
- },
- });
+ void (async () => {
+ const authMetadata = await identityContext.getAuthMetadata();
+ const defaultHeaders =
+ createDefaultHTTPRequestHeaders(authMetadata);
+ await fetch(makeBlobServiceEndpointURL(endpoint), {
+ method: endpoint.method,
+ body: JSON.stringify({
+ holder,
+ blob_hash: blobHash,
+ }),
+ headers: {
+ ...defaultHeaders,
+ 'content-type': 'application/json',
+ },
+ });
+ })();
}
}
const newPendingUploads = _omit([localUploadID])(currentPendingUploads);
@@ -1656,6 +1677,7 @@
const dispatch = useDispatch();
const dispatchActionPromise = useDispatchActionPromise();
const modalContext = useModalContext();
+ const identityContext = React.useContext(IdentityClientContext);
const [sendCallbacks, setSendCallbacks] = React.useState<
$ReadOnlyArray<() => mixed>,
@@ -1696,6 +1718,7 @@
registerSendCallback={registerSendCallback}
unregisterSendCallback={unregisterSendCallback}
textMessageCreationSideEffectsFunc={textMessageCreationSideEffectsFunc}
+ identityContext={identityContext}
/>
);
});
diff --git a/web/media/media-utils.js b/web/media/media-utils.js
--- a/web/media/media-utils.js
+++ b/web/media/media-utils.js
@@ -5,13 +5,16 @@
import { thumbHashToDataURL } from 'thumbhash';
import { fetchableMediaURI } from 'lib/media/media-utils.js';
+import type { AuthMetadata } from 'lib/shared/identity-client-context.js';
import type {
MediaType,
Dimensions,
MediaMissionStep,
MediaMissionFailure,
} from 'lib/types/media-types.js';
+import { isBlobServiceURI } from 'lib/utils/blob-service.js';
import { getMessageForException } from 'lib/utils/errors.js';
+import { createDefaultHTTPRequestHeaders } from 'lib/utils/services-utils.js';
import { probeFile } from './blob-utils.js';
import { decryptThumbhashToDataURL } from './encryption-utils.js';
@@ -64,15 +67,23 @@
* @returns Steps and the result of the preload. The preload is successful
* if the HTTP response is OK (20x).
*/
-async function preloadMediaResource(uri: string): Promise<{
+async function preloadMediaResource(
+ uri: string,
+ authMetadata: AuthMetadata,
+): Promise<{
steps: $ReadOnlyArray<MediaMissionStep>,
result: { +success: boolean },
}> {
+ let headers;
+ if (isBlobServiceURI(uri)) {
+ headers = createDefaultHTTPRequestHeaders(authMetadata);
+ }
+
const start = Date.now();
const mediaURI = fetchableMediaURI(uri);
let success, exceptionMessage;
try {
- const response = await fetch(mediaURI);
+ const response = await fetch(mediaURI, { headers });
// we need to read the blob to make sure the browser caches it
await response.blob();
success = response.ok;

File Metadata

Mime Type
text/plain
Expires
Mon, Nov 25, 12:57 AM (20 h, 57 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2577851
Default Alt Text
D10913.id36668.diff (18 KB)

Event Timeline