fix double-requests by removing verify on connect

This commit is contained in:
Michael Peters 2022-10-25 22:37:40 -07:00
parent aa90edb142
commit 366022cebb
7 changed files with 26 additions and 30 deletions

View File

@ -5,7 +5,6 @@ const LOG = Logger.create(__filename, electronConsole);
import { Changes, WithEquals } from './data-types'; import { Changes, WithEquals } from './data-types';
import * as uuid from 'uuid';
import assert from 'assert'; import assert from 'assert';
export enum AutoVerifierChangesType { export enum AutoVerifierChangesType {
@ -30,8 +29,6 @@ export class AutoVerifier<T> {
private verifyPromise: Promise<boolean> | null = null; private verifyPromise: Promise<boolean> | null = null;
public trustedStatus: 'none' | 'fetching' | 'verifying' | 'verified' = 'none'; public trustedStatus: 'none' | 'fetching' | 'verifying' | 'verified' = 'none';
private verifierId: string;
/** /**
* allows a trusted function to verify the primary function * allows a trusted function to verify the primary function
* @param primaryFunc The primary function * @param primaryFunc The primary function
@ -42,9 +39,8 @@ export class AutoVerifier<T> {
private primaryFunc: () => Promise<T | null>, private primaryFunc: () => Promise<T | null>,
private trustedFunc: () => Promise<T | null>, private trustedFunc: () => Promise<T | null>,
private verifyFunc: (primaryResult: T | null, trustedResult: T | null) => Promise<boolean>, private verifyFunc: (primaryResult: T | null, trustedResult: T | null) => Promise<boolean>,
) { private name: string | null = null, // for debugging
this.verifierId = uuid.v4().slice(undefined, 4); ) {}
}
/** returns the changes that must be made to primaryResult given trustedResult */ /** returns the changes that must be made to primaryResult given trustedResult */
static getChanges<T extends WithEquals<T> & { id: string }>( static getChanges<T extends WithEquals<T> & { id: string }>(
@ -125,6 +121,7 @@ export class AutoVerifier<T> {
primaryFunc: () => Promise<T[] | null>, primaryFunc: () => Promise<T[] | null>,
trustedFunc: () => Promise<T[] | null>, trustedFunc: () => Promise<T[] | null>,
changesFunc: (changesType: AutoVerifierChangesType, changes: Changes<T>) => Promise<boolean>, changesFunc: (changesType: AutoVerifierChangesType, changes: Changes<T>) => Promise<boolean>,
name: string | null = null,
) { ) {
return new AutoVerifier<T[]>( return new AutoVerifier<T[]>(
primaryFunc, primaryFunc,
@ -134,6 +131,7 @@ export class AutoVerifier<T> {
const changesType = AutoVerifier.getListChangesType<T>(primaryResult, trustedResult, changes); const changesType = AutoVerifier.getListChangesType<T>(primaryResult, trustedResult, changes);
return await changesFunc(changesType, changes); return await changesFunc(changesType, changes);
}, },
name,
); );
} }
@ -145,6 +143,7 @@ export class AutoVerifier<T> {
primaryResult: T | null, primaryResult: T | null,
trustedResult: T | null, trustedResult: T | null,
) => Promise<boolean>, ) => Promise<boolean>,
name: string | null = null,
) { ) {
return new AutoVerifier<T>( return new AutoVerifier<T>(
primaryFunc, primaryFunc,
@ -153,6 +152,7 @@ export class AutoVerifier<T> {
const changesType = AutoVerifier.getSingleChangesType<T>(primaryResult, trustedResult); const changesType = AutoVerifier.getSingleChangesType<T>(primaryResult, trustedResult);
return await changesFunc(changesType, primaryResult, trustedResult); return await changesFunc(changesType, primaryResult, trustedResult);
}, },
name,
); );
} }

View File

