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
5 changes: 3 additions & 2 deletions src/constants/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,9 @@ module.exports = {
READ_ACCESS: 'r',
TYPE_ALL: 'all',
ENGLISH_LANGUGE_CODE: 'en',
ORG_CODE_HEADER: 'organizationcode',
TENANT_CODE_HEADER: 'tenantcode',
ORG_CODE_HEADER: process.env.ORG_CODE_HEADER_NAME.toLowerCase(),
ORG_ID_HEADER: process.env.ORG_ID_HEADER_NAME.toLowerCase(),
TENANT_CODE_HEADER: process.env.TENANT_CODE_HEADER_NAME.toLowerCase(),
DELETE_METHOD: 'DELETE',
SEQUELIZE_FOREIGN_KEY_CONSTRAINT_ERROR: 'SequelizeForeignKeyConstraintError',
BULK_INVITATION_VALIDITY: '604800000', //SET to one week by default if not set by tenant (In Sec),
Expand Down
4 changes: 2 additions & 2 deletions src/controllers/v1/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ module.exports = class Admin {
req.body.organization_id,
req.decodedToken.id,
req.body?.identifier,
req.headers?.['tenant-id'],
req.headers?.[common.TENANT_CODE_HEADER],
req.body?.phone_code
)
return orgAdminCreation
Expand Down Expand Up @@ -154,7 +154,7 @@ module.exports = class Admin {

const result = await adminService.deactivateOrg(
req.params.id,
req.headers?.['tenant-id'],
req.headers?.[common.TENANT_CODE_HEADER],
req.decodedToken.id
)
return result
Expand Down
3 changes: 2 additions & 1 deletion src/controllers/v1/form.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

// Dependencies
const formsService = require('@services/form')
const common = require('@constants/common')

module.exports = class Form {
/**
Expand Down Expand Up @@ -100,7 +101,7 @@ module.exports = class Form {
req.params.id,
params,
req?.decodedToken?.organization_id || null,
req?.decodedToken?.tenant_code || req?.headers?.tenant_code || null,
req?.decodedToken?.tenant_code || req?.headers?.[common.TENANT_CODE_HEADER] || null,
domain
)
return form
Expand Down
3 changes: 2 additions & 1 deletion src/controllers/v1/public.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
const publicService = require('@services/public')
const { getDomainFromRequest } = require('@utils/domain')
const common = require('@constants/common')

module.exports = class Public {
async branding(req) {
let domain = ''
let tenantCode = req?.headers?.tenantid || null
let tenantCode = req?.headers?.[common.TENANT_CODE_HEADER] || null
if (!tenantCode) {
domain = getDomainFromRequest(req)
}
Expand Down
4 changes: 2 additions & 2 deletions src/controllers/v1/tenant.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ module.exports = class Tenant {
const tenant = await tenantService.userBulkUpload(
req.body.file_path,
req.decodedToken.id,
req.headers.organization,
req.headers.tenant,
req.headers?.[common.ORG_CODE_HEADER],
req.headers?.[common.TENANT_CODE_HEADER],
req?.body?.editable_fields,
req?.body?.upload_type.toUpperCase()
)
Expand Down
15 changes: 15 additions & 0 deletions src/envVariables.js
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,21 @@ let enviromentVariables = {
value: 'true',
},
},
ORG_ID_HEADER_NAME: {
message: 'Required ORG_ID_HEADER_NAME',
optional: true,
default: 'x-org-id',
},
ORG_CODE_HEADER_NAME: {
message: 'Required ORG_CODE_HEADER_NAME',
optional: true,
default: 'x-org-code',
},
TENANT_CODE_HEADER_NAME: {
message: 'Required TENANT_CODE_HEADER_NAME',
optional: true,
default: 'x-tenant-code',
},
}
let success = true

Expand Down
5 changes: 4 additions & 1 deletion src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -182,5 +182,8 @@
"REG_CODE_ERROR": "registration_code is not valid or unique.",
"INVALID_REG_CODE_ERROR": "registration_codes {{errorMessage}}. Invalid Code(s) : {{errorValues}}",
"UNIQUE_CONSTRAINT_ERROR": "{{fields}} is Invalid.",
"USER_PROFILE_FETCHED_SUCCESSFULLY": "User profile fetched successfully!"
"USER_PROFILE_FETCHED_SUCCESSFULLY": "User profile fetched successfully!",
"ADD_ORG_HEADER": "Please provide all required organization headers: {{orgCodeHeader}}, and {{tenantCodeHeader}} for admin override.",
"INVALID_ORG_ID": "Organization ID must be a valid positive integer.",
"INVALID_ORG_OR_TENANT_CODE": "The provided organization or tenant code is invalid or does not match."
}
75 changes: 59 additions & 16 deletions src/middlewares/authenticator.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ const jwt = require('jsonwebtoken')
const httpStatusCode = require('@generics/http-status')
const common = require('@constants/common')
const userQueries = require('@database/queries/users')
const roleQueries = require('@database/queries/user-role')

const rolePermissionMappingQueries = require('@database/queries/role-permission-mapping')
const { Op } = require('sequelize')
const responses = require('@helpers/responses')
const utilsHelper = require('@generics/utils')
const { verifyCaptchaToken } = require('@utils/captcha')
const { getDomainFromRequest } = require('@utils/domain')
const tenantDomainQueries = require('@database/queries/tenantDomain')
const organizationQueries = require('@database/queries/organization')

async function checkPermissions(roleTitle, requestPath, requestMethod) {
const parts = requestPath.match(/[^/]+/g)
Expand Down Expand Up @@ -110,18 +111,13 @@ module.exports = async function (req, res, next) {
})
}

const domain = getDomainFromRequest(req) || null
const tenant_code =
req?.headers?.tenantId ||
req?.headers?.tenantid ||
req?.headers?.tenant_Id ||
req?.headers?.tenant_id ||
req?.headers?.tenant ||
req?.headers?.tenant_code ||
req.headers.tenantCode ||
null
const tenantFilter = {}
const domain = getDomainFromRequest(req)

const tenant_code = req?.headers?.[common.TENANT_CODE_HEADER] ?? null

const tenantFilter = domain ? { domain } : tenant_code ? { tenant_code } : null || {}
if (domain) tenantFilter.domain = domain
else if (tenant_code) tenantFilter.tenant_code = tenant_code

if (Object.keys(tenantFilter).length > 0) {
const tenantDomain = await tenantDomainQueries.findOne(tenantFilter, {
Expand Down Expand Up @@ -159,6 +155,7 @@ module.exports = async function (req, res, next) {
token = extractedToken.trim()
} else token = authHeader.trim()

let decodedToken
let org
try {
decodedToken = jwt.verify(token, process.env.ACCESS_TOKEN_SECRET)
Expand Down Expand Up @@ -209,10 +206,56 @@ module.exports = async function (req, res, next) {
//check for admin user
let isAdmin = false
if (decodedToken.data.roles) {
isAdmin = decodedToken.data.roles.some((role) => role.title == common.ADMIN_ROLE)
if (isAdmin) {
req.decodedToken = decodedToken.data
return next()
isAdmin = decodedToken.data.roles.some((role) => role.title === common.ADMIN_ROLE)
}

if (isAdmin) {
// For admin users, allow overriding tenant_code and organization_code via headers
// Header names are configurable via environment variables with sensible defaults
const orgCodeHeaderName = common.ORG_CODE_HEADER
const tenantCodeHeaderName = common.TENANT_CODE_HEADER

// Extract and sanitize header values (trim whitespace, case-insensitive header lookup)
const orgCode = (req.headers[orgCodeHeaderName.toLowerCase()] || '').trim()
const tenantCode = (req.headers[tenantCodeHeaderName.toLowerCase()] || '').trim()

// If any override header is provided (non-empty after trim), both must be present and non-empty
const hasAnyOverrideHeader = orgCode || tenantCode
if (hasAnyOverrideHeader) {
if (!orgCode || !tenantCode) {
throw responses.failureResponse({
message: {
key: 'ADD_ORG_HEADER',
interpolation: {
orgCodeHeader: orgCodeHeaderName,
tenantCodeHeader: tenantCodeHeaderName,
},
},
statusCode: httpStatusCode.bad_request,
responseCode: 'CLIENT_ERROR',
})
}

// Query the database to find the organization based on orgCode and tenantCode
const org = await organizationQueries.findOne({
code: orgCode,
tenant_code: tenantCode,
status: common.ACTIVE_STATUS,
deleted_at: null,
})

if (!org) {
throw responses.failureResponse({
message: 'INVALID_ORG_OR_TENANT_CODE',
statusCode: httpStatusCode.bad_request,
responseCode: 'CLIENT_ERROR',
})
}

// Override the values from the token with sanitized header values and fetched orgId
decodedToken.data.tenant_code = tenantCode
decodedToken.data.organization_id = org.id // Use the ID from the database
decodedToken.data.organization_code = orgCode
}
}

Expand Down