Skip to content
Merged
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
84 changes: 77 additions & 7 deletions src/generics/middleware/authenticator.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ module.exports = async function (req, res, next, token = '') {
return
}
}

if (!token) {
rspObj.errCode = CONSTANTS.apiResponses.TOKEN_MISSING_CODE
rspObj.errMsg = CONSTANTS.apiResponses.TOKEN_MISSING_MESSAGE
Expand Down Expand Up @@ -216,10 +215,13 @@ module.exports = async function (req, res, next, token = '') {
defaultTokenExtraction = true
}

let organizationKey = 'organization_id'

// Create user details to request
req.userDetails = {
userToken: token,
}

// performing default token data extraction
if (defaultTokenExtraction) {
if (!decodedToken.data.organization_ids || !decodedToken.data.tenant_id) {
Expand Down Expand Up @@ -248,16 +250,37 @@ module.exports = async function (req, res, next, token = '') {
tenantId: decodedToken.data.tenant_id.toString(),
}
} else {
// Iterate through each key in the config object
for (let key in configData) {
let stringTypeKeys = ['userId', 'tenantId', 'organizationId']
if (configData.hasOwnProperty(key)) {
let keyValue = getNestedValue(decodedToken, configData[key])
if (stringTypeKeys.includes(key)) {
keyValue = keyValue.toString()
if (key == 'userId') {
keyValue = keyValue?.toString()
}
if (key === organizationKey) {
let value = getOrgId(req.headers, decodedToken, configData[key])
userInformation[`organizationId`] = value.toString()
decodedToken.data[key] = value
continue
}
if (key === 'roles') {
let orgId = getOrgId(req.headers, decodedToken, configData[organizationKey])
// Now extract roles using fully dynamic path
const rolePathTemplate = configData['roles']
decodedToken.data[organizationKey] = orgId
const resolvedRolePath = resolvePathTemplate(rolePathTemplate, decodedToken.data)
const roles = getNestedValue(decodedToken, resolvedRolePath) || []
userInformation[`${key}`] = roles
decodedToken.data[key] = roles
continue
}

// For each key in config, assign the corresponding value from decodedToken
userInformation[`${key}`] = keyValue
decodedToken.data[key] = keyValue
if (key == 'tenant_id') {
userInformation[`tenantId`] = keyValue.toString()
} else {
userInformation[`${key}`] = keyValue
}
}
}
if (userInformation.roles && Array.isArray(userInformation.roles) && userInformation.roles.length) {
Expand Down Expand Up @@ -497,9 +520,56 @@ module.exports = async function (req, res, next, token = '') {
if (decodedToken.data.tenantAndOrgInfo) {
req.userDetails.tenantAndOrgInfo = decodedToken.data.tenantAndOrgInfo
}

// Helper function to access nested properties
function getOrgId(headers, decodedToken, orgConfigData) {
if (headers['organization_id']) {
return (orgId = headers['organization_id'].toString())
} else {
const orgIdPath = orgConfigData
return (orgId = getNestedValue(decodedToken, orgIdPath)?.toString())
}
}

function getNestedValue(obj, path) {
return path.split('.').reduce((acc, part) => acc && acc[part], obj)
const parts = path.split('.')
let current = obj

for (const part of parts) {
if (!current) return undefined

// Conditional match: key[?field=value]
const conditionalMatch = part.match(/^(\w+)\[\?(\w+)=([^\]]+)\]$/)
if (conditionalMatch) {
const [, arrayKey, field, expected] = conditionalMatch
const array = current[arrayKey]
if (!Array.isArray(array)) return undefined
const found = array.find((item) => String(item[field]) === String(expected))
if (!found) return undefined
current = found
continue
}

// Index match: key[0]
const indexMatch = part.match(/^(\w+)\[(\d+)\]$/)
if (indexMatch) {
const [, key, index] = indexMatch
const array = current[key]
if (!Array.isArray(array)) return undefined
current = array[parseInt(index, 10)]
continue
}

current = current[part]
}
return current
}

function resolvePathTemplate(template, contextObject) {
return template.replace(/\{\{(.*?)\}\}/g, (_, path) => {
const value = getNestedValue(contextObject, path.trim())
return value?.toString?.() ?? ''
})
}
next()
}