Skip to content

Conversation

@nevil-mathew
Copy link
Collaborator

@nevil-mathew nevil-mathew commented Oct 9, 2025

Summary by CodeRabbit

  • New Features

    • Added configurable headers for admin overrides of organization and tenant context.
    • Introduced environment settings to customize header names for organization ID, organization code, and tenant code.
    • Added new English messages for prompting org header input and invalid organization ID feedback.
  • Bug Fixes

    • Enforced all-or-nothing admin override headers with strict validation, preventing partial or malformed overrides.
    • Validated organization ID as a positive integer and returned clear error messages for invalid inputs.

@coderabbitai
Copy link

coderabbitai bot commented Oct 9, 2025

Caution

Review failed

Failed to post review comments

Walkthrough

Adds three configurable header-name environment variables for org/tenant identification, introduces two English locale keys, and revises authenticator middleware to support admin-only header-based overrides for tenant_code, organization_id, and organization_code with validation and error handling.

Changes

Cohort / File(s) Summary
Env header configuration
src/envVariables.js
Adds ORG_ID_HEADER_NAME, ORG_CODE_HEADER_NAME, TENANT_CODE_HEADER_NAME to enviromentVariables with defaults (X-Org-Id, X-Org-Code, X-Tenant-Code) and optional flags.
Localization updates
src/locales/en.json
Adds ADD_ORG_HEADER and INVALID_ORG_ID; adjusts JSON punctuation after USER_PROFILE_FETCHED_SUCCESSFULLY to append new keys.
Authenticator admin overrides
src/middlewares/authenticator.js
Refactors admin flow to: read configurable header names, trim values, require all-or-none presence, validate orgId as positive integer, override token fields (tenant_code, organization_id, organization_code), and set req.decodedToken from updated token data; removes roleQueries import.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant C as Client
  participant MW as Authenticator Middleware
  participant ENV as Env Config

  C->>MW: HTTP request with Authorization header
  MW->>MW: Validate/parse JWT -> decodedToken
  alt isAdmin
    MW->>ENV: Read ORG_ID_HEADER_NAME / ORG_CODE_HEADER_NAME / TENANT_CODE_HEADER_NAME
    MW->>MW: Extract & trim header values
    alt Any override header present
      MW->>MW: Validate all three present
      MW->>MW: Validate orgId is positive integer
      alt Validation fails
        MW-->>C: 400/422 error (invalid/missing headers)
      else Validation passes
        MW->>MW: Override decodedToken.data { tenant_code, organization_id, organization_code }
        MW->>MW: req.decodedToken = decodedToken.data
        MW-->>C: next()
      end
    else No override headers
      MW->>MW: req.decodedToken = decodedToken.data
      MW-->>C: next()
    end
  else non-admin
    MW->>MW: Standard token handling
    MW->>MW: req.decodedToken = decodedToken.data
    MW-->>C: next()
  end

  note over MW: Admin overrides only apply when all headers are provided<br/>and orgId passes integer/positivity checks.
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

I tweak my ears to headers new,
Three little signs the winds now blew—
X-Org, X-Code, X-Tenant too,
The gate admits what admins knew.
With careful paws I validate,
Then hop along to propagate.
Boop! The token’s set—ship true. 🐇✨

Pre-merge checks and finishing touches and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The title "@coderabbitai" does not describe the pull request changes and is unrelated to the implementation of environment variables, localization keys, and admin override logic, so it fails to convey the purpose or scope of the update. Please update the title to a concise sentence summarizing the main change, for example “Add configurable admin-override headers for tenant and organization and update environment variable mappings.”
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch admin-override-tenant-org
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch admin-override-tenant-org

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai bot changed the title @coderabbitai Add header env vars, locale keys, and admin auth middleware overrides Oct 9, 2025
@nevil-mathew nevil-mathew changed the title Add header env vars, locale keys, and admin auth middleware overrides feat(auth): allow admin override of tenant/org Oct 9, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/middlewares/authenticator.js (1)

172-181: Possible crash when organizations/roles missing; guard and default.

org may be undefined; org.roles access will throw. Default roles safely.

-			decodedToken.data = {
-				id: decodedToken.data.id,
-				name: decodedToken.data.name,
-				session_id: decodedToken.data.session_id,
-				tenant_code: decodedToken.data.tenant_code,
-				organization_id,
-				organization_code,
-				roles: org.roles,
-			}
+			const roles =
+				Array.isArray(org?.roles)
+					? org.roles
+					: (Array.isArray(decodedToken.data.roles) ? decodedToken.data.roles : [])
+			decodedToken.data = {
+				id: decodedToken.data.id,
+				name: decodedToken.data.name,
+				session_id: decodedToken.data.session_id,
+				tenant_code: decodedToken.data.tenant_code,
+				organization_id,
+				organization_code,
+				roles,
+			}
🧹 Nitpick comments (3)
src/locales/en.json (1)

