Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions src/commands/app/deploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const {
const rtLib = require('@adobe/aio-lib-runtime')
const LogForwarding = require('../../lib/log-forwarding')
const { sendAppAssetsDeployedAuditLog, sendAppDeployAuditLog } = require('../../lib/audit-logger')
const { setRuntimeApiHostAndAuthHandler, getAccessToken, getTokenData } = require('../../lib/auth-helper')
const { setRuntimeApiHostAndAuthHandler, getAccessToken } = require('../../lib/auth-helper')
const logActions = require('../../lib/log-actions')

const PRE_DEPLOY_EVENT_REG = 'pre-deploy-event-reg'
Expand Down Expand Up @@ -68,8 +68,6 @@ class Deploy extends BuildCommand {

if (cliDetails?.accessToken) {
try {
// store user id from token data for cdn deploy audit metadata
appInfo.auditUserId = getTokenData(cliDetails.accessToken)?.user_id
// send audit log at start (don't wait for deployment to finish)
await sendAppDeployAuditLog({
accessToken: cliDetails?.accessToken,
Expand Down Expand Up @@ -132,10 +130,7 @@ class Deploy extends BuildCommand {
// - break into smaller pieces deploy, allowing to first deploy all actions then all web assets
for (let i = 0; i < keys.length; ++i) {
const k = keys[i]
// auditUserId is only set if it is available in the token data
// falsy because "", 0, false, null, undefined, NaN, etc. are all invalid values
const v = {
...(appInfo.auditUserId && { auditUserId: appInfo.auditUserId }),
...setRuntimeApiHostAndAuthHandler(values[i])
}
await this.deploySingleConfig({ name: k, config: v, originalConfig: values[i], flags, spinner })
Expand Down
23 changes: 1 addition & 22 deletions src/lib/auth-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ OF ANY KIND, either express or implied. See the License for the specific languag
governing permissions and limitations under the License.
*/

const { getToken, context, getTokenData: getImsTokenData } = require('@adobe/aio-lib-ims')
const { getToken, context } = require('@adobe/aio-lib-ims')
const { CLI } = require('@adobe/aio-lib-ims/src/context')
const { getCliEnv } = require('@adobe/aio-lib-env')
const aioLogger = require('@adobe/aio-lib-core-logging')('@adobe/aio-cli-plugin-app:auth-helper', { provider: 'debug' })
Expand Down Expand Up @@ -90,29 +90,8 @@ const setRuntimeApiHostAndAuthHandler = (_config) => {
}
}

/**
* Decodes a JWT token and returns its payload as a JavaScript object.
*
* @function getTokenData
* @param {string} token - The JWT token to decode
* @returns {object|null} The decoded payload of the JWT token or null if the token is invalid or cannot be decoded
*/
const getTokenData = (token) => {
if (typeof token !== 'string') {
aioLogger.error('Invalid token provided to getTokenData :: not a string')
return null
}
try {
return getImsTokenData(token)
} catch (e) {
aioLogger.error('Error decoding token payload in getTokenData ::', e)
return null
}
}

module.exports = {
getAccessToken,
getTokenData,
bearerAuthHandler,
setRuntimeApiHostAndAuthHandler
}
134 changes: 1 addition & 133 deletions test/commands/app/deploy.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,7 @@ beforeEach(() => {
env: 'stage'
}
})
authHelper.getTokenData.mockImplementation(() => {
return null // default to null, tests can override
})

LogForwarding.init.mockResolvedValue(mockLogForwarding)

command = new TheCommand([])
Expand Down Expand Up @@ -673,136 +671,6 @@ describe('run', () => {
expect(open).toHaveBeenCalledWith('http://prefix?fake=https://example.com')
})

test('deploy should pass auditUserId to deployWeb config when user_id is present in token', async () => {
const mockUserId = 'test-user-123'
const mockToken = 'mock.token.value'

authHelper.getAccessToken.mockResolvedValueOnce({
accessToken: mockToken,
env: 'stage'
})
authHelper.getTokenData.mockReturnValueOnce({
user_id: mockUserId
})

command.getAppExtConfigs.mockResolvedValueOnce(createAppConfig(command.appConfig))
mockWebLib.deployWeb.mockResolvedValue('https://example.com')

command.argv = []
await command.run()

expect(command.error).toHaveBeenCalledTimes(0)
expect(authHelper.getTokenData).toHaveBeenCalledWith(mockToken)
expect(mockWebLib.deployWeb).toHaveBeenCalledWith(
expect.objectContaining({
auditUserId: mockUserId
}),
expect.any(Function)
)
})

test('deploy should NOT include auditUserId in config when user_id is undefined', async () => {
const mockToken = 'mock.token.value'

authHelper.getAccessToken.mockResolvedValueOnce({
accessToken: mockToken,
env: 'stage'
})
authHelper.getTokenData.mockReturnValueOnce({
// user_id is undefined
})

command.getAppExtConfigs.mockResolvedValueOnce(createAppConfig(command.appConfig))
mockWebLib.deployWeb.mockResolvedValue('https://example.com')

command.argv = []
await command.run()

expect(command.error).toHaveBeenCalledTimes(0)
expect(mockWebLib.deployWeb).toHaveBeenCalledWith(
expect.not.objectContaining({
auditUserId: expect.anything()
}),
expect.any(Function)
)
})

test('deploy should NOT include auditUserId in config when user_id is null', async () => {
const mockToken = 'mock.token.value'

authHelper.getAccessToken.mockResolvedValueOnce({
accessToken: mockToken,
env: 'stage'
})
authHelper.getTokenData.mockReturnValueOnce({
user_id: null
})

command.getAppExtConfigs.mockResolvedValueOnce(createAppConfig(command.appConfig))
mockWebLib.deployWeb.mockResolvedValue('https://example.com')

command.argv = []
await command.run()

expect(command.error).toHaveBeenCalledTimes(0)
expect(mockWebLib.deployWeb).toHaveBeenCalledWith(
expect.not.objectContaining({
auditUserId: expect.anything()
}),
expect.any(Function)
)
})

test('deploy should NOT include auditUserId in config when user_id is empty string', async () => {
const mockToken = 'mock.token.value'

authHelper.getAccessToken.mockResolvedValueOnce({
accessToken: mockToken,
env: 'stage'
})
authHelper.getTokenData.mockReturnValueOnce({
user_id: ''
})

command.getAppExtConfigs.mockResolvedValueOnce(createAppConfig(command.appConfig))
mockWebLib.deployWeb.mockResolvedValue('https://example.com')

command.argv = []
await command.run()

expect(command.error).toHaveBeenCalledTimes(0)
expect(mockWebLib.deployWeb).toHaveBeenCalledWith(
expect.not.objectContaining({
auditUserId: expect.anything()
}),
expect.any(Function)
)
})

test('deploy should NOT include auditUserId when getTokenData returns null', async () => {
const mockToken = 'mock.token.value'

authHelper.getAccessToken.mockResolvedValueOnce({
accessToken: mockToken,
env: 'stage'
})
authHelper.getTokenData.mockReturnValueOnce(null)

command.getAppExtConfigs.mockResolvedValueOnce(createAppConfig(command.appConfig))
mockWebLib.deployWeb.mockResolvedValue('https://example.com')

command.argv = []
await command.run()

expect(command.error).toHaveBeenCalledTimes(0)
expect(mockWebLib.deployWeb).toHaveBeenCalledWith(
expect.not.objectContaining({
auditUserId: expect.anything()
}),
expect.any(Function)
)
})

test('deploy should show action urls (web-export: true)', async () => {
command.getAppExtConfigs.mockResolvedValueOnce(createAppConfig(command.appConfig))
mockRuntimeLib.deployActions.mockResolvedValue({
Expand Down
35 changes: 2 additions & 33 deletions test/commands/lib/auth-helper.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { getAccessToken, bearerAuthHandler, setRuntimeApiHostAndAuthHandler, getTokenData } = require('../../../src/lib/auth-helper')
const { getToken, context, getTokenData: getImsTokenData } = require('@adobe/aio-lib-ims')
const { getAccessToken, bearerAuthHandler, setRuntimeApiHostAndAuthHandler } = require('../../../src/lib/auth-helper')
const { getToken, context } = require('@adobe/aio-lib-ims')
const { CLI } = require('@adobe/aio-lib-ims/src/context')
const { getCliEnv } = require('@adobe/aio-lib-env')

Expand Down Expand Up @@ -57,37 +57,6 @@ describe('getAccessToken', () => {
})
})

describe('getTokenData', () => {
beforeEach(() => {
jest.clearAllMocks()
})

test('should call through to getImsTokenData to decode JWT token and return payload', () => {
getImsTokenData.mockReturnValue({ user_id: '12345', name: 'Test User' })
// Example JWT token with payload: {"user_id":"12345","name":"Test User"}
const exampleToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMTIzNDUiLCJuYW1lIjoiVGVzdCBVc2VyIn0.sflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'
const result = getTokenData(exampleToken)
expect(result).toEqual({ user_id: '12345', name: 'Test User' })
})
test('should return null for invalid token', () => {
getImsTokenData.mockImplementation(() => { throw new Error('Invalid token') })
const invalidToken = 'invalid.token.string'
const result = getTokenData(invalidToken)
expect(result).toBeNull()
})
test('should return null for malformed token', () => {
getImsTokenData.mockImplementation(() => { throw new Error('Malformed token') })
const malformedToken = 'malformedtoken'
const result = getTokenData(malformedToken)
expect(result).toBeNull()
})
test('should return null for non-string token', () => {
const nonStringToken = 12345
const result = getTokenData(nonStringToken)
expect(result).toBeNull()
})
})

describe('bearerAuthHandler', () => {
beforeEach(() => {
jest.clearAllMocks()
Expand Down