Skip to content

Commit cb1d67b

Browse files
authored
Merge pull request oxen-io#2571 from Bilb/fix-private-sogs-authentication
fix: include auth sogs headers for join room and file download
2 parents f0b0877 + ec5f330 commit cb1d67b

File tree

18 files changed

+166
-129
lines changed

18 files changed

+166
-129
lines changed

ts/components/dialog/SessionPasswordDialog.tsx

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,18 @@ export class SessionPasswordDialog extends React.Component<Props, State> {
4242
}
4343

4444
public componentDidMount() {
45+
document.addEventListener('keyup', this.onEnterPressed);
46+
4547
setTimeout(() => {
4648
// tslint:disable-next-line: no-unused-expression
4749
this.passportInput && this.passportInput.focus();
4850
}, 1);
4951
}
5052

53+
public componentWillUnmount() {
54+
document.removeEventListener('keyup', this.onEnterPressed);
55+
}
56+
5157
public render() {
5258
const { passwordAction } = this.props;
5359
let placeholders: Array<string> = [];
@@ -93,15 +99,17 @@ export class SessionPasswordDialog extends React.Component<Props, State> {
9399
this.passportInput = input;
94100
}}
95101
placeholder={placeholders[0]}
96-
onKeyUp={this.onPasswordInput}
102+
onChange={this.onPasswordInput}
103+
onPaste={this.onPasswordInput}
97104
data-testid="password-input"
98105
/>
99106
{passwordAction !== 'enter' && passwordAction !== 'remove' && (
100107
<input
101108
type="password"
102109
id="password-modal-input-confirm"
103110
placeholder={placeholders[1]}
104-
onKeyUp={this.onPasswordConfirmInput}
111+
onChange={this.onPasswordConfirmInput}
112+
onPaste={this.onPasswordConfirmInput}
105113
data-testid="password-input-confirm"
106114
/>
107115
)}
@@ -110,7 +118,8 @@ export class SessionPasswordDialog extends React.Component<Props, State> {
110118
type="password"
111119
id="password-modal-input-reconfirm"
112120
placeholder={placeholders[2]}
113-
onKeyUp={this.onPasswordRetypeInput}
121+
onPaste={this.onPasswordRetypeInput}
122+
onChange={this.onPasswordRetypeInput}
114123
data-testid="password-input-reconfirm"
115124
/>
116125
)}
@@ -258,6 +267,13 @@ export class SessionPasswordDialog extends React.Component<Props, State> {
258267
this.closeDialog();
259268
}
260269

