From aa19e5ec80f08a11d479c04c60cc924d2dbb0afb Mon Sep 17 00:00:00 2001 From: Matheus-Aguilar Date: Tue, 25 Jun 2024 10:17:00 -0300 Subject: [PATCH] fix: reduce cognitive load of the functions --- node/directives/helper.ts | 4 +- node/directives/validateStoreUserAccess.ts | 154 +++++++++++---------- node/metrics/auth.ts | 17 +-- 3 files changed, 90 insertions(+), 85 deletions(-) diff --git a/node/directives/helper.ts b/node/directives/helper.ts index d1a342e..47133dc 100644 --- a/node/directives/helper.ts +++ b/node/directives/helper.ts @@ -29,12 +29,10 @@ export const validateAdminToken = async ( hasCurrentValidAdminToken = true if (authUser?.audience === 'admin') { - const hasAdminPermissions = await lm.getUserAdminPermissions( + hasValidAdminToken = await lm.getUserAdminPermissions( authUser.account, authUser.id ) - - hasValidAdminToken = hasAdminPermissions } } catch (err) { // noop so we leave hasValidAdminToken as false diff --git a/node/directives/validateStoreUserAccess.ts b/node/directives/validateStoreUserAccess.ts index 770869d..24ef80e 100644 --- a/node/directives/validateStoreUserAccess.ts +++ b/node/directives/validateStoreUserAccess.ts @@ -3,7 +3,7 @@ import type { GraphQLField } from 'graphql' import { defaultFieldResolver } from 'graphql' import { SchemaDirectiveVisitor } from 'graphql-tools' -import type { AuthAuditTokenMetrics } from '../metrics/auth' +import type { AuthAuditMetric } from '../metrics/auth' import sendAuthMetric, { AuthMetric } from '../metrics/auth' import { validateAdminToken, @@ -25,98 +25,108 @@ export class ValidateStoreUserAccess extends SchemaDirectiveVisitor { vtex: { adminUserAuthToken, storeUserAuthToken, logger }, } = context - let hasTokens = true - let hasValidTokens = true + // get metrics data + const operation = field?.astNode?.name?.value ?? context?.request?.url + const userAgent = context?.request?.headers['user-agent'] as string + const caller = context?.request?.headers['x-vtex-caller'] as string + const forwardedHost = context?.request?.headers[ + 'x-forwarded-host' + ] as string + + // set metric fields with initial data + let metricFields: AuthAuditMetric = { + operation, + forwardedHost, + caller, + userAgent, + } const { hasAdminToken, hasValidAdminToken } = await validateAdminToken( context, adminUserAuthToken as string ) - // add Admin token metrics - let tokenMetrics: AuthAuditTokenMetrics = { - hasAdminToken, - hasValidAdminToken, + // add admin token metrics + metricFields = { ...metricFields, hasAdminToken, hasValidAdminToken } + + // allow access if has valid admin token + if (hasValidAdminToken) { + sendAuthMetric( + logger, + new AuthMetric( + context?.vtex?.account, + metricFields, + 'ValidateStoreUserAccessAudit' + ) + ) + + return resolve(root, args, context, info) } - if (!hasAdminToken || !hasValidAdminToken) { - const { hasApiToken, hasValidApiToken } = await validateApiToken( - context - ) + const { hasApiToken, hasValidApiToken } = await validateApiToken(context) - // add API token metrics - tokenMetrics = { - ...tokenMetrics, - hasApiToken, - hasValidApiToken, - } - - if (!hasApiToken || !hasValidApiToken) { - const { hasStoreToken, hasValidStoreToken } = - await validateStoreToken(context, storeUserAuthToken as string) - - // add store token metrics - tokenMetrics = { - ...tokenMetrics, - hasStoreToken, - hasValidStoreToken, - } - - if (!hasStoreToken || !hasValidStoreToken) { - hasTokens = hasAdminToken || hasApiToken || hasStoreToken - hasValidTokens = - hasValidAdminToken || hasValidApiToken || hasValidStoreToken - } - } + // add API token metrics + metricFields = { + ...metricFields, + hasApiToken, + hasValidApiToken, } - // now we emit a metric with all the collected data before we proceed - const operation = field?.astNode?.name?.value ?? context?.request?.url - const userAgent = context?.request?.headers['user-agent'] as string - const caller = context?.request?.headers['x-vtex-caller'] as string - const forwardedHost = context?.request?.headers[ - 'x-forwarded-host' - ] as string + // allow access if has valid API token + if (hasValidApiToken) { + sendAuthMetric( + logger, + new AuthMetric( + context?.vtex?.account, + metricFields, + 'ValidateStoreUserAccessAudit' + ) + ) + + return resolve(root, args, context, info) + } - const auditMetric = new AuthMetric( - context?.vtex?.account, - { - operation, - forwardedHost, - caller, - userAgent, - ...tokenMetrics, - }, - 'ValidateStoreUserAccessAudit' + const { hasStoreToken, hasValidStoreToken } = await validateStoreToken( + context, + storeUserAuthToken as string ) - sendAuthMetric(logger, auditMetric) + // add store token metrics + metricFields = { + ...metricFields, + hasStoreToken, + hasValidStoreToken, + } - if (!hasTokens) { - logger.warn({ - message: 'ValidateStoreUserAccess: No token provided', - userAgent, - caller, - forwardedHost, - operation, - ...tokenMetrics, - }) - throw new AuthenticationError('No token was provided') + // allow access if has valid store token + if (hasValidStoreToken) { + sendAuthMetric( + logger, + new AuthMetric( + context?.vtex?.account, + metricFields, + 'ValidateStoreUserAccessAudit' + ) + ) + + return resolve(root, args, context, info) } - if (!hasValidTokens) { + // deny access if no tokens were provided + if (!hasAdminToken && !hasApiToken && !hasStoreToken) { logger.warn({ - message: `ValidateStoreUserAccess: Invalid token`, - userAgent, - caller, - forwardedHost, - operation, - ...tokenMetrics, + message: 'ValidateStoreUserAccess: No token provided', + ...metricFields, }) - throw new ForbiddenError('Unauthorized Access') + throw new AuthenticationError('No token was provided') } - return resolve(root, args, context, info) + // deny access if no valid tokens were provided + logger.warn({ + message: `ValidateStoreUserAccess: Invalid token`, + ...metricFields, + }) + throw new ForbiddenError('Unauthorized Access') } } } diff --git a/node/metrics/auth.ts b/node/metrics/auth.ts index 846be72..f58f0a6 100644 --- a/node/metrics/auth.ts +++ b/node/metrics/auth.ts @@ -3,22 +3,19 @@ import type { Logger } from '@vtex/api/lib/service/logger/logger' import type { Metric } from '../clients/metrics' import { B2B_METRIC_NAME, sendMetric } from '../clients/metrics' -export interface AuthAuditTokenMetrics { - hasAdminToken: boolean - hasValidAdminToken?: boolean - hasStoreToken?: boolean - hasValidStoreToken?: boolean - hasApiToken?: boolean - hasValidApiToken?: boolean -} - -export interface AuthAuditMetric extends AuthAuditTokenMetrics { +export interface AuthAuditMetric { operation: string forwardedHost: string caller: string userAgent: string role?: string permissions?: string[] + hasAdminToken?: boolean + hasValidAdminToken?: boolean + hasStoreToken?: boolean + hasValidStoreToken?: boolean + hasApiToken?: boolean + hasValidApiToken?: boolean } export class AuthMetric implements Metric {