Switch to the /v2/ storage-service endpoints for group operations
This commit is contained in:
parent
711f7d3352
commit
408444352f
6 changed files with 68 additions and 36 deletions
|
@ -211,7 +211,7 @@
|
||||||
"@formatjs/intl": "2.6.7",
|
"@formatjs/intl": "2.6.7",
|
||||||
"@indutny/rezip-electron": "1.3.1",
|
"@indutny/rezip-electron": "1.3.1",
|
||||||
"@mixer/parallel-prettier": "2.0.3",
|
"@mixer/parallel-prettier": "2.0.3",
|
||||||
"@signalapp/mock-server": "6.3.0",
|
"@signalapp/mock-server": "6.4.1",
|
||||||
"@storybook/addon-a11y": "7.4.5",
|
"@storybook/addon-a11y": "7.4.5",
|
||||||
"@storybook/addon-actions": "7.4.5",
|
"@storybook/addon-actions": "7.4.5",
|
||||||
"@storybook/addon-controls": "7.4.5",
|
"@storybook/addon-controls": "7.4.5",
|
||||||
|
|
|
@ -220,6 +220,19 @@ message GroupChange {
|
||||||
uint32 changeEpoch = 3; // Allows clients to decide whether their change logic can successfully apply this diff
|
uint32 changeEpoch = 3; // Allows clients to decide whether their change logic can successfully apply this diff
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// External credentials
|
||||||
|
|
||||||
|
message ExternalGroupCredential {
|
||||||
|
string token = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// API responses
|
||||||
|
|
||||||
|
message GroupResponse {
|
||||||
|
Group group = 1;
|
||||||
|
bytes groupSendEndorsementResponse = 2;
|
||||||
|
}
|
||||||
|
|
||||||
message GroupChanges {
|
message GroupChanges {
|
||||||
message GroupChangeState {
|
message GroupChangeState {
|
||||||
GroupChange groupChange = 1;
|
GroupChange groupChange = 1;
|
||||||
|
@ -227,6 +240,12 @@ message GroupChanges {
|
||||||
}
|
}
|
||||||
|
|
||||||
repeated GroupChangeState groupChanges = 1;
|
repeated GroupChangeState groupChanges = 1;
|
||||||
|
bytes groupSendEndorsementResponse = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GroupChangeResponse {
|
||||||
|
GroupChange groupChange = 1;
|
||||||
|
bytes groupSendEndorsementResponse = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message GroupAttributeBlob {
|
message GroupAttributeBlob {
|
||||||
|
@ -238,10 +257,6 @@ message GroupAttributeBlob {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
message GroupExternalCredential {
|
|
||||||
string token = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message GroupInviteLink {
|
message GroupInviteLink {
|
||||||
message GroupInviteLinkContentsV1 {
|
message GroupInviteLinkContentsV1 {
|
||||||
bytes groupMasterKey = 1;
|
bytes groupMasterKey = 1;
|
||||||
|
|
16
ts/groups.ts
16
ts/groups.ts
|
@ -1446,7 +1446,7 @@ async function uploadGroupChange({
|
||||||
actions: Proto.GroupChange.IActions;
|
actions: Proto.GroupChange.IActions;
|
||||||
group: ConversationAttributesType;
|
group: ConversationAttributesType;
|
||||||
inviteLinkPassword?: string;
|
inviteLinkPassword?: string;
|
||||||
}): Promise<Proto.IGroupChange> {
|
}): Promise<Proto.IGroupChangeResponse> {
|
||||||
const logId = idForLogging(group.groupId);
|
const logId = idForLogging(group.groupId);
|
||||||
|
|
||||||
// Ensure we have the credentials we need before attempting GroupsV2 operations
|
// Ensure we have the credentials we need before attempting GroupsV2 operations
|
||||||
|
@ -1552,11 +1552,13 @@ export async function modifyGroupV2({
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upload. If we don't have permission, the server will return an error here.
|
// Upload. If we don't have permission, the server will return an error here.
|
||||||
const groupChange = await uploadGroupChange({
|
const groupChangeResponse = await uploadGroupChange({
|
||||||
actions,
|
actions,
|
||||||
inviteLinkPassword,
|
inviteLinkPassword,
|
||||||
group: conversation.attributes,
|
group: conversation.attributes,
|
||||||
});
|
});
|
||||||
|
const { groupChange } = groupChangeResponse;
|
||||||
|
strictAssert(groupChange, 'missing groupChange');
|
||||||
|
|
||||||
const groupChangeBuffer =
|
const groupChangeBuffer =
|
||||||
Proto.GroupChange.encode(groupChange).finish();
|
Proto.GroupChange.encode(groupChange).finish();
|
||||||
|
@ -2738,12 +2740,13 @@ export async function respondToGroupV2Migration({
|
||||||
`respondToGroupV2Migration/${logId}: Failed to access log endpoint; fetching full group state`
|
`respondToGroupV2Migration/${logId}: Failed to access log endpoint; fetching full group state`
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
firstGroupState = await makeRequestWithCredentials({
|
const groupResponse = await makeRequestWithCredentials({
|
||||||
logId: `getGroup/${logId}`,
|
logId: `getGroup/${logId}`,
|
||||||
publicParams,
|
publicParams,
|
||||||
secretParams,
|
secretParams,
|
||||||
request: (sender, options) => sender.getGroup(options),
|
request: (sender, options) => sender.getGroup(options),
|
||||||
});
|
});
|
||||||
|
firstGroupState = groupResponse.group;
|
||||||
} catch (secondError) {
|
} catch (secondError) {
|
||||||
if (secondError.code === GROUP_ACCESS_DENIED_CODE) {
|
if (secondError.code === GROUP_ACCESS_DENIED_CODE) {
|
||||||
log.info(
|
log.info(
|
||||||
|
@ -3652,13 +3655,16 @@ async function updateGroupViaState({
|
||||||
throw new Error('updateGroupViaState: group was missing publicParams!');
|
throw new Error('updateGroupViaState: group was missing publicParams!');
|
||||||
}
|
}
|
||||||
|
|
||||||
const groupState = await makeRequestWithCredentials({
|
const groupResponse = await makeRequestWithCredentials({
|
||||||
logId: `getGroup/${logId}`,
|
logId: `getGroup/${logId}`,
|
||||||
publicParams,
|
publicParams,
|
||||||
secretParams,
|
secretParams,
|
||||||
request: (sender, requestOptions) => sender.getGroup(requestOptions),
|
request: (sender, requestOptions) => sender.getGroup(requestOptions),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const groupState = groupResponse.group;
|
||||||
|
strictAssert(groupState, 'Group state must be present');
|
||||||
|
|
||||||
const decryptedGroupState = decryptGroupState(
|
const decryptedGroupState = decryptGroupState(
|
||||||
groupState,
|
groupState,
|
||||||
secretParams,
|
secretParams,
|
||||||
|
@ -3792,7 +3798,7 @@ async function updateGroupViaLogs({
|
||||||
// `integrateGroupChanges`.
|
// `integrateGroupChanges`.
|
||||||
let revisionToFetch = isNumber(currentRevision) ? currentRevision : undefined;
|
let revisionToFetch = isNumber(currentRevision) ? currentRevision : undefined;
|
||||||
|
|
||||||
let response;
|
let response: GroupLogResponseType;
|
||||||
const changes: Array<Proto.IGroupChanges> = [];
|
const changes: Array<Proto.IGroupChanges> = [];
|
||||||
do {
|
do {
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
|
|
@ -2153,7 +2153,7 @@ export default class MessageSender {
|
||||||
async createGroup(
|
async createGroup(
|
||||||
group: Readonly<Proto.IGroup>,
|
group: Readonly<Proto.IGroup>,
|
||||||
options: Readonly<GroupCredentialsType>
|
options: Readonly<GroupCredentialsType>
|
||||||
): Promise<void> {
|
): Promise<Proto.IGroupResponse> {
|
||||||
return this.server.createGroup(group, options);
|
return this.server.createGroup(group, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2166,7 +2166,7 @@ export default class MessageSender {
|
||||||
|
|
||||||
async getGroup(
|
async getGroup(
|
||||||
options: Readonly<GroupCredentialsType>
|
options: Readonly<GroupCredentialsType>
|
||||||
): Promise<Proto.Group> {
|
): Promise<Proto.IGroupResponse> {
|
||||||
return this.server.getGroup(options);
|
return this.server.getGroup(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2192,7 +2192,7 @@ export default class MessageSender {
|
||||||
changes: Readonly<Proto.GroupChange.IActions>,
|
changes: Readonly<Proto.GroupChange.IActions>,
|
||||||
options: Readonly<GroupCredentialsType>,
|
options: Readonly<GroupCredentialsType>,
|
||||||
inviteLinkBase64?: string
|
inviteLinkBase64?: string
|
||||||
): Promise<Proto.IGroupChange> {
|
): Promise<Proto.IGroupChangeResponse> {
|
||||||
return this.server.modifyGroup(changes, options, inviteLinkBase64);
|
return this.server.modifyGroup(changes, options, inviteLinkBase64);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2243,8 +2243,8 @@ export default class MessageSender {
|
||||||
|
|
||||||
async getGroupMembershipToken(
|
async getGroupMembershipToken(
|
||||||
options: Readonly<GroupCredentialsType>
|
options: Readonly<GroupCredentialsType>
|
||||||
): Promise<Proto.GroupExternalCredential> {
|
): Promise<Proto.IExternalGroupCredential> {
|
||||||
return this.server.getGroupExternalCredential(options);
|
return this.server.getExternalGroupCredential(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendChallengeResponse(
|
public async sendChallengeResponse(
|
||||||
|
|
|
@ -538,9 +538,9 @@ const URL_CALLS = {
|
||||||
getBackupCDNCredentials: 'v1/archives/auth/read',
|
getBackupCDNCredentials: 'v1/archives/auth/read',
|
||||||
getBackupUploadForm: 'v1/archives/upload/form',
|
getBackupUploadForm: 'v1/archives/upload/form',
|
||||||
getBackupMediaUploadForm: 'v1/archives/media/upload/form',
|
getBackupMediaUploadForm: 'v1/archives/media/upload/form',
|
||||||
groupLog: 'v1/groups/logs',
|
groupLog: 'v2/groups/logs',
|
||||||
groupJoinedAtVersion: 'v1/groups/joined_at_version',
|
groupJoinedAtVersion: 'v1/groups/joined_at_version',
|
||||||
groups: 'v1/groups',
|
groups: 'v2/groups',
|
||||||
groupsViaLink: 'v1/groups/join/',
|
groupsViaLink: 'v1/groups/join/',
|
||||||
groupToken: 'v1/groups/token',
|
groupToken: 'v1/groups/token',
|
||||||
keys: 'v2/keys',
|
keys: 'v2/keys',
|
||||||
|
@ -715,6 +715,7 @@ export type GroupLogResponseType = {
|
||||||
start?: number;
|
start?: number;
|
||||||
end?: number;
|
end?: number;
|
||||||
changes: Proto.GroupChanges;
|
changes: Proto.GroupChanges;
|
||||||
|
groupSendEndorsementResponse: Uint8Array | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ProfileRequestDataType = {
|
export type ProfileRequestDataType = {
|
||||||
|
@ -1144,7 +1145,7 @@ export type WebAPIType = {
|
||||||
createGroup: (
|
createGroup: (
|
||||||
group: Proto.IGroup,
|
group: Proto.IGroup,
|
||||||
options: GroupCredentialsType
|
options: GroupCredentialsType
|
||||||
) => Promise<void>;
|
) => Promise<Proto.IGroupResponse>;
|
||||||
deleteUsername: (abortSignal?: AbortSignal) => Promise<void>;
|
deleteUsername: (abortSignal?: AbortSignal) => Promise<void>;
|
||||||
downloadOnboardingStories: (
|
downloadOnboardingStories: (
|
||||||
version: string,
|
version: string,
|
||||||
|
@ -1172,7 +1173,7 @@ export type WebAPIType = {
|
||||||
}) => Promise<Readable>;
|
}) => Promise<Readable>;
|
||||||
getAvatar: (path: string) => Promise<Uint8Array>;
|
getAvatar: (path: string) => Promise<Uint8Array>;
|
||||||
getHasSubscription: (subscriberId: Uint8Array) => Promise<boolean>;
|
getHasSubscription: (subscriberId: Uint8Array) => Promise<boolean>;
|
||||||
getGroup: (options: GroupCredentialsType) => Promise<Proto.Group>;
|
getGroup: (options: GroupCredentialsType) => Promise<Proto.IGroupResponse>;
|
||||||
getGroupFromLink: (
|
getGroupFromLink: (
|
||||||
inviteLinkPassword: string | undefined,
|
inviteLinkPassword: string | undefined,
|
||||||
auth: GroupCredentialsType
|
auth: GroupCredentialsType
|
||||||
|
@ -1181,9 +1182,9 @@ export type WebAPIType = {
|
||||||
getGroupCredentials: (
|
getGroupCredentials: (
|
||||||
options: GetGroupCredentialsOptionsType
|
options: GetGroupCredentialsOptionsType
|
||||||
) => Promise<GetGroupCredentialsResultType>;
|
) => Promise<GetGroupCredentialsResultType>;
|
||||||
getGroupExternalCredential: (
|
getExternalGroupCredential: (
|
||||||
options: GroupCredentialsType
|
options: GroupCredentialsType
|
||||||
) => Promise<Proto.GroupExternalCredential>;
|
) => Promise<Proto.IExternalGroupCredential>;
|
||||||
getGroupLog: (
|
getGroupLog: (
|
||||||
options: GetGroupLogOptionsType,
|
options: GetGroupLogOptionsType,
|
||||||
credentials: GroupCredentialsType
|
credentials: GroupCredentialsType
|
||||||
|
@ -1253,7 +1254,7 @@ export type WebAPIType = {
|
||||||
changes: Proto.GroupChange.IActions,
|
changes: Proto.GroupChange.IActions,
|
||||||
options: GroupCredentialsType,
|
options: GroupCredentialsType,
|
||||||
inviteLinkBase64?: string
|
inviteLinkBase64?: string
|
||||||
) => Promise<Proto.IGroupChange>;
|
) => Promise<Proto.IGroupChangeResponse>;
|
||||||
modifyStorageRecords: MessageSender['modifyStorageRecords'];
|
modifyStorageRecords: MessageSender['modifyStorageRecords'];
|
||||||
postBatchIdentityCheck: (
|
postBatchIdentityCheck: (
|
||||||
elements: VerifyServiceIdRequestType
|
elements: VerifyServiceIdRequestType
|
||||||
|
@ -1662,7 +1663,7 @@ export function initialize({
|
||||||
getGroup,
|
getGroup,
|
||||||
getGroupAvatar,
|
getGroupAvatar,
|
||||||
getGroupCredentials,
|
getGroupCredentials,
|
||||||
getGroupExternalCredential,
|
getExternalGroupCredential,
|
||||||
getGroupFromLink,
|
getGroupFromLink,
|
||||||
getGroupLog,
|
getGroupLog,
|
||||||
getHasSubscription,
|
getHasSubscription,
|
||||||
|
@ -3605,9 +3606,9 @@ export function initialize({
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getGroupExternalCredential(
|
async function getExternalGroupCredential(
|
||||||
options: GroupCredentialsType
|
options: GroupCredentialsType
|
||||||
): Promise<Proto.GroupExternalCredential> {
|
): Promise<Proto.IExternalGroupCredential> {
|
||||||
const basicAuth = generateGroupAuth(
|
const basicAuth = generateGroupAuth(
|
||||||
options.groupPublicParamsHex,
|
options.groupPublicParamsHex,
|
||||||
options.authCredentialPresentationHex
|
options.authCredentialPresentationHex
|
||||||
|
@ -3623,7 +3624,7 @@ export function initialize({
|
||||||
disableSessionResumption: true,
|
disableSessionResumption: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
return Proto.GroupExternalCredential.decode(response);
|
return Proto.ExternalGroupCredential.decode(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
function verifyAttributes(attributes: Proto.IAvatarUploadAttributes) {
|
function verifyAttributes(attributes: Proto.IAvatarUploadAttributes) {
|
||||||
|
@ -3727,14 +3728,14 @@ export function initialize({
|
||||||
async function createGroup(
|
async function createGroup(
|
||||||
group: Proto.IGroup,
|
group: Proto.IGroup,
|
||||||
options: GroupCredentialsType
|
options: GroupCredentialsType
|
||||||
): Promise<void> {
|
): Promise<Proto.IGroupResponse> {
|
||||||
const basicAuth = generateGroupAuth(
|
const basicAuth = generateGroupAuth(
|
||||||
options.groupPublicParamsHex,
|
options.groupPublicParamsHex,
|
||||||
options.authCredentialPresentationHex
|
options.authCredentialPresentationHex
|
||||||
);
|
);
|
||||||
const data = Proto.Group.encode(group).finish();
|
const data = Proto.Group.encode(group).finish();
|
||||||
|
|
||||||
await _ajax({
|
const response = await _ajax({
|
||||||
basicAuth,
|
basicAuth,
|
||||||
call: 'groups',
|
call: 'groups',
|
||||||
contentType: 'application/x-protobuf',
|
contentType: 'application/x-protobuf',
|
||||||
|
@ -3742,12 +3743,15 @@ export function initialize({
|
||||||
host: storageUrl,
|
host: storageUrl,
|
||||||
disableSessionResumption: true,
|
disableSessionResumption: true,
|
||||||
httpType: 'PUT',
|
httpType: 'PUT',
|
||||||
|
responseType: 'bytes',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return Proto.GroupResponse.decode(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getGroup(
|
async function getGroup(
|
||||||
options: GroupCredentialsType
|
options: GroupCredentialsType
|
||||||
): Promise<Proto.Group> {
|
): Promise<Proto.IGroupResponse> {
|
||||||
const basicAuth = generateGroupAuth(
|
const basicAuth = generateGroupAuth(
|
||||||
options.groupPublicParamsHex,
|
options.groupPublicParamsHex,
|
||||||
options.authCredentialPresentationHex
|
options.authCredentialPresentationHex
|
||||||
|
@ -3763,7 +3767,7 @@ export function initialize({
|
||||||
responseType: 'bytes',
|
responseType: 'bytes',
|
||||||
});
|
});
|
||||||
|
|
||||||
return Proto.Group.decode(response);
|
return Proto.GroupResponse.decode(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getGroupFromLink(
|
async function getGroupFromLink(
|
||||||
|
@ -3799,7 +3803,7 @@ export function initialize({
|
||||||
changes: Proto.GroupChange.IActions,
|
changes: Proto.GroupChange.IActions,
|
||||||
options: GroupCredentialsType,
|
options: GroupCredentialsType,
|
||||||
inviteLinkBase64?: string
|
inviteLinkBase64?: string
|
||||||
): Promise<Proto.IGroupChange> {
|
): Promise<Proto.IGroupChangeResponse> {
|
||||||
const basicAuth = generateGroupAuth(
|
const basicAuth = generateGroupAuth(
|
||||||
options.groupPublicParamsHex,
|
options.groupPublicParamsHex,
|
||||||
options.authCredentialPresentationHex
|
options.authCredentialPresentationHex
|
||||||
|
@ -3826,7 +3830,7 @@ export function initialize({
|
||||||
: undefined,
|
: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
return Proto.GroupChange.decode(response);
|
return Proto.GroupChangeResponse.decode(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getGroupLog(
|
async function getGroupLog(
|
||||||
|
@ -3876,6 +3880,10 @@ export function initialize({
|
||||||
disableSessionResumption: true,
|
disableSessionResumption: true,
|
||||||
httpType: 'GET',
|
httpType: 'GET',
|
||||||
responseType: 'byteswithdetails',
|
responseType: 'byteswithdetails',
|
||||||
|
headers: {
|
||||||
|
// TODO(jamie): To be implmented in DESKTOP-699
|
||||||
|
'Cached-Send-Endorsements': '0',
|
||||||
|
},
|
||||||
urlParameters:
|
urlParameters:
|
||||||
`/${startVersion}?` +
|
`/${startVersion}?` +
|
||||||
`includeFirstState=${Boolean(includeFirstState)}&` +
|
`includeFirstState=${Boolean(includeFirstState)}&` +
|
||||||
|
@ -3884,6 +3892,7 @@ export function initialize({
|
||||||
});
|
});
|
||||||
const { data, response } = withDetails;
|
const { data, response } = withDetails;
|
||||||
const changes = Proto.GroupChanges.decode(data);
|
const changes = Proto.GroupChanges.decode(data);
|
||||||
|
const { groupSendEndorsementResponse } = changes;
|
||||||
|
|
||||||
if (response && response.status === 206) {
|
if (response && response.status === 206) {
|
||||||
const range = response.headers.get('Content-Range');
|
const range = response.headers.get('Content-Range');
|
||||||
|
@ -3904,12 +3913,14 @@ export function initialize({
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
currentRevision,
|
currentRevision,
|
||||||
|
groupSendEndorsementResponse,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
changes,
|
changes,
|
||||||
|
groupSendEndorsementResponse,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4001,10 +4001,10 @@
|
||||||
type-fest "^3.5.0"
|
type-fest "^3.5.0"
|
||||||
uuid "^8.3.0"
|
uuid "^8.3.0"
|
||||||
|
|
||||||
"@signalapp/mock-server@6.3.0":
|
"@signalapp/mock-server@6.4.1":
|
||||||
version "6.3.0"
|
version "6.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/@signalapp/mock-server/-/mock-server-6.3.0.tgz#5715fc1ff4517310caacc767ab4790530f11c673"
|
resolved "https://registry.yarnpkg.com/@signalapp/mock-server/-/mock-server-6.4.1.tgz#b49700f8d43b0c76d3f02820dd3b3da82a910f12"
|
||||||
integrity sha512-mC4QXqS7+MH1p3U7kTuUqJsFUHfBZ6wemuzvQvhk3+4bRoc77Wynu1uIN0WRLhx/faOGwBkSiAWNiLhQt0Vscw==
|
integrity sha512-is75JwGL2CjLJ3NakMxw6rkgx379aKc3n328lSaiwLKVgBpuG/ms8wF3fNALxFstKoMl41lPzooOMWeqm+ubVQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@signalapp/libsignal-client" "^0.42.0"
|
"@signalapp/libsignal-client" "^0.42.0"
|
||||||
debug "^4.3.2"
|
debug "^4.3.2"
|
||||||
|
|
Loading…
Reference in a new issue