270+
private async onEnterPressed(event: any) {
271+
if (event.key === 'Enter') {
272+
event.stopPropagation();
273+
return this.setPassword();
274+
}
275+
}
276+
261277
private async handleActionEnter(enteredPassword: string) {
262278
// be sure the password is valid
263279
if (!this.validatePassword(enteredPassword)) {
@@ -321,30 +337,18 @@ export class SessionPasswordDialog extends React.Component<Props, State> {
321337
window.inboxStore?.dispatch(sessionPassword(null));
322338
}
323339

324-
private async onPasswordInput(event: any) {
325-
if (event.key === 'Enter') {
326-
return this.setPassword();
327-
}
340+
private onPasswordInput(event: any) {
328341
const currentPasswordEntered = event.target.value;
329-
330342
this.setState({ currentPasswordEntered });
331343
}
332344

333-
private async onPasswordConfirmInput(event: any) {
334-
if (event.key === 'Enter') {
335-
return this.setPassword();
336-
}
345+
private onPasswordConfirmInput(event: any) {
337346
const currentPasswordConfirmEntered = event.target.value;
338-
339347
this.setState({ currentPasswordConfirmEntered });
340348
}
341349

342-
private async onPasswordRetypeInput(event: any) {
343-
if (event.key === 'Enter') {
344-
return this.setPassword();
345-
}
350+
private onPasswordRetypeInput(event: any) {
346351
const currentPasswordRetypeEntered = event.target.value;
347-
348352
this.setState({ currentPasswordRetypeEntered });
349353
}
350354
}

ts/components/settings/section/CategoryPrivacy.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ export const SettingsCategoryPrivacy = (props: {
9797
displayPasswordModal('change', props.onPasswordUpdated);
9898
}}
9999
buttonText={window.i18n('changePassword')}
100+
dataTestId="change-password-settings-button"
100101
/>
101102
)}
102103
{props.hasPassword && (
@@ -108,6 +109,7 @@ export const SettingsCategoryPrivacy = (props: {
108109
}}
109110
buttonColor={SessionButtonColor.Danger}
110111
buttonText={window.i18n('removePassword')}
112+
dataTestId="remove-password-settings-button"
111113
/>
112114
)}
113115
</>

ts/session/apis/open_group_api/opengroupV2/ApiUtil.ts

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -142,36 +142,33 @@ const defaultServerPublicKey = 'a03c383cf63c3c4efe67acc52112a6dd734b3a946b9545f4
142142
const defaultRoom = `${defaultServer}/main?public_key=${defaultServerPublicKey}`;
143143

144144
const loadDefaultRoomsSingle = () =>
145-
allowOnlyOneAtATime(
146-
'loadDefaultRoomsSingle',
147-
async (): Promise<Array<OpenGroupV2InfoJoinable>> => {
148-
const roomInfos = parseOpenGroupV2(defaultRoom);
149-
if (roomInfos) {
150-
try {
151-
const roomsGot = await getAllRoomInfos(roomInfos);
152-
153-
if (!roomsGot) {
154-
return [];
155-
}
156-
157-
return roomsGot.map(room => {
158-
return {
159-
...room,
160-
completeUrl: getCompleteUrlFromRoom({
161-
serverUrl: roomInfos.serverUrl,
162-
serverPublicKey: roomInfos.serverPublicKey,
163-
roomId: room.id,
164-
}),
165-
};
166-
});
167-
} catch (e) {
168-
window?.log?.warn('loadDefaultRoomloadDefaultRoomssIfNeeded failed', e);
145+
allowOnlyOneAtATime('loadDefaultRoomsSingle', async () => {
146+
const roomInfos = parseOpenGroupV2(defaultRoom);
147+
if (roomInfos) {
148+
try {
149+
const roomsGot = await getAllRoomInfos(roomInfos);
150+
151+
if (!roomsGot) {
152+
return [];
169153
}
170-
return [];
154+
155+
return roomsGot.map(room => {
156+
return {
157+
...room,
158+
completeUrl: getCompleteUrlFromRoom({
159+
serverUrl: roomInfos.serverUrl,
160+
serverPublicKey: roomInfos.serverPublicKey,
161+
roomId: room.id,
162+
}),
163+
};
164+
});
165+
} catch (e) {
166+
window?.log?.warn('loadDefaultRoomloadDefaultRoomssIfNeeded failed', e);
171167
}
172168
return [];
173169
}
174-
);
170+
return [];
171+
});
175172

176173
/**
177174
* Load to the cache all the details of the room of the default opengroupv2 server

ts/session/apis/open_group_api/opengroupV2/OpenGroupManagerV2.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export class OpenGroupManagerV2 {
4545
serverUrl: string,
4646
roomId: string,
4747
publicKey: string
48-
): Promise<ConversationModel> {
48+
): Promise<ConversationModel | undefined> {
4949
const oneAtaTimeStr = `oneAtaTimeOpenGroupV2Join:${serverUrl}${roomId}`;
5050
return allowOnlyOneAtATime(oneAtaTimeStr, async () => {
5151
return this.attemptConnectionV2(serverUrl, roomId, publicKey);

ts/session/apis/open_group_api/sogsv3/sogsV3Capabilities.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@ import { OpenGroupData, OpenGroupV2Room } from '../../../../data/opengroups';
55
import AbortController, { AbortSignal } from 'abort-controller';
66
import { batchGlobalIsSuccess } from './sogsV3BatchPoll';
77

8-
export const capabilitiesFetchForServer = async (
8+
const capabilitiesFetchForServer = async (
99
serverUrl: string,
1010
serverPubKey: string,
1111
abortSignal: AbortSignal
1212
): Promise<Array<string> | null> => {
1313
const endpoint = '/capabilities';
1414
const method = 'GET';
1515
const serverPubkey = serverPubKey;
16-
const blinded = false; // for capabilities, blinding is always false as the request will fail if the server requires blinding
16+
// for the capabilities call, we require blinded to be ON now. A sogs with blinding disabled will still allow this call and verify the blinded signature
17+
const blinded = true;
1718
const capabilityHeaders = await OpenGroupPollingUtils.getOurOpenGroupHeaders(
1819
serverPubkey,
1920
endpoint,
@@ -33,7 +34,6 @@ export const capabilitiesFetchForServer = async (
3334
serverPubkey,
3435
serverUrl,
3536
stringifiedBody: null,
36-
doNotIncludeOurSogsHeaders: true, // the first capabilities needs to not have any authentification to pass on a blinding-required sogs,
3737
headers: null,
3838
throwErrors: false,
3939
});

ts/session/apis/open_group_api/sogsv3/sogsV3FetchFile.ts

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import AbortController, { AbortSignal } from 'abort-controller';
22
import { isUndefined, toNumber } from 'lodash';
3-
import { OpenGroupV2Room, OpenGroupV2RoomWithImageID } from '../../../../data/opengroups';
3+
import {
4+
OpenGroupData,
5+
OpenGroupV2Room,
6+
OpenGroupV2RoomWithImageID,
7+
} from '../../../../data/opengroups';
48
import { MIME } from '../../../../types';
59
import { processNewAttachment } from '../../../../types/MessageAttachment';
610
import { callUtilsWorker } from '../../../../webworker/workers/util_worker_interface';
@@ -16,7 +20,6 @@ export async function fetchBinaryFromSogsWithOnionV4(sendOptions: {
1620
serverPubkey: string;
1721
blinded: boolean;
1822
abortSignal: AbortSignal;
19-
doNotIncludeOurSogsHeaders?: boolean;
2023
headers: Record<string, any> | null;
2124
roomId: string;
2225
fileId: string;
@@ -28,7 +31,6 @@ export async function fetchBinaryFromSogsWithOnionV4(sendOptions: {
2831
blinded,
2932
abortSignal,
3033
headers: includedHeaders,
31-
doNotIncludeOurSogsHeaders,
3234
roomId,
3335
fileId,
3436
throwError,
@@ -41,15 +43,13 @@ export async function fetchBinaryFromSogsWithOnionV4(sendOptions: {
4143
throw new Error('endpoint needs a leading /');
4244
}
4345
const builtUrl = new URL(`${serverUrl}${endpoint}`);
44-
let headersWithSogsHeadersIfNeeded = doNotIncludeOurSogsHeaders
45-
? {}
46-
: await OpenGroupPollingUtils.getOurOpenGroupHeaders(
47-
serverPubkey,
48-
endpoint,
49-
method,
50-
blinded,
51-
stringifiedBody
52-
);
46+
let headersWithSogsHeadersIfNeeded = await OpenGroupPollingUtils.getOurOpenGroupHeaders(
47+
serverPubkey,
48+
endpoint,
49+
method,
50+
blinded,
51+
stringifiedBody
52+
);
5353

5454
if (isUndefined(headersWithSogsHeadersIfNeeded)) {
5555
return null;
@@ -98,12 +98,15 @@ export async function sogsV3FetchPreviewAndSaveIt(roomInfos: OpenGroupV2RoomWith
9898
return;
9999
}
100100

101+
const room = OpenGroupData.getV2OpenGroupRoom(convoId);
102+
const blinded = roomHasBlindEnabled(room);
103+
101104
// make sure this runs only once for each rooms.
102-
// we don't want to trigger one of those on each setPollInfo resultsas it happens on each batch poll.
103-
const oneAtAtimeResult = (await allowOnlyOneAtATime(
105+
// we don't want to trigger one of those on each setPollInfo results as it happens on each batch poll.
106+
const oneAtAtimeResult = await allowOnlyOneAtATime(
104107
`sogsV3FetchPreview-${serverUrl}-${roomId}`,
105-
() => sogsV3FetchPreview(roomInfos)
106-
)) as Uint8Array | null; // force the return type as allowOnlyOneAtATime does not keep it
108+
() => sogsV3FetchPreview(roomInfos, blinded)
109+
);
107110

108111
if (!oneAtAtimeResult || !oneAtAtimeResult?.byteLength) {
109112
window?.log?.warn('sogsV3FetchPreviewAndSaveIt failed for room: ', roomId);
@@ -139,7 +142,7 @@ export async function sogsV3FetchPreviewAndSaveIt(roomInfos: OpenGroupV2RoomWith
139142
* @returns the fetchedData in base64
140143
*/
141144
export async function sogsV3FetchPreviewBase64(roomInfos: OpenGroupV2RoomWithImageID) {
142-
const fetched = await sogsV3FetchPreview(roomInfos);
145+
const fetched = await sogsV3FetchPreview(roomInfos, true); // left pane are session official default rooms, which do require blinded
143146
if (fetched && fetched.byteLength) {
144147
return callUtilsWorker('arrayBufferToStringBase64', fetched);
145148
}
@@ -155,7 +158,8 @@ export async function sogsV3FetchPreviewBase64(roomInfos: OpenGroupV2RoomWithIma
155158
* Those default rooms do not have a conversation associated with them, as they are not joined yet
156159
*/
157160
const sogsV3FetchPreview = async (
158-
roomInfos: OpenGroupV2RoomWithImageID
161+
roomInfos: OpenGroupV2RoomWithImageID,
162+
blinded: boolean
159163
): Promise<Uint8Array | null> => {
160164
if (!roomInfos || !roomInfos.imageID) {
161165
return null;
@@ -164,11 +168,10 @@ const sogsV3FetchPreview = async (
164168
// not a batch call yet as we need to exclude headers for this call for now
165169
const fetched = await fetchBinaryFromSogsWithOnionV4({
166170
abortSignal: new AbortController().signal,
167-
blinded: false,
171+
blinded,
168172
headers: null,
169173
serverPubkey: roomInfos.serverPublicKey,
170174
serverUrl: roomInfos.serverUrl,
171-
doNotIncludeOurSogsHeaders: true,
172175
roomId: roomInfos.roomId,
173176
fileId: roomInfos.imageID,
174177
throwError: false,
@@ -198,7 +201,6 @@ export const sogsV3FetchFileByFileID = async (
198201
headers: null,
199202
serverPubkey: roomInfos.serverPublicKey,
200203
serverUrl: roomInfos.serverUrl,
201-
doNotIncludeOurSogsHeaders: true,
202204
roomId: roomInfos.roomId,
203205
fileId,
204206
throwError: true,

ts/session/apis/open_group_api/sogsv3/sogsV3RoomInfos.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,14 @@ import {
1111

1212
export const getAllRoomInfos = async (roomInfos: OpenGroupV2Room) => {
1313
const result = await OnionSending.sendJsonViaOnionV4ToSogs({
14-
blinded: false,
14+
blinded: true,
1515
endpoint: '/rooms',
1616
method: 'GET',
1717
serverPubkey: roomInfos.serverPublicKey,
1818
stringifiedBody: null,
1919
abortSignal: new AbortController().signal,
2020
serverUrl: roomInfos.serverUrl,
2121
headers: null,
22-
doNotIncludeOurSogsHeaders: true,
2322
throwErrors: false,
2423
});
2524

@@ -91,7 +90,6 @@ export async function openGroupV2GetRoomInfoViaOnionV4({
9190
stringifiedBody: null,
9291
serverPubkey,
9392
headers: null,
94-
doNotIncludeOurSogsHeaders: true,
9593
throwErrors: false,
9694
});
9795
const room = result?.body as Record<string, any> | undefined;

ts/session/apis/seed_node_api/SeedNodeAPI.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,7 @@ export interface SnodeFromSeed {
146146
}
147147

148148
const getSnodeListFromSeednodeOneAtAtime = async (seedNodes: Array<string>) =>
149-
allowOnlyOneAtATime('getSnodeListFromSeednode', () =>
150-
getSnodeListFromSeednode(seedNodes)
151-
) as Promise<Array<SnodeFromSeed>>;
149+
allowOnlyOneAtATime('getSnodeListFromSeednode', () => getSnodeListFromSeednode(seedNodes));
152150

153151
/**
154152
* This call will try 4 times to contact a seed nodes (random) and get the snode list from it.

ts/session/apis/snode_api/onions.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,13 @@ async function processAnyOtherErrorOnPath(
338338
if (status !== 200) {
339339
window?.log?.warn(`[path] Got status: ${status}`);
340340

341+
if (status === 404 || status === 400) {
342+
window?.log?.warn(
343+
'processAnyOtherErrorOnPathgot 404 or 400, probably a dead sogs. Skipping bad path update'
344+
);
345+
return;
346+
}
347+
341348
// If we have a specific node in fault we can exclude just this node.
342349
if (ciphertext?.startsWith(NEXT_NODE_NOT_FOUND_PREFIX)) {
343350
const nodeNotFound = ciphertext.substr(NEXT_NODE_NOT_FOUND_PREFIX.length);

0 commit comments

Comments
 (0)