@continuous-excellence/ze-great-dashboard-aws 0.30.6 → 0.31.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.
package/dist/cli.js CHANGED
@@ -320,14 +320,41 @@ var boardSchema = z2.object({
320
320
  params: { constraint: "Important panels require one board attention treatment." }
321
321
  });
322
322
  });
323
+ var authorizationSchema = z2.discriminatedUnion("mode", [
324
+ z2.strictObject({ mode: z2.literal("authenticated") }),
325
+ z2.strictObject({ mode: z2.literal("subjects"), subjects: z2.array(z2.string().min(1)).min(1) }),
326
+ z2.strictObject({
327
+ mode: z2.literal("claim"),
328
+ /** Exact JWT payload key; namespaced provider claims are keys, never path expressions. */
329
+ claim: z2.string().min(1),
330
+ values: z2.array(z2.string().min(1)).min(1),
331
+ match: z2.enum(["any", "all"])
332
+ })
333
+ ]);
334
+ var legacySubjectAllowSchema = z2.strictObject({ subjects: z2.array(z2.string().min(1)).min(1) });
323
335
  var authSchema = z2.strictObject({
324
336
  issuer: z2.url(),
325
337
  /** Browser SPA client id. It is public, unlike every source credential. */
326
338
  client_id: z2.string().min(1),
327
339
  /** The separately registered API resource accepted by the dashboard proxy. */
328
340
  audience: z2.string().min(1),
329
- /** A deliberately portable, explicit identity boundary for the first direct-OIDC release. */
330
- allow: z2.strictObject({ subjects: z2.array(z2.string().min(1)).min(1) })
341
+ /** Canonical authorization policy. */
342
+ authorization: authorizationSchema.optional(),
343
+ /** Legacy subject policy retained only for a configuration migration. */
344
+ allow: legacySubjectAllowSchema.optional()
345
+ }).superRefine((auth, ctx) => {
346
+ if (!auth.authorization && !auth.allow)
347
+ ctx.addIssue({
348
+ code: "custom",
349
+ path: ["authorization"],
350
+ message: "configure authorization, or the legacy allow.subjects migration policy"
351
+ });
352
+ if (auth.authorization && auth.allow)
353
+ ctx.addIssue({
354
+ code: "custom",
355
+ path: ["authorization"],
356
+ message: "configure authorization or legacy allow.subjects, not both"
357
+ });
331
358
  });
332
359
  var securityPolicies = ["warn", "required", "unsecured"];
333
360
  var securityPolicySchema = z2.enum(securityPolicies);
@@ -537,43 +564,58 @@ var httpValueSchema = z4.object({
537
564
  value: z4.union([z4.string(), z4.number(), z4.boolean()])
538
565
  });
539
566
 
540
- // packages/server/src/adapters/azure-devops.ts
567
+ // packages/shared/src/security-details.ts
541
568
  import { z as z5 } from "zod";
