diff --git a/app/src/app/error/codes/mfa.ts b/app/src/app/error/codes/mfa.ts index ff85cca0..bc0249ca 100644 --- a/app/src/app/error/codes/mfa.ts +++ b/app/src/app/error/codes/mfa.ts @@ -23,6 +23,10 @@ export const ADDR_IN_USE_ERR: ErrorStruct = buildErrorStruct( name, prefix, `ADDR_IN_USE`, `HTTP Server could not start, because address/port is in use`, ); +export const INSUFFICIENT_PRIVILEGES: ErrorStruct = buildErrorStruct( + name, prefix, `ADDR_IN_USE`, `HTTP Server could not start, because user has insufficient privileges to open address/port`, +); + export const SUBMIT_FAILED: ErrorStruct = buildErrorStruct( name, prefix, `SUBMIT_FAILED`, `Unable to submit MFA code`, ); diff --git a/app/src/app/event/error-handler.ts b/app/src/app/event/error-handler.ts index 58c4935b..e8df38f6 100644 --- a/app/src/app/event/error-handler.ts +++ b/app/src/app/event/error-handler.ts @@ -19,6 +19,7 @@ const reportDenyList = [ ERR_SIGINT.code, ERR_SIGTERM.code, MFA_ERR.ADDR_IN_USE_ERR.code, // Only happens if port/address is in use + MFA_ERR.INSUFFICIENT_PRIVILEGES.code, // Only happens if user is lacking privileges to open port/address MFA_ERR.SERVER_TIMEOUT.code, // Only happens if user does not interact within 10 minutes LIBRARY_ERR.LOCKED.code, // Only happens if library is locked AUTH_ERR.UNAUTHORIZED.code, // Only happens if username/password don't match diff --git a/app/src/lib/icloud/mfa/mfa-server.ts b/app/src/lib/icloud/mfa/mfa-server.ts index be62a30e..2bee0cb5 100644 --- a/app/src/lib/icloud/mfa/mfa-server.ts +++ b/app/src/lib/icloud/mfa/mfa-server.ts @@ -46,9 +46,17 @@ export class MFAServer { Resources.logger(this).debug(`Preparing MFA server on port ${Resources.manager().mfaServerPort}`); this.server = http.createServer(this.handleRequest.bind(this)); this.server.on(`error`, err => { - const icpsErr = (Object.hasOwn(err, `code`) && (err as any).code === `EADDRINUSE`) - ? new iCPSError(MFA_ERR.ADDR_IN_USE_ERR).addContext(`port`, Resources.manager().mfaServerPort) - : new iCPSError(MFA_ERR.SERVER_ERR); + let icpsErr = new iCPSError(MFA_ERR.SERVER_ERR); + + if (Object.hasOwn(err, `code`)) { + if ((err as any).code === `EADDRINUSE`) { + icpsErr = new iCPSError(MFA_ERR.ADDR_IN_USE_ERR).addContext(`port`, Resources.manager().mfaServerPort); + } + + if ((err as any).code === `EACCES`) { + icpsErr = new iCPSError(MFA_ERR.INSUFFICIENT_PRIVILEGES).addContext(`port`, Resources.manager().mfaServerPort); + } + } icpsErr.addCause(err); diff --git a/app/test/unit/icloud.mfa-server.test.ts b/app/test/unit/icloud.mfa-server.test.ts index 6faab946..59d5257f 100644 --- a/app/test/unit/icloud.mfa-server.test.ts +++ b/app/test/unit/icloud.mfa-server.test.ts @@ -262,9 +262,18 @@ describe(`Server lifecycle`, () => { const errorEvent = mockedEventManager.spyOnEvent(iCPSEventMFA.ERROR); const error = new Error(`Address in use`); (error as any).code = `EADDRINUSE`; - server.server.emit(`error`, new Error(`Address in use`)); + server.server.emit(`error`, error); - expect(errorEvent).toHaveBeenCalledWith(new Error(`HTTP Server Error`)); + expect(errorEvent).toHaveBeenCalledWith(new iCPSError(MFA_ERR.ADDR_IN_USE_ERR)); + }); + + test(`Handle EACCES error`, () => { + const errorEvent = mockedEventManager.spyOnEvent(iCPSEventMFA.ERROR); + const error = new Error(`No privileges`); + (error as any).code = `EACCES`; + server.server.emit(`error`, error); + + expect(errorEvent).toHaveBeenCalledWith(new iCPSError(MFA_ERR.INSUFFICIENT_PRIVILEGES)); }); test(`Handle MFA timeout`, () => {