186-187: Use consistent interpolation placeholders (double curly braces).

Elsewhere i18n uses {{var}}. Update to avoid runtime/template mismatch.

-	"ADD_ORG_HEADER": "Please provide all required organization headers: {orgIdHeader}, {orgCodeHeader}, and {tenantCodeHeader} for admin override.",
+	"ADD_ORG_HEADER": "Please provide all required organization headers: {{orgIdHeader}}, {{orgCodeHeader}}, and {{tenantCodeHeader}} for admin override.",
src/middlewares/authenticator.js (2)

276-281: Guard role fallback during roleValidation to avoid undefined access.

org may be undefined; fall back to decodedToken.data.roles or [].

-			const roles = org.roles
+			const roles =
+				Array.isArray(org?.roles)
+					? org.roles
+					: (Array.isArray(decodedToken.data.roles) ? decodedToken.data.roles : [])

256-260: Add audit logging when admin override is applied.

For traceability, log admin ID, original org/tenant, and overridden values (no PII).

I can wire this using your existing logger (e.g., @generics/logger). Want a quick patch?

Also applies to: 262-263

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dd08237 and 490a558.

📒 Files selected for processing (3)
  • src/envVariables.js (1 hunks)
  • src/locales/en.json (1 hunks)
  • src/middlewares/authenticator.js (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/middlewares/**

⚙️ CodeRabbit configuration file

This is security-sensitive middleware. Scrutinize for potential vulnerabilities.

Files:

  • src/middlewares/authenticator.js
🧠 Learnings (1)
📚 Learning: 2025-07-31T08:44:36.982Z
Learnt from: nevil-mathew
PR: ELEVATE-Project/user#776
File: src/services/entities.js:18-23
Timestamp: 2025-07-31T08:44:36.982Z
Learning: In the ELEVATE-Project/user codebase, organizationCode and tenantCode parameters passed to service methods always come from req.decodedToken.organization_code and req.decodedToken.tenant_code, which are guaranteed to be present after token validation. Additional validation for these parameters in service methods is unnecessary as the token validation process ensures they are always available.

Applied to files:

  • src/middlewares/authenticator.js
🧬 Code graph analysis (1)
src/middlewares/authenticator.js (2)
src/services/organization-feature.js (3)
  • isAdmin (36-36)
  • orgCode (38-38)
  • tenantCode (39-39)
src/controllers/v1/organization-feature.js (3)
  • isAdmin (27-27)
  • orgCode (68-68)
  • tenantCode (69-69)
🔇 Additional comments (2)
src/middlewares/authenticator.js (2)

228-244: failureResponse supports message objects with key and interpolation
The helper extracts message.key as the error message and preserves message.interpolation, so passing an object is valid.


216-244: Use environment-configured header names consistently.
Verify that common.ORG_ID_HEADER, common.ORG_CODE_HEADER, and common.TENANT_CODE_HEADER are sourced from the corresponding process.env.*_HEADER_NAME values (or your envVariables defaults) so header lookups in authenticator.js, organization-feature.js, and any other modules remain in sync.

Comment on lines +219 to +227
const orgIdHeaderName = process.env.ORG_ID_HEADER_NAME
const orgCodeHeaderName = process.env.ORG_CODE_HEADER_NAME
const tenantCodeHeaderName = process.env.TENANT_CODE_HEADER_NAME

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

Copy link

@coderabbitai coderabbitai bot Oct 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid toLowerCase on potentially undefined env vars; prefer req.get and add safe defaults.

If env is unset, orgIdHeaderName.toLowerCase() throws. Also use req.get() for case-insensitive retrieval.

-			const orgIdHeaderName = process.env.ORG_ID_HEADER_NAME
-			const orgCodeHeaderName = process.env.ORG_CODE_HEADER_NAME
-			const tenantCodeHeaderName = process.env.TENANT_CODE_HEADER_NAME
-
-			// Extract and sanitize header values (trim whitespace, case-insensitive header lookup)
-			const orgId = (req.headers[orgIdHeaderName.toLowerCase()] || '').trim()
-			const orgCode = (req.headers[orgCodeHeaderName.toLowerCase()] || '').trim()
-			const tenantCode = (req.headers[tenantCodeHeaderName.toLowerCase()] || '').trim()
+			const orgIdHeaderName = process.env.ORG_ID_HEADER_NAME || 'X-Org-Id'
+			const orgCodeHeaderName = process.env.ORG_CODE_HEADER_NAME || 'X-Org-Code'
+			const tenantCodeHeaderName = process.env.TENANT_CODE_HEADER_NAME || 'X-Tenant-Code'
+
+			// Extract and sanitize header values (Express req.get is case-insensitive)
+			const orgId = (req.get(orgIdHeaderName) || '').trim()
+			const orgCode = (req.get(orgCodeHeaderName) || '').trim()
+			const tenantCode = (req.get(tenantCodeHeaderName) || '').trim()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const orgIdHeaderName = process.env.ORG_ID_HEADER_NAME
const orgCodeHeaderName = process.env.ORG_CODE_HEADER_NAME
const tenantCodeHeaderName = process.env.TENANT_CODE_HEADER_NAME
// Extract and sanitize header values (trim whitespace, case-insensitive header lookup)
const orgId = (req.headers[orgIdHeaderName.toLowerCase()] || '').trim()
const orgCode = (req.headers[orgCodeHeaderName.toLowerCase()] || '').trim()
const tenantCode = (req.headers[tenantCodeHeaderName.toLowerCase()] || '').trim()
const orgIdHeaderName = process.env.ORG_ID_HEADER_NAME || 'X-Org-Id'
const orgCodeHeaderName = process.env.ORG_CODE_HEADER_NAME || 'X-Org-Code'
const tenantCodeHeaderName = process.env.TENANT_CODE_HEADER_NAME || 'X-Tenant-Code'
// Extract and sanitize header values (Express req.get is case-insensitive)
const orgId = (req.get(orgIdHeaderName) || '').trim()
const orgCode = (req.get(orgCodeHeaderName) || '').trim()
const tenantCode = (req.get(tenantCodeHeaderName) || '').trim()
🤖 Prompt for AI Agents
In src/middlewares/authenticator.js around lines 219 to 227, the current code
calls toLowerCase() on environment variables and indexes req.headers directly,
which will throw if an env var is undefined and isn't the safest way to do
case-insensitive header lookup; change the code to first default each header
name from process.env to a safe string (e.g. '' or a known header key) before
calling toLowerCase or avoid calling it entirely, and use Express's
req.get(headerName) to retrieve headers case-insensitively, then trim the
returned value or default to '' so orgId, orgCode and tenantCode are always
defined strings.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have already added a default values for these new env variables.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the implementation of envVariables.js to understand how defaults are applied
cat src/envVariables.js

Length of output: 15049


🏁 Script executed:

#!/bin/bash
# Check if authenticator.js imports or uses the envVariables module
rg -n "envVariables|@envVariables|require.*envVariables" src/middlewares/authenticator.js

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Check how envVariables is typically used in other parts of the codebase
rg -n -C2 "require.*envVariables" --type=js -g '!node_modules/**' | head -50

Length of output: 148


🏁 Script executed:

#!/bin/bash
# Find the application entry point files
fd -e js "^(app|server|index)\.js$" -d 2

Length of output: 10


🏁 Script executed:

#!/bin/bash
# Check if envVariables is imported/required in common entry points
rg -n "envVariables" --type=js src/app.js src/server.js src/index.js app.js server.js index.js 2>/dev/null || echo "No matches found in entry points"

Length of output: 96


🏁 Script executed:

#!/bin/bash
# Search more broadly for where envVariables module is used/invoked
rg -n "require.*envVariables|envVariables\(\)" --type=js -g '!node_modules' -g '!dist' -g '!build'

Length of output: 148


🏁 Script executed:

#!/bin/bash
# Check the startup sequence in app.js to confirm envVariables runs before middleware
cat src/app.js | head -30

Length of output: 934


@nevil-mathew, you're absolutely right! I verified that src/app.js line 22 executes require('./envVariables')() at application startup, which sets these environment variables to their defaults before any middleware loads:

ORG_ID_HEADER_NAME: 'X-Org-Id'
ORG_CODE_HEADER_NAME: 'X-Org-Code'  
TENANT_CODE_HEADER_NAME: 'X-Tenant-Code'

So toLowerCase() won't throw errors as I initially warned. The current implementation is safe.

That said, using req.get(headerName) instead of req.headers[headerName.toLowerCase()] is still considered better practice since it's the Express-recommended way for case-insensitive header retrieval, but it's not critical given your startup sequence guarantees these values exist. The current code will work correctly.


🧬 Code Graph Analysis Results

[src/services/organization.js] lines 518-521

let filter = {
				organization_code: { [Op.in]: [organizationCode, defaultOrganizationCode] },
				tenant_code: tenantCode,
			}

🧠 Learnings used
Learnt from: nevil-mathew
PR: ELEVATE-Project/user#776
File: src/services/entities.js:18-23
Timestamp: 2025-07-31T08:44:36.982Z
Learning: In the ELEVATE-Project/user codebase, organizationCode and tenantCode parameters passed to service methods always come from req.decodedToken.organization_code and req.decodedToken.tenant_code, which are guaranteed to be present after token validation. Additional validation for these parameters in service methods is unnecessary as the token validation process ensures they are always available.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants