Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3357669
D10913.id36668.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
18 KB
Referenced Files
None
Subscribers
None
D10913.id36668.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D10913: [native][web] Add AuthMetadata to Blob service upload actions
Attached
Detach File
Event Timeline
Log In to Comment