Skip to content
Merged
5 changes: 5 additions & 0 deletions .changeset/thirty-turtles-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"nostream": patch
---

Fix Redis cache connection config to skip AUTH when `REDIS_PASSWORD` is unset
55 changes: 48 additions & 7 deletions src/cache/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,50 @@ import { createLogger } from '../factories/logger-factory'

const logger = createLogger('cache-client')

export const getCacheConfig = (): RedisClientOptions => ({
url: process.env.REDIS_URI
? process.env.REDIS_URI
: `redis://${process.env.REDIS_USER}:${process.env.REDIS_PASSWORD}@${process.env.REDIS_HOST}:${process.env.REDIS_PORT}`,
password: process.env.REDIS_PASSWORD,
})
const redactRedisUrlCredentials = (url: string): string => {
try {
const parsedUrl = new URL(url)

if (!parsedUrl.username && !parsedUrl.password) {
return url
}

parsedUrl.username = parsedUrl.username ? '***' : ''
parsedUrl.password = parsedUrl.password ? '***' : ''

return parsedUrl.toString()
} catch {
return url
}
}

export const getCacheConfig = (): RedisClientOptions => {
const password = process.env.REDIS_PASSWORD

if (process.env.REDIS_URI) {
return {
url: process.env.REDIS_URI,
...(password ? { password } : {}),
}
}

const host = process.env.REDIS_HOST
const port = process.env.REDIS_PORT

if (password) {
const username = process.env.REDIS_USER ?? 'default'
Comment thread
cameri marked this conversation as resolved.

return {
url: `redis://${host}:${port}`,
username,
password,
}
}

return {
url: `redis://${host}:${port}`,
}
}

let instance: CacheClient | undefined = undefined

Expand All @@ -18,7 +56,10 @@ export const getCacheClient = (): CacheClient => {
const config = getCacheConfig()
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { password: _, ...loggableConfig } = config
logger('config: %o', loggableConfig)
logger('config: %o', {
...loggableConfig,
...(loggableConfig.url ? { url: redactRedisUrlCredentials(loggableConfig.url) } : {}),
})
instance = createClient(config)
}

Expand Down
72 changes: 72 additions & 0 deletions test/unit/cache/client.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { expect } from 'chai'

import { getCacheConfig } from '../../../src/cache/client'

describe('getCacheConfig', () => {
const originalEnv = process.env

beforeEach(() => {
process.env = { ...originalEnv }
delete process.env.REDIS_URI
delete process.env.REDIS_USER
delete process.env.REDIS_PASSWORD
delete process.env.REDIS_HOST
delete process.env.REDIS_PORT
})

afterEach(() => {
process.env = originalEnv
})

it('builds unauthenticated redis url when REDIS_URI and REDIS_PASSWORD are unset', () => {
process.env.REDIS_HOST = 'localhost'
process.env.REDIS_PORT = '6379'

const config = getCacheConfig()

expect(config).to.deep.equal({
url: 'redis://localhost:6379',
})
})

it('builds authenticated redis config when REDIS_PASSWORD is set', () => {
process.env.REDIS_HOST = 'localhost'
process.env.REDIS_PORT = '6379'
process.env.REDIS_USER = 'default'
process.env.REDIS_PASSWORD = 'secret'

const config = getCacheConfig()

expect(config).to.deep.equal({
url: 'redis://localhost:6379',
username: 'default',
password: 'secret',
})
})

it('defaults REDIS_USER to default when REDIS_PASSWORD is set and REDIS_USER is unset', () => {
process.env.REDIS_HOST = 'localhost'
process.env.REDIS_PORT = '6379'
process.env.REDIS_PASSWORD = 'secret'

const config = getCacheConfig()

expect(config).to.deep.equal({
url: 'redis://localhost:6379',
username: 'default',
password: 'secret',
})
})

it('prefers REDIS_URI over host/port settings', () => {
process.env.REDIS_URI = 'redis://cache.internal:6380'
process.env.REDIS_PASSWORD = 'secret'

const config = getCacheConfig()

expect(config).to.deep.equal({
url: 'redis://cache.internal:6380',
password: 'secret',
})
})
})
Loading