@superblocksteam/shared 0.9607.2 → 0.9608.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/database-lifecycle/appDatabaseCompatibility.d.ts +49 -0
- package/dist/database-lifecycle/appDatabaseCompatibility.d.ts.map +1 -0
- package/dist/database-lifecycle/appDatabaseCompatibility.js +32 -0
- package/dist/database-lifecycle/appDatabaseCompatibility.js.map +1 -0
- package/dist/database-lifecycle/appDatabaseCompatibility.test.d.ts +2 -0
- package/dist/database-lifecycle/appDatabaseCompatibility.test.d.ts.map +1 -0
- package/dist/database-lifecycle/appDatabaseCompatibility.test.js +47 -0
- package/dist/database-lifecycle/appDatabaseCompatibility.test.js.map +1 -0
- package/dist/database-lifecycle/destructiveMigrations.d.ts +12 -0
- package/dist/database-lifecycle/destructiveMigrations.d.ts.map +1 -1
- package/dist/database-lifecycle/destructiveMigrations.js +114 -0
- package/dist/database-lifecycle/destructiveMigrations.js.map +1 -1
- package/dist/database-lifecycle/destructiveMigrations.test.js +79 -0
- package/dist/database-lifecycle/destructiveMigrations.test.js.map +1 -1
- package/dist/database-lifecycle/index.d.ts +46 -2
- package/dist/database-lifecycle/index.d.ts.map +1 -1
- package/dist/database-lifecycle/index.js +127 -1
- package/dist/database-lifecycle/index.js.map +1 -1
- package/dist/database-lifecycle/providerDiagnostics.test.d.ts +2 -0
- package/dist/database-lifecycle/providerDiagnostics.test.d.ts.map +1 -0
- package/dist/database-lifecycle/providerDiagnostics.test.js +119 -0
- package/dist/database-lifecycle/providerDiagnostics.test.js.map +1 -0
- package/dist/environment-compatibility/appEnvironmentCompatibility.d.ts +65 -0
- package/dist/environment-compatibility/appEnvironmentCompatibility.d.ts.map +1 -0
- package/dist/environment-compatibility/appEnvironmentCompatibility.js +87 -0
- package/dist/environment-compatibility/appEnvironmentCompatibility.js.map +1 -0
- package/dist/environment-compatibility/appEnvironmentCompatibility.test.d.ts +2 -0
- package/dist/environment-compatibility/appEnvironmentCompatibility.test.d.ts.map +1 -0
- package/dist/environment-compatibility/appEnvironmentCompatibility.test.js +285 -0
- package/dist/environment-compatibility/appEnvironmentCompatibility.test.js.map +1 -0
- package/dist/errors/http.d.ts +15 -4
- package/dist/errors/http.d.ts.map +1 -1
- package/dist/errors/http.js +25 -7
- package/dist/errors/http.js.map +1 -1
- package/dist/errors/http.test.d.ts +2 -0
- package/dist/errors/http.test.d.ts.map +1 -0
- package/dist/errors/http.test.js +28 -0
- package/dist/errors/http.test.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/plugins/templates/restApiIntegration.d.ts.map +1 -1
- package/dist/plugins/templates/restApiIntegration.js +2 -0
- package/dist/plugins/templates/restApiIntegration.js.map +1 -1
- package/dist/plugins/templates/shared/auth.d.ts +3 -1
- package/dist/plugins/templates/shared/auth.d.ts.map +1 -1
- package/dist/plugins/templates/shared/auth.js +129 -17
- package/dist/plugins/templates/shared/auth.js.map +1 -1
- package/dist/plugins/templates/shared/auth.test.d.ts +2 -0
- package/dist/plugins/templates/shared/auth.test.d.ts.map +1 -0
- package/dist/plugins/templates/shared/auth.test.js +116 -0
- package/dist/plugins/templates/shared/auth.test.js.map +1 -0
- package/dist/plugins/templates/shared/restapiintegration.d.ts +2 -0
- package/dist/plugins/templates/shared/restapiintegration.d.ts.map +1 -1
- package/dist/plugins/templates/shared/restapiintegration.js +3 -1
- package/dist/plugins/templates/shared/restapiintegration.js.map +1 -1
- package/dist/socket/dist-entry.test.d.ts +2 -0
- package/dist/socket/dist-entry.test.d.ts.map +1 -0
- package/dist/socket/dist-entry.test.js +34 -0
- package/dist/socket/dist-entry.test.js.map +1 -0
- package/dist/socket/heartbeat.d.ts +258 -0
- package/dist/socket/heartbeat.d.ts.map +1 -0
- package/dist/socket/heartbeat.js +307 -0
- package/dist/socket/heartbeat.js.map +1 -0
- package/dist/socket/heartbeat.test.d.ts +2 -0
- package/dist/socket/heartbeat.test.d.ts.map +1 -0
- package/dist/socket/heartbeat.test.js +431 -0
- package/dist/socket/heartbeat.test.js.map +1 -0
- package/dist/socket/index.d.ts +1 -0
- package/dist/socket/index.d.ts.map +1 -1
- package/dist/socket/index.js +1 -0
- package/dist/socket/index.js.map +1 -1
- package/dist/socket/util.d.ts.map +1 -1
- package/dist/socket/util.js +15 -1
- package/dist/socket/util.js.map +1 -1
- package/dist/socket/util.test.js +12 -2
- package/dist/socket/util.test.js.map +1 -1
- package/dist/types/ai/index.d.ts +3 -0
- package/dist/types/ai/index.d.ts.map +1 -1
- package/dist/types/ai/index.js +3 -0
- package/dist/types/ai/index.js.map +1 -1
- package/dist/types/application/index.d.ts +12 -0
- package/dist/types/application/index.d.ts.map +1 -1
- package/dist/types/application/index.js.map +1 -1
- package/dist/types/codeSecurity/index.d.ts +55 -1
- package/dist/types/codeSecurity/index.d.ts.map +1 -1
- package/dist/types/codeSecurity/index.js +21 -1
- package/dist/types/codeSecurity/index.js.map +1 -1
- package/dist/types/codeSecurity/index.test.d.ts +2 -0
- package/dist/types/codeSecurity/index.test.d.ts.map +1 -0
- package/dist/types/codeSecurity/index.test.js +19 -0
- package/dist/types/codeSecurity/index.test.js.map +1 -0
- package/dist/types/datasource/auth.d.ts +12 -1
- package/dist/types/datasource/auth.d.ts.map +1 -1
- package/dist/types/datasource/auth.js +20 -2
- package/dist/types/datasource/auth.js.map +1 -1
- package/dist/types/datasource/auth.test.js +3 -1
- package/dist/types/datasource/auth.test.js.map +1 -1
- package/dist/types/group/index.d.ts +1 -3
- package/dist/types/group/index.d.ts.map +1 -1
- package/dist/types/group/index.js +1 -3
- package/dist/types/group/index.js.map +1 -1
- package/dist/types/integration/index.d.ts +11 -1
- package/dist/types/integration/index.d.ts.map +1 -1
- package/dist/types/integration/index.js +11 -1
- package/dist/types/integration/index.js.map +1 -1
- package/dist/types/plugin/form.d.ts +32 -0
- package/dist/types/plugin/form.d.ts.map +1 -1
- package/dist/types/plugin/form.js +73 -1
- package/dist/types/plugin/form.js.map +1 -1
- package/dist/types/plugin/form.test.js +128 -0
- package/dist/types/plugin/form.test.js.map +1 -1
- package/dist/types/profile/index.d.ts +16 -1
- package/dist/types/profile/index.d.ts.map +1 -1
- package/dist/types/profile/index.js +7 -1
- package/dist/types/profile/index.js.map +1 -1
- package/dist/types/rbac/index.d.ts +2 -0
- package/dist/types/rbac/index.d.ts.map +1 -1
- package/dist/types/rbac/index.js +2 -0
- package/dist/types/rbac/index.js.map +1 -1
- package/dist/types/response/GetRecentApplicationsResponseBody.d.ts +4 -0
- package/dist/types/response/GetRecentApplicationsResponseBody.d.ts.map +1 -0
- package/dist/types/response/GetRecentApplicationsResponseBody.js +3 -0
- package/dist/types/response/GetRecentApplicationsResponseBody.js.map +1 -0
- package/dist/types/response/index.d.ts +1 -0
- package/dist/types/response/index.d.ts.map +1 -1
- package/dist/types/response/index.js +1 -0
- package/dist/types/response/index.js.map +1 -1
- package/dist-esm/database-lifecycle/appDatabaseCompatibility.d.ts +49 -0
- package/dist-esm/database-lifecycle/appDatabaseCompatibility.d.ts.map +1 -0
- package/dist-esm/database-lifecycle/appDatabaseCompatibility.js +28 -0
- package/dist-esm/database-lifecycle/appDatabaseCompatibility.js.map +1 -0
- package/dist-esm/database-lifecycle/appDatabaseCompatibility.test.d.ts +2 -0
- package/dist-esm/database-lifecycle/appDatabaseCompatibility.test.d.ts.map +1 -0
- package/dist-esm/database-lifecycle/appDatabaseCompatibility.test.js +45 -0
- package/dist-esm/database-lifecycle/appDatabaseCompatibility.test.js.map +1 -0
- package/dist-esm/database-lifecycle/destructiveMigrations.d.ts +12 -0
- package/dist-esm/database-lifecycle/destructiveMigrations.d.ts.map +1 -1
- package/dist-esm/database-lifecycle/destructiveMigrations.js +113 -0
- package/dist-esm/database-lifecycle/destructiveMigrations.js.map +1 -1
- package/dist-esm/database-lifecycle/destructiveMigrations.test.js +80 -1
- package/dist-esm/database-lifecycle/destructiveMigrations.test.js.map +1 -1
- package/dist-esm/database-lifecycle/index.d.ts +46 -2
- package/dist-esm/database-lifecycle/index.d.ts.map +1 -1
- package/dist-esm/database-lifecycle/index.js +123 -1
- package/dist-esm/database-lifecycle/index.js.map +1 -1
- package/dist-esm/database-lifecycle/providerDiagnostics.test.d.ts +2 -0
- package/dist-esm/database-lifecycle/providerDiagnostics.test.d.ts.map +1 -0
- package/dist-esm/database-lifecycle/providerDiagnostics.test.js +117 -0
- package/dist-esm/database-lifecycle/providerDiagnostics.test.js.map +1 -0
- package/dist-esm/environment-compatibility/appEnvironmentCompatibility.d.ts +65 -0
- package/dist-esm/environment-compatibility/appEnvironmentCompatibility.d.ts.map +1 -0
- package/dist-esm/environment-compatibility/appEnvironmentCompatibility.js +79 -0
- package/dist-esm/environment-compatibility/appEnvironmentCompatibility.js.map +1 -0
- package/dist-esm/environment-compatibility/appEnvironmentCompatibility.test.d.ts +2 -0
- package/dist-esm/environment-compatibility/appEnvironmentCompatibility.test.d.ts.map +1 -0
- package/dist-esm/environment-compatibility/appEnvironmentCompatibility.test.js +283 -0
- package/dist-esm/environment-compatibility/appEnvironmentCompatibility.test.js.map +1 -0
- package/dist-esm/errors/http.d.ts +15 -4
- package/dist-esm/errors/http.d.ts.map +1 -1
- package/dist-esm/errors/http.js +22 -6
- package/dist-esm/errors/http.js.map +1 -1
- package/dist-esm/errors/http.test.d.ts +2 -0
- package/dist-esm/errors/http.test.d.ts.map +1 -0
- package/dist-esm/errors/http.test.js +26 -0
- package/dist-esm/errors/http.test.js.map +1 -0
- package/dist-esm/index.d.ts +2 -0
- package/dist-esm/index.d.ts.map +1 -1
- package/dist-esm/index.js +2 -0
- package/dist-esm/index.js.map +1 -1
- package/dist-esm/plugins/templates/restApiIntegration.d.ts.map +1 -1
- package/dist-esm/plugins/templates/restApiIntegration.js +2 -0
- package/dist-esm/plugins/templates/restApiIntegration.js.map +1 -1
- package/dist-esm/plugins/templates/shared/auth.d.ts +3 -1
- package/dist-esm/plugins/templates/shared/auth.d.ts.map +1 -1
- package/dist-esm/plugins/templates/shared/auth.js +129 -17
- package/dist-esm/plugins/templates/shared/auth.js.map +1 -1
- package/dist-esm/plugins/templates/shared/auth.test.d.ts +2 -0
- package/dist-esm/plugins/templates/shared/auth.test.d.ts.map +1 -0
- package/dist-esm/plugins/templates/shared/auth.test.js +114 -0
- package/dist-esm/plugins/templates/shared/auth.test.js.map +1 -0
- package/dist-esm/plugins/templates/shared/restapiintegration.d.ts +2 -0
- package/dist-esm/plugins/templates/shared/restapiintegration.d.ts.map +1 -1
- package/dist-esm/plugins/templates/shared/restapiintegration.js +3 -1
- package/dist-esm/plugins/templates/shared/restapiintegration.js.map +1 -1
- package/dist-esm/socket/dist-entry.test.d.ts +2 -0
- package/dist-esm/socket/dist-entry.test.d.ts.map +1 -0
- package/dist-esm/socket/dist-entry.test.js +29 -0
- package/dist-esm/socket/dist-entry.test.js.map +1 -0
- package/dist-esm/socket/heartbeat.d.ts +258 -0
- package/dist-esm/socket/heartbeat.d.ts.map +1 -0
- package/dist-esm/socket/heartbeat.js +303 -0
- package/dist-esm/socket/heartbeat.js.map +1 -0
- package/dist-esm/socket/heartbeat.test.d.ts +2 -0
- package/dist-esm/socket/heartbeat.test.d.ts.map +1 -0
- package/dist-esm/socket/heartbeat.test.js +429 -0
- package/dist-esm/socket/heartbeat.test.js.map +1 -0
- package/dist-esm/socket/index.d.ts +1 -0
- package/dist-esm/socket/index.d.ts.map +1 -1
- package/dist-esm/socket/index.js +1 -0
- package/dist-esm/socket/index.js.map +1 -1
- package/dist-esm/socket/util.d.ts.map +1 -1
- package/dist-esm/socket/util.js +15 -1
- package/dist-esm/socket/util.js.map +1 -1
- package/dist-esm/socket/util.test.js +12 -2
- package/dist-esm/socket/util.test.js.map +1 -1
- package/dist-esm/types/ai/index.d.ts +3 -0
- package/dist-esm/types/ai/index.d.ts.map +1 -1
- package/dist-esm/types/ai/index.js +3 -0
- package/dist-esm/types/ai/index.js.map +1 -1
- package/dist-esm/types/application/index.d.ts +12 -0
- package/dist-esm/types/application/index.d.ts.map +1 -1
- package/dist-esm/types/application/index.js.map +1 -1
- package/dist-esm/types/codeSecurity/index.d.ts +55 -1
- package/dist-esm/types/codeSecurity/index.d.ts.map +1 -1
- package/dist-esm/types/codeSecurity/index.js +17 -1
- package/dist-esm/types/codeSecurity/index.js.map +1 -1
- package/dist-esm/types/codeSecurity/index.test.d.ts +2 -0
- package/dist-esm/types/codeSecurity/index.test.d.ts.map +1 -0
- package/dist-esm/types/codeSecurity/index.test.js +17 -0
- package/dist-esm/types/codeSecurity/index.test.js.map +1 -0
- package/dist-esm/types/datasource/auth.d.ts +12 -1
- package/dist-esm/types/datasource/auth.d.ts.map +1 -1
- package/dist-esm/types/datasource/auth.js +19 -2
- package/dist-esm/types/datasource/auth.js.map +1 -1
- package/dist-esm/types/datasource/auth.test.js +3 -1
- package/dist-esm/types/datasource/auth.test.js.map +1 -1
- package/dist-esm/types/group/index.d.ts +1 -3
- package/dist-esm/types/group/index.d.ts.map +1 -1
- package/dist-esm/types/group/index.js +1 -3
- package/dist-esm/types/group/index.js.map +1 -1
- package/dist-esm/types/integration/index.d.ts +11 -1
- package/dist-esm/types/integration/index.d.ts.map +1 -1
- package/dist-esm/types/integration/index.js +11 -1
- package/dist-esm/types/integration/index.js.map +1 -1
- package/dist-esm/types/plugin/form.d.ts +32 -0
- package/dist-esm/types/plugin/form.d.ts.map +1 -1
- package/dist-esm/types/plugin/form.js +68 -0
- package/dist-esm/types/plugin/form.js.map +1 -1
- package/dist-esm/types/plugin/form.test.js +129 -1
- package/dist-esm/types/plugin/form.test.js.map +1 -1
- package/dist-esm/types/profile/index.d.ts +16 -1
- package/dist-esm/types/profile/index.d.ts.map +1 -1
- package/dist-esm/types/profile/index.js +7 -1
- package/dist-esm/types/profile/index.js.map +1 -1
- package/dist-esm/types/rbac/index.d.ts +2 -0
- package/dist-esm/types/rbac/index.d.ts.map +1 -1
- package/dist-esm/types/rbac/index.js +2 -0
- package/dist-esm/types/rbac/index.js.map +1 -1
- package/dist-esm/types/response/GetRecentApplicationsResponseBody.d.ts +4 -0
- package/dist-esm/types/response/GetRecentApplicationsResponseBody.d.ts.map +1 -0
- package/dist-esm/types/response/GetRecentApplicationsResponseBody.js +2 -0
- package/dist-esm/types/response/GetRecentApplicationsResponseBody.js.map +1 -0
- package/dist-esm/types/response/index.d.ts +1 -0
- package/dist-esm/types/response/index.d.ts.map +1 -1
- package/dist-esm/types/response/index.js +1 -0
- package/dist-esm/types/response/index.js.map +1 -1
- package/package.json +1 -1
- package/src/database-lifecycle/appDatabaseCompatibility.test.ts +55 -0
- package/src/database-lifecycle/appDatabaseCompatibility.ts +62 -0
- package/src/database-lifecycle/destructiveMigrations.test.ts +108 -1
- package/src/database-lifecycle/destructiveMigrations.ts +129 -0
- package/src/database-lifecycle/index.ts +173 -0
- package/src/database-lifecycle/providerDiagnostics.test.ts +134 -0
- package/src/environment-compatibility/appEnvironmentCompatibility.test.ts +326 -0
- package/src/environment-compatibility/appEnvironmentCompatibility.ts +173 -0
- package/src/errors/http.test.ts +32 -0
- package/src/errors/http.ts +24 -6
- package/src/index.ts +2 -0
- package/src/plugins/templates/restApiIntegration.ts +2 -0
- package/src/plugins/templates/shared/auth.test.ts +162 -0
- package/src/plugins/templates/shared/auth.ts +135 -18
- package/src/plugins/templates/shared/restapiintegration.ts +3 -1
- package/src/socket/dist-entry.test.ts +29 -0
- package/src/socket/heartbeat.test.ts +538 -0
- package/src/socket/heartbeat.ts +469 -0
- package/src/socket/index.ts +1 -0
- package/src/socket/util.test.ts +14 -2
- package/src/socket/util.ts +15 -1
- package/src/types/ai/index.ts +3 -0
- package/src/types/application/index.ts +14 -0
- package/src/types/codeSecurity/index.test.ts +21 -0
- package/src/types/codeSecurity/index.ts +83 -1
- package/src/types/datasource/auth.test.ts +3 -1
- package/src/types/datasource/auth.ts +24 -3
- package/src/types/group/index.ts +1 -3
- package/src/types/integration/index.ts +14 -0
- package/src/types/plugin/form.test.ts +174 -1
- package/src/types/plugin/form.ts +100 -2
- package/src/types/profile/index.ts +19 -1
- package/src/types/rbac/index.ts +2 -0
- package/src/types/response/GetRecentApplicationsResponseBody.ts +4 -0
- package/src/types/response/index.ts +1 -0
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
// server handlers and the UI so the two can't drift. The taxonomy below is
|
|
3
3
|
// mirrored by the PackageVulnerability DB entity (its columns import these).
|
|
4
4
|
|
|
5
|
+
import semver from 'semver';
|
|
6
|
+
|
|
5
7
|
export type Ecosystem = 'npm';
|
|
6
8
|
export type VulnerabilitySource = 'osv';
|
|
7
9
|
export type AdvisoryKind = 'vulnerability' | 'malware';
|
|
@@ -13,7 +15,7 @@ export type SeverityLevel = (typeof SEVERITY_LEVELS)[number];
|
|
|
13
15
|
|
|
14
16
|
// needs_review: version couldn't be pinned, so a "clear" verdict would be unearned.
|
|
15
17
|
export type DependencyScanStatus = 'clear' | 'needs_review' | 'unresolved';
|
|
16
|
-
export const DEPENDENCY_SCAN_DATE_FILTERS = ['all', 'today', '-7d', '-30d', 'ytd'] as const;
|
|
18
|
+
export const DEPENDENCY_SCAN_DATE_FILTERS = ['all', 'none', 'today', '-7d', '-30d', 'ytd'] as const;
|
|
17
19
|
export type DependencyScanDateFilter = (typeof DEPENDENCY_SCAN_DATE_FILTERS)[number];
|
|
18
20
|
export type DependencyScanOrder = 'asc' | 'desc';
|
|
19
21
|
export const DEPENDENCY_SCAN_SORTS = ['severity', 'last_updated', 'last_published'] as const;
|
|
@@ -26,6 +28,21 @@ export function parseDependencyScanDateFilter(value: string | null | undefined):
|
|
|
26
28
|
return 'all';
|
|
27
29
|
}
|
|
28
30
|
|
|
31
|
+
// Pins a package usage row to the single version the org scan evaluates it
|
|
32
|
+
// against: the resolved version when we have one, otherwise the minimum
|
|
33
|
+
// version satisfying the declared range. Shared between the server (which
|
|
34
|
+
// produces AdvisoryDetail.currentVersion this way) and the UI (which must key
|
|
35
|
+
// its own package rows identically to look advisories back up) so the two
|
|
36
|
+
// can't drift into looking up different version strings for the same package.
|
|
37
|
+
export function resolveScannedPackageVersion(declared: string, resolved: string | null | undefined): string | null {
|
|
38
|
+
const source = resolved && resolved.length > 0 ? resolved : declared;
|
|
39
|
+
try {
|
|
40
|
+
return semver.minVersion(source)?.version ?? null;
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
29
46
|
export interface AdvisoryDetail {
|
|
30
47
|
advisoryId: string;
|
|
31
48
|
source: VulnerabilitySource;
|
|
@@ -63,3 +80,68 @@ export interface DependencyScanSummary {
|
|
|
63
80
|
publishedWithVulns: number;
|
|
64
81
|
totalProjects: number;
|
|
65
82
|
}
|
|
83
|
+
|
|
84
|
+
// Wire contract for GET /api/v2/applications/package-usage, backed by the
|
|
85
|
+
// application_package_usage table: one row per (application, commit,
|
|
86
|
+
// package). Shared so the server handler and the UI's SBOM view can't drift.
|
|
87
|
+
export interface AppPackageUsageItem {
|
|
88
|
+
application_id: string;
|
|
89
|
+
application_name: string;
|
|
90
|
+
commit_id: string;
|
|
91
|
+
commit_message: string | null;
|
|
92
|
+
is_deployed: boolean;
|
|
93
|
+
package_name: string;
|
|
94
|
+
package_version_declared: string;
|
|
95
|
+
package_version_resolved: string | null;
|
|
96
|
+
dependency_source: string | null;
|
|
97
|
+
indexed_at: string;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface AppPackageUsageResponse {
|
|
101
|
+
source: 'all' | 'deployed' | 'not_deployed';
|
|
102
|
+
deployed_filter: boolean | null;
|
|
103
|
+
package_name: string | null;
|
|
104
|
+
application_id: string | null;
|
|
105
|
+
commit_id: string | null;
|
|
106
|
+
package_version_declared: string | null;
|
|
107
|
+
package_version_resolved: string | null;
|
|
108
|
+
dependency_source: string | null;
|
|
109
|
+
limit: number;
|
|
110
|
+
offset: number;
|
|
111
|
+
total: number;
|
|
112
|
+
items: AppPackageUsageItem[];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Minimal CycloneDX 1.5 JSON document, hand-built server-side from
|
|
116
|
+
// application_package_usage rows for one (application, commit) — see
|
|
117
|
+
// service/codeSecurity/sbom.ts. Only the fields this feature populates are
|
|
118
|
+
// modeled; CycloneDX defines many more optional fields we don't emit.
|
|
119
|
+
export interface CycloneDxProperty {
|
|
120
|
+
name: string;
|
|
121
|
+
value: string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface CycloneDxComponent {
|
|
125
|
+
type: 'library';
|
|
126
|
+
name: string;
|
|
127
|
+
version: string;
|
|
128
|
+
purl: string;
|
|
129
|
+
properties: CycloneDxProperty[];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export interface CycloneDxBom {
|
|
133
|
+
bomFormat: 'CycloneDX';
|
|
134
|
+
specVersion: '1.5';
|
|
135
|
+
serialNumber: string;
|
|
136
|
+
version: 1;
|
|
137
|
+
metadata: {
|
|
138
|
+
timestamp: string;
|
|
139
|
+
component: {
|
|
140
|
+
type: 'application';
|
|
141
|
+
name: string;
|
|
142
|
+
version: string;
|
|
143
|
+
'bom-ref': string;
|
|
144
|
+
};
|
|
145
|
+
};
|
|
146
|
+
components: CycloneDxComponent[];
|
|
147
|
+
}
|
|
@@ -148,10 +148,12 @@ describe('extractPublic normalizes boolean tokenScope for OAUTH2_CODE', () => {
|
|
|
148
148
|
test('preserves the PKCE marker', () => {
|
|
149
149
|
const result = extractPublic(IntegrationAuthType.OAUTH2_CODE, {
|
|
150
150
|
clientId: 'public-client',
|
|
151
|
-
usePkce: true
|
|
151
|
+
usePkce: true,
|
|
152
|
+
usePkceClientSecret: 'true'
|
|
152
153
|
});
|
|
153
154
|
|
|
154
155
|
expect(result.usePkce).toBe(true);
|
|
156
|
+
expect(result.usePkceClientSecret).toBe(true);
|
|
155
157
|
});
|
|
156
158
|
|
|
157
159
|
test('normalizes boolean true tokenScope to TokenScope.DATASOURCE', () => {
|
|
@@ -18,6 +18,23 @@ export function getOAuthCodeVerifierStorageKey(oneTimeCode: string): string {
|
|
|
18
18
|
return `${LS_OAUTH_CODE_VERIFIER_PREFIX}:${oneTimeCode}`;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Persisted integration configuration stores some booleans as strings or
|
|
23
|
+
* numbers, because form controls and older writers coerced them on the way in
|
|
24
|
+
* (hence the `FakeBoolean` fields below). Returns the boolean such a value
|
|
25
|
+
* stands for, or `undefined` when it stands for neither and the caller has to
|
|
26
|
+
* decide what an unrecognized value means.
|
|
27
|
+
*/
|
|
28
|
+
export function asFakeBoolean(value: unknown): boolean | undefined {
|
|
29
|
+
if (value === true || value === 'true' || value === 1 || value === '1') {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
if (value === false || value === 'false' || value === 0 || value === '0' || value === '' || value === null || value === undefined) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
21
38
|
/**
|
|
22
39
|
* `usePkce` is typed as a boolean but reaches us from persisted integration
|
|
23
40
|
* configuration, where a form checkbox can store it as the string `'true'`.
|
|
@@ -25,7 +42,7 @@ export function getOAuthCodeVerifierStorageKey(oneTimeCode: string): string {
|
|
|
25
42
|
* sites disagree about whether PKCE is on.
|
|
26
43
|
*/
|
|
27
44
|
export function isPkceEnabled(usePkce: unknown): boolean {
|
|
28
|
-
return usePkce === true
|
|
45
|
+
return asFakeBoolean(usePkce) === true;
|
|
29
46
|
}
|
|
30
47
|
|
|
31
48
|
export type AuthId = string;
|
|
@@ -344,13 +361,15 @@ type PublicOAuthCodeConfig = {
|
|
|
344
361
|
userEmail?: string; // Used by integrations that need user identity (e.g., Lakebase Token Federation)
|
|
345
362
|
sendOAuthState?: boolean;
|
|
346
363
|
usePkce?: boolean;
|
|
364
|
+
usePkceClientSecret?: boolean;
|
|
347
365
|
};
|
|
348
366
|
|
|
349
367
|
// Persisted configuration can hold the PKCE marker as a string, so the stored
|
|
350
368
|
// shape is wider than the public one that `extractPublic` normalizes it into.
|
|
351
|
-
type OAuthCodeConfig = Omit<PublicOAuthCodeConfig, 'usePkce'> & {
|
|
369
|
+
type OAuthCodeConfig = Omit<PublicOAuthCodeConfig, 'usePkce' | 'usePkceClientSecret'> & {
|
|
352
370
|
clientSecret?: string;
|
|
353
371
|
usePkce?: FakeBoolean;
|
|
372
|
+
usePkceClientSecret?: FakeBoolean;
|
|
354
373
|
};
|
|
355
374
|
|
|
356
375
|
export enum TokenScope {
|
|
@@ -444,7 +463,8 @@ export const extractPublic = (authType: AuthType | undefined, authConfig: AuthCo
|
|
|
444
463
|
tokenScope: normalizeTokenScope(authConfig?.tokenScope),
|
|
445
464
|
promptType: authConfig?.promptType,
|
|
446
465
|
sendOAuthState: authConfig?.sendOAuthState,
|
|
447
|
-
usePkce: isPkceEnabled(authConfig?.usePkce)
|
|
466
|
+
usePkce: isPkceEnabled(authConfig?.usePkce),
|
|
467
|
+
usePkceClientSecret: isPkceEnabled(authConfig?.usePkceClientSecret)
|
|
448
468
|
};
|
|
449
469
|
case IntegrationAuthType.OAUTH2_IMPLICIT:
|
|
450
470
|
return {
|
|
@@ -609,6 +629,7 @@ export type DatasourceAuthState = DatasourceOneTimeState & {
|
|
|
609
629
|
scope: string | undefined;
|
|
610
630
|
clientAuthMethod?: ClientAuthMethod;
|
|
611
631
|
usePkce?: boolean;
|
|
632
|
+
usePkceClientSecret?: boolean;
|
|
612
633
|
};
|
|
613
634
|
origin: string;
|
|
614
635
|
};
|
package/src/types/group/index.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { AssignmentDto } from '../rbac/index.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* This enum is used in DB. DO NOT CHANGE the existing values.
|
|
5
|
-
* indirectly referenced in the authorization.polar file, so references there
|
|
6
|
-
* need to be updated if this changes.
|
|
4
|
+
* This enum is used in DB. DO NOT CHANGE the existing values.
|
|
7
5
|
*/
|
|
8
6
|
export enum GroupType {
|
|
9
7
|
ALL_USERS = 0,
|
|
@@ -104,6 +104,14 @@ export class SupersetIntegrationDto extends IntegrationBaseDto {
|
|
|
104
104
|
created?: Date;
|
|
105
105
|
updated?: Date;
|
|
106
106
|
configurations: IntegrationConfigurationDto[];
|
|
107
|
+
/**
|
|
108
|
+
* Which data tags this integration is configured for, and whether it has a default
|
|
109
|
+
* configuration that covers any other tag. Reported separately from `configurations` because
|
|
110
|
+
* those bodies hold secrets and are withheld from callers who cannot edit the integration,
|
|
111
|
+
* while the coverage itself is not sensitive.
|
|
112
|
+
*/
|
|
113
|
+
configuredProfileKeys: string[];
|
|
114
|
+
hasDefaultConfiguration: boolean;
|
|
107
115
|
slug?: string;
|
|
108
116
|
ownerEmail?: string;
|
|
109
117
|
readOnly: boolean;
|
|
@@ -123,7 +131,9 @@ export class SupersetIntegrationDto extends IntegrationBaseDto {
|
|
|
123
131
|
kind,
|
|
124
132
|
organizationId,
|
|
125
133
|
configurations,
|
|
134
|
+
configuredProfileKeys,
|
|
126
135
|
demoIntegrationId,
|
|
136
|
+
hasDefaultConfiguration,
|
|
127
137
|
isUserConfigured,
|
|
128
138
|
ownerEmail,
|
|
129
139
|
slug,
|
|
@@ -145,6 +155,8 @@ export class SupersetIntegrationDto extends IntegrationBaseDto {
|
|
|
145
155
|
kind: IntegrationKind;
|
|
146
156
|
organizationId: string;
|
|
147
157
|
configurations: IntegrationConfigurationDto[];
|
|
158
|
+
configuredProfileKeys?: string[];
|
|
159
|
+
hasDefaultConfiguration?: boolean;
|
|
148
160
|
demoIntegrationId?: string;
|
|
149
161
|
isUserConfigured: boolean;
|
|
150
162
|
ownerEmail?: string;
|
|
@@ -168,6 +180,8 @@ export class SupersetIntegrationDto extends IntegrationBaseDto {
|
|
|
168
180
|
this.organizationId = organizationId;
|
|
169
181
|
this.applicationId = applicationId;
|
|
170
182
|
this.configurations = configurations;
|
|
183
|
+
this.configuredProfileKeys = configuredProfileKeys ?? [];
|
|
184
|
+
this.hasDefaultConfiguration = hasDefaultConfiguration ?? false;
|
|
171
185
|
this.demoIntegrationId = demoIntegrationId;
|
|
172
186
|
this.isUserConfigured = isUserConfigured;
|
|
173
187
|
this.managedBy = managedBy;
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { IntegrationAuthType } from '../datasource/index.js';
|
|
4
|
+
import {
|
|
5
|
+
FormComponentType,
|
|
6
|
+
assertPkcePrerequisites,
|
|
7
|
+
assertSelectedDropdownOptionsSupported,
|
|
8
|
+
getDisplayOnlyFieldNames,
|
|
9
|
+
isDropdownOptionSupported
|
|
10
|
+
} from './form.js';
|
|
4
11
|
import type { FormTemplate } from './form.js';
|
|
5
12
|
|
|
6
13
|
describe('getDisplayOnlyFieldNames', () => {
|
|
@@ -159,3 +166,169 @@ describe('getDisplayOnlyFieldNames', () => {
|
|
|
159
166
|
expect(result.has('path')).toBe(false);
|
|
160
167
|
});
|
|
161
168
|
});
|
|
169
|
+
|
|
170
|
+
describe('isDropdownOptionSupported', () => {
|
|
171
|
+
it('includes versioned options only within their supported range', () => {
|
|
172
|
+
const option = {
|
|
173
|
+
key: 'versioned',
|
|
174
|
+
value: 'versioned',
|
|
175
|
+
startVersion: '0.0.16',
|
|
176
|
+
endVersion: '0.0.20'
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
expect(isDropdownOptionSupported(option, '0.0.15')).toBe(false);
|
|
180
|
+
expect(isDropdownOptionSupported(option, '0.0.16')).toBe(true);
|
|
181
|
+
expect(isDropdownOptionSupported(option, '0.0.20')).toBe(true);
|
|
182
|
+
expect(isDropdownOptionSupported(option, '0.0.21')).toBe(false);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
describe('assertPkcePrerequisites', () => {
|
|
187
|
+
const authCode = { authType: IntegrationAuthType.OAUTH2_CODE };
|
|
188
|
+
|
|
189
|
+
it('rejects PKCE with the OAuth state parameter turned off', () => {
|
|
190
|
+
expect(() => assertPkcePrerequisites({ ...authCode, 'authConfig.usePkce': true, 'authConfig.sendOAuthState': false })).toThrow(
|
|
191
|
+
'PKCE requires the OAuth state parameter to be enabled.'
|
|
192
|
+
);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('rejects a stringified PKCE marker with the state parameter turned off', () => {
|
|
196
|
+
expect(() => assertPkcePrerequisites({ ...authCode, 'authConfig.usePkce': 'true' })).toThrow(
|
|
197
|
+
'PKCE requires the OAuth state parameter to be enabled.'
|
|
198
|
+
);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('rejects a stringified state parameter that stands for false', () => {
|
|
202
|
+
// Checkboxes persist booleans as strings in older configs. The string
|
|
203
|
+
// 'false' is truthy, so a plain truthiness check would let this through
|
|
204
|
+
// at save time only for the login flow to refuse it later.
|
|
205
|
+
expect(() => assertPkcePrerequisites({ ...authCode, 'authConfig.usePkce': true, 'authConfig.sendOAuthState': 'false' })).toThrow(
|
|
206
|
+
'PKCE requires the OAuth state parameter to be enabled.'
|
|
207
|
+
);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('accepts PKCE with the state parameter on', () => {
|
|
211
|
+
expect(() => assertPkcePrerequisites({ ...authCode, 'authConfig.usePkce': true, 'authConfig.sendOAuthState': true })).not.toThrow();
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('accepts a stringified state parameter that stands for true', () => {
|
|
215
|
+
expect(() => assertPkcePrerequisites({ ...authCode, 'authConfig.usePkce': true, 'authConfig.sendOAuthState': 'true' })).not.toThrow();
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('leaves ordinary OAuth alone', () => {
|
|
219
|
+
expect(() => assertPkcePrerequisites({ ...authCode, 'authConfig.sendOAuthState': false })).not.toThrow();
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('ignores a leftover PKCE marker on another auth type', () => {
|
|
223
|
+
// The guard runs on raw form values before dependency clearing, so a
|
|
224
|
+
// stale usePkce from an earlier authorization-code setup must not block
|
|
225
|
+
// saving an integration that switched to a different auth type.
|
|
226
|
+
expect(() =>
|
|
227
|
+
assertPkcePrerequisites({ authType: 'basic', 'authConfig.usePkce': true, 'authConfig.sendOAuthState': false })
|
|
228
|
+
).not.toThrow();
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
describe('assertSelectedDropdownOptionsSupported', () => {
|
|
233
|
+
const template: FormTemplate = {
|
|
234
|
+
sections: [
|
|
235
|
+
{
|
|
236
|
+
name: 'auth',
|
|
237
|
+
items: [
|
|
238
|
+
{
|
|
239
|
+
name: 'authType',
|
|
240
|
+
secondaryName: 'usePkce',
|
|
241
|
+
label: 'Authentication method',
|
|
242
|
+
startVersion: '0.0.1',
|
|
243
|
+
componentType: FormComponentType.DROPDOWN,
|
|
244
|
+
options: [
|
|
245
|
+
{ key: 'basic', value: 'basic' },
|
|
246
|
+
{ key: 'oauth', value: 'oauth' },
|
|
247
|
+
{ key: 'pkce', value: 'oauth', secondaryValue: true, startVersion: '0.0.16' }
|
|
248
|
+
]
|
|
249
|
+
}
|
|
250
|
+
]
|
|
251
|
+
}
|
|
252
|
+
]
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
it('rejects an unrecognized secondary value instead of skipping version validation', () => {
|
|
256
|
+
expect(() => assertSelectedDropdownOptionsSupported({ authType: 'oauth', usePkce: 'maybe' }, template, '0.0.15')).toThrow(
|
|
257
|
+
'The selected authentication method is invalid.'
|
|
258
|
+
);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it.each([
|
|
262
|
+
['undefined', undefined],
|
|
263
|
+
['null', null],
|
|
264
|
+
['false', false],
|
|
265
|
+
['empty string', ''],
|
|
266
|
+
['the string false', 'false'],
|
|
267
|
+
['zero', 0],
|
|
268
|
+
['the string zero', '0']
|
|
269
|
+
] as const)('accepts the plain option when the secondary marker is %s', (_label, usePkce) => {
|
|
270
|
+
expect(() => assertSelectedDropdownOptionsSupported({ authType: 'oauth', usePkce }, template, '0.0.15')).not.toThrow();
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// A marker that round-tripped through a form control comes back as a string
|
|
274
|
+
// or a number. The runtime reads it as PKCE on, so the form has to agree:
|
|
275
|
+
// it is the same selection, and the same version gate applies to it.
|
|
276
|
+
it.each([
|
|
277
|
+
['the string true', 'true'],
|
|
278
|
+
['one', 1],
|
|
279
|
+
['the string one', '1']
|
|
280
|
+
] as const)('gates the secondary option on version when the marker is %s', (_label, usePkce) => {
|
|
281
|
+
expect(() => assertSelectedDropdownOptionsSupported({ authType: 'oauth', usePkce }, template, '0.0.15')).toThrow(
|
|
282
|
+
'The selected authentication method requires a newer agent version.'
|
|
283
|
+
);
|
|
284
|
+
expect(() => assertSelectedDropdownOptionsSupported({ authType: 'oauth', usePkce }, template, '0.0.16')).not.toThrow();
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it('accepts the plain option when the secondary marker is absent', () => {
|
|
288
|
+
expect(() => assertSelectedDropdownOptionsSupported({ authType: 'oauth' }, template, '0.0.15')).not.toThrow();
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('ignores a stale secondary marker for a primary option that does not use it', () => {
|
|
292
|
+
expect(() => assertSelectedDropdownOptionsSupported({ authType: 'basic', usePkce: true }, template, '0.0.15')).not.toThrow();
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it('rejects the secondary option when the plugin version is too old', () => {
|
|
296
|
+
expect(() => assertSelectedDropdownOptionsSupported({ authType: 'oauth', usePkce: true }, template, '0.0.15')).toThrow(
|
|
297
|
+
'The selected authentication method requires a newer agent version.'
|
|
298
|
+
);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('rejects the secondary option when agent support is unreported', () => {
|
|
302
|
+
expect(() => assertSelectedDropdownOptionsSupported({ authType: 'oauth', usePkce: true }, template, '0.0.1')).toThrow(
|
|
303
|
+
'Cannot verify that the connected agent supports the selected authentication method.'
|
|
304
|
+
);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it('rejects a version-gated option when its minimum equals the unreported fallback', () => {
|
|
308
|
+
const fallbackGatedTemplate: FormTemplate = {
|
|
309
|
+
sections: [
|
|
310
|
+
{
|
|
311
|
+
name: 'auth',
|
|
312
|
+
items: [
|
|
313
|
+
{
|
|
314
|
+
name: 'authType',
|
|
315
|
+
secondaryName: 'usePkce',
|
|
316
|
+
label: 'Authentication method',
|
|
317
|
+
startVersion: '0.0.1',
|
|
318
|
+
componentType: FormComponentType.DROPDOWN,
|
|
319
|
+
options: [{ key: 'pkce', value: 'oauth', secondaryValue: true, startVersion: '0.0.1' }]
|
|
320
|
+
}
|
|
321
|
+
]
|
|
322
|
+
}
|
|
323
|
+
]
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
expect(() => assertSelectedDropdownOptionsSupported({ authType: 'oauth', usePkce: true }, fallbackGatedTemplate, '0.0.1')).toThrow(
|
|
327
|
+
'Cannot verify that the connected agent supports the selected authentication method.'
|
|
328
|
+
);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it('accepts the secondary option once the plugin version supports it', () => {
|
|
332
|
+
expect(() => assertSelectedDropdownOptionsSupported({ authType: 'oauth', usePkce: true }, template, '0.0.16')).not.toThrow();
|
|
333
|
+
});
|
|
334
|
+
});
|
package/src/types/plugin/form.ts
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { AuthorizationExtraParams, AuthorizationStateConfig, ClientAuthMethod } from '../../plugins/index.js';
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
asFakeBoolean,
|
|
4
|
+
AuthConfig,
|
|
5
|
+
AuthType,
|
|
6
|
+
DynamicWorkflowConfig,
|
|
7
|
+
IntegrationAuthType,
|
|
8
|
+
isPkceEnabled,
|
|
9
|
+
TokenScope
|
|
10
|
+
} from '../datasource/index.js';
|
|
11
|
+
import { AgentType, compareSemVer } from '../organization/index.js';
|
|
12
|
+
import { VERSION_INITIAL } from './index.js';
|
|
4
13
|
|
|
5
14
|
export interface FormTemplate {
|
|
6
15
|
sections: FormSection[];
|
|
@@ -372,6 +381,7 @@ type ConnectOAuthPayload = {
|
|
|
372
381
|
tokenScope: TokenScope;
|
|
373
382
|
clientAuthMethod?: ClientAuthMethod;
|
|
374
383
|
usePkce?: boolean;
|
|
384
|
+
usePkceClientSecret?: boolean;
|
|
375
385
|
};
|
|
376
386
|
dynamicWorkflowConfiguration?: DynamicWorkflowConfig;
|
|
377
387
|
};
|
|
@@ -465,6 +475,10 @@ export const getDisplayOnlyFieldNames = (template: FormTemplate): Set<string> =>
|
|
|
465
475
|
export interface DropdownOption {
|
|
466
476
|
key: string;
|
|
467
477
|
value: string;
|
|
478
|
+
disabled?: boolean;
|
|
479
|
+
startVersion?: string;
|
|
480
|
+
endVersion?: string;
|
|
481
|
+
secondaryValue?: InitialValue;
|
|
468
482
|
enumValue?: number;
|
|
469
483
|
displayName?: string;
|
|
470
484
|
subText?: string;
|
|
@@ -479,3 +493,87 @@ export interface DropdownOption {
|
|
|
479
493
|
prefixWidth?: number;
|
|
480
494
|
hasDivider?: boolean;
|
|
481
495
|
}
|
|
496
|
+
|
|
497
|
+
export const isDropdownOptionSupported = (option: DropdownOption, pluginVersion: string): boolean =>
|
|
498
|
+
(!option.startVersion || compareSemVer(option.startVersion, pluginVersion) <= 0) &&
|
|
499
|
+
(!option.endVersion || compareSemVer(option.endVersion, pluginVersion) >= 0);
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Options carry their marker as a real boolean, but the same marker can come
|
|
503
|
+
* back from persisted configuration as a string or a number. Both sides go
|
|
504
|
+
* through the coercion the runtime reads use (`isPkceEnabled`) so the form and
|
|
505
|
+
* the runtime cannot disagree about which option is selected. A value that
|
|
506
|
+
* stands for neither boolean is compared as-is, so an unrecognized marker
|
|
507
|
+
* still fails validation instead of matching the option that leaves the
|
|
508
|
+
* marker unset.
|
|
509
|
+
*/
|
|
510
|
+
const normalizeSecondaryValue = (value: unknown): unknown => asFakeBoolean(value) ?? value;
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Find the option a dropdown currently has selected. Options can share a value
|
|
514
|
+
* and differ only by the secondary marker they set (ordinary versus PKCE
|
|
515
|
+
* authorization code), so both have to be compared to tell them apart.
|
|
516
|
+
*/
|
|
517
|
+
export function findSelectedDropdownOption(
|
|
518
|
+
item: { name: string; options: DropdownOption[]; secondaryName?: string },
|
|
519
|
+
formValues: Record<string, unknown>
|
|
520
|
+
): DropdownOption | undefined {
|
|
521
|
+
const optionsForValue = item.options.filter((option) => option.value === formValues[item.name]);
|
|
522
|
+
const usesSecondaryValue = optionsForValue.some((option) => option.secondaryValue !== undefined);
|
|
523
|
+
|
|
524
|
+
return optionsForValue.find(
|
|
525
|
+
(option) =>
|
|
526
|
+
!item.secondaryName ||
|
|
527
|
+
!usesSecondaryValue ||
|
|
528
|
+
normalizeSecondaryValue(option.secondaryValue) === normalizeSecondaryValue(formValues[item.secondaryName])
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
export const PKCE_STATE_PARAMETER_REQUIRED_MESSAGE = 'PKCE requires the OAuth state parameter to be enabled.';
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* PKCE correlates its verifier through the OAuth state parameter, so a PKCE
|
|
536
|
+
* integration with state turned off cannot log in at all. The login modal
|
|
537
|
+
* refuses that combination with the same wording; refusing it at save means it
|
|
538
|
+
* never gets persisted and the user learns about it while they can still fix
|
|
539
|
+
* it.
|
|
540
|
+
*
|
|
541
|
+
* Runs on raw form values before dependency clearing, so the auth type has to
|
|
542
|
+
* be checked here: a leftover PKCE marker from an earlier authorization-code
|
|
543
|
+
* setup must not block saving under a different auth type. The state value is
|
|
544
|
+
* read through asFakeBoolean because older configs persist it as a string,
|
|
545
|
+
* where a plain truthiness check reads 'false' as on.
|
|
546
|
+
*/
|
|
547
|
+
export function assertPkcePrerequisites(formValues: Record<string, unknown>): void {
|
|
548
|
+
if (
|
|
549
|
+
formValues['authType'] === IntegrationAuthType.OAUTH2_CODE &&
|
|
550
|
+
isPkceEnabled(formValues['authConfig.usePkce']) &&
|
|
551
|
+
asFakeBoolean(formValues['authConfig.sendOAuthState']) !== true
|
|
552
|
+
) {
|
|
553
|
+
throw new Error(PKCE_STATE_PARAMETER_REQUIRED_MESSAGE);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
export function assertSelectedDropdownOptionsSupported(
|
|
558
|
+
formValues: Record<string, unknown>,
|
|
559
|
+
template: FormTemplate | undefined,
|
|
560
|
+
pluginVersion: string
|
|
561
|
+
): void {
|
|
562
|
+
for (const section of template?.sections ?? []) {
|
|
563
|
+
for (const sectionItem of section.items) {
|
|
564
|
+
for (const item of getRowItemsFromSectionItem(sectionItem)) {
|
|
565
|
+
if (!('options' in item)) continue;
|
|
566
|
+
const selectedOption = findSelectedDropdownOption(item, formValues);
|
|
567
|
+
if (!selectedOption && item.options.some((option) => option.value === formValues[item.name])) {
|
|
568
|
+
throw new Error('The selected authentication method is invalid.');
|
|
569
|
+
}
|
|
570
|
+
if (selectedOption?.startVersion && pluginVersion === VERSION_INITIAL) {
|
|
571
|
+
throw new Error('Cannot verify that the connected agent supports the selected authentication method.');
|
|
572
|
+
}
|
|
573
|
+
if (selectedOption && !isDropdownOptionSupported(selectedOption, pluginVersion)) {
|
|
574
|
+
throw new Error('The selected authentication method requires a newer agent version.');
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
@@ -14,6 +14,9 @@ export class Profile {
|
|
|
14
14
|
allowedInEditor?: boolean;
|
|
15
15
|
allowedInPreview?: boolean;
|
|
16
16
|
allowedInDeployed?: boolean;
|
|
17
|
+
defaultInEditor?: boolean;
|
|
18
|
+
defaultInPreview?: boolean;
|
|
19
|
+
defaultInDeployed?: boolean;
|
|
17
20
|
|
|
18
21
|
constructor({
|
|
19
22
|
id,
|
|
@@ -23,7 +26,10 @@ export class Profile {
|
|
|
23
26
|
type,
|
|
24
27
|
allowedInEditor,
|
|
25
28
|
allowedInPreview,
|
|
26
|
-
allowedInDeployed
|
|
29
|
+
allowedInDeployed,
|
|
30
|
+
defaultInEditor,
|
|
31
|
+
defaultInPreview,
|
|
32
|
+
defaultInDeployed
|
|
27
33
|
}: {
|
|
28
34
|
id: string;
|
|
29
35
|
key: string;
|
|
@@ -33,6 +39,9 @@ export class Profile {
|
|
|
33
39
|
allowedInEditor?: boolean;
|
|
34
40
|
allowedInPreview?: boolean;
|
|
35
41
|
allowedInDeployed?: boolean;
|
|
42
|
+
defaultInEditor?: boolean;
|
|
43
|
+
defaultInPreview?: boolean;
|
|
44
|
+
defaultInDeployed?: boolean;
|
|
36
45
|
}) {
|
|
37
46
|
this.id = id;
|
|
38
47
|
this.key = key;
|
|
@@ -42,6 +51,9 @@ export class Profile {
|
|
|
42
51
|
this.allowedInEditor = allowedInEditor;
|
|
43
52
|
this.allowedInPreview = allowedInPreview;
|
|
44
53
|
this.allowedInDeployed = allowedInDeployed;
|
|
54
|
+
this.defaultInEditor = defaultInEditor;
|
|
55
|
+
this.defaultInPreview = defaultInPreview;
|
|
56
|
+
this.defaultInDeployed = defaultInDeployed;
|
|
45
57
|
}
|
|
46
58
|
}
|
|
47
59
|
|
|
@@ -57,3 +69,9 @@ export type EntityProfileSettings = {
|
|
|
57
69
|
[ViewMode.DEPLOYED]: EntityModeProfileSettings;
|
|
58
70
|
};
|
|
59
71
|
};
|
|
72
|
+
|
|
73
|
+
export type ApplicationEnvironmentSettings = EntityProfileSettings & {
|
|
74
|
+
removedProfiles: Record<string, Array<{ id: string; key: string; displayName: string }>>;
|
|
75
|
+
needsAttention: Record<ViewMode, boolean>;
|
|
76
|
+
updated: number;
|
|
77
|
+
};
|
package/src/types/rbac/index.ts
CHANGED
|
@@ -180,6 +180,7 @@ export enum ActionTypeEnum {
|
|
|
180
180
|
DELETE = 'delete',
|
|
181
181
|
DEPLOY = 'deploy',
|
|
182
182
|
MANAGE = 'manage',
|
|
183
|
+
MANAGE_ENVIRONMENTS = 'manage_environments',
|
|
183
184
|
MANAGE_SCHEDULE = 'manage_schedule',
|
|
184
185
|
MANAGE_VISIBILITY = 'manage_visibility',
|
|
185
186
|
PREVIEW = 'preview',
|
|
@@ -262,6 +263,7 @@ export const ActionTypeByResourceType = {
|
|
|
262
263
|
CREATE: ActionTypeEnum.CREATE,
|
|
263
264
|
DELETE: ActionTypeEnum.DELETE,
|
|
264
265
|
DEPLOY: ActionTypeEnum.DEPLOY,
|
|
266
|
+
MANAGE_ENVIRONMENTS: ActionTypeEnum.MANAGE_ENVIRONMENTS,
|
|
265
267
|
MANAGE_VISIBILITY: ActionTypeEnum.MANAGE_VISIBILITY,
|
|
266
268
|
PREVIEW: ActionTypeEnum.PREVIEW,
|
|
267
269
|
SHARE: ActionTypeEnum.SHARE,
|
|
@@ -41,6 +41,7 @@ export * from './GetPublicApiResponseBody.js';
|
|
|
41
41
|
export * from './GetPublicOrganizationSummaryResponseBody.js';
|
|
42
42
|
export * from './IssueSuperblocksResponseBody.js';
|
|
43
43
|
export * from './GetAllApplicationsResponseBody.js';
|
|
44
|
+
export * from './GetRecentApplicationsResponseBody.js';
|
|
44
45
|
export * from './PatchApplicationResponseBody.js';
|
|
45
46
|
export * from './PatchHomePageItemsResponseBody.js';
|
|
46
47
|
export * from './ListApplicationBuildsResponseBody.js';
|