c8y-nitro 0.4.1 → 0.4.2

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.
@@ -1,4 +1,4 @@
1
- import { n as name, r as version, t as description } from "../package-CobwNpP9.mjs";
1
+ import { n as name, r as version, t as description } from "../package-C4HtuVu_.mjs";
2
2
  import { defineCommand, runMain } from "citty";
3
3
  //#region src/cli/index.ts
4
4
  runMain(defineCommand({
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { a as findMicroserviceByName, g as GENERATED_READINESS_ROUTE, h as GENERATED_LIVENESS_ROUTE, l as subscribeToApplication, m as createC8yManifestFromNitro, n as createBasicAuthHeader, o as getBootstrapCredentials, p as createC8yManifest, r as createMicroservice } from "./c8y-api-BBSKRwKs.mjs";
2
2
  import { t as writeBootstrapCredentials } from "./env-file-B0BK-uZW.mjs";
3
- import { n as name } from "./package-CobwNpP9.mjs";
3
+ import { n as name } from "./package-C4HtuVu_.mjs";
4
4
  import { basename, dirname, join, relative } from "node:path";
5
5
  import { mkdir, writeFile } from "node:fs/promises";
6
6
  import { x } from "tinyexec";
@@ -1,6 +1,6 @@
1
1
  //#region package.json
2
2
  var name = "c8y-nitro";
3
- var version = "0.4.1";
3
+ var version = "0.4.2";
4
4
  var description = "Lightning fast Cumulocity IoT microservice development powered by Nitro";
5
5
  //#endregion
6
6
  export { name as n, version as r, description as t };
package/dist/utils.mjs CHANGED
@@ -1,7 +1,9 @@
1
+ import { Buffer } from "node:buffer";
1
2
  import process from "node:process";
2
3
  import { useLogger } from "evlog/nitro/v3";
3
4
  import { BasicAuth, Client, MicroserviceClientRequestAuth } from "@c8y/client";
4
5
  import { defineCachedFunction } from "nitro/cache";
6
+ import { createHash, randomBytes } from "node:crypto";
5
7
  import { HTTPError, defineHandler } from "nitro/h3";
6
8
  import { useStorage } from "nitro/storage";
7
9
  import { useRuntimeConfig } from "nitro/runtime-config";
@@ -30,6 +32,55 @@ function convertRequestHeadersToC8yFormat(request) {
30
32
  return headers;
31
33
  }
32
34
  //#endregion
35
+ //#region src/utils/internal/tenant.ts
36
+ const USER_TENANT_CACHE_SALT = randomBytes(32).toString("hex");
37
+ function getCookieValue(cookieHeader, name) {
38
+ try {
39
+ const value = cookieHeader?.match(`(^|;)\\s*${name}\\s*=\\s*([^;]+)`);
40
+ return value ? value.pop() : void 0;
41
+ } catch {
42
+ return;
43
+ }
44
+ }
45
+ function getCurrentUserTenantCacheKeyMaterial(requestOrEvent) {
46
+ const request = "req" in requestOrEvent ? requestOrEvent.req : requestOrEvent;
47
+ const cookieAuth = getCookieValue(request.headers.get("cookie"), "authorization");
48
+ if (cookieAuth) return `cookie:${cookieAuth}`;
49
+ const authorization = request.headers.get("authorization");
50
+ if (authorization) return `header:${authorization}`;
51
+ }
52
+ function createCurrentUserTenantCacheKey(requestOrEvent) {
53
+ const material = getCurrentUserTenantCacheKeyMaterial(requestOrEvent);
54
+ if (!material) throw new Error("Cannot create current user tenant cache key without auth material");
55
+ return createHash("sha256").update(USER_TENANT_CACHE_SALT).update(":").update(material).digest("hex");
56
+ }
57
+ function tryGetTenantFromBasicAuth(requestOrEvent) {
58
+ const authorization = ("req" in requestOrEvent ? requestOrEvent.req : requestOrEvent).headers.get("authorization");
59
+ if (!authorization?.startsWith("Basic ")) return;
60
+ try {
61
+ const decoded = Buffer.from(authorization.slice(6), "base64").toString("utf8");
62
+ const separatorIndex = decoded.indexOf(":");
63
+ const userPart = separatorIndex === -1 ? decoded : decoded.slice(0, separatorIndex);
64
+ const slashIndex = userPart.indexOf("/");
65
+ if (slashIndex <= 0) return;
66
+ return userPart.slice(0, slashIndex);
67
+ } catch {
68
+ return;
69
+ }
70
+ }
71
+ const getCurrentUserTenantId = defineCachedFunction(async (requestOrEvent) => {
72
+ const basicTenant = tryGetTenantFromBasicAuth(requestOrEvent);
73
+ if (basicTenant) return basicTenant;
74
+ return (await useUserClient(requestOrEvent).tenant.current()).data.name;
75
+ }, {
76
+ maxAge: 60,
77
+ name: "_c8y_nitro_get_current_user_tenant_id",
78
+ group: "c8y_nitro",
79
+ swr: false,
80
+ getKey: (requestOrEvent) => createCurrentUserTenantCacheKey(requestOrEvent),
81
+ shouldBypassCache: (requestOrEvent) => !getCurrentUserTenantCacheKeyMaterial(requestOrEvent)
82
+ });
83
+ //#endregion
33
84
  //#region src/utils/credentials.ts
34
85
  /**
35
86
  * Fetches credentials for all tenants subscribed to this microservice.\
@@ -128,7 +179,7 @@ const useDeployedTenantCredentials = Object.assign(async () => {
128
179
  async function useUserTenantCredentials(requestOrEvent) {
129
180
  const request = "req" in requestOrEvent ? requestOrEvent.req : requestOrEvent;
130
181
  if (request.context?.["c8y_user_tenant_credentials"]) return request.context["c8y_user_tenant_credentials"];
131
- const tenantId = useUserClient(requestOrEvent).core.tenant;
182
+ const tenantId = await getCurrentUserTenantId(requestOrEvent);
132
183
  const userTenantCreds = (await useSubscribedTenantCredentials())[tenantId];
133
184
  if (!userTenantCreds) throw new HTTPError({
134
185
  message: `No subscribed tenant credentials found for user tenant '${tenantId}'`,
@@ -172,7 +223,7 @@ function useUserClient(requestOrEvent) {
172
223
  async function useUserTenantClient(requestOrEvent) {
173
224
  const request = "req" in requestOrEvent ? requestOrEvent.req : requestOrEvent;
174
225
  if (request.context?.["c8y_user_tenant_client"]) return request.context["c8y_user_tenant_client"];
175
- const tenantId = useUserClient(requestOrEvent).core.tenant;
226
+ const tenantId = await getCurrentUserTenantId(requestOrEvent);
176
227
  const creds = await useSubscribedTenantCredentials();
177
228
  if (!creds[tenantId]) throw new HTTPError({
178
229
  message: `No subscribed tenant credentials found for user tenant '${tenantId}'`,
@@ -281,7 +332,7 @@ function hasUserRequiredRole(roleOrRoles) {
281
332
  function isUserFromAllowedTenant(tenantIdOrIds) {
282
333
  return defineHandler(async (event) => {
283
334
  const allowedTenants = Array.isArray(tenantIdOrIds) ? tenantIdOrIds : [tenantIdOrIds];
284
- const userTenantId = useUserClient(event).core.tenant;
335
+ const userTenantId = await getCurrentUserTenantId(event);
285
336
  if (!allowedTenants.includes(userTenantId)) throw new HTTPError({
286
337
  status: 403,
287
338
  statusText: "Forbidden",
@@ -306,7 +357,7 @@ function isUserFromAllowedTenant(tenantIdOrIds) {
306
357
  */
307
358
  function isUserFromDeployedTenant() {
308
359
  return defineHandler(async (event) => {
309
- const userTenantId = useUserClient(event).core.tenant;
360
+ const userTenantId = await getCurrentUserTenantId(event);
310
361
  const deployedTenantId = process.env.C8Y_BOOTSTRAP_TENANT;
311
362
  if (!deployedTenantId) throw new HTTPError({
312
363
  status: 500,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "c8y-nitro",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "type": "module",
5
5
  "description": "Lightning fast Cumulocity IoT microservice development powered by Nitro",
6
6
  "keywords": [