@vegait/accounts-sdk 0.1.1-beta.45404 → 0.1.1-beta.45436
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +14 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +14 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -93,7 +93,8 @@ var PRODUCT_CATALOG = {
|
|
|
93
93
|
code: "media-hub",
|
|
94
94
|
name: "Media Hub",
|
|
95
95
|
description: "Sinalizacao digital nas unidades",
|
|
96
|
-
url: "
|
|
96
|
+
url: "",
|
|
97
|
+
// sem frontend publico deployado; override local via NEXT_PUBLIC_MEDIA_HUB_URL
|
|
97
98
|
accent: "#EF802D",
|
|
98
99
|
ssoEnabled: true
|
|
99
100
|
},
|
|
@@ -109,7 +110,9 @@ var PRODUCT_CATALOG = {
|
|
|
109
110
|
code: "gdi",
|
|
110
111
|
name: "GDI",
|
|
111
112
|
description: "Gestao de dispositivos e internet",
|
|
112
|
-
url: "
|
|
113
|
+
url: "",
|
|
114
|
+
// ainda nao esta em prod; override de HML via NEXT_PUBLIC_GDI_URL
|
|
115
|
+
// quando ligar em prod, sera https://gdi.app.vegait.com/
|
|
113
116
|
accent: "#7B8B7E",
|
|
114
117
|
ssoEnabled: false
|
|
115
118
|
},
|
|
@@ -117,7 +120,7 @@ var PRODUCT_CATALOG = {
|
|
|
117
120
|
code: "sim",
|
|
118
121
|
name: "SIM",
|
|
119
122
|
description: "Service Issue Management",
|
|
120
|
-
url: "https://sim.
|
|
123
|
+
url: "https://sim.app.vegait.com/",
|
|
121
124
|
accent: "#5B7DB1",
|
|
122
125
|
ssoEnabled: false
|
|
123
126
|
},
|
|
@@ -125,7 +128,8 @@ var PRODUCT_CATALOG = {
|
|
|
125
128
|
code: "hxp",
|
|
126
129
|
name: "HXP",
|
|
127
130
|
description: "Hotel Experience Platform",
|
|
128
|
-
url: "
|
|
131
|
+
url: "",
|
|
132
|
+
// nao ligado no Accounts
|
|
129
133
|
accent: "#9B6A4F",
|
|
130
134
|
ssoEnabled: false
|
|
131
135
|
},
|
|
@@ -133,7 +137,8 @@ var PRODUCT_CATALOG = {
|
|
|
133
137
|
code: "feedback",
|
|
134
138
|
name: "Feedback",
|
|
135
139
|
description: "Pesquisas e NPS de hospedes",
|
|
136
|
-
url: "
|
|
140
|
+
url: "",
|
|
141
|
+
// nao ligado no Accounts
|
|
137
142
|
accent: "#C26A8C",
|
|
138
143
|
ssoEnabled: false
|
|
139
144
|
},
|
|
@@ -141,7 +146,7 @@ var PRODUCT_CATALOG = {
|
|
|
141
146
|
code: "behavior",
|
|
142
147
|
name: "Behavior",
|
|
143
148
|
description: "Inteligencia comportamental",
|
|
144
|
-
url: "https://behavior.
|
|
149
|
+
url: "https://behavior.app.vegait.com/",
|
|
145
150
|
accent: "#4A7C7E",
|
|
146
151
|
ssoEnabled: false
|
|
147
152
|
}
|
|
@@ -173,15 +178,15 @@ function getCatalogWithAccess(claims, opts) {
|
|
|
173
178
|
return Object.values(PRODUCT_CATALOG).filter((p) => p.code !== "accounts").map((p) => ({
|
|
174
179
|
...urls?.[p.code] ? { ...p, url: urls[p.code] } : p,
|
|
175
180
|
accessible: isVega || accessibleCodes.has(p.code)
|
|
176
|
-
}));
|
|
181
|
+
})).filter((p) => p.url);
|
|
177
182
|
}
|
|
178
183
|
function getAccessibleProducts(claims, opts) {
|
|
179
184
|
if (!claims) return [];
|
|
180
185
|
const urls = opts?.urls;
|
|
181
186
|
if (derivePersona(claims) === "vega") {
|
|
182
|
-
return Object.values(PRODUCT_CATALOG).filter((p) => p.code !== "accounts"
|
|
187
|
+
return Object.values(PRODUCT_CATALOG).filter((p) => p.code !== "accounts").map((p) => urls?.[p.code] ? { ...p, url: urls[p.code] } : p).filter((p) => p.url);
|
|
183
188
|
}
|
|
184
|
-
return parseJsonArrayClaim(claims.products).map((code) => resolveProduct(code, urls)).filter((p) => p.
|
|
189
|
+
return parseJsonArrayClaim(claims.products).map((code) => resolveProduct(code, urls)).filter((p) => p.url);
|
|
185
190
|
}
|
|
186
191
|
|
|
187
192
|
// src/identityModel.ts
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/claims.ts","../src/persona.ts","../src/productCatalog.ts","../src/identityModel.ts"],"sourcesContent":["// Barrel do @vegait/accounts-sdk. Preenchido pelas tasks seguintes.\n\nexport type { AccountsClaims } from \"./claims\";\nexport {\n decodeJwtPayload,\n decodeAccessToken,\n parseJsonArrayClaim,\n normalizeClaimArray,\n} from \"./claims\";\n\nexport type { Persona } from \"./persona\";\nexport { derivePersona } from \"./persona\";\n\nexport type { ProductMeta, CatalogProduct } from \"./productCatalog\";\nexport {\n PRODUCT_CATALOG,\n resolveProduct,\n getAccessibleProducts,\n getCatalogWithAccess,\n} from \"./productCatalog\";\n\nexport type { IdentityModel } from \"./identityModel\";\nexport { buildIdentityModel } from \"./identityModel\";\n","// Decoder de payload JWT sem verificacao criptografica. Universal (browser +\n// Node). Usar APENAS para extrair claims de tokens ja confiaveis (recebidos do\n// Accounts via fluxo OIDC). NUNCA para validar tokens de origem externa.\n\nexport function decodeJwtPayload<T = Record<string, unknown>>(token: string): T {\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n throw new Error(\"Invalid JWT format\");\n }\n const payload = parts[1].replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = payload + \"=\".repeat((4 - (payload.length % 4)) % 4);\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n const json = new TextDecoder(\"utf-8\").decode(bytes);\n return JSON.parse(json) as T;\n}\n\n// Claims do AT emitido pelo Accounts via OpenIddict. Tudo opcional (nem todo\n// campo chega em todo contexto). unit_ids/products vem como string contendo\n// JSON array. platform_role aparece so para Time VEGA. Espelha o token; fonte\n// da verdade = VegaClaimsHandler no accounts.api.\nexport type AccountsClaims = {\n sub?: string;\n email?: string;\n name?: string;\n platform_role?: string;\n can_manage_scope_members?: string;\n org_id?: string | string[];\n unit_ids?: string;\n active_unit_id?: string;\n products?: string;\n exp?: number;\n iat?: number;\n iss?: string;\n aud?: string | string[];\n};\n\n// Normaliza claim que pode vir como string ou array (OpenIddict emite array\n// quando ha multiplos valores, scalar quando ha um so).\nexport function normalizeClaimArray(\n value: string | string[] | undefined,\n): string[] {\n if (!value) return [];\n return Array.isArray(value) ? value : [value];\n}\n\n// Parseia claim cujo value e string contendo JSON array (unit_ids, products).\n// Lista vazia em claim ausente ou JSON malformado.\nexport function parseJsonArrayClaim(value: string | undefined): string[] {\n if (!value) return [];\n try {\n const parsed = JSON.parse(value);\n return Array.isArray(parsed)\n ? parsed.filter((v): v is string => typeof v === \"string\")\n : [];\n } catch {\n return [];\n }\n}\n\n// Wrapper safe que devolve null em vez de jogar em token invalido.\nexport function decodeAccessToken(\n token: string | null | undefined,\n): AccountsClaims | null {\n if (!token) return null;\n try {\n return decodeJwtPayload<AccountsClaims>(token);\n } catch {\n return null;\n }\n}\n","import type { AccountsClaims } from \"./claims\";\n\nexport type Persona = \"usuario\" | \"gestor\" | \"vega\";\n\n// Deriva a persona a partir das claims do AT. Fail-closed: sem claims ou sem\n// sinal de gestao, cai em 'usuario' (menor privilegio). platform_role (Time\n// VEGA) tem precedencia sobre a flag de gestor de cliente.\n// ATENCAO: presentation gating, NAO autorizacao (ver README).\nexport function derivePersona(claims: AccountsClaims | null): Persona {\n if (claims?.platform_role) return \"vega\";\n if (claims?.can_manage_scope_members === \"true\") return \"gestor\";\n return \"usuario\";\n}\n","import type { AccountsClaims } from \"./claims\";\nimport { parseJsonArrayClaim } from \"./claims\";\nimport { derivePersona } from \"./persona\";\n\n// Catalogo cliente-side dos produtos VEGA. Mapeia o code (claim `products` do\n// AT) pra display name + URL default + accent. codes batem com o seed do\n// backend (SeedMediaHubInProductCatalog etc.). ssoEnabled = produto JA loga via\n// Accounts (OIDC unificado); a gaveta so mostra ssoEnabled=true.\n//\n// URLs aqui sao DEFAULTS de producao. O consumidor sobrescreve por ambiente via\n// o parametro `urls` (do seu proprio env) -- o SDK NAO le process.env.\n\nexport type ProductMeta = {\n code: string;\n name: string;\n description: string;\n url: string;\n accent: string;\n ssoEnabled: boolean;\n};\n\nexport const PRODUCT_CATALOG: Record<string, ProductMeta> = {\n accounts: {\n code: \"accounts\",\n name: \"Accounts\",\n description: \"Identidade unificada do ecossistema\",\n url: \"/\",\n accent: \"#1F2E52\",\n ssoEnabled: true, // e o proprio IdP; excluido da gaveta de qualquer forma\n },\n \"media-hub\": {\n code: \"media-hub\",\n name: \"Media Hub\",\n description: \"Sinalizacao digital nas unidades\",\n url: \"https://media-hub.vega.com.br/\",\n accent: \"#EF802D\",\n ssoEnabled: true,\n },\n sig: {\n code: \"sig\",\n name: \"SIG\",\n description: \"Housekeeping e governanca das unidades\",\n url: \"https://sig.app.vegait.com/\",\n accent: \"#B0C925\",\n ssoEnabled: true,\n },\n gdi: {\n code: \"gdi\",\n name: \"GDI\",\n description: \"Gestao de dispositivos e internet\",\n url: \"https://gdi.vega.com.br/\",\n accent: \"#7B8B7E\",\n ssoEnabled: false,\n },\n sim: {\n code: \"sim\",\n name: \"SIM\",\n description: \"Service Issue Management\",\n url: \"https://sim.vega.com.br/\",\n accent: \"#5B7DB1\",\n ssoEnabled: false,\n },\n hxp: {\n code: \"hxp\",\n name: \"HXP\",\n description: \"Hotel Experience Platform\",\n url: \"https://hxp.vega.com.br/\",\n accent: \"#9B6A4F\",\n ssoEnabled: false,\n },\n feedback: {\n code: \"feedback\",\n name: \"Feedback\",\n description: \"Pesquisas e NPS de hospedes\",\n url: \"https://feedback.vega.com.br/\",\n accent: \"#C26A8C\",\n ssoEnabled: false,\n },\n behavior: {\n code: \"behavior\",\n name: \"Behavior\",\n description: \"Inteligencia comportamental\",\n url: \"https://behavior.vega.com.br/\",\n accent: \"#4A7C7E\",\n ssoEnabled: false,\n },\n};\n\n// Aliases de code legado -> canonico. Resolve dados gravados ANTES de uma\n// renomeacao (ex.: governanca -> sig) sem duplicar entrada no catalogo.\nconst LEGACY_CODE_ALIASES: Record<string, string> = {\n governanca: \"sig\",\n};\n\n// Resolve um code (aplicando alias) pra ProductMeta. `urls` sobrescreve a URL\n// default pelo code CANONICO. Code desconhecido => fallback ssoEnabled=false.\nexport function resolveProduct(\n code: string,\n urls?: Record<string, string>,\n): ProductMeta {\n const canonical = LEGACY_CODE_ALIASES[code] ?? code;\n const base =\n PRODUCT_CATALOG[canonical] ??\n ({\n code,\n name: code,\n description: \"\",\n url: \"#\",\n accent: \"#1F2E52\",\n ssoEnabled: false,\n } satisfies ProductMeta);\n const override = urls?.[canonical];\n return override ? { ...base, url: override } : base;\n}\n\nexport type CatalogProduct = ProductMeta & { accessible: boolean };\n\n// Catalogo COMPLETO pro app launcher (menos o Accounts, que e o proprio IdP),\n// com flag de acesso: Time VEGA acessa tudo; cliente acessa os codes da claim\n// `products` (aliases legados resolvidos). ssoEnabled NAO esconde produto aqui\n// — e metadado de COMO o produto abre, nao de quem ve. Sem claims: tudo\n// inacessivel (fail-closed). `urls` sobrescreve URLs por ambiente.\nexport function getCatalogWithAccess(\n claims: AccountsClaims | null,\n opts?: { urls?: Record<string, string> },\n): CatalogProduct[] {\n const urls = opts?.urls;\n const isVega = claims != null && derivePersona(claims) === \"vega\";\n const accessibleCodes = new Set(\n parseJsonArrayClaim(claims?.products).map(\n (code) => LEGACY_CODE_ALIASES[code] ?? code,\n ),\n );\n return Object.values(PRODUCT_CATALOG)\n .filter((p) => p.code !== \"accounts\")\n .map((p) => ({\n ...(urls?.[p.code] ? { ...p, url: urls[p.code] } : p),\n accessible: isVega || accessibleCodes.has(p.code),\n }));\n}\n\n// Produtos do app launcher. Time VEGA ve o catalogo inteiro (menos o Accounts,\n// que e o IdP); cliente ve so os codes da claim `products`. Ambos filtram\n// ssoEnabled. `urls` sobrescreve URLs por ambiente.\nexport function getAccessibleProducts(\n claims: AccountsClaims | null,\n opts?: { urls?: Record<string, string> },\n): ProductMeta[] {\n if (!claims) return [];\n const urls = opts?.urls;\n if (derivePersona(claims) === \"vega\") {\n return Object.values(PRODUCT_CATALOG)\n .filter((p) => p.code !== \"accounts\" && p.ssoEnabled)\n .map((p) => (urls?.[p.code] ? { ...p, url: urls[p.code] } : p));\n }\n return parseJsonArrayClaim(claims.products)\n .map((code) => resolveProduct(code, urls))\n .filter((p) => p.ssoEnabled);\n}\n","import type { AccountsClaims } from \"./claims\";\nimport { derivePersona, type Persona } from \"./persona\";\nimport {\n getAccessibleProducts,\n getCatalogWithAccess,\n type CatalogProduct,\n type ProductMeta,\n} from \"./productCatalog\";\n\n// Modelo de dados da casca de identidade (SEM React). O host mapeia isto pros\n// props do <IdentityBar> do @vegait/lyra-ds-v2, adicionando icones/labels/\n// callbacks a partir de `capabilities`.\nexport type IdentityModel = {\n persona: Persona;\n user: { name: string; email: string };\n products: ProductMeta[];\n /** Catalogo completo (menos Accounts) com flag de acesso — pro waffle\n * mostrar tudo e desabilitar o que a persona nao acessa. `products`\n * permanece so-acessiveis por retrocompat (SIG consome hoje). */\n catalog: CatalogProduct[];\n currentProductCode?: string;\n capabilities: {\n manageUsers: boolean; // gestor/vega\n adminConsole: boolean; // vega\n switchContext: boolean; // gestor/vega\n manageNetwork: boolean; // gestor (rodape do launcher)\n };\n};\n\nexport function buildIdentityModel(\n claims: AccountsClaims | null,\n cfg?: { currentProductCode?: string; urls?: Record<string, string> },\n): IdentityModel {\n const persona = derivePersona(claims);\n return {\n persona,\n user: { name: claims?.name ?? \"\", email: claims?.email ?? \"\" },\n products: getAccessibleProducts(claims, { urls: cfg?.urls }),\n catalog: getCatalogWithAccess(claims, { urls: cfg?.urls }),\n currentProductCode: cfg?.currentProductCode,\n capabilities: {\n manageUsers: persona === \"gestor\" || persona === \"vega\",\n adminConsole: persona === \"vega\",\n switchContext: persona === \"gestor\" || persona === \"vega\",\n manageNetwork: persona === \"gestor\",\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACIO,SAAS,iBAA8C,OAAkB;AAC9E,QAAM,QAAQ,MAAM,MAAM,GAAG;AAC7B,MAAI,MAAM,WAAW,GAAG;AACtB,UAAM,IAAI,MAAM,oBAAoB;AAAA,EACtC;AACA,QAAM,UAAU,MAAM,CAAC,EAAE,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,GAAG;AAC7D,QAAM,SAAS,UAAU,IAAI,QAAQ,IAAK,QAAQ,SAAS,KAAM,CAAC;AAClE,QAAM,SAAS,KAAK,MAAM;AAC1B,QAAM,QAAQ,IAAI,WAAW,OAAO,MAAM;AAC1C,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,UAAM,CAAC,IAAI,OAAO,WAAW,CAAC;AAAA,EAChC;AACA,QAAM,OAAO,IAAI,YAAY,OAAO,EAAE,OAAO,KAAK;AAClD,SAAO,KAAK,MAAM,IAAI;AACxB;AAwBO,SAAS,oBACd,OACU;AACV,MAAI,CAAC,MAAO,QAAO,CAAC;AACpB,SAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AAC9C;AAIO,SAAS,oBAAoB,OAAqC;AACvE,MAAI,CAAC,MAAO,QAAO,CAAC;AACpB,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,WAAO,MAAM,QAAQ,MAAM,IACvB,OAAO,OAAO,CAAC,MAAmB,OAAO,MAAM,QAAQ,IACvD,CAAC;AAAA,EACP,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAGO,SAAS,kBACd,OACuB;AACvB,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI;AACF,WAAO,iBAAiC,KAAK;AAAA,EAC/C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ACjEO,SAAS,cAAc,QAAwC;AACpE,MAAI,QAAQ,cAAe,QAAO;AAClC,MAAI,QAAQ,6BAA6B,OAAQ,QAAO;AACxD,SAAO;AACT;;;ACSO,IAAM,kBAA+C;AAAA,EAC1D,UAAU;AAAA,IACR,MAAM;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA,IACb,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,YAAY;AAAA;AAAA,EACd;AAAA,EACA,aAAa;AAAA,IACX,MAAM;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA,IACb,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,YAAY;AAAA,EACd;AAAA,EACA,KAAK;AAAA,IACH,MAAM;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA,IACb,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,YAAY;AAAA,EACd;AAAA,EACA,KAAK;AAAA,IACH,MAAM;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA,IACb,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,YAAY;AAAA,EACd;AAAA,EACA,KAAK;AAAA,IACH,MAAM;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA,IACb,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,YAAY;AAAA,EACd;AAAA,EACA,KAAK;AAAA,IACH,MAAM;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA,IACb,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,YAAY;AAAA,EACd;AAAA,EACA,UAAU;AAAA,IACR,MAAM;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA,IACb,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,YAAY;AAAA,EACd;AAAA,EACA,UAAU;AAAA,IACR,MAAM;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA,IACb,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,YAAY;AAAA,EACd;AACF;AAIA,IAAM,sBAA8C;AAAA,EAClD,YAAY;AACd;AAIO,SAAS,eACd,MACA,MACa;AACb,QAAM,YAAY,oBAAoB,IAAI,KAAK;AAC/C,QAAM,OACJ,gBAAgB,SAAS,KACxB;AAAA,IACC;AAAA,IACA,MAAM;AAAA,IACN,aAAa;AAAA,IACb,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,YAAY;AAAA,EACd;AACF,QAAM,WAAW,OAAO,SAAS;AACjC,SAAO,WAAW,EAAE,GAAG,MAAM,KAAK,SAAS,IAAI;AACjD;AASO,SAAS,qBACd,QACA,MACkB;AAClB,QAAM,OAAO,MAAM;AACnB,QAAM,SAAS,UAAU,QAAQ,cAAc,MAAM,MAAM;AAC3D,QAAM,kBAAkB,IAAI;AAAA,IAC1B,oBAAoB,QAAQ,QAAQ,EAAE;AAAA,MACpC,CAAC,SAAS,oBAAoB,IAAI,KAAK;AAAA,IACzC;AAAA,EACF;AACA,SAAO,OAAO,OAAO,eAAe,EACjC,OAAO,CAAC,MAAM,EAAE,SAAS,UAAU,EACnC,IAAI,CAAC,OAAO;AAAA,IACX,GAAI,OAAO,EAAE,IAAI,IAAI,EAAE,GAAG,GAAG,KAAK,KAAK,EAAE,IAAI,EAAE,IAAI;AAAA,IACnD,YAAY,UAAU,gBAAgB,IAAI,EAAE,IAAI;AAAA,EAClD,EAAE;AACN;AAKO,SAAS,sBACd,QACA,MACe;AACf,MAAI,CAAC,OAAQ,QAAO,CAAC;AACrB,QAAM,OAAO,MAAM;AACnB,MAAI,cAAc,MAAM,MAAM,QAAQ;AACpC,WAAO,OAAO,OAAO,eAAe,EACjC,OAAO,CAAC,MAAM,EAAE,SAAS,cAAc,EAAE,UAAU,EACnD,IAAI,CAAC,MAAO,OAAO,EAAE,IAAI,IAAI,EAAE,GAAG,GAAG,KAAK,KAAK,EAAE,IAAI,EAAE,IAAI,CAAE;AAAA,EAClE;AACA,SAAO,oBAAoB,OAAO,QAAQ,EACvC,IAAI,CAAC,SAAS,eAAe,MAAM,IAAI,CAAC,EACxC,OAAO,CAAC,MAAM,EAAE,UAAU;AAC/B;;;ACjIO,SAAS,mBACd,QACA,KACe;AACf,QAAM,UAAU,cAAc,MAAM;AACpC,SAAO;AAAA,IACL;AAAA,IACA,MAAM,EAAE,MAAM,QAAQ,QAAQ,IAAI,OAAO,QAAQ,SAAS,GAAG;AAAA,IAC7D,UAAU,sBAAsB,QAAQ,EAAE,MAAM,KAAK,KAAK,CAAC;AAAA,IAC3D,SAAS,qBAAqB,QAAQ,EAAE,MAAM,KAAK,KAAK,CAAC;AAAA,IACzD,oBAAoB,KAAK;AAAA,IACzB,cAAc;AAAA,MACZ,aAAa,YAAY,YAAY,YAAY;AAAA,MACjD,cAAc,YAAY;AAAA,MAC1B,eAAe,YAAY,YAAY,YAAY;AAAA,MACnD,eAAe,YAAY;AAAA,IAC7B;AAAA,EACF;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/claims.ts","../src/persona.ts","../src/productCatalog.ts","../src/identityModel.ts"],"sourcesContent":["// Barrel do @vegait/accounts-sdk. Preenchido pelas tasks seguintes.\n\nexport type { AccountsClaims } from \"./claims\";\nexport {\n decodeJwtPayload,\n decodeAccessToken,\n parseJsonArrayClaim,\n normalizeClaimArray,\n} from \"./claims\";\n\nexport type { Persona } from \"./persona\";\nexport { derivePersona } from \"./persona\";\n\nexport type { ProductMeta, CatalogProduct } from \"./productCatalog\";\nexport {\n PRODUCT_CATALOG,\n resolveProduct,\n getAccessibleProducts,\n getCatalogWithAccess,\n} from \"./productCatalog\";\n\nexport type { IdentityModel } from \"./identityModel\";\nexport { buildIdentityModel } from \"./identityModel\";\n","// Decoder de payload JWT sem verificacao criptografica. Universal (browser +\n// Node). Usar APENAS para extrair claims de tokens ja confiaveis (recebidos do\n// Accounts via fluxo OIDC). NUNCA para validar tokens de origem externa.\n\nexport function decodeJwtPayload<T = Record<string, unknown>>(token: string): T {\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n throw new Error(\"Invalid JWT format\");\n }\n const payload = parts[1].replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = payload + \"=\".repeat((4 - (payload.length % 4)) % 4);\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n const json = new TextDecoder(\"utf-8\").decode(bytes);\n return JSON.parse(json) as T;\n}\n\n// Claims do AT emitido pelo Accounts via OpenIddict. Tudo opcional (nem todo\n// campo chega em todo contexto). unit_ids/products vem como string contendo\n// JSON array. platform_role aparece so para Time VEGA. Espelha o token; fonte\n// da verdade = VegaClaimsHandler no accounts.api.\nexport type AccountsClaims = {\n sub?: string;\n email?: string;\n name?: string;\n platform_role?: string;\n can_manage_scope_members?: string;\n org_id?: string | string[];\n unit_ids?: string;\n active_unit_id?: string;\n products?: string;\n exp?: number;\n iat?: number;\n iss?: string;\n aud?: string | string[];\n};\n\n// Normaliza claim que pode vir como string ou array (OpenIddict emite array\n// quando ha multiplos valores, scalar quando ha um so).\nexport function normalizeClaimArray(\n value: string | string[] | undefined,\n): string[] {\n if (!value) return [];\n return Array.isArray(value) ? value : [value];\n}\n\n// Parseia claim cujo value e string contendo JSON array (unit_ids, products).\n// Lista vazia em claim ausente ou JSON malformado.\nexport function parseJsonArrayClaim(value: string | undefined): string[] {\n if (!value) return [];\n try {\n const parsed = JSON.parse(value);\n return Array.isArray(parsed)\n ? parsed.filter((v): v is string => typeof v === \"string\")\n : [];\n } catch {\n return [];\n }\n}\n\n// Wrapper safe que devolve null em vez de jogar em token invalido.\nexport function decodeAccessToken(\n token: string | null | undefined,\n): AccountsClaims | null {\n if (!token) return null;\n try {\n return decodeJwtPayload<AccountsClaims>(token);\n } catch {\n return null;\n }\n}\n","import type { AccountsClaims } from \"./claims\";\n\nexport type Persona = \"usuario\" | \"gestor\" | \"vega\";\n\n// Deriva a persona a partir das claims do AT. Fail-closed: sem claims ou sem\n// sinal de gestao, cai em 'usuario' (menor privilegio). platform_role (Time\n// VEGA) tem precedencia sobre a flag de gestor de cliente.\n// ATENCAO: presentation gating, NAO autorizacao (ver README).\nexport function derivePersona(claims: AccountsClaims | null): Persona {\n if (claims?.platform_role) return \"vega\";\n if (claims?.can_manage_scope_members === \"true\") return \"gestor\";\n return \"usuario\";\n}\n","import type { AccountsClaims } from \"./claims\";\nimport { parseJsonArrayClaim } from \"./claims\";\nimport { derivePersona } from \"./persona\";\n\n// Catalogo cliente-side dos produtos VEGA. Mapeia o code (claim `products` do\n// AT) pra display name + URL default + accent. codes batem com o seed do\n// backend (SeedMediaHubInProductCatalog etc.).\n//\n// ssoEnabled = METADADO de COMO o produto abre (SSO unificado via Accounts vs.\n// login proprio). NAO e gate de visibilidade desde o deploy waffle-completo\n// (17/08). Um produto com ssoEnabled=false aparece normalmente; ao clicar, o\n// usuario cai no login proprio do produto — comportamento aceito pelo produto/PO.\n// Visibilidade = acesso (persona/claim) AND url truthy (pos-override de env).\n//\n// URLs aqui sao DEFAULTS de producao. O consumidor sobrescreve por ambiente via\n// o parametro `urls` (do seu proprio env) -- o SDK NAO le process.env.\n\nexport type ProductMeta = {\n code: string;\n name: string;\n description: string;\n url: string;\n accent: string;\n ssoEnabled: boolean;\n};\n\nexport const PRODUCT_CATALOG: Record<string, ProductMeta> = {\n accounts: {\n code: \"accounts\",\n name: \"Accounts\",\n description: \"Identidade unificada do ecossistema\",\n url: \"/\",\n accent: \"#1F2E52\",\n ssoEnabled: true, // e o proprio IdP; excluido da gaveta de qualquer forma\n },\n \"media-hub\": {\n code: \"media-hub\",\n name: \"Media Hub\",\n description: \"Sinalizacao digital nas unidades\",\n url: \"\", // sem frontend publico deployado; override local via NEXT_PUBLIC_MEDIA_HUB_URL\n accent: \"#EF802D\",\n ssoEnabled: true,\n },\n sig: {\n code: \"sig\",\n name: \"SIG\",\n description: \"Housekeeping e governanca das unidades\",\n url: \"https://sig.app.vegait.com/\",\n accent: \"#B0C925\",\n ssoEnabled: true,\n },\n gdi: {\n code: \"gdi\",\n name: \"GDI\",\n description: \"Gestao de dispositivos e internet\",\n url: \"\", // ainda nao esta em prod; override de HML via NEXT_PUBLIC_GDI_URL\n // quando ligar em prod, sera https://gdi.app.vegait.com/\n accent: \"#7B8B7E\",\n ssoEnabled: false,\n },\n sim: {\n code: \"sim\",\n name: \"SIM\",\n description: \"Service Issue Management\",\n url: \"https://sim.app.vegait.com/\",\n accent: \"#5B7DB1\",\n ssoEnabled: false,\n },\n hxp: {\n code: \"hxp\",\n name: \"HXP\",\n description: \"Hotel Experience Platform\",\n url: \"\", // nao ligado no Accounts\n accent: \"#9B6A4F\",\n ssoEnabled: false,\n },\n feedback: {\n code: \"feedback\",\n name: \"Feedback\",\n description: \"Pesquisas e NPS de hospedes\",\n url: \"\", // nao ligado no Accounts\n accent: \"#C26A8C\",\n ssoEnabled: false,\n },\n behavior: {\n code: \"behavior\",\n name: \"Behavior\",\n description: \"Inteligencia comportamental\",\n url: \"https://behavior.app.vegait.com/\",\n accent: \"#4A7C7E\",\n ssoEnabled: false,\n },\n};\n\n// Aliases de code legado -> canonico. Resolve dados gravados ANTES de uma\n// renomeacao (ex.: governanca -> sig) sem duplicar entrada no catalogo.\nconst LEGACY_CODE_ALIASES: Record<string, string> = {\n governanca: \"sig\",\n};\n\n// resolveProduct: retorna o meta cru do produto (com override aplicado se houver).\n// NAO filtra url falsy — o chamador decide o que fazer com produto sem URL.\n// getCatalogWithAccess e getAccessibleProducts filtram apos o override (Opcao A).\n// `urls` sobrescreve a URL default pelo code CANONICO. Code desconhecido => fallback ssoEnabled=false.\nexport function resolveProduct(\n code: string,\n urls?: Record<string, string>,\n): ProductMeta {\n const canonical = LEGACY_CODE_ALIASES[code] ?? code;\n const base =\n PRODUCT_CATALOG[canonical] ??\n ({\n code,\n name: code,\n description: \"\",\n url: \"#\",\n accent: \"#1F2E52\",\n ssoEnabled: false,\n } satisfies ProductMeta);\n const override = urls?.[canonical];\n return override ? { ...base, url: override } : base;\n}\n\nexport type CatalogProduct = ProductMeta & { accessible: boolean };\n\n// Catalogo COMPLETO pro app launcher (menos o Accounts, que e o proprio IdP),\n// com flag de acesso: Time VEGA acessa tudo; cliente acessa os codes da claim\n// `products` (aliases legados resolvidos). ssoEnabled NAO esconde produto aqui\n// — e metadado de COMO o produto abre, nao de quem ve. Sem claims: tudo\n// inacessivel (fail-closed). `urls` sobrescreve URLs por ambiente.\nexport function getCatalogWithAccess(\n claims: AccountsClaims | null,\n opts?: { urls?: Record<string, string> },\n): CatalogProduct[] {\n const urls = opts?.urls;\n const isVega = claims != null && derivePersona(claims) === \"vega\";\n const accessibleCodes = new Set(\n parseJsonArrayClaim(claims?.products).map(\n (code) => LEGACY_CODE_ALIASES[code] ?? code,\n ),\n );\n return Object.values(PRODUCT_CATALOG)\n .filter((p) => p.code !== \"accounts\")\n .map((p) => ({\n ...(urls?.[p.code] ? { ...p, url: urls[p.code] } : p),\n accessible: isVega || accessibleCodes.has(p.code),\n }))\n .filter((p) => p.url); // Opcao A: produto sem URL publica some do waffle/card/drawer\n}\n\n// Produtos do app launcher (gaveta/drawer). Visibilidade = acesso (persona/claim)\n// AND url truthy (pos-override de env). ssoEnabled NAO filtra — e apenas metadado\n// de como o produto abre. `urls` sobrescreve URLs por ambiente (override aplicado\n// ANTES do filtro de url, para que env possa ressuscitar produto sem URL default).\nexport function getAccessibleProducts(\n claims: AccountsClaims | null,\n opts?: { urls?: Record<string, string> },\n): ProductMeta[] {\n if (!claims) return [];\n const urls = opts?.urls;\n if (derivePersona(claims) === \"vega\") {\n return Object.values(PRODUCT_CATALOG)\n .filter((p) => p.code !== \"accounts\")\n .map((p) => (urls?.[p.code] ? { ...p, url: urls[p.code] } : p))\n .filter((p) => p.url); // Opcao A: sem URL publica = nao lancavel\n }\n return parseJsonArrayClaim(claims.products)\n .map((code) => resolveProduct(code, urls))\n .filter((p) => p.url); // Opcao A: idem; ssoEnabled nao filtra\n}\n","import type { AccountsClaims } from \"./claims\";\nimport { derivePersona, type Persona } from \"./persona\";\nimport {\n getAccessibleProducts,\n getCatalogWithAccess,\n type CatalogProduct,\n type ProductMeta,\n} from \"./productCatalog\";\n\n// Modelo de dados da casca de identidade (SEM React). O host mapeia isto pros\n// props do <IdentityBar> do @vegait/lyra-ds-v2, adicionando icones/labels/\n// callbacks a partir de `capabilities`.\nexport type IdentityModel = {\n persona: Persona;\n user: { name: string; email: string };\n products: ProductMeta[];\n /** Catalogo completo (menos Accounts) com flag de acesso — pro waffle\n * mostrar tudo e desabilitar o que a persona nao acessa. `products`\n * permanece so-acessiveis por retrocompat (SIG consome hoje). */\n catalog: CatalogProduct[];\n currentProductCode?: string;\n capabilities: {\n manageUsers: boolean; // gestor/vega\n adminConsole: boolean; // vega\n switchContext: boolean; // gestor/vega\n manageNetwork: boolean; // gestor (rodape do launcher)\n };\n};\n\nexport function buildIdentityModel(\n claims: AccountsClaims | null,\n cfg?: { currentProductCode?: string; urls?: Record<string, string> },\n): IdentityModel {\n const persona = derivePersona(claims);\n return {\n persona,\n user: { name: claims?.name ?? \"\", email: claims?.email ?? \"\" },\n products: getAccessibleProducts(claims, { urls: cfg?.urls }),\n catalog: getCatalogWithAccess(claims, { urls: cfg?.urls }),\n currentProductCode: cfg?.currentProductCode,\n capabilities: {\n manageUsers: persona === \"gestor\" || persona === \"vega\",\n adminConsole: persona === \"vega\",\n switchContext: persona === \"gestor\" || persona === \"vega\",\n manageNetwork: persona === \"gestor\",\n },\n };\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.js
CHANGED
|
@@ -58,7 +58,8 @@ var PRODUCT_CATALOG = {
|
|
|
58
58
|
code: "media-hub",
|
|
59
59
|
name: "Media Hub",
|
|
60
60
|
description: "Sinalizacao digital nas unidades",
|
|
61
|
-
url: "
|
|
61
|
+
url: "",
|
|
62
|
+
// sem frontend publico deployado; override local via NEXT_PUBLIC_MEDIA_HUB_URL
|
|
62
63
|
accent: "#EF802D",
|
|
63
64
|
ssoEnabled: true
|
|
64
65
|
},
|
|
@@ -74,7 +75,9 @@ var PRODUCT_CATALOG = {
|
|
|
74
75
|
code: "gdi",
|
|
75
76
|
name: "GDI",
|
|
76
77
|
description: "Gestao de dispositivos e internet",
|
|
77
|
-
url: "
|
|
78
|
+
url: "",
|
|
79
|
+
// ainda nao esta em prod; override de HML via NEXT_PUBLIC_GDI_URL
|
|
80
|
+
// quando ligar em prod, sera https://gdi.app.vegait.com/
|
|
78
81
|
accent: "#7B8B7E",
|
|
79
82
|
ssoEnabled: false
|
|
80
83
|
},
|
|
@@ -82,7 +85,7 @@ var PRODUCT_CATALOG = {
|
|
|
82
85
|
code: "sim",
|
|
83
86
|
name: "SIM",
|
|
84
87
|
description: "Service Issue Management",
|
|
85
|
-
url: "https://sim.
|
|
88
|
+
url: "https://sim.app.vegait.com/",
|
|
86
89
|
accent: "#5B7DB1",
|
|
87
90
|
ssoEnabled: false
|
|
88
91
|
},
|
|
@@ -90,7 +93,8 @@ var PRODUCT_CATALOG = {
|
|
|
90
93
|
code: "hxp",
|
|
91
94
|
name: "HXP",
|
|
92
95
|
description: "Hotel Experience Platform",
|
|
93
|
-
url: "
|
|
96
|
+
url: "",
|
|
97
|
+
// nao ligado no Accounts
|
|
94
98
|
accent: "#9B6A4F",
|
|
95
99
|
ssoEnabled: false
|
|
96
100
|
},
|
|
@@ -98,7 +102,8 @@ var PRODUCT_CATALOG = {
|
|
|
98
102
|
code: "feedback",
|
|
99
103
|
name: "Feedback",
|
|
100
104
|
description: "Pesquisas e NPS de hospedes",
|
|
101
|
-
url: "
|
|
105
|
+
url: "",
|
|
106
|
+
// nao ligado no Accounts
|
|
102
107
|
accent: "#C26A8C",
|
|
103
108
|
ssoEnabled: false
|
|
104
109
|
},
|
|
@@ -106,7 +111,7 @@ var PRODUCT_CATALOG = {
|
|
|
106
111
|
code: "behavior",
|
|
107
112
|
name: "Behavior",
|
|
108
113
|
description: "Inteligencia comportamental",
|
|
109
|
-
url: "https://behavior.
|
|
114
|
+
url: "https://behavior.app.vegait.com/",
|
|
110
115
|
accent: "#4A7C7E",
|
|
111
116
|
ssoEnabled: false
|
|
112
117
|
}
|
|
@@ -138,15 +143,15 @@ function getCatalogWithAccess(claims, opts) {
|
|
|
138
143
|
return Object.values(PRODUCT_CATALOG).filter((p) => p.code !== "accounts").map((p) => ({
|
|
139
144
|
...urls?.[p.code] ? { ...p, url: urls[p.code] } : p,
|
|
140
145
|
accessible: isVega || accessibleCodes.has(p.code)
|
|
141
|
-
}));
|
|
146
|
+
})).filter((p) => p.url);
|
|
142
147
|
}
|
|
143
148
|
function getAccessibleProducts(claims, opts) {
|
|
144
149
|
if (!claims) return [];
|
|
145
150
|
const urls = opts?.urls;
|
|
146
151
|
if (derivePersona(claims) === "vega") {
|
|
147
|
-
return Object.values(PRODUCT_CATALOG).filter((p) => p.code !== "accounts"
|
|
152
|
+
return Object.values(PRODUCT_CATALOG).filter((p) => p.code !== "accounts").map((p) => urls?.[p.code] ? { ...p, url: urls[p.code] } : p).filter((p) => p.url);
|
|
148
153
|
}
|
|
149
|
-
return parseJsonArrayClaim(claims.products).map((code) => resolveProduct(code, urls)).filter((p) => p.
|
|
154
|
+
return parseJsonArrayClaim(claims.products).map((code) => resolveProduct(code, urls)).filter((p) => p.url);
|
|
150
155
|
}
|
|
151
156
|
|
|
152
157
|
// src/identityModel.ts
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/claims.ts","../src/persona.ts","../src/productCatalog.ts","../src/identityModel.ts"],"sourcesContent":["// Decoder de payload JWT sem verificacao criptografica. Universal (browser +\n// Node). Usar APENAS para extrair claims de tokens ja confiaveis (recebidos do\n// Accounts via fluxo OIDC). NUNCA para validar tokens de origem externa.\n\nexport function decodeJwtPayload<T = Record<string, unknown>>(token: string): T {\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n throw new Error(\"Invalid JWT format\");\n }\n const payload = parts[1].replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = payload + \"=\".repeat((4 - (payload.length % 4)) % 4);\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n const json = new TextDecoder(\"utf-8\").decode(bytes);\n return JSON.parse(json) as T;\n}\n\n// Claims do AT emitido pelo Accounts via OpenIddict. Tudo opcional (nem todo\n// campo chega em todo contexto). unit_ids/products vem como string contendo\n// JSON array. platform_role aparece so para Time VEGA. Espelha o token; fonte\n// da verdade = VegaClaimsHandler no accounts.api.\nexport type AccountsClaims = {\n sub?: string;\n email?: string;\n name?: string;\n platform_role?: string;\n can_manage_scope_members?: string;\n org_id?: string | string[];\n unit_ids?: string;\n active_unit_id?: string;\n products?: string;\n exp?: number;\n iat?: number;\n iss?: string;\n aud?: string | string[];\n};\n\n// Normaliza claim que pode vir como string ou array (OpenIddict emite array\n// quando ha multiplos valores, scalar quando ha um so).\nexport function normalizeClaimArray(\n value: string | string[] | undefined,\n): string[] {\n if (!value) return [];\n return Array.isArray(value) ? value : [value];\n}\n\n// Parseia claim cujo value e string contendo JSON array (unit_ids, products).\n// Lista vazia em claim ausente ou JSON malformado.\nexport function parseJsonArrayClaim(value: string | undefined): string[] {\n if (!value) return [];\n try {\n const parsed = JSON.parse(value);\n return Array.isArray(parsed)\n ? parsed.filter((v): v is string => typeof v === \"string\")\n : [];\n } catch {\n return [];\n }\n}\n\n// Wrapper safe que devolve null em vez de jogar em token invalido.\nexport function decodeAccessToken(\n token: string | null | undefined,\n): AccountsClaims | null {\n if (!token) return null;\n try {\n return decodeJwtPayload<AccountsClaims>(token);\n } catch {\n return null;\n }\n}\n","import type { AccountsClaims } from \"./claims\";\n\nexport type Persona = \"usuario\" | \"gestor\" | \"vega\";\n\n// Deriva a persona a partir das claims do AT. Fail-closed: sem claims ou sem\n// sinal de gestao, cai em 'usuario' (menor privilegio). platform_role (Time\n// VEGA) tem precedencia sobre a flag de gestor de cliente.\n// ATENCAO: presentation gating, NAO autorizacao (ver README).\nexport function derivePersona(claims: AccountsClaims | null): Persona {\n if (claims?.platform_role) return \"vega\";\n if (claims?.can_manage_scope_members === \"true\") return \"gestor\";\n return \"usuario\";\n}\n","import type { AccountsClaims } from \"./claims\";\nimport { parseJsonArrayClaim } from \"./claims\";\nimport { derivePersona } from \"./persona\";\n\n// Catalogo cliente-side dos produtos VEGA. Mapeia o code (claim `products` do\n// AT) pra display name + URL default + accent. codes batem com o seed do\n// backend (SeedMediaHubInProductCatalog etc.). ssoEnabled = produto JA loga via\n// Accounts (OIDC unificado); a gaveta so mostra ssoEnabled=true.\n//\n// URLs aqui sao DEFAULTS de producao. O consumidor sobrescreve por ambiente via\n// o parametro `urls` (do seu proprio env) -- o SDK NAO le process.env.\n\nexport type ProductMeta = {\n code: string;\n name: string;\n description: string;\n url: string;\n accent: string;\n ssoEnabled: boolean;\n};\n\nexport const PRODUCT_CATALOG: Record<string, ProductMeta> = {\n accounts: {\n code: \"accounts\",\n name: \"Accounts\",\n description: \"Identidade unificada do ecossistema\",\n url: \"/\",\n accent: \"#1F2E52\",\n ssoEnabled: true, // e o proprio IdP; excluido da gaveta de qualquer forma\n },\n \"media-hub\": {\n code: \"media-hub\",\n name: \"Media Hub\",\n description: \"Sinalizacao digital nas unidades\",\n url: \"https://media-hub.vega.com.br/\",\n accent: \"#EF802D\",\n ssoEnabled: true,\n },\n sig: {\n code: \"sig\",\n name: \"SIG\",\n description: \"Housekeeping e governanca das unidades\",\n url: \"https://sig.app.vegait.com/\",\n accent: \"#B0C925\",\n ssoEnabled: true,\n },\n gdi: {\n code: \"gdi\",\n name: \"GDI\",\n description: \"Gestao de dispositivos e internet\",\n url: \"https://gdi.vega.com.br/\",\n accent: \"#7B8B7E\",\n ssoEnabled: false,\n },\n sim: {\n code: \"sim\",\n name: \"SIM\",\n description: \"Service Issue Management\",\n url: \"https://sim.vega.com.br/\",\n accent: \"#5B7DB1\",\n ssoEnabled: false,\n },\n hxp: {\n code: \"hxp\",\n name: \"HXP\",\n description: \"Hotel Experience Platform\",\n url: \"https://hxp.vega.com.br/\",\n accent: \"#9B6A4F\",\n ssoEnabled: false,\n },\n feedback: {\n code: \"feedback\",\n name: \"Feedback\",\n description: \"Pesquisas e NPS de hospedes\",\n url: \"https://feedback.vega.com.br/\",\n accent: \"#C26A8C\",\n ssoEnabled: false,\n },\n behavior: {\n code: \"behavior\",\n name: \"Behavior\",\n description: \"Inteligencia comportamental\",\n url: \"https://behavior.vega.com.br/\",\n accent: \"#4A7C7E\",\n ssoEnabled: false,\n },\n};\n\n// Aliases de code legado -> canonico. Resolve dados gravados ANTES de uma\n// renomeacao (ex.: governanca -> sig) sem duplicar entrada no catalogo.\nconst LEGACY_CODE_ALIASES: Record<string, string> = {\n governanca: \"sig\",\n};\n\n// Resolve um code (aplicando alias) pra ProductMeta. `urls` sobrescreve a URL\n// default pelo code CANONICO. Code desconhecido => fallback ssoEnabled=false.\nexport function resolveProduct(\n code: string,\n urls?: Record<string, string>,\n): ProductMeta {\n const canonical = LEGACY_CODE_ALIASES[code] ?? code;\n const base =\n PRODUCT_CATALOG[canonical] ??\n ({\n code,\n name: code,\n description: \"\",\n url: \"#\",\n accent: \"#1F2E52\",\n ssoEnabled: false,\n } satisfies ProductMeta);\n const override = urls?.[canonical];\n return override ? { ...base, url: override } : base;\n}\n\nexport type CatalogProduct = ProductMeta & { accessible: boolean };\n\n// Catalogo COMPLETO pro app launcher (menos o Accounts, que e o proprio IdP),\n// com flag de acesso: Time VEGA acessa tudo; cliente acessa os codes da claim\n// `products` (aliases legados resolvidos). ssoEnabled NAO esconde produto aqui\n// — e metadado de COMO o produto abre, nao de quem ve. Sem claims: tudo\n// inacessivel (fail-closed). `urls` sobrescreve URLs por ambiente.\nexport function getCatalogWithAccess(\n claims: AccountsClaims | null,\n opts?: { urls?: Record<string, string> },\n): CatalogProduct[] {\n const urls = opts?.urls;\n const isVega = claims != null && derivePersona(claims) === \"vega\";\n const accessibleCodes = new Set(\n parseJsonArrayClaim(claims?.products).map(\n (code) => LEGACY_CODE_ALIASES[code] ?? code,\n ),\n );\n return Object.values(PRODUCT_CATALOG)\n .filter((p) => p.code !== \"accounts\")\n .map((p) => ({\n ...(urls?.[p.code] ? { ...p, url: urls[p.code] } : p),\n accessible: isVega || accessibleCodes.has(p.code),\n }));\n}\n\n// Produtos do app launcher. Time VEGA ve o catalogo inteiro (menos o Accounts,\n// que e o IdP); cliente ve so os codes da claim `products`. Ambos filtram\n// ssoEnabled. `urls` sobrescreve URLs por ambiente.\nexport function getAccessibleProducts(\n claims: AccountsClaims | null,\n opts?: { urls?: Record<string, string> },\n): ProductMeta[] {\n if (!claims) return [];\n const urls = opts?.urls;\n if (derivePersona(claims) === \"vega\") {\n return Object.values(PRODUCT_CATALOG)\n .filter((p) => p.code !== \"accounts\" && p.ssoEnabled)\n .map((p) => (urls?.[p.code] ? { ...p, url: urls[p.code] } : p));\n }\n return parseJsonArrayClaim(claims.products)\n .map((code) => resolveProduct(code, urls))\n .filter((p) => p.ssoEnabled);\n}\n","import type { AccountsClaims } from \"./claims\";\nimport { derivePersona, type Persona } from \"./persona\";\nimport {\n getAccessibleProducts,\n getCatalogWithAccess,\n type CatalogProduct,\n type ProductMeta,\n} from \"./productCatalog\";\n\n// Modelo de dados da casca de identidade (SEM React). O host mapeia isto pros\n// props do <IdentityBar> do @vegait/lyra-ds-v2, adicionando icones/labels/\n// callbacks a partir de `capabilities`.\nexport type IdentityModel = {\n persona: Persona;\n user: { name: string; email: string };\n products: ProductMeta[];\n /** Catalogo completo (menos Accounts) com flag de acesso — pro waffle\n * mostrar tudo e desabilitar o que a persona nao acessa. `products`\n * permanece so-acessiveis por retrocompat (SIG consome hoje). */\n catalog: CatalogProduct[];\n currentProductCode?: string;\n capabilities: {\n manageUsers: boolean; // gestor/vega\n adminConsole: boolean; // vega\n switchContext: boolean; // gestor/vega\n manageNetwork: boolean; // gestor (rodape do launcher)\n };\n};\n\nexport function buildIdentityModel(\n claims: AccountsClaims | null,\n cfg?: { currentProductCode?: string; urls?: Record<string, string> },\n): IdentityModel {\n const persona = derivePersona(claims);\n return {\n persona,\n user: { name: claims?.name ?? \"\", email: claims?.email ?? \"\" },\n products: getAccessibleProducts(claims, { urls: cfg?.urls }),\n catalog: getCatalogWithAccess(claims, { urls: cfg?.urls }),\n currentProductCode: cfg?.currentProductCode,\n capabilities: {\n manageUsers: persona === \"gestor\" || persona === \"vega\",\n adminConsole: persona === \"vega\",\n switchContext: persona === \"gestor\" || persona === \"vega\",\n manageNetwork: persona === \"gestor\",\n },\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/claims.ts","../src/persona.ts","../src/productCatalog.ts","../src/identityModel.ts"],"sourcesContent":["// Decoder de payload JWT sem verificacao criptografica. Universal (browser +\n// Node). Usar APENAS para extrair claims de tokens ja confiaveis (recebidos do\n// Accounts via fluxo OIDC). NUNCA para validar tokens de origem externa.\n\nexport function decodeJwtPayload<T = Record<string, unknown>>(token: string): T {\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n throw new Error(\"Invalid JWT format\");\n }\n const payload = parts[1].replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = payload + \"=\".repeat((4 - (payload.length % 4)) % 4);\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n const json = new TextDecoder(\"utf-8\").decode(bytes);\n return JSON.parse(json) as T;\n}\n\n// Claims do AT emitido pelo Accounts via OpenIddict. Tudo opcional (nem todo\n// campo chega em todo contexto). unit_ids/products vem como string contendo\n// JSON array. platform_role aparece so para Time VEGA. Espelha o token; fonte\n// da verdade = VegaClaimsHandler no accounts.api.\nexport type AccountsClaims = {\n sub?: string;\n email?: string;\n name?: string;\n platform_role?: string;\n can_manage_scope_members?: string;\n org_id?: string | string[];\n unit_ids?: string;\n active_unit_id?: string;\n products?: string;\n exp?: number;\n iat?: number;\n iss?: string;\n aud?: string | string[];\n};\n\n// Normaliza claim que pode vir como string ou array (OpenIddict emite array\n// quando ha multiplos valores, scalar quando ha um so).\nexport function normalizeClaimArray(\n value: string | string[] | undefined,\n): string[] {\n if (!value) return [];\n return Array.isArray(value) ? value : [value];\n}\n\n// Parseia claim cujo value e string contendo JSON array (unit_ids, products).\n// Lista vazia em claim ausente ou JSON malformado.\nexport function parseJsonArrayClaim(value: string | undefined): string[] {\n if (!value) return [];\n try {\n const parsed = JSON.parse(value);\n return Array.isArray(parsed)\n ? parsed.filter((v): v is string => typeof v === \"string\")\n : [];\n } catch {\n return [];\n }\n}\n\n// Wrapper safe que devolve null em vez de jogar em token invalido.\nexport function decodeAccessToken(\n token: string | null | undefined,\n): AccountsClaims | null {\n if (!token) return null;\n try {\n return decodeJwtPayload<AccountsClaims>(token);\n } catch {\n return null;\n }\n}\n","import type { AccountsClaims } from \"./claims\";\n\nexport type Persona = \"usuario\" | \"gestor\" | \"vega\";\n\n// Deriva a persona a partir das claims do AT. Fail-closed: sem claims ou sem\n// sinal de gestao, cai em 'usuario' (menor privilegio). platform_role (Time\n// VEGA) tem precedencia sobre a flag de gestor de cliente.\n// ATENCAO: presentation gating, NAO autorizacao (ver README).\nexport function derivePersona(claims: AccountsClaims | null): Persona {\n if (claims?.platform_role) return \"vega\";\n if (claims?.can_manage_scope_members === \"true\") return \"gestor\";\n return \"usuario\";\n}\n","import type { AccountsClaims } from \"./claims\";\nimport { parseJsonArrayClaim } from \"./claims\";\nimport { derivePersona } from \"./persona\";\n\n// Catalogo cliente-side dos produtos VEGA. Mapeia o code (claim `products` do\n// AT) pra display name + URL default + accent. codes batem com o seed do\n// backend (SeedMediaHubInProductCatalog etc.).\n//\n// ssoEnabled = METADADO de COMO o produto abre (SSO unificado via Accounts vs.\n// login proprio). NAO e gate de visibilidade desde o deploy waffle-completo\n// (17/08). Um produto com ssoEnabled=false aparece normalmente; ao clicar, o\n// usuario cai no login proprio do produto — comportamento aceito pelo produto/PO.\n// Visibilidade = acesso (persona/claim) AND url truthy (pos-override de env).\n//\n// URLs aqui sao DEFAULTS de producao. O consumidor sobrescreve por ambiente via\n// o parametro `urls` (do seu proprio env) -- o SDK NAO le process.env.\n\nexport type ProductMeta = {\n code: string;\n name: string;\n description: string;\n url: string;\n accent: string;\n ssoEnabled: boolean;\n};\n\nexport const PRODUCT_CATALOG: Record<string, ProductMeta> = {\n accounts: {\n code: \"accounts\",\n name: \"Accounts\",\n description: \"Identidade unificada do ecossistema\",\n url: \"/\",\n accent: \"#1F2E52\",\n ssoEnabled: true, // e o proprio IdP; excluido da gaveta de qualquer forma\n },\n \"media-hub\": {\n code: \"media-hub\",\n name: \"Media Hub\",\n description: \"Sinalizacao digital nas unidades\",\n url: \"\", // sem frontend publico deployado; override local via NEXT_PUBLIC_MEDIA_HUB_URL\n accent: \"#EF802D\",\n ssoEnabled: true,\n },\n sig: {\n code: \"sig\",\n name: \"SIG\",\n description: \"Housekeeping e governanca das unidades\",\n url: \"https://sig.app.vegait.com/\",\n accent: \"#B0C925\",\n ssoEnabled: true,\n },\n gdi: {\n code: \"gdi\",\n name: \"GDI\",\n description: \"Gestao de dispositivos e internet\",\n url: \"\", // ainda nao esta em prod; override de HML via NEXT_PUBLIC_GDI_URL\n // quando ligar em prod, sera https://gdi.app.vegait.com/\n accent: \"#7B8B7E\",\n ssoEnabled: false,\n },\n sim: {\n code: \"sim\",\n name: \"SIM\",\n description: \"Service Issue Management\",\n url: \"https://sim.app.vegait.com/\",\n accent: \"#5B7DB1\",\n ssoEnabled: false,\n },\n hxp: {\n code: \"hxp\",\n name: \"HXP\",\n description: \"Hotel Experience Platform\",\n url: \"\", // nao ligado no Accounts\n accent: \"#9B6A4F\",\n ssoEnabled: false,\n },\n feedback: {\n code: \"feedback\",\n name: \"Feedback\",\n description: \"Pesquisas e NPS de hospedes\",\n url: \"\", // nao ligado no Accounts\n accent: \"#C26A8C\",\n ssoEnabled: false,\n },\n behavior: {\n code: \"behavior\",\n name: \"Behavior\",\n description: \"Inteligencia comportamental\",\n url: \"https://behavior.app.vegait.com/\",\n accent: \"#4A7C7E\",\n ssoEnabled: false,\n },\n};\n\n// Aliases de code legado -> canonico. Resolve dados gravados ANTES de uma\n// renomeacao (ex.: governanca -> sig) sem duplicar entrada no catalogo.\nconst LEGACY_CODE_ALIASES: Record<string, string> = {\n governanca: \"sig\",\n};\n\n// resolveProduct: retorna o meta cru do produto (com override aplicado se houver).\n// NAO filtra url falsy — o chamador decide o que fazer com produto sem URL.\n// getCatalogWithAccess e getAccessibleProducts filtram apos o override (Opcao A).\n// `urls` sobrescreve a URL default pelo code CANONICO. Code desconhecido => fallback ssoEnabled=false.\nexport function resolveProduct(\n code: string,\n urls?: Record<string, string>,\n): ProductMeta {\n const canonical = LEGACY_CODE_ALIASES[code] ?? code;\n const base =\n PRODUCT_CATALOG[canonical] ??\n ({\n code,\n name: code,\n description: \"\",\n url: \"#\",\n accent: \"#1F2E52\",\n ssoEnabled: false,\n } satisfies ProductMeta);\n const override = urls?.[canonical];\n return override ? { ...base, url: override } : base;\n}\n\nexport type CatalogProduct = ProductMeta & { accessible: boolean };\n\n// Catalogo COMPLETO pro app launcher (menos o Accounts, que e o proprio IdP),\n// com flag de acesso: Time VEGA acessa tudo; cliente acessa os codes da claim\n// `products` (aliases legados resolvidos). ssoEnabled NAO esconde produto aqui\n// — e metadado de COMO o produto abre, nao de quem ve. Sem claims: tudo\n// inacessivel (fail-closed). `urls` sobrescreve URLs por ambiente.\nexport function getCatalogWithAccess(\n claims: AccountsClaims | null,\n opts?: { urls?: Record<string, string> },\n): CatalogProduct[] {\n const urls = opts?.urls;\n const isVega = claims != null && derivePersona(claims) === \"vega\";\n const accessibleCodes = new Set(\n parseJsonArrayClaim(claims?.products).map(\n (code) => LEGACY_CODE_ALIASES[code] ?? code,\n ),\n );\n return Object.values(PRODUCT_CATALOG)\n .filter((p) => p.code !== \"accounts\")\n .map((p) => ({\n ...(urls?.[p.code] ? { ...p, url: urls[p.code] } : p),\n accessible: isVega || accessibleCodes.has(p.code),\n }))\n .filter((p) => p.url); // Opcao A: produto sem URL publica some do waffle/card/drawer\n}\n\n// Produtos do app launcher (gaveta/drawer). Visibilidade = acesso (persona/claim)\n// AND url truthy (pos-override de env). ssoEnabled NAO filtra — e apenas metadado\n// de como o produto abre. `urls` sobrescreve URLs por ambiente (override aplicado\n// ANTES do filtro de url, para que env possa ressuscitar produto sem URL default).\nexport function getAccessibleProducts(\n claims: AccountsClaims | null,\n opts?: { urls?: Record<string, string> },\n): ProductMeta[] {\n if (!claims) return [];\n const urls = opts?.urls;\n if (derivePersona(claims) === \"vega\") {\n return Object.values(PRODUCT_CATALOG)\n .filter((p) => p.code !== \"accounts\")\n .map((p) => (urls?.[p.code] ? { ...p, url: urls[p.code] } : p))\n .filter((p) => p.url); // Opcao A: sem URL publica = nao lancavel\n }\n return parseJsonArrayClaim(claims.products)\n .map((code) => resolveProduct(code, urls))\n .filter((p) => p.url); // Opcao A: idem; ssoEnabled nao filtra\n}\n","import type { AccountsClaims } from \"./claims\";\nimport { derivePersona, type Persona } from \"./persona\";\nimport {\n getAccessibleProducts,\n getCatalogWithAccess,\n type CatalogProduct,\n type ProductMeta,\n} from \"./productCatalog\";\n\n// Modelo de dados da casca de identidade (SEM React). O host mapeia isto pros\n// props do <IdentityBar> do @vegait/lyra-ds-v2, adicionando icones/labels/\n// callbacks a partir de `capabilities`.\nexport type IdentityModel = {\n persona: Persona;\n user: { name: string; email: string };\n products: ProductMeta[];\n /** Catalogo completo (menos Accounts) com flag de acesso — pro waffle\n * mostrar tudo e desabilitar o que a persona nao acessa. `products`\n * permanece so-acessiveis por retrocompat (SIG consome hoje). */\n catalog: CatalogProduct[];\n currentProductCode?: string;\n capabilities: {\n manageUsers: boolean; // gestor/vega\n adminConsole: boolean; // vega\n switchContext: boolean; // gestor/vega\n manageNetwork: boolean; // gestor (rodape do launcher)\n };\n};\n\nexport function buildIdentityModel(\n claims: AccountsClaims | null,\n cfg?: { currentProductCode?: string; urls?: Record<string, string> },\n): IdentityModel {\n const persona = derivePersona(claims);\n return {\n persona,\n user: { name: claims?.name ?? \"\", email: claims?.email ?? \"\" },\n products: getAccessibleProducts(claims, { urls: cfg?.urls }),\n catalog: getCatalogWithAccess(claims, { urls: cfg?.urls }),\n currentProductCode: cfg?.currentProductCode,\n capabilities: {\n manageUsers: persona === \"gestor\" || persona === \"vega\",\n adminConsole: persona === \"vega\",\n switchContext: persona === \"gestor\" || persona === \"vega\",\n manageNetwork: persona === \"gestor\",\n },\n };\n}\n"],"mappings":"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","names":[]}
|