Skip to content

Commit

Permalink
feat: aggregate error
Browse files Browse the repository at this point in the history
  • Loading branch information
JadsonLucena committed Mar 4, 2024
1 parent 31a75c5 commit dde1a56
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 73 deletions.
37 changes: 16 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ The subscription ensures that the permissions for a particular resource are not
* @constructor
* @throws {TypeError} Invalid algorithm
* @throws {TypeError} Invalid key
* @throws {TypeError} Invalid ttl
* @throws {TypeError|SyntaxError} Invalid ttl
* @throws {AggregateError} Invalid arguments
*/
SignedAccess(
{
Expand Down Expand Up @@ -64,8 +65,7 @@ algorithm(param?: string = 'sha512'): void
key(param?: string | ArrayBuffer | Buffer | TypedArray | DataView | KeyObject | CryptoKey): void

/**
* @throws {TypeError} Invalid ttl
* @throws {SyntaxError} Invalid ttl
* @throws {TypeError|SyntaxError} Invalid ttl
* @see https://wikipedia.org/wiki/Time_to_live
*/
ttl(param?: number = 86400): void
Expand All @@ -75,15 +75,13 @@ ttl(param?: number = 86400): void
/**
* @method
* @throws {TypeError} Invalid prefix
* @throws {TypeError} Invalid accessControlAllowMethods
* @throws {SyntaxError} Invalid accessControlAllowMethods
* @throws {TypeError|SyntaxError} Invalid accessControlAllowMethods
* @throws {TypeError} Invalid algorithm
* @throws {TypeError} Invalid key
* @throws {TypeError} Invalid nonce
* @throws {TypeError} Invalid remoteAddress
* @throws {SyntaxError} Invalid remoteAddress
* @throws {TypeError} Invalid ttl
* @throws {SyntaxError} Invalid ttl
* @throws {TypeError|SyntaxError} Invalid remoteAddress
* @throws {TypeError|SyntaxError} Invalid ttl
* @throws {AggregateError} Invalid arguments
*/
signCookie(
prefix: string, // A prefix encodes a scheme (either http:// or https://), FQDN, and an optional path. Ending the path with a / is optional but recommended. The prefix shouldn't include query parameters or fragments such as ? or #.
Expand Down Expand Up @@ -111,10 +109,10 @@ signCookie(
* @throws {TypeError} Invalid algorithm
* @throws {TypeError} Invalid key
* @throws {TypeError} Invalid method
* @throws {TypeError} Invalid remoteAddress
* @throws {SyntaxError} Invalid remoteAddress
* @throws {TypeError|SyntaxError} Invalid remoteAddress
* @throws {Error} method required
* @throws {Error} remoteAddress required
* @throws {AggregateError} Invalid arguments
*/
verifyCookie(
url: string,
Expand All @@ -136,16 +134,13 @@ verifyCookie(
* @method
* @throws {TypeError} Invalid url
* @throws {TypeError} Invalid algorithm
* @throws {TypeError} Invalid accessControlAllowMethods
* @throws {SyntaxError} Invalid accessControlAllowMethods
* @throws {TypeError|SyntaxError} Invalid accessControlAllowMethods
* @throws {TypeError} Invalid key
* @throws {TypeError} Invalid nonce
* @throws {TypeError} Invalid pathname
* @throws {SyntaxError} Invalid pathname
* @throws {TypeError} Invalid remoteAddress
* @throws {SyntaxError} Invalid remoteAddress
* @throws {TypeError} Invalid ttl
* @throws {SyntaxError} Invalid ttl
* @throws {TypeError|SyntaxError} Invalid pathname
* @throws {TypeError|SyntaxError} Invalid remoteAddress
* @throws {TypeError|SyntaxError} Invalid ttl
* @throws {AggregateError} Invalid arguments
*/
signURL(
url: string,
Expand Down Expand Up @@ -174,10 +169,10 @@ signURL(
* @throws {TypeError} Invalid algorithm
* @throws {TypeError} Invalid key
* @throws {TypeError} Invalid method
* @throws {TypeError} Invalid remoteAddress
* @throws {SyntaxError} Invalid remoteAddress
* @throws {TypeError|SyntaxError} Invalid remoteAddress
* @throws {Error} method required
* @throws {Error} remoteAddress required
* @throws {AggregateError} Invalid arguments
*/
verifyURL(
url: string,
Expand Down
164 changes: 112 additions & 52 deletions src/SignedAccess.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,39 @@ class SignedAccess {
* @param {number} [options.ttl=86400] - {@link https://wikipedia.org/wiki/Time_to_live Time to Live} in seconds
*
* @throws {TypeError} Invalid algorithm
* @throws {TypeError} Invalid ttl
* @throws {TypeError} Invalid key
* @throws {TypeError|SyntaxError} Invalid ttl
* @throws {AggregateError} Invalid arguments
*/
constructor ({
algorithm,
key,
ttl
}) {
this.algorithm = algorithm
this.key = key
this.ttl = ttl
const errors = []
try {
this.algorithm = algorithm
} catch (err) {
errors.push(err)
}

try {
this.key = key
} catch (err) {
errors.push(err)
}

try {
this.ttl = ttl
} catch (err) {
errors.push(err)
}

if (errors.length > 1) {
throw new AggregateError(errors, 'Invalid arguments')
} else if (errors.length === 1) {
throw errors.pop()
}

this.#HTTPMethods = ['CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'TRACE']
}
Expand Down Expand Up @@ -91,8 +113,7 @@ class SignedAccess {
* @type {number}
* @default 86400
*
* @throws {TypeError} Invalid ttl
* @throws {SyntaxError} Invalid ttl
* @throws {TypeError|SyntaxError} Invalid ttl
*
* @see https://wikipedia.org/wiki/Time_to_live
*/
Expand Down Expand Up @@ -160,16 +181,13 @@ class SignedAccess {
*
* @throws {TypeError} Invalid url
* @throws {TypeError} Invalid algorithm
* @throws {TypeError} Invalid accessControlAllowMethods
* @throws {SyntaxError} Invalid accessControlAllowMethods
* @throws {TypeError|SyntaxError} Invalid accessControlAllowMethods
* @throws {TypeError} Invalid key
* @throws {TypeError} Invalid nonce
* @throws {TypeError} Invalid pathname
* @throws {SyntaxError} Invalid pathname
* @throws {TypeError} Invalid remoteAddress
* @throws {SyntaxError} Invalid remoteAddress
* @throws {TypeError} Invalid ttl
* @throws {SyntaxError} Invalid ttl
* @throws {TypeError|SyntaxError} Invalid pathname
* @throws {TypeError|SyntaxError} Invalid remoteAddress
* @throws {TypeError|SyntaxError} Invalid ttl
* @throws {AggregateError} Invalid arguments
*
* @return {string} Signed URL
*/
Expand All @@ -187,24 +205,32 @@ class SignedAccess {
) {
url = new URL(url)

const errors = []

if (typeof accessControlAllowMethods !== 'string') {
throw new TypeError('Invalid accessControlAllowMethods')
errors.push(new TypeError('Invalid accessControlAllowMethods'))
} else if (!new RegExp(`^\\s*(\\*|(${this.#HTTPMethods.join('|')})(\\s*,\\s*(${this.#HTTPMethods.join('|')}))*)\\s*$`, 'i').test(accessControlAllowMethods)) {
throw new SyntaxError('Invalid accessControlAllowMethods')
errors.push(new SyntaxError('Invalid accessControlAllowMethods'))
} else if (typeof nonce !== 'string') {
throw new TypeError('Invalid nonce')
errors.push(new TypeError('Invalid nonce'))
} else if (typeof pathname !== 'string') {
throw new TypeError('Invalid pathname')
errors.push(new TypeError('Invalid pathname'))
} else if (pathname && !decodeURIComponent(url.pathname).startsWith(pathname)) {
throw new SyntaxError('Invalid pathname')
errors.push(new SyntaxError('Invalid pathname'))
} else if (typeof remoteAddress !== 'string') {
throw new TypeError('Invalid remoteAddress')
errors.push(new TypeError('Invalid remoteAddress'))
} else if (remoteAddress && net.isIP(remoteAddress) === 0) {
throw new SyntaxError('Invalid remoteAddress')
errors.push(new SyntaxError('Invalid remoteAddress'))
} else if (!Number.isSafeInteger(ttl)) {
throw new TypeError('Invalid ttl')
errors.push(new TypeError('Invalid ttl'))
} else if (ttl < 1) {
throw new SyntaxError('Invalid ttl')
errors.push(new SyntaxError('Invalid ttl'))
}

if (errors.length > 1) {
throw new AggregateError(errors, 'Invalid arguments')
} else if (errors.length === 1) {
throw errors.pop()
}

url.searchParams.delete('expires')
Expand Down Expand Up @@ -244,10 +270,10 @@ class SignedAccess {
* @throws {TypeError} Invalid algorithm
* @throws {TypeError} Invalid key
* @throws {TypeError} Invalid method
* @throws {TypeError} Invalid remoteAddress
* @throws {SyntaxError} Invalid remoteAddress
* @throws {TypeError|SyntaxError} Invalid remoteAddress
* @throws {Error} method required
* @throws {Error} remoteAddress required
* @throws {AggregateError} Invalid arguments
*
* @return {boolean}
*/
Expand All @@ -262,18 +288,32 @@ class SignedAccess {
) {
url = new URL(url)

const errors = []

if (typeof method !== 'string' || !['', ...this.#HTTPMethods].includes(method.trim().toUpperCase())) {
throw new TypeError('Invalid method')
errors.push(new TypeError('Invalid method'))
} else if (typeof remoteAddress !== 'string') {
throw new TypeError('Invalid remoteAddress')
errors.push(new TypeError('Invalid remoteAddress'))
} else if (remoteAddress && net.isIP(remoteAddress) === 0) {
throw new SyntaxError('Invalid remoteAddress')
errors.push(new SyntaxError('Invalid remoteAddress'))
}

if (errors.length > 1) {
throw new AggregateError(errors, 'Invalid arguments')
} else if (errors.length === 1) {
throw errors.pop()
}

if (url.searchParams.has('method') && !method.trim()) {
throw new Error('method required')
errors.push(new Error('method required'))
} else if (url.searchParams.has('ip') && !remoteAddress.trim()) {
throw new Error('remoteAddress required')
errors.push(new Error('remoteAddress required'))
}

if (errors.length > 1) {
throw new AggregateError(errors, 'Invalid url')
} else if (errors.length === 1) {
throw errors.pop()
}

const signature = url.searchParams.get('signature')
Expand Down Expand Up @@ -319,15 +359,13 @@ class SignedAccess {
* @param {number} [options.ttl=86400] - {@link https://wikipedia.org/wiki/Time_to_live Time to Live} in seconds
*
* @throws {TypeError} Invalid prefix
* @throws {TypeError} Invalid accessControlAllowMethods
* @throws {SyntaxError} Invalid accessControlAllowMethods
* @throws {TypeError|SyntaxError} Invalid accessControlAllowMethods
* @throws {TypeError} Invalid algorithm
* @throws {TypeError} Invalid key
* @throws {TypeError} Invalid nonce
* @throws {TypeError} Invalid remoteAddress
* @throws {SyntaxError} Invalid remoteAddress
* @throws {TypeError} Invalid ttl
* @throws {SyntaxError} Invalid ttl
* @throws {TypeError|SyntaxError} Invalid remoteAddress
* @throws {TypeError|SyntaxError} Invalid ttl
* @throws {AggregateError} Invalid arguments
*
* @return {string} Signed cookie
*/
Expand All @@ -342,22 +380,30 @@ class SignedAccess {
ttl = this.#ttl
} = {}
) {
const errors = []

if (typeof prefix !== 'string') {
throw new TypeError('Invalid prefix')
errors.push(new TypeError('Invalid prefix'))
} else if (typeof accessControlAllowMethods !== 'string') {
throw new TypeError('Invalid accessControlAllowMethods')
errors.push(new TypeError('Invalid accessControlAllowMethods'))
} else if (!new RegExp(`^\\s*(\\*|(${this.#HTTPMethods.join('|')})(\\s*,\\s*(${this.#HTTPMethods.join('|')}))*)\\s*$`, 'i').test(accessControlAllowMethods)) {
throw new SyntaxError('Invalid accessControlAllowMethods')
errors.push(new SyntaxError('Invalid accessControlAllowMethods'))
} else if (typeof nonce !== 'string') {
throw new TypeError('Invalid nonce')
errors.push(new TypeError('Invalid nonce'))
} else if (typeof remoteAddress !== 'string') {
throw new TypeError('Invalid remoteAddress')
errors.push(new TypeError('Invalid remoteAddress'))
} else if (remoteAddress && net.isIP(remoteAddress) === 0) {
throw new SyntaxError('Invalid remoteAddress')
errors.push(new SyntaxError('Invalid remoteAddress'))
} else if (!Number.isSafeInteger(ttl)) {
throw new TypeError('Invalid ttl')
errors.push(new TypeError('Invalid ttl'))
} else if (ttl < 1) {
throw new SyntaxError('Invalid ttl')
errors.push(new SyntaxError('Invalid ttl'))
}

if (errors.length > 1) {
throw new AggregateError(errors, 'Invalid arguments')
} else if (errors.length === 1) {
throw errors.pop()
}

const cookie = new URLSearchParams()
Expand Down Expand Up @@ -388,10 +434,10 @@ class SignedAccess {
* @throws {TypeError} Invalid algorithm
* @throws {TypeError} Invalid key
* @throws {TypeError} Invalid method
* @throws {TypeError} Invalid remoteAddress
* @throws {SyntaxError} Invalid remoteAddress
* @throws {TypeError|SyntaxError} Invalid remoteAddress
* @throws {Error} method required
* @throws {Error} remoteAddress required
* @throws {AggregateError} Invalid arguments
*
* @return {boolean}
*/
Expand All @@ -408,20 +454,34 @@ class SignedAccess {
cookie = new URLSearchParams(cookie)
url = new URL(url)

const errors = []

if (!cookie.has('prefix') || !cookie.has('expires') || !cookie.has('signature')) {
throw new TypeError('Invalid cookie')
errors.push(new TypeError('Invalid cookie'))
} else if (typeof method !== 'string' || !['', ...this.#HTTPMethods].includes(method.trim().toUpperCase())) {
throw new TypeError('Invalid method')
errors.push(new TypeError('Invalid method'))
} else if (typeof remoteAddress !== 'string') {
throw new TypeError('Invalid remoteAddress')
errors.push(new TypeError('Invalid remoteAddress'))
} else if (remoteAddress && net.isIP(remoteAddress) === 0) {
throw new SyntaxError('Invalid remoteAddress')
errors.push(new SyntaxError('Invalid remoteAddress'))
}

if (errors.length > 1) {
throw new AggregateError(errors, 'Invalid arguments')
} else if (errors.length === 1) {
throw errors.pop()
}

if (cookie.has('method') && !method.trim()) {
throw new Error('method required')
errors.push(new Error('method required'))
} else if (cookie.has('ip') && !remoteAddress.trim()) {
throw new Error('remoteAddress required')
errors.push(new Error('remoteAddress required'))
}

if (errors.length > 1) {
throw new AggregateError(errors, 'Invalid cookie')
} else if (errors.length === 1) {
throw errors.pop()
}

const signature = cookie.get('signature')
Expand Down

0 comments on commit dde1a56

Please sign in to comment.