@acarmisc/backstage-plugin-litellm-backend 0.16.0 → 0.17.0

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.
@@ -17,6 +17,11 @@ function registerBridgeRoutes(router, ctx, bridgeOpts) {
17
17
  // own auth. Lets CLI clients list/mint virtual keys without holding the master
18
18
  // key. Gated by litellm.bridge.enabled; the verifier needs litellm.bridge.issuer.
19
19
  const bridgeCfg = (0, bridge_1.readBridgeConfig)(config);
20
+ // Same userIdDomain rule as the UI; the trusted email domains gate who may use the bridge.
21
+ const bridgeIdentity = {
22
+ userIdDomain,
23
+ trustedEmailDomains: bridgeCfg.allowedEmailDomains,
24
+ };
20
25
  let tokenVerifier = bridgeOpts.tokenVerifier;
21
26
  if (bridgeCfg.enabled && !tokenVerifier) {
22
27
  try {
@@ -63,7 +68,7 @@ function registerBridgeRoutes(router, ctx, bridgeOpts) {
63
68
  router.get('/bridge/keys', async (req, res) => {
64
69
  try {
65
70
  const claims = await requireClaims(req);
66
- const keys = await (0, bridge_1.bridgeListKeys)(client, claims, provisioningEnabled, provisioningDefaults, logger, userIdDomain);
71
+ const keys = await (0, bridge_1.bridgeListKeys)(client, claims, provisioningEnabled, provisioningDefaults, logger, bridgeIdentity);
67
72
  res.json(keys);
68
73
  }
69
74
  catch (error) {
@@ -87,7 +92,7 @@ function registerBridgeRoutes(router, ctx, bridgeOpts) {
87
92
  }
88
93
  const input = parseResult.data;
89
94
  // ── Resolve user identity from verified claims ───────────────────────
90
- const userId = (0, bridge_1.resolveBridgeUserId)(claims, userIdDomain);
95
+ const userId = (0, bridge_1.resolveBridgeUserId)(claims, bridgeIdentity);
91
96
  // ── Create key via unified service ──────────────────────────────────
92
97
  const keyCreateCtx = {
93
98
  client,
@@ -343,15 +343,15 @@ function registerKeysRoutes(router, ctx) {
343
343
  try {
344
344
  const { keyId } = req.params;
345
345
  const { tokenEntityRef, key } = await authorizeKeyAction(req, keyId);
346
- // Check if the key can be unblocked:
347
- // - Owner can unblock a self-blocked key (metadata.blocked_by === tokenEntityRef)
348
- // - Otherwise require the unblock permission
349
- const isOwnerUnblockingOwnBlock = key.metadata?.blocked_by === tokenEntityRef;
350
- if (!isOwnerUnblockingOwnBlock) {
351
- if (!(await assertPermission(req, backstage_plugin_litellm_common_1.litellmKeyUnblockPermission))) {
352
- sendPermissionDenied(res, backstage_plugin_litellm_common_1.litellmKeyUnblockPermission);
353
- return;
354
- }
346
+ // Unblocking always needs the dedicated permission, on top of ownership.
347
+ // `metadata.blocked_by` is deliberately NOT trusted here: it lives in a
348
+ // record the owner can influence (older keys, or a stale value left after
349
+ // an admin unblocked in the LiteLLM UI), so "I blocked it myself" can't be
350
+ // told apart from "an admin blocked it". Grant `litellm.key.unblock` only
351
+ // to roles that may lift blocks; everyone else asks an administrator.
352
+ if (!(await assertPermission(req, backstage_plugin_litellm_common_1.litellmKeyUnblockPermission))) {
353
+ sendPermissionDenied(res, backstage_plugin_litellm_common_1.litellmKeyUnblockPermission);
354
+ return;
355
355
  }
356
356
  await client.unblockKey(keyId);
357
357
  // Clear the block record on unblock. Set the fields to null rather than
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acarmisc/backstage-plugin-litellm-backend",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "The Backstage backend plugin for LiteLLM governance",
5
5
  "backstage": {
6
6
  "role": "backend-plugin",