Skip to content

Commit

Permalink
fix: reduce cognitive load of the functions
Browse files Browse the repository at this point in the history
  • Loading branch information
Matheus-Aguilar committed Jun 25, 2024
1 parent cd7cd14 commit aa19e5e
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 85 deletions.
4 changes: 1 addition & 3 deletions node/directives/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
154 changes: 82 additions & 72 deletions node/directives/validateStoreUserAccess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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')
}
}
}
17 changes: 7 additions & 10 deletions node/metrics/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit aa19e5e

Please sign in to comment.