@ -55,6 +55,7 @@ const ChannelElement: FC<ChannelElementProps> = (props: ChannelElementProps) =>
setOverlay(<ChannelOverlay channel={channel} />); setOverlay(<ChannelOverlay channel={channel} />);
}, [setOverlay, channel]); }, [setOverlay, channel]);
// TODO: Restrict for permissions
return ( return (
<div className={baseClassName} onClick={setSelfActiveChannel}> <div className={baseClassName} onClick={setSelfActiveChannel}>
<div className="icon">{BaseElements.TEXT_CHANNEL_ICON}</div> <div className="icon">{BaseElements.TEXT_CHANNEL_ICON}</div>

View File

@ -22,7 +22,7 @@ export default class PairVerifierFetchable extends EventEmitter<Conflictable> im
public readonly fetchMessagesBeforeVerifier: AutoVerifierWithArg<Message[], PartialMessageListQuery>; public readonly fetchMessagesBeforeVerifier: AutoVerifierWithArg<Message[], PartialMessageListQuery>;
public readonly fetchMessagesAfterVerifier: AutoVerifierWithArg<Message[], PartialMessageListQuery>; public readonly fetchMessagesAfterVerifier: AutoVerifierWithArg<Message[], PartialMessageListQuery>;
constructor(private primary: Fetchable & Lackable, private trusted: Fetchable & Ensurable) { constructor(private primary: Fetchable & Lackable, private trusted: Fetchable & Ensurable, private name: string | null = null) {
super(); super();
if (trusted instanceof PairVerifierFetchable) { if (trusted instanceof PairVerifierFetchable) {
@ -239,7 +239,6 @@ export default class PairVerifierFetchable extends EventEmitter<Conflictable> im
} }
unverify() { unverify() {
// lOG.debug('unverifying all');
this.fetchMetadataVerifier.unverify(); this.fetchMetadataVerifier.unverify();
this.fetchMembersVerifier.unverify(); this.fetchMembersVerifier.unverify();
this.fetchChannelsVerifier.unverify(); this.fetchChannelsVerifier.unverify();
@ -250,10 +249,6 @@ export default class PairVerifierFetchable extends EventEmitter<Conflictable> im
this.fetchMessagesRecentVerifier.unverifyAll(); this.fetchMessagesRecentVerifier.unverifyAll();
this.fetchMessagesBeforeVerifier.unverifyAll(); this.fetchMessagesBeforeVerifier.unverifyAll();
this.fetchMessagesAfterVerifier.unverifyAll(); this.fetchMessagesAfterVerifier.unverifyAll();
if (this.trusted instanceof PairVerifierFetchable) {
this.trusted.unverify();
}
} }
async fetchMetadata(): Promise<GuildMetadata | null> { async fetchMetadata(): Promise<GuildMetadata | null> {

View File

@ -59,14 +59,15 @@ export default class CombinedGuild
this.personalDBGuild = new PersonalDBGuild(personalDB, this.id, this.memberId); this.personalDBGuild = new PersonalDBGuild(personalDB, this.id, this.memberId);
this.socketGuild = new SocketGuild(socket, socketVerifier); this.socketGuild = new SocketGuild(socket, socketVerifier);
const diskSocket = new PairVerifierFetchable(this.personalDBGuild, this.socketGuild); const diskSocket = new PairVerifierFetchable(this.personalDBGuild, this.socketGuild, 'Disk -> Socket');
const ramDiskSocket = new PairVerifierFetchable(this.ramGuild, diskSocket); const ramDiskSocket = new PairVerifierFetchable(this.ramGuild, diskSocket, 'RAM -> Disk');
// TODO: I think this unverify is causing the RARE ALERTs
// do we need to unverify on connect? or just disconnect?
// connect/Disconnect // connect/Disconnect
this.socketGuild.on('connect', () => { this.socketGuild.on('connect', () => {
LOG.info(`g#${this.id} connected`); LOG.info(`g#${this.id} connected`);
diskSocket.unverify(); ramDiskSocket.unverify(); // make sure to query the disk-socket AutoVerifier again
ramDiskSocket.unverify();
this.emit('connect'); this.emit('connect');
}); });
this.socketGuild.on('disconnect', async () => { this.socketGuild.on('disconnect', async () => {

View File

@ -53,7 +53,7 @@ export default class SocketVerifier extends EventEmitter<{ verified: () => void
} }
this.memberId = memberId; this.memberId = memberId;
this.isVerified = true; this.isVerified = true;
LOG.debug(`verified as u#${memberId}`); LOG.debug(`verified as u#${memberId.slice(0, 4)}`);
resolve(memberId); resolve(memberId);
this.emit('verified'); this.emit('verified');
}); });

View File

@ -67,7 +67,7 @@ export default class DB {
// eslint-disable-next-line newline-per-chained-call // eslint-disable-next-line newline-per-chained-call
LOG.silly( LOG.silly(
`searching for public key (der hash: ${LOG.inspect( `searching for public key (der hash: ${LOG.inspect(
crypto.createHash('sha256').update(der).digest().toString('hex'), crypto.createHash('sha256').update(der).digest().toString('hex').slice(0, 8),
)})`, )})`,
); );

View File

@ -127,7 +127,6 @@ function bindEvent(
} }
} }
} }
//lOG.debug(`c#${client.id}: ${name}`);
try { try {
await handler(...args); await handler(...args);
} catch (e) { } catch (e) {
@ -139,7 +138,7 @@ function bindEvent(
} }
} catch (e) { } catch (e) {
if (e instanceof SignatureError) { if (e instanceof SignatureError) {
LOG.warn(`c#${client.id}: ${name} request does not match expected signature`, { LOG.warn(`c#${client.id.slice(0, 4)}: ${name} request does not match expected signature`, {
signature, signature,
args, args,
msg: e.message, msg: e.message,
@ -147,7 +146,7 @@ function bindEvent(
// do not respond to requests with invalid signatures // do not respond to requests with invalid signatures
} else if (e instanceof EventError) { } else if (e instanceof EventError) {
LOG.warn( LOG.warn(
`c#${client.id}: ${e.message}${e.extended_message ? ' / ' + e.extended_message : ''}`, `c#${client.id.slice(0, 4)}: ${e.message}${e.extended_message ? ' / ' + e.extended_message : ''}`,
e.cause, e.cause,
); );
respond(e.message); respond(e.message);
@ -204,7 +203,7 @@ function bindRegistrationEvents(io: socketio.Server, client: socketio.Socket): v
const member = await DB.getMember(guildId, memberId); const member = await DB.getMember(guildId, memberId);
const meta = await DB.getGuild(guildId); const meta = await DB.getGuild(guildId);
LOG.info(`c#${client.id}: registered with t#${token} as u#${member.id} / ${member.display_name}`); LOG.info(`c#${client.id.slice(0, 4)}: registered with t#${token} as u#${member.id} / ${member.display_name}`);
respond(null, member, meta); respond(null, member, meta);
@ -245,7 +244,7 @@ function bindChallengeVerificationEvents(io: socketio.Server, client: socketio.S
throw new EventError('unauthorized public key', e as Error); throw new EventError('unauthorized public key', e as Error);
} }
LOG.debug(`c#${client.id}: challenging for u#${identity.memberId?.slice(0, 4)}`); LOG.debug(`c#${client.id.slice(0, 4)}: challenging for u#${identity.memberId?.slice(0, 4)}`);
if (connected.find(i => i.memberId === identity.memberId && i.verified)) { if (connected.find(i => i.memberId === identity.memberId && i.verified)) {
throw new EventError('member already connected'); throw new EventError('member already connected');
@ -315,10 +314,10 @@ function bindChallengeVerificationEvents(io: socketio.Server, client: socketio.S
.map((privilege: string) => guildPrivilegeRoomName(identity.guildId as string, privilege)) .map((privilege: string) => guildPrivilegeRoomName(identity.guildId as string, privilege))
: [], : [],
); );
LOG.debug(`c#${client.id} joining ${rooms.join(', ')}`); LOG.debug(`c#${client.id.slice(0, 4)} joining g#[${rooms.map(room => room.slice(0, 4)).join(', ')}]`);
client.join(rooms); client.join(rooms);
LOG.info(`c#${client.id}: verified as g#${identity.guildId} u#${identity.memberId?.slice(0, 4)}`); LOG.info(`c#${client.id.slice(0, 4)}: verified as g#${identity.guildId.slice(0, 4)} u#${identity.memberId?.slice(0, 4)}`);
respond(null, identity.memberId); respond(null, identity.memberId);
}); });
} }
@ -753,7 +752,7 @@ function bindActionEvents(io: socketio.Server, client: socketio.Socket, identity
if (!identity.memberId) throw new EventError('identity no memberId'); if (!identity.memberId) throw new EventError('identity no memberId');
if (avatarBuff.length > MAX_AVATAR_SIZE) { if (avatarBuff.length > MAX_AVATAR_SIZE) {
LOG.warn(`c#${client.id}: avatar too large`); LOG.warn(`c#${client.id.slice(0, 4)}: avatar too large`);
respond('buffer too large'); respond('buffer too large');
return; return;
} }
@ -825,7 +824,7 @@ function bindFetchEvents(client: socketio.Socket, identity: IIdentity): void {
if (!identity.guildId) throw new EventError('identity no guildId'); if (!identity.guildId) throw new EventError('identity no guildId');
if (!identity.memberId) throw new EventError('identity no memberId'); if (!identity.memberId) throw new EventError('identity no memberId');
LOG.info( LOG.info(
`u#${identity.memberId?.slice(0, 4)}: fetching recent messages for ch#${channelId} number: ${number}`, `u#${identity.memberId?.slice(0, 4)}: fetching recent messages for ch#${channelId.slice(0, 4)} number: ${number}`,
); );
const messages = await DB.getMessagesRecent(identity.guildId, channelId, number); const messages = await DB.getMessagesRecent(identity.guildId, channelId, number);
respond(null, messages); respond(null, messages);
@ -929,7 +928,7 @@ function bindFetchEvents(client: socketio.Socket, identity: IIdentity): void {
export function bindSocketEvents(io: socketio.Server): void { export function bindSocketEvents(io: socketio.Server): void {
io.on('connection', client => { io.on('connection', client => {
LOG.info(`c#${client.id}: connected`); LOG.info(`c#${client.id.slice(0, 4)}: connected`);
const identity: IIdentity = { const identity: IIdentity = {
guildId: null, guildId: null,
@ -953,7 +952,7 @@ export function bindSocketEvents(io: socketio.Server): void {
1, 1,
); );
if (identity.verified) { if (identity.verified) {
LOG.info(`c#${client.id}: disconnected (was u#${identity.memberId?.slice(0, 4)})`); LOG.info(`c#${client.id.slice(0, 4)}: disconnected (was u#${identity.memberId?.slice(0, 4)})`);
(async () => { (async () => {
if (!identity.guildId || !identity.memberId) return; if (!identity.guildId || !identity.memberId) return;
try { try {
@ -963,7 +962,7 @@ export function bindSocketEvents(io: socketio.Server): void {
} }
})(); })();
} else { } else {
LOG.info(`c#${client.id}: disconnected (was unverified)`); LOG.info(`c#${client.id.slice(0, 4)}: disconnected (was unverified)`);
} }
}); });
}); });