542
- var pipelinePanelSchema = z5.object({
543
- id: z5.string().min(1),
544
- type: z5.literal("pipeline-status"),
545
- source: z5.string().min(1),
546
- pipeline: z5.number().int().positive()
569
+ var securityDetailsResponseSchema = z5.strictObject({
570
+ oidc: z5.literal(true),
571
+ policy: z5.discriminatedUnion("mode", [
572
+ z5.strictObject({ mode: z5.literal("authenticated") }),
573
+ z5.strictObject({ mode: z5.literal("subjects") }),
574
+ z5.strictObject({
575
+ mode: z5.literal("claim"),
576
+ claim: z5.string().min(1),
577
+ match: z5.enum(["any", "all"])
578
+ })
579
+ ])
580
+ });
581
+
582
+ // packages/server/src/adapters/azure-devops.ts
583
+ import { z as z6 } from "zod";
584
+ var pipelinePanelSchema = z6.object({
585
+ id: z6.string().min(1),
586
+ type: z6.literal("pipeline-status"),
587
+ source: z6.string().min(1),
588
+ pipeline: z6.number().int().positive()
547
589
  });
548
- var buildSchema = z5.object({
549
- id: z5.number().int().positive(),
550
- buildNumber: z5.string().min(1),
551
- status: z5.string().nullable().optional(),
552
- result: z5.string().nullable().optional(),
553
- sourceBranch: z5.string().min(1).nullable().optional(),
554
- startTime: z5.string().nullable().optional(),
555
- finishTime: z5.string().nullable().optional(),
556
- lastChangedDate: z5.string().nullable().optional()
590
+ var buildSchema = z6.object({
591
+ id: z6.number().int().positive(),
592
+ buildNumber: z6.string().min(1),
593
+ status: z6.string().nullable().optional(),
594
+ result: z6.string().nullable().optional(),
595
+ sourceBranch: z6.string().min(1).nullable().optional(),
596
+ startTime: z6.string().nullable().optional(),
597
+ finishTime: z6.string().nullable().optional(),
598
+ lastChangedDate: z6.string().nullable().optional()
557
599
  });
558
- var buildsSchema = z5.object({ value: z5.array(buildSchema) });
559
- var timelineSchema = z5.object({
560
- records: z5.array(
561
- z5.object({
562
- id: z5.string().min(1),
563
- parentId: z5.string().min(1).nullable().optional(),
564
- type: z5.string().min(1),
565
- name: z5.string().min(1),
566
- state: z5.string().nullable().optional()
600
+ var buildsSchema = z6.object({ value: z6.array(buildSchema) });
601
+ var timelineSchema = z6.object({
602
+ records: z6.array(
603
+ z6.object({
604
+ id: z6.string().min(1),
605
+ parentId: z6.string().min(1).nullable().optional(),
606
+ type: z6.string().min(1),
607
+ name: z6.string().min(1),
608
+ state: z6.string().nullable().optional()
567
609
  })
568
610
  )
569
611
  });
570
612
  function permittedAzureDevOpsCalls(panel, source) {
571
- const { panel: parsedPanel, source: parsedSource } = z5.object({ panel: pipelinePanelSchema, source: azureDevOpsSourceSchema }).parse({ panel, source });
613
+ const { panel: parsedPanel, source: parsedSource } = z6.object({ panel: pipelinePanelSchema, source: azureDevOpsSourceSchema }).parse({ panel, source });
572
614
  return [buildsCall(parsedPanel.pipeline, parsedSource)];
573
615
  }
574
- var delegatedTokenFileSchema = z5.object({
575
- accessToken: z5.string().min(1),
576
- expiresAt: z5.string().min(1)
616
+ var delegatedTokenFileSchema = z6.object({
617
+ accessToken: z6.string().min(1),
618
+ expiresAt: z6.string().min(1)
577
619
  });
578
620
  function buildsCall(definitionId, source) {
579
621
  const url = apiUrl(source, "build/builds");
@@ -595,59 +637,59 @@ function adoBranch(branch) {
595
637
  }
596
638
 
597
639
  // packages/server/src/adapters/github-actions.ts
598
- import { z as z6 } from "zod";
599
- var pipelinePanelSchema2 = z6.object({
600
- id: z6.string().min(1),
601
- type: z6.literal("pipeline-status"),
602
- source: z6.string().min(1),
640
+ import { z as z7 } from "zod";
641
+ var pipelinePanelSchema2 = z7.object({
642
+ id: z7.string().min(1),
643
+ type: z7.literal("pipeline-status"),
644
+ source: z7.string().min(1),
603
645
  /** GitHub accepts a workflow filename or numeric workflow id in this URL segment. */
604
- pipeline: z6.string().min(1)
646
+ pipeline: z7.string().min(1)
605
647
  });
606
- var pullRequestHealthPanelSchema = z6.object({
607
- id: z6.string().min(1),
608
- type: z6.literal("pull-request-health"),
609
- source: z6.string().min(1),
610
- base_branch: z6.string().min(1),
611
- update_workflows: z6.array(
612
- z6.object({
613
- workflow: z6.string().min(1),
614
- branch_prefixes: z6.array(z6.string().min(1)).min(1)
648
+ var pullRequestHealthPanelSchema = z7.object({
649
+ id: z7.string().min(1),
650
+ type: z7.literal("pull-request-health"),
651
+ source: z7.string().min(1),
652
+ base_branch: z7.string().min(1),
653
+ update_workflows: z7.array(
654
+ z7.object({
655
+ workflow: z7.string().min(1),
656
+ branch_prefixes: z7.array(z7.string().min(1)).min(1)
615
657
  })
616
658
  ).min(1),
617
- build_workflow: z6.string().min(1)
659
+ build_workflow: z7.string().min(1)
618
660
  });
619
- var runSchema = z6.object({
620
- id: z6.number().int().positive().optional(),
621
- status: z6.string(),
622
- conclusion: z6.string().nullable(),
623
- name: z6.string().min(1),
624
- html_url: z6.url(),
661
+ var runSchema = z7.object({
662
+ id: z7.number().int().positive().optional(),
663
+ status: z7.string(),
664
+ conclusion: z7.string().nullable(),
665
+ name: z7.string().min(1),
666
+ html_url: z7.url(),
625
667
  /** The branch that produced this run; absent for some event types. */
626
- head_branch: z6.string().min(1).nullable().optional(),
668
+ head_branch: z7.string().min(1).nullable().optional(),
627
669
  // Timestamp quality should only remove timing advice, never make an otherwise useful run unreadable.
628
- run_started_at: z6.string().nullable().optional(),
629
- updated_at: z6.string().optional()
670
+ run_started_at: z7.string().nullable().optional(),
671
+ updated_at: z7.string().optional()
630
672
  });
631
- var runsSchema = z6.object({ workflow_runs: z6.array(runSchema) });
632
- var jobsSchema = z6.object({
633
- jobs: z6.array(
634
- z6.object({
635
- name: z6.string().min(1),
636
- status: z6.string(),
637
- steps: z6.array(z6.object({ name: z6.string().min(1), status: z6.string() })).optional()
673
+ var runsSchema = z7.object({ workflow_runs: z7.array(runSchema) });
674
+ var jobsSchema = z7.object({
675
+ jobs: z7.array(
676
+ z7.object({
677
+ name: z7.string().min(1),
678
+ status: z7.string(),
679
+ steps: z7.array(z7.object({ name: z7.string().min(1), status: z7.string() })).optional()
638
680
  })
639
681
  )
640
682
  });
641
- var pullRequestsSchema = z6.array(
642
- z6.object({
643
- number: z6.number().int().positive(),
644
- html_url: z6.url(),
645
- head: z6.object({ ref: z6.string().min(1) }),
646
- base: z6.object({ ref: z6.string().min(1) })
683
+ var pullRequestsSchema = z7.array(
684
+ z7.object({
685
+ number: z7.number().int().positive(),
686
+ html_url: z7.url(),
687
+ head: z7.object({ ref: z7.string().min(1) }),
688
+ base: z7.object({ ref: z7.string().min(1) })
647
689
  })
648
690
  );
649
691
  function permittedGithubActionsCalls(panel, source, _credentials) {
650
- const { panel: parsedPanel, source: parsedSource } = z6.object({ panel: pipelinePanelSchema2, source: githubActionsSourceSchema }).parse({ panel, source });
692
+ const { panel: parsedPanel, source: parsedSource } = z7.object({ panel: pipelinePanelSchema2, source: githubActionsSourceSchema }).parse({ panel, source });
651
693
  const url = new URL(
652
694
  `https://api.github.com/repos/${parsedSource.repo}/actions/workflows/${encodeURIComponent(parsedPanel.pipeline)}/runs`
653
695
  );
@@ -656,7 +698,7 @@ function permittedGithubActionsCalls(panel, source, _credentials) {
656
698
  return [{ url: url.toString(), headers: new Headers() }];
657
699
  }
658
700
  function pullRequestHealthCapabilities(panel) {
659
- const { panel: parsed } = z6.object({ panel: pullRequestHealthPanelSchema }).parse({ panel });
701
+ const { panel: parsed } = z7.object({ panel: pullRequestHealthPanelSchema }).parse({ panel });
660
702
  return {
661
703
  panel: parsed,
662
704
  configuredUpdateWorkflow(workflow) {
@@ -671,23 +713,23 @@ function pullRequestHealthCapabilities(panel) {
671
713
  }
672
714
 
673
715
  // packages/server/src/adapters/gitlab-ci.ts
674
- import { z as z7 } from "zod";
675
- var pipelinePanelSchema3 = z7.object({
676
- id: z7.string().min(1),
677
- type: z7.literal("pipeline-status"),
678
- source: z7.string().min(1)
716
+ import { z as z8 } from "zod";
717
+ var pipelinePanelSchema3 = z8.object({
718
+ id: z8.string().min(1),
719
+ type: z8.literal("pipeline-status"),
720
+ source: z8.string().min(1)
679
721
  });
680
- var pipelineSchema = z7.object({
681
- id: z7.number().int().positive(),
682
- name: z7.string().min(1).nullable().optional(),
683
- status: z7.string(),
684
- ref: z7.string().min(1).nullable().optional(),
685
- created_at: z7.string().nullable().optional(),
686
- updated_at: z7.string().nullable().optional(),
687
- web_url: z7.url()
722
+ var pipelineSchema = z8.object({
723
+ id: z8.number().int().positive(),
724
+ name: z8.string().min(1).nullable().optional(),
725
+ status: z8.string(),
726
+ ref: z8.string().min(1).nullable().optional(),
727
+ created_at: z8.string().nullable().optional(),
728
+ updated_at: z8.string().nullable().optional(),
729
+ web_url: z8.url()
688
730
  });
689
731
  function permittedGitlabCiCalls(panel, source) {
690
- const { source: parsedSource } = z7.object({ panel: pipelinePanelSchema3, source: gitlabCiSourceSchema }).parse({ panel, source });
732
+ const { source: parsedSource } = z8.object({ panel: pipelinePanelSchema3, source: gitlabCiSourceSchema }).parse({ panel, source });
691
733
  return [pipelinesCall(parsedSource)];
692
734
  }
693
735
  function pipelinesCall(source) {
@@ -699,10 +741,10 @@ function pipelinesCall(source) {
699
741
  }
700
742
 
701
743
  // packages/server/src/adapters/http-value.ts
702
- import { z as z8 } from "zod";
744
+ import { z as z9 } from "zod";
703
745
  function permittedHttpValueCalls(panel) {
704
746
  const grouped = httpValueGroupedPanelSchema.safeParse(panel);
705
- const urls = grouped.success ? grouped.data.facts.map((fact) => fact.url) : [z8.object({ panel: httpValueScalarPanelSchema }).parse({ panel }).panel.url];
747
+ const urls = grouped.success ? grouped.data.facts.map((fact) => fact.url) : [z9.object({ panel: httpValueScalarPanelSchema }).parse({ panel }).panel.url];
706
748
  return urls.map(permittedCall);
707
749
  }
708
750
  function permittedCall(value2) {
package/dist/index.js CHANGED
@@ -1001,14 +1001,41 @@ var boardSchema = z2.object({
1001
1001
  params: { constraint: "Important panels require one board attention treatment." }
1002
1002
  });
1003
1003
  });
1004
+ var authorizationSchema = z2.discriminatedUnion("mode", [
1005
+ z2.strictObject({ mode: z2.literal("authenticated") }),
1006
+ z2.strictObject({ mode: z2.literal("subjects"), subjects: z2.array(z2.string().min(1)).min(1) }),
1007
+ z2.strictObject({
1008
+ mode: z2.literal("claim"),
1009
+ /** Exact JWT payload key; namespaced provider claims are keys, never path expressions. */
1010
+ claim: z2.string().min(1),
1011
+ values: z2.array(z2.string().min(1)).min(1),
1012
+ match: z2.enum(["any", "all"])
1013
+ })
1014
+ ]);
1015
+ var legacySubjectAllowSchema = z2.strictObject({ subjects: z2.array(z2.string().min(1)).min(1) });
1004
1016
  var authSchema = z2.strictObject({
1005
1017
  issuer: z2.url(),
1006
1018
  /** Browser SPA client id. It is public, unlike every source credential. */
1007
1019
  client_id: z2.string().min(1),
1008
1020
  /** The separately registered API resource accepted by the dashboard proxy. */
1009
1021
  audience: z2.string().min(1),
1010
- /** A deliberately portable, explicit identity boundary for the first direct-OIDC release. */
1011
- allow: z2.strictObject({ subjects: z2.array(z2.string().min(1)).min(1) })
1022
+ /** Canonical authorization policy. */
1023
+ authorization: authorizationSchema.optional(),
1024
+ /** Legacy subject policy retained only for a configuration migration. */
1025
+ allow: legacySubjectAllowSchema.optional()
1026
+ }).superRefine((auth, ctx) => {
1027
+ if (!auth.authorization && !auth.allow)
1028
+ ctx.addIssue({
1029
+ code: "custom",
1030
+ path: ["authorization"],
1031
+ message: "configure authorization, or the legacy allow.subjects migration policy"
1032
+ });
1033
+ if (auth.authorization && auth.allow)
1034
+ ctx.addIssue({
1035
+ code: "custom",
1036
+ path: ["authorization"],
1037
+ message: "configure authorization or legacy allow.subjects, not both"
1038
+ });
1012
1039
  });
1013
1040
  var securityPolicies = ["warn", "required", "unsecured"];
1014
1041
  var securityPolicySchema = z2.enum(securityPolicies);
@@ -1218,43 +1245,58 @@ var httpValueSchema = z4.object({
1218
1245
  value: z4.union([z4.string(), z4.number(), z4.boolean()])
1219
1246
  });
1220
1247
 
1221
- // packages/server/src/adapters/azure-devops.ts
1248
+ // packages/shared/src/security-details.ts
1222
1249
  import { z as z5 } from "zod";
1223
- var pipelinePanelSchema = z5.object({
1224
- id: z5.string().min(1),
1225
- type: z5.literal("pipeline-status"),
1226
- source: z5.string().min(1),
1227
- pipeline: z5.number().int().positive()
1250
+ var securityDetailsResponseSchema = z5.strictObject({
1251
+ oidc: z5.literal(true),
1252
+ policy: z5.discriminatedUnion("mode", [
1253
+ z5.strictObject({ mode: z5.literal("authenticated") }),
1254
+ z5.strictObject({ mode: z5.literal("subjects") }),
1255
+ z5.strictObject({
1256
+ mode: z5.literal("claim"),
1257
+ claim: z5.string().min(1),
1258
+ match: z5.enum(["any", "all"])
1259
+ })
1260
+ ])
1228
1261
  });
1229
- var buildSchema = z5.object({
1230
- id: z5.number().int().positive(),
1231
- buildNumber: z5.string().min(1),
1232
- status: z5.string().nullable().optional(),
1233
- result: z5.string().nullable().optional(),
1234
- sourceBranch: z5.string().min(1).nullable().optional(),
1235
- startTime: z5.string().nullable().optional(),
1236
- finishTime: z5.string().nullable().optional(),
1237
- lastChangedDate: z5.string().nullable().optional()
1262
+
1263
+ // packages/server/src/adapters/azure-devops.ts
1264
+ import { z as z6 } from "zod";
1265
+ var pipelinePanelSchema = z6.object({
1266
+ id: z6.string().min(1),
1267
+ type: z6.literal("pipeline-status"),
1268
+ source: z6.string().min(1),
1269
+ pipeline: z6.number().int().positive()
1270
+ });
1271
+ var buildSchema = z6.object({
1272
+ id: z6.number().int().positive(),
1273
+ buildNumber: z6.string().min(1),
1274
+ status: z6.string().nullable().optional(),
1275
+ result: z6.string().nullable().optional(),
1276
+ sourceBranch: z6.string().min(1).nullable().optional(),
1277
+ startTime: z6.string().nullable().optional(),
1278
+ finishTime: z6.string().nullable().optional(),
1279
+ lastChangedDate: z6.string().nullable().optional()
1238
1280
  });
1239
- var buildsSchema = z5.object({ value: z5.array(buildSchema) });
1240
- var timelineSchema = z5.object({
1241
- records: z5.array(
1242
- z5.object({
1243
- id: z5.string().min(1),
1244
- parentId: z5.string().min(1).nullable().optional(),
1245
- type: z5.string().min(1),
1246
- name: z5.string().min(1),
1247
- state: z5.string().nullable().optional()
1281
+ var buildsSchema = z6.object({ value: z6.array(buildSchema) });
1282
+ var timelineSchema = z6.object({
1283
+ records: z6.array(
1284
+ z6.object({
1285
+ id: z6.string().min(1),
1286
+ parentId: z6.string().min(1).nullable().optional(),
1287
+ type: z6.string().min(1),
1288
+ name: z6.string().min(1),
1289
+ state: z6.string().nullable().optional()
1248
1290
  })
1249
1291
  )
1250
1292
  });
1251
1293
  function permittedAzureDevOpsCalls(panel, source) {
1252
- const { panel: parsedPanel, source: parsedSource } = z5.object({ panel: pipelinePanelSchema, source: azureDevOpsSourceSchema }).parse({ panel, source });
1294
+ const { panel: parsedPanel, source: parsedSource } = z6.object({ panel: pipelinePanelSchema, source: azureDevOpsSourceSchema }).parse({ panel, source });
1253
1295
  return [buildsCall(parsedPanel.pipeline, parsedSource)];
1254
1296
  }
1255
- var delegatedTokenFileSchema = z5.object({
1256
- accessToken: z5.string().min(1),
1257
- expiresAt: z5.string().min(1)
1297
+ var delegatedTokenFileSchema = z6.object({
1298
+ accessToken: z6.string().min(1),
1299
+ expiresAt: z6.string().min(1)
1258
1300
  });
1259
1301
  function buildsCall(definitionId, source) {
1260
1302
  const url = apiUrl(source, "build/builds");
@@ -1276,59 +1318,59 @@ function adoBranch(branch) {
1276
1318
  }
1277
1319
 
1278
1320
  // packages/server/src/adapters/github-actions.ts
1279
- import { z as z6 } from "zod";
1280
- var pipelinePanelSchema2 = z6.object({
1281
- id: z6.string().min(1),
1282
- type: z6.literal("pipeline-status"),
1283
- source: z6.string().min(1),
1321
+ import { z as z7 } from "zod";
1322
+ var pipelinePanelSchema2 = z7.object({
1323
+ id: z7.string().min(1),
1324
+ type: z7.literal("pipeline-status"),
1325
+ source: z7.string().min(1),
1284
1326
  /** GitHub accepts a workflow filename or numeric workflow id in this URL segment. */
1285
- pipeline: z6.string().min(1)
1327
+ pipeline: z7.string().min(1)
1286
1328
  });
1287
- var pullRequestHealthPanelSchema = z6.object({
1288
- id: z6.string().min(1),
1289
- type: z6.literal("pull-request-health"),
1290
- source: z6.string().min(1),
1291
- base_branch: z6.string().min(1),
1292
- update_workflows: z6.array(
1293
- z6.object({
1294
- workflow: z6.string().min(1),
1295
- branch_prefixes: z6.array(z6.string().min(1)).min(1)
1329
+ var pullRequestHealthPanelSchema = z7.object({
1330
+ id: z7.string().min(1),
1331
+ type: z7.literal("pull-request-health"),
1332
+ source: z7.string().min(1),
1333
+ base_branch: z7.string().min(1),
1334
+ update_workflows: z7.array(
1335
+ z7.object({
1336
+ workflow: z7.string().min(1),
1337
+ branch_prefixes: z7.array(z7.string().min(1)).min(1)
1296
1338
  })
1297
1339
  ).min(1),
1298
- build_workflow: z6.string().min(1)
1340
+ build_workflow: z7.string().min(1)
1299
1341
  });
1300
- var runSchema = z6.object({
1301
- id: z6.number().int().positive().optional(),
1302
- status: z6.string(),
1303
- conclusion: z6.string().nullable(),
1304
- name: z6.string().min(1),
1305
- html_url: z6.url(),
1342
+ var runSchema = z7.object({
1343
+ id: z7.number().int().positive().optional(),
1344
+ status: z7.string(),
1345
+ conclusion: z7.string().nullable(),
1346
+ name: z7.string().min(1),
1347
+ html_url: z7.url(),
1306
1348
  /** The branch that produced this run; absent for some event types. */
1307
- head_branch: z6.string().min(1).nullable().optional(),
1349
+ head_branch: z7.string().min(1).nullable().optional(),
1308
1350
  // Timestamp quality should only remove timing advice, never make an otherwise useful run unreadable.
1309
- run_started_at: z6.string().nullable().optional(),
1310
- updated_at: z6.string().optional()
1351
+ run_started_at: z7.string().nullable().optional(),
1352
+ updated_at: z7.string().optional()
1311
1353
  });
1312
- var runsSchema = z6.object({ workflow_runs: z6.array(runSchema) });
1313
- var jobsSchema = z6.object({
1314
- jobs: z6.array(
1315
- z6.object({
1316
- name: z6.string().min(1),
1317
- status: z6.string(),
1318
- steps: z6.array(z6.object({ name: z6.string().min(1), status: z6.string() })).optional()
1354
+ var runsSchema = z7.object({ workflow_runs: z7.array(runSchema) });
1355
+ var jobsSchema = z7.object({
1356
+ jobs: z7.array(
1357
+ z7.object({
1358
+ name: z7.string().min(1),
1359
+ status: z7.string(),
1360
+ steps: z7.array(z7.object({ name: z7.string().min(1), status: z7.string() })).optional()
1319
1361
  })
1320
1362
  )
1321
1363
  });
1322
- var pullRequestsSchema = z6.array(
1323
- z6.object({
1324
- number: z6.number().int().positive(),
1325
- html_url: z6.url(),
1326
- head: z6.object({ ref: z6.string().min(1) }),
1327
- base: z6.object({ ref: z6.string().min(1) })
1364
+ var pullRequestsSchema = z7.array(
1365
+ z7.object({
1366
+ number: z7.number().int().positive(),
1367
+ html_url: z7.url(),
1368
+ head: z7.object({ ref: z7.string().min(1) }),
1369
+ base: z7.object({ ref: z7.string().min(1) })
1328
1370
  })
1329
1371
  );
1330
1372
  function permittedGithubActionsCalls(panel, source, _credentials) {
1331
- const { panel: parsedPanel, source: parsedSource } = z6.object({ panel: pipelinePanelSchema2, source: githubActionsSourceSchema }).parse({ panel, source });
1373
+ const { panel: parsedPanel, source: parsedSource } = z7.object({ panel: pipelinePanelSchema2, source: githubActionsSourceSchema }).parse({ panel, source });
1332
1374
  const url = new URL(
1333
1375
  `https://api.github.com/repos/${parsedSource.repo}/actions/workflows/${encodeURIComponent(parsedPanel.pipeline)}/runs`
1334
1376
  );
@@ -1337,7 +1379,7 @@ function permittedGithubActionsCalls(panel, source, _credentials) {
1337
1379
  return [{ url: url.toString(), headers: new Headers() }];
1338
1380
  }
1339
1381
  function pullRequestHealthCapabilities(panel) {
1340
- const { panel: parsed } = z6.object({ panel: pullRequestHealthPanelSchema }).parse({ panel });
1382
+ const { panel: parsed } = z7.object({ panel: pullRequestHealthPanelSchema }).parse({ panel });
1341
1383
  return {
1342
1384
  panel: parsed,
1343
1385
  configuredUpdateWorkflow(workflow) {
@@ -1352,23 +1394,23 @@ function pullRequestHealthCapabilities(panel) {
1352
1394
  }
1353
1395
 
1354
1396
  // packages/server/src/adapters/gitlab-ci.ts
1355
- import { z as z7 } from "zod";
1356
- var pipelinePanelSchema3 = z7.object({
1357
- id: z7.string().min(1),
1358
- type: z7.literal("pipeline-status"),
1359
- source: z7.string().min(1)
1397
+ import { z as z8 } from "zod";
1398
+ var pipelinePanelSchema3 = z8.object({
1399
+ id: z8.string().min(1),
1400
+ type: z8.literal("pipeline-status"),
1401
+ source: z8.string().min(1)
1360
1402
  });
1361
- var pipelineSchema = z7.object({
1362
- id: z7.number().int().positive(),
1363
- name: z7.string().min(1).nullable().optional(),
1364
- status: z7.string(),
1365
- ref: z7.string().min(1).nullable().optional(),
1366
- created_at: z7.string().nullable().optional(),
1367
- updated_at: z7.string().nullable().optional(),
1368
- web_url: z7.url()
1403
+ var pipelineSchema = z8.object({
1404
+ id: z8.number().int().positive(),
1405
+ name: z8.string().min(1).nullable().optional(),
1406
+ status: z8.string(),
1407
+ ref: z8.string().min(1).nullable().optional(),
1408
+ created_at: z8.string().nullable().optional(),
1409
+ updated_at: z8.string().nullable().optional(),
1410
+ web_url: z8.url()
1369
1411
  });
1370
1412
  function permittedGitlabCiCalls(panel, source) {
1371
- const { source: parsedSource } = z7.object({ panel: pipelinePanelSchema3, source: gitlabCiSourceSchema }).parse({ panel, source });
1413
+ const { source: parsedSource } = z8.object({ panel: pipelinePanelSchema3, source: gitlabCiSourceSchema }).parse({ panel, source });
1372
1414
  return [pipelinesCall(parsedSource)];
1373
1415
  }
1374
1416
  function pipelinesCall(source) {
@@ -1380,10 +1422,10 @@ function pipelinesCall(source) {
1380
1422
  }
1381
1423
 
1382
1424
  // packages/server/src/adapters/http-value.ts
1383
- import { z as z8 } from "zod";
1425
+ import { z as z9 } from "zod";
1384
1426
  function permittedHttpValueCalls(panel) {
1385
1427
  const grouped = httpValueGroupedPanelSchema.safeParse(panel);
1386
- const urls = grouped.success ? grouped.data.facts.map((fact) => fact.url) : [z8.object({ panel: httpValueScalarPanelSchema }).parse({ panel }).panel.url];
1428
+ const urls = grouped.success ? grouped.data.facts.map((fact) => fact.url) : [z9.object({ panel: httpValueScalarPanelSchema }).parse({ panel }).panel.url];
1387
1429
  return urls.map(permittedCall);
1388
1430
  }
1389
1431
  function permittedCall(value2) {
package/dist/lambda.mjs CHANGED
@@ -69356,15 +69356,47 @@ var boardSchema = external_exports.object({
69356
69356
  params: { constraint: "Important panels require one board attention treatment." }
69357
69357
  });
69358
69358
  });
69359
+ var authorizationSchema = external_exports.discriminatedUnion("mode", [
69360
+ external_exports.strictObject({ mode: external_exports.literal("authenticated") }),
69361
+ external_exports.strictObject({ mode: external_exports.literal("subjects"), subjects: external_exports.array(external_exports.string().min(1)).min(1) }),
69362
+ external_exports.strictObject({
69363
+ mode: external_exports.literal("claim"),
69364
+ /** Exact JWT payload key; namespaced provider claims are keys, never path expressions. */
69365
+ claim: external_exports.string().min(1),
69366
+ values: external_exports.array(external_exports.string().min(1)).min(1),
69367
+ match: external_exports.enum(["any", "all"])
69368
+ })
69369
+ ]);
69370
+ var legacySubjectAllowSchema = external_exports.strictObject({ subjects: external_exports.array(external_exports.string().min(1)).min(1) });
69359
69371
  var authSchema = external_exports.strictObject({
69360
69372
  issuer: external_exports.url(),
69361
69373
  /** Browser SPA client id. It is public, unlike every source credential. */
69362
69374
  client_id: external_exports.string().min(1),
69363
69375
  /** The separately registered API resource accepted by the dashboard proxy. */
69364
69376
  audience: external_exports.string().min(1),
69365
- /** A deliberately portable, explicit identity boundary for the first direct-OIDC release. */
69366
- allow: external_exports.strictObject({ subjects: external_exports.array(external_exports.string().min(1)).min(1) })
69377
+ /** Canonical authorization policy. */
69378
+ authorization: authorizationSchema.optional(),
69379
+ /** Legacy subject policy retained only for a configuration migration. */
69380
+ allow: legacySubjectAllowSchema.optional()
69381
+ }).superRefine((auth, ctx) => {
69382
+ if (!auth.authorization && !auth.allow)
69383
+ ctx.addIssue({
69384
+ code: "custom",
69385
+ path: ["authorization"],
69386
+ message: "configure authorization, or the legacy allow.subjects migration policy"
69387
+ });
69388
+ if (auth.authorization && auth.allow)
69389
+ ctx.addIssue({
69390
+ code: "custom",
69391
+ path: ["authorization"],
69392
+ message: "configure authorization or legacy allow.subjects, not both"
69393
+ });
69367
69394
  });
69395
+ function authorizationForAuth(auth) {
69396
+ if (auth.authorization) return auth.authorization;
69397
+ if (auth.allow) return { mode: "subjects", subjects: auth.allow.subjects };
69398
+ throw new Error("OIDC authorization policy is missing.");
69399
+ }
69368
69400
  var securityPolicies = ["warn", "required", "unsecured"];
69369
69401
  var securityPolicySchema = external_exports.enum(securityPolicies);
69370
69402
  var boardConfigSchema = external_exports.object({
@@ -69647,6 +69679,20 @@ function nearestLegalRectangle(desired, accepted) {
69647
69679
  return { x: candidate.x, y: candidate.y, w: candidate.w, h: candidate.h };
69648
69680
  }
69649
69681
 
69682
+ // packages/shared/src/security-details.ts
69683
+ var securityDetailsResponseSchema = external_exports.strictObject({
69684
+ oidc: external_exports.literal(true),
69685
+ policy: external_exports.discriminatedUnion("mode", [
69686
+ external_exports.strictObject({ mode: external_exports.literal("authenticated") }),
69687
+ external_exports.strictObject({ mode: external_exports.literal("subjects") }),
69688
+ external_exports.strictObject({
69689
+ mode: external_exports.literal("claim"),
69690
+ claim: external_exports.string().min(1),
69691
+ match: external_exports.enum(["any", "all"])
69692
+ })
69693
+ ])
69694
+ });
69695
+
69650
69696
  // packages/server/src/adapters/azure-devops.ts
69651
69697
  import { readFile } from "node:fs/promises";
69652
69698
 
@@ -73865,14 +73911,22 @@ async function createAccessTokenVerifier(auth, fetcher = globalThis.fetch) {
73865
73911
  const { payload: payload2 } = await jwtVerify(token, keys, { issuer, audience: auth.audience });
73866
73912
  if (typeof payload2.sub !== "string" || !payload2.sub)
73867
73913
  throw new Error("Access token has no subject.");
73868
- if (!auth.allow.subjects.includes(payload2.sub)) {
73914
+ if (!permitsIdentity(authorizationForAuth(auth), payload2.sub, payload2)) {
73869
73915
  const error63 = new Error("Authenticated subject is not allowed to view this dashboard.");
73870
- error63.name = "UnauthorizedSubjectError";
73916
+ error63.name = "AccessDeniedError";
73871
73917
  throw error63;
73872
73918
  }
73873
73919
  return { subject: payload2.sub };
73874
73920
  };
73875
73921
  }
73922
+ function permitsIdentity(policy, subject, claims) {
73923
+ if (policy.mode === "authenticated") return true;
73924
+ if (policy.mode === "subjects") return policy.subjects.includes(subject);
73925
+ const value = claims[policy.claim];
73926
+ const actual = typeof value === "string" && value.length > 0 ? [value] : Array.isArray(value) && value.every((item) => typeof item === "string" && item.length > 0) ? value : void 0;
73927
+ if (!actual) return false;
73928
+ return policy.match === "any" ? policy.values.some((expected) => actual.includes(expected)) : policy.values.every((expected) => actual.includes(expected));
73929
+ }
73876
73930
  async function fetchJson(url2, fetcher) {
73877
73931
  const response = await fetcher(url2);
73878
73932
  if (!response.ok) throw new Error(`OIDC discovery request failed: ${response.status}`);
@@ -73882,8 +73936,8 @@ async function fetchJson(url2, fetcher) {
73882
73936
  throw new Error("OIDC discovery did not return JSON.");
73883
73937
  }
73884
73938
  }
73885
- function unauthorizedSubject(error63) {
73886
- return error63 instanceof Error && error63.name === "UnauthorizedSubjectError";
73939
+ function accessDenied(error63) {
73940
+ return error63 instanceof Error && error63.name === "AccessDeniedError";
73887
73941
  }
73888
73942
 
73889
73943
  // packages/server/src/template.ts
@@ -73992,8 +74046,8 @@ function createApp(deps) {
73992
74046
  await deps.accessTokenVerifier(authorization.slice("Bearer ".length));
73993
74047
  } catch (error63) {
73994
74048
  return c5.json(
73995
- { error: unauthorizedSubject(error63) ? "Access denied." : "Invalid access token." },
73996
- unauthorizedSubject(error63) ? 403 : 401
74049
+ { error: accessDenied(error63) ? "Access denied." : "Invalid access token." },
74050
+ accessDenied(error63) ? 403 : 401
73997
74051
  );
73998
74052
  }
73999
74053
  return next();
@@ -74035,6 +74089,17 @@ function createApp(deps) {
74035
74089
  })
74036
74090
  });
74037
74091
  });
74092
+ app.get("/api/boards/:board/security", (c5) => {
74093
+ if (!deps.boardConfig?.boards[c5.req.param("board")] || !deps.boardConfig.auth)
74094
+ return c5.notFound();
74095
+ const policy = authorizationForAuth(deps.boardConfig.auth);
74096
+ return c5.json(
74097
+ securityDetailsResponseSchema.parse({
74098
+ oidc: true,
74099
+ policy: policy.mode === "claim" ? { mode: policy.mode, claim: policy.claim, match: policy.match } : { mode: policy.mode }
74100
+ })
74101
+ );
74102
+ });
74038
74103
  const layoutDownload = (c5, mode) => {
74039
74104
  const boardName = c5.req.param("board");
74040
74105
  const board = deps.boardConfig?.boards[boardName];
@@ -74055,8 +74120,7 @@ function createApp(deps) {
74055
74120
  const outputConfig = {
74056
74121
  sources,
74057
74122
  boards: { [boardName]: outputBoard },
74058
- ...deps.boardConfig.security ? { security: deps.boardConfig.security } : {},
74059
- ...deps.boardConfig.auth ? { auth: deps.boardConfig.auth } : {}
74123
+ ...deps.boardConfig.security ? { security: deps.boardConfig.security } : {}
74060
74124
  };
74061
74125
  const filenameBoard = safeDownloadFilename(boardName);
74062
74126
  return new Response(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@continuous-excellence/ze-great-dashboard-aws",
3
- "version": "0.30.6",
3
+ "version": "0.31.0",
4
4
  "type": "module",
5
5
  "description": "AWS server runtime and CloudFormation deployment tooling for Ze Great Dashboard.",
6
6
  "keywords": [