@mbc-cqrs-serverless/core
Version:
CQRS and event base core
102 lines • 3.32 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.UserContext = void 0;
exports.getUserContext = getUserContext;
const constants_1 = require("../constants");
const invoke_1 = require("./invoke");
class UserContext {
constructor(partial) {
Object.assign(this, partial);
}
}
exports.UserContext = UserContext;
function parseTenantGroups(raw, tenantCode) {
if (!tenantCode) {
return [];
}
let parsed;
try {
parsed = JSON.parse(raw || '[]');
}
catch {
return [];
}
if (!Array.isArray(parsed)) {
return [];
}
const memberships = parsed.map((membership) => ({
...membership,
tenant: (membership.tenant || '').toLowerCase(),
}));
const match = memberships.find((membership) => membership.tenant === tenantCode);
const groups = match?.groups;
return Array.isArray(groups) ? groups : [];
}
function collectTenantRoles(roles, tenantCode) {
if (!tenantCode) {
return [];
}
const seen = new Set();
const result = [];
const add = (role) => {
if (!seen.has(role)) {
seen.add(role);
result.push(role);
}
};
for (const { tenant, role } of roles) {
if (tenant === tenantCode) {
add(role);
}
}
for (const { tenant, role } of roles) {
if (tenant === '') {
add(role);
}
}
return result;
}
/**
* Extract user context from JWT claims and request headers.
*
* Tenant code determination:
* 1. If `custom:tenant` exists in JWT claims, use it (user bound to specific tenant)
* 2. Otherwise, use `x-tenant-code` header (for cross-tenant operations)
*
* Note: Security validation for header-based tenant override is handled by RolesGuard,
* not by this function. This allows for flexible security policies at the application level.
*/
function getUserContext(ctx) {
if ('getHandler' in ctx) {
ctx = (0, invoke_1.extractInvokeContext)(ctx);
}
const claims = (0, invoke_1.getAuthorizerClaims)(ctx);
const userId = claims.sub;
// Parse roles
const roles = JSON.parse(claims['custom:roles'] || '[]').map((role) => ({ ...role, tenant: (role.tenant || '').toLowerCase() }));
// Determine tenant code
// 1. Cognito custom:tenant attribute takes priority
// 2. Otherwise, use header value (security check delegated to RolesGuard)
// Note: tenantCode is normalized to lowercase for case-insensitive matching with role.tenant
const tenantCode = (claims['custom:tenant'] || (ctx?.event?.headers || {})[constants_1.HEADER_TENANT_CODE])?.toLowerCase();
const tenantGroupIds = parseTenantGroups(claims['custom:groups'], tenantCode);
const tenantRoles = collectTenantRoles(roles, tenantCode);
// Find tenantRole (case-insensitive matching - both tenantCode and role.tenant are lowercase)
let tenantRole = '';
for (const { tenant, role } of roles) {
if (tenant === '' || tenant === tenantCode) {
tenantRole = role;
if (tenant !== '') {
break;
}
}
}
return {
userId,
tenantRole,
tenantCode,
tenantRoles,
tenantGroupIds,
};
}
//# sourceMappingURL=user.js.map