@oneuptime/common 11.0.3 → 11.0.5
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/Models/DatabaseModels/GlobalConfig.ts +19 -0
- package/Models/DatabaseModels/GlobalOidc.ts +351 -0
- package/Models/DatabaseModels/GlobalOidcProject.ts +265 -0
- package/Models/DatabaseModels/GlobalSso.ts +312 -0
- package/Models/DatabaseModels/GlobalSsoProject.ts +268 -0
- package/Models/DatabaseModels/Index.ts +8 -0
- package/Models/DatabaseModels/Project.ts +31 -0
- package/Models/DatabaseModels/StatusPage.ts +82 -0
- package/Server/API/StatusPageAPI.ts +2 -0
- package/Server/Infrastructure/GlobalCache.ts +11 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/{1781587937032-MigrationName.ts → 1781750000000-MigrationName.ts} +2 -2
- package/Server/Infrastructure/Postgres/SchemaMigrations/1782000000000-AddGlobalSsoAndOidc.ts +176 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1782100000000-AddStatusPageImageAltText.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1782200000000-AddRequireSsoForLoginToGlobalProviders.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1782300000000-MoveRequireSsoForLoginToGlobalConfig.ts +38 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1782310000000-MigrationName.ts +299 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1782400000000-RemoveIsTestedFromGlobalSsoAndOidc.ts +21 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +14 -2
- package/Server/Middleware/UserAuthorization.ts +113 -42
- package/Server/Services/CephClusterService.ts +22 -10
- package/Server/Services/CloudResourceService.ts +22 -10
- package/Server/Services/DockerHostService.ts +22 -10
- package/Server/Services/DockerSwarmClusterService.ts +22 -10
- package/Server/Services/GlobalConfigService.ts +50 -0
- package/Server/Services/GlobalOidcProjectService.ts +85 -0
- package/Server/Services/GlobalOidcService.ts +10 -0
- package/Server/Services/GlobalSsoProjectService.ts +85 -0
- package/Server/Services/GlobalSsoService.ts +10 -0
- package/Server/Services/HostService.ts +22 -10
- package/Server/Services/Index.ts +8 -0
- package/Server/Services/KubernetesClusterService.ts +22 -10
- package/Server/Services/PodmanHostService.ts +22 -10
- package/Server/Services/ProjectService.ts +44 -1
- package/Server/Services/ProxmoxClusterService.ts +22 -10
- package/Server/Services/RumApplicationService.ts +22 -10
- package/Server/Services/ServerlessFunctionService.ts +22 -10
- package/Server/Utils/Cookie.ts +39 -5
- package/Server/Utils/JsonWebToken.ts +7 -0
- package/Server/Utils/ValidateGlobalProviderProjectTeams.ts +119 -0
- package/Tests/Server/Infrastructure/GlobalCache.test.ts +43 -0
- package/Tests/Server/Middleware/UserAuthorization.test.ts +51 -13
- package/Tests/Server/Middleware/UserAuthorizationSSOProvider.test.ts +163 -0
- package/Tests/Server/Utils/CookieSSOToken.test.ts +130 -0
- package/Types/JsonWebTokenData.ts +3 -0
- package/Types/SSO/SsoProviderType.ts +8 -0
- package/UI/Components/Accordion/Accordion.tsx +5 -1
- package/UI/Components/CardSelect/CardSelect.tsx +6 -1
- package/UI/Components/CategoryCheckbox/Index.tsx +2 -1
- package/UI/Components/Charts/Area/AreaChart.tsx +4 -8
- package/UI/Components/Charts/Bar/BarChart.tsx +4 -8
- package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +4 -8
- package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +1 -1
- package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +1 -1
- package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +1 -1
- package/UI/Components/Charts/Line/LineChart.tsx +4 -8
- package/UI/Components/CodeEditor/CodeEditor.tsx +2 -0
- package/UI/Components/CollapsibleSection/CollapsibleSection.tsx +8 -1
- package/UI/Components/Dropdown/Dropdown.tsx +2 -0
- package/UI/Components/EditionLabel/EditionLabel.tsx +68 -16
- package/UI/Components/EntityDropdown/EntityDropdown.tsx +3 -0
- package/UI/Components/FilePicker/FilePicker.tsx +2 -0
- package/UI/Components/Forms/Fields/ColorPicker.tsx +2 -0
- package/UI/Components/Forms/Fields/FieldLabel.tsx +4 -0
- package/UI/Components/Forms/Fields/FormField.tsx +72 -15
- package/UI/Components/Forms/Fields/IconPicker.tsx +2 -0
- package/UI/Components/Forms/Validation.ts +107 -23
- package/UI/Components/Input/Input.tsx +4 -0
- package/UI/Components/Link/Link.tsx +23 -0
- package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
- package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +3 -0
- package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +63 -2
- package/UI/Components/Modal/Modal.tsx +4 -1
- package/UI/Components/Radio/Radio.tsx +2 -0
- package/UI/Components/RadioButtons/GroupRadioButtons.tsx +6 -1
- package/UI/Components/Tabs/Tabs.tsx +63 -0
- package/UI/Components/TextArea/TextArea.tsx +2 -0
- package/UI/Components/TimePicker/TimePicker.tsx +2 -0
- package/UI/Components/Toggle/Toggle.tsx +2 -1
- package/UI/Components/Tooltip/Tooltip.tsx +6 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +20 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalOidc.js +379 -0
- package/build/dist/Models/DatabaseModels/GlobalOidc.js.map +1 -0
- package/build/dist/Models/DatabaseModels/GlobalOidcProject.js +276 -0
- package/build/dist/Models/DatabaseModels/GlobalOidcProject.js.map +1 -0
- package/build/dist/Models/DatabaseModels/GlobalSso.js +341 -0
- package/build/dist/Models/DatabaseModels/GlobalSso.js.map +1 -0
- package/build/dist/Models/DatabaseModels/GlobalSsoProject.js +279 -0
- package/build/dist/Models/DatabaseModels/GlobalSsoProject.js.map +1 -0
- package/build/dist/Models/DatabaseModels/Index.js +8 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +32 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +84 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +2 -0
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/GlobalCache.js +13 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/{1781587937032-MigrationName.js → 1781750000000-MigrationName.js} +3 -3
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/{1781587937032-MigrationName.js.map → 1781750000000-MigrationName.js.map} +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1782000000000-AddGlobalSsoAndOidc.js +73 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1782000000000-AddGlobalSsoAndOidc.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1782100000000-AddStatusPageImageAltText.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1782100000000-AddStatusPageImageAltText.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1782200000000-AddRequireSsoForLoginToGlobalProviders.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1782200000000-AddRequireSsoForLoginToGlobalProviders.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1782300000000-MoveRequireSsoForLoginToGlobalConfig.js +23 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1782300000000-MoveRequireSsoForLoginToGlobalConfig.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1782310000000-MigrationName.js +106 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1782310000000-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1782400000000-RemoveIsTestedFromGlobalSsoAndOidc.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1782400000000-RemoveIsTestedFromGlobalSsoAndOidc.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +14 -2
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/UserAuthorization.js +77 -34
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/CephClusterService.js +19 -2
- package/build/dist/Server/Services/CephClusterService.js.map +1 -1
- package/build/dist/Server/Services/CloudResourceService.js +19 -2
- package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostService.js +19 -2
- package/build/dist/Server/Services/DockerHostService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterService.js +19 -2
- package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
- package/build/dist/Server/Services/GlobalConfigService.js +55 -0
- package/build/dist/Server/Services/GlobalConfigService.js.map +1 -1
- package/build/dist/Server/Services/GlobalOidcProjectService.js +80 -0
- package/build/dist/Server/Services/GlobalOidcProjectService.js.map +1 -0
- package/build/dist/Server/Services/GlobalOidcService.js +9 -0
- package/build/dist/Server/Services/GlobalOidcService.js.map +1 -0
- package/build/dist/Server/Services/GlobalSsoProjectService.js +80 -0
- package/build/dist/Server/Services/GlobalSsoProjectService.js.map +1 -0
- package/build/dist/Server/Services/GlobalSsoService.js +9 -0
- package/build/dist/Server/Services/GlobalSsoService.js.map +1 -0
- package/build/dist/Server/Services/HostService.js +19 -2
- package/build/dist/Server/Services/HostService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +8 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterService.js +19 -2
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
- package/build/dist/Server/Services/PodmanHostService.js +19 -2
- package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +36 -1
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxClusterService.js +19 -2
- package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
- package/build/dist/Server/Services/RumApplicationService.js +19 -2
- package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
- package/build/dist/Server/Services/ServerlessFunctionService.js +19 -2
- package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
- package/build/dist/Server/Utils/Cookie.js +32 -3
- package/build/dist/Server/Utils/Cookie.js.map +1 -1
- package/build/dist/Server/Utils/JsonWebToken.js +6 -0
- package/build/dist/Server/Utils/JsonWebToken.js.map +1 -1
- package/build/dist/Server/Utils/ValidateGlobalProviderProjectTeams.js +66 -0
- package/build/dist/Server/Utils/ValidateGlobalProviderProjectTeams.js.map +1 -0
- package/build/dist/Types/SSO/SsoProviderType.js +9 -0
- package/build/dist/Types/SSO/SsoProviderType.js.map +1 -0
- package/build/dist/UI/Components/Accordion/Accordion.js +5 -3
- package/build/dist/UI/Components/Accordion/Accordion.js.map +1 -1
- package/build/dist/UI/Components/CardSelect/CardSelect.js +1 -1
- package/build/dist/UI/Components/CardSelect/CardSelect.js.map +1 -1
- package/build/dist/UI/Components/CategoryCheckbox/Index.js +1 -1
- package/build/dist/UI/Components/CategoryCheckbox/Index.js.map +1 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js +2 -6
- package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js +2 -6
- package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +8 -8
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js +2 -6
- package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js +1 -1
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
- package/build/dist/UI/Components/CollapsibleSection/CollapsibleSection.js +4 -2
- package/build/dist/UI/Components/CollapsibleSection/CollapsibleSection.js.map +1 -1
- package/build/dist/UI/Components/Dropdown/Dropdown.js +1 -1
- package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +31 -5
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +2 -2
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
- package/build/dist/UI/Components/FilePicker/FilePicker.js +1 -1
- package/build/dist/UI/Components/FilePicker/FilePicker.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/ColorPicker.js +1 -1
- package/build/dist/UI/Components/Forms/Fields/ColorPicker.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FieldLabel.js +1 -1
- package/build/dist/UI/Components/Forms/Fields/FieldLabel.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +58 -22
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/IconPicker.js +1 -1
- package/build/dist/UI/Components/Forms/Fields/IconPicker.js.map +1 -1
- package/build/dist/UI/Components/Forms/Validation.js +64 -15
- package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +1 -1
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/Link/Link.js +22 -1
- package/build/dist/UI/Components/Link/Link.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +2 -2
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +46 -2
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/Modal/Modal.js +1 -1
- package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
- package/build/dist/UI/Components/Radio/Radio.js +1 -1
- package/build/dist/UI/Components/Radio/Radio.js.map +1 -1
- package/build/dist/UI/Components/RadioButtons/GroupRadioButtons.js +1 -1
- package/build/dist/UI/Components/RadioButtons/GroupRadioButtons.js.map +1 -1
- package/build/dist/UI/Components/Tabs/Tabs.js +50 -1
- package/build/dist/UI/Components/Tabs/Tabs.js.map +1 -1
- package/build/dist/UI/Components/TextArea/TextArea.js +1 -1
- package/build/dist/UI/Components/TextArea/TextArea.js.map +1 -1
- package/build/dist/UI/Components/TimePicker/TimePicker.js +1 -1
- package/build/dist/UI/Components/TimePicker/TimePicker.js.map +1 -1
- package/build/dist/UI/Components/Toggle/Toggle.js +1 -1
- package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js +6 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import CookieUtil from "../../../Server/Utils/Cookie";
|
|
2
|
+
import JSONWebToken from "../../../Server/Utils/JsonWebToken";
|
|
3
|
+
import { JSONObject } from "../../../Types/JSON";
|
|
4
|
+
import JSONWebTokenData from "../../../Types/JsonWebTokenData";
|
|
5
|
+
import Email from "../../../Types/Email";
|
|
6
|
+
import Name from "../../../Types/Name";
|
|
7
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
8
|
+
import SsoProviderType from "../../../Types/SSO/SsoProviderType";
|
|
9
|
+
import User from "../../../Models/DatabaseModels/User";
|
|
10
|
+
import { describe, expect, test } from "@jest/globals";
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
* Tests for the SSO-provider discriminator that CookieUtil.getSSOToken stamps
|
|
14
|
+
* onto a per-project SSO token. These run real JWT signing (no mocks): the JWT
|
|
15
|
+
* secret falls back to EncryptionSecret = "secret" when ENCRYPTION_SECRET is
|
|
16
|
+
* unset (see Common/Server/EnvironmentConfig.ts), so no env setup is required.
|
|
17
|
+
*/
|
|
18
|
+
describe("CookieUtil.getSSOToken - SSO provider discriminator", () => {
|
|
19
|
+
const buildUser: () => User = (): User => {
|
|
20
|
+
const user: User = new User();
|
|
21
|
+
user.id = ObjectID.generate();
|
|
22
|
+
user.name = new Name("Test User");
|
|
23
|
+
user.email = new Email("test@oneuptime.com");
|
|
24
|
+
return user;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
test("token carries userId and projectId (round-trips through JSONWebToken.decode)", () => {
|
|
28
|
+
const user: User = buildUser();
|
|
29
|
+
const projectId: ObjectID = ObjectID.generate();
|
|
30
|
+
|
|
31
|
+
const token: string = CookieUtil.getSSOToken({
|
|
32
|
+
user,
|
|
33
|
+
projectId,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
expect(typeof token).toBe("string");
|
|
37
|
+
expect(token.length).toBeGreaterThan(0);
|
|
38
|
+
|
|
39
|
+
const decoded: JSONWebTokenData = JSONWebToken.decode(token);
|
|
40
|
+
|
|
41
|
+
expect(decoded.userId?.toString()).toBe(user.id!.toString());
|
|
42
|
+
expect(decoded.projectId?.toString()).toBe(projectId.toString());
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("token carries the ssoProviderId + ssoProviderType discriminator when provided", () => {
|
|
46
|
+
const user: User = buildUser();
|
|
47
|
+
const projectId: ObjectID = ObjectID.generate();
|
|
48
|
+
const ssoProviderId: ObjectID = ObjectID.generate();
|
|
49
|
+
|
|
50
|
+
const token: string = CookieUtil.getSSOToken({
|
|
51
|
+
user,
|
|
52
|
+
projectId,
|
|
53
|
+
ssoProviderId,
|
|
54
|
+
ssoProviderType: SsoProviderType.GlobalSSO,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
/*
|
|
58
|
+
* The discriminator lives on the raw JWT payload. JSONWebToken.decode()
|
|
59
|
+
* intentionally re-shapes the payload into JSONWebTokenData and does not
|
|
60
|
+
* surface ssoProviderId / ssoProviderType, so we assert the round-trip on
|
|
61
|
+
* the raw payload (decodeJsonPayload), which is the actual transport layer.
|
|
62
|
+
*/
|
|
63
|
+
const payload: JSONObject = JSONWebToken.decodeJsonPayload(token);
|
|
64
|
+
|
|
65
|
+
expect(payload["userId"]).toBe(user.id!.toString());
|
|
66
|
+
expect(payload["projectId"]).toBe(projectId.toString());
|
|
67
|
+
expect(payload["ssoProviderId"]).toBe(ssoProviderId.toString());
|
|
68
|
+
expect(payload["ssoProviderType"]).toBe(SsoProviderType.GlobalSSO);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("each SsoProviderType enum value round-trips on the token", () => {
|
|
72
|
+
const user: User = buildUser();
|
|
73
|
+
const projectId: ObjectID = ObjectID.generate();
|
|
74
|
+
const ssoProviderId: ObjectID = ObjectID.generate();
|
|
75
|
+
|
|
76
|
+
const providerTypes: Array<SsoProviderType> = [
|
|
77
|
+
SsoProviderType.ProjectSSO,
|
|
78
|
+
SsoProviderType.ProjectOIDC,
|
|
79
|
+
SsoProviderType.GlobalSSO,
|
|
80
|
+
SsoProviderType.GlobalOIDC,
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
for (const providerType of providerTypes) {
|
|
84
|
+
const token: string = CookieUtil.getSSOToken({
|
|
85
|
+
user,
|
|
86
|
+
projectId,
|
|
87
|
+
ssoProviderId,
|
|
88
|
+
ssoProviderType: providerType,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const payload: JSONObject = JSONWebToken.decodeJsonPayload(token);
|
|
92
|
+
|
|
93
|
+
expect(payload["ssoProviderId"]).toBe(ssoProviderId.toString());
|
|
94
|
+
expect(payload["ssoProviderType"]).toBe(providerType);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("ssoProviderId / ssoProviderType are absent (undefined) when not provided (legacy token shape)", () => {
|
|
99
|
+
const user: User = buildUser();
|
|
100
|
+
const projectId: ObjectID = ObjectID.generate();
|
|
101
|
+
|
|
102
|
+
const token: string = CookieUtil.getSSOToken({
|
|
103
|
+
user,
|
|
104
|
+
projectId,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const payload: JSONObject = JSONWebToken.decodeJsonPayload(token);
|
|
108
|
+
|
|
109
|
+
// No discriminator was provided, so neither field should carry a value.
|
|
110
|
+
expect(payload["ssoProviderId"]).toBeUndefined();
|
|
111
|
+
expect(payload["ssoProviderType"]).toBeUndefined();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("ssoProviderId without ssoProviderType still records the provider id", () => {
|
|
115
|
+
const user: User = buildUser();
|
|
116
|
+
const projectId: ObjectID = ObjectID.generate();
|
|
117
|
+
const ssoProviderId: ObjectID = ObjectID.generate();
|
|
118
|
+
|
|
119
|
+
const token: string = CookieUtil.getSSOToken({
|
|
120
|
+
user,
|
|
121
|
+
projectId,
|
|
122
|
+
ssoProviderId,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const payload: JSONObject = JSONWebToken.decodeJsonPayload(token);
|
|
126
|
+
|
|
127
|
+
expect(payload["ssoProviderId"]).toBe(ssoProviderId.toString());
|
|
128
|
+
expect(payload["ssoProviderType"]).toBeUndefined();
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -2,6 +2,7 @@ import Email from "./Email";
|
|
|
2
2
|
import { JSONObject } from "./JSON";
|
|
3
3
|
import Name from "./Name";
|
|
4
4
|
import ObjectID from "./ObjectID";
|
|
5
|
+
import SsoProviderType from "./SSO/SsoProviderType";
|
|
5
6
|
|
|
6
7
|
export default interface JSONWebTokenData extends JSONObject {
|
|
7
8
|
userId: ObjectID;
|
|
@@ -12,4 +13,6 @@ export default interface JSONWebTokenData extends JSONObject {
|
|
|
12
13
|
projectId?: ObjectID | undefined; // for SSO logins.
|
|
13
14
|
isGlobalLogin: boolean; // If this is OneUptime username and password login. This is true, if this is SSO login. Then, this is false.
|
|
14
15
|
sessionId?: ObjectID | undefined;
|
|
16
|
+
ssoProviderId?: ObjectID | undefined; // which SSO provider (Project or Global SSO/OIDC) issued this per-project SSO token.
|
|
17
|
+
ssoProviderType?: SsoProviderType | undefined;
|
|
15
18
|
}
|
|
@@ -60,7 +60,9 @@ const Accordion: FunctionComponent<ComponentProps> = (
|
|
|
60
60
|
className = "-ml-5 -mr-5 p-5 mt-1";
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
const
|
|
63
|
+
const generatedId: string = React.useId();
|
|
64
|
+
const accordionId: string = `accordion-content-${generatedId}`;
|
|
65
|
+
const accordionTitleId: string = `accordion-title-${generatedId}`;
|
|
64
66
|
|
|
65
67
|
const handleKeyDown: (event: React.KeyboardEvent) => void = (
|
|
66
68
|
event: React.KeyboardEvent,
|
|
@@ -84,6 +86,7 @@ const Accordion: FunctionComponent<ComponentProps> = (
|
|
|
84
86
|
tabIndex={0}
|
|
85
87
|
aria-expanded={isOpen}
|
|
86
88
|
aria-controls={accordionId}
|
|
89
|
+
aria-labelledby={props.title ? accordionTitleId : undefined}
|
|
87
90
|
onClick={() => {
|
|
88
91
|
setIsOpen(!isOpen);
|
|
89
92
|
}}
|
|
@@ -121,6 +124,7 @@ const Accordion: FunctionComponent<ComponentProps> = (
|
|
|
121
124
|
}`}
|
|
122
125
|
>
|
|
123
126
|
<div
|
|
127
|
+
id={accordionTitleId}
|
|
124
128
|
className={`text-gray-900 leading-snug ${props.titleClassName || ""}`}
|
|
125
129
|
>
|
|
126
130
|
{props.title}
|
|
@@ -30,6 +30,7 @@ export interface ComponentProps {
|
|
|
30
30
|
error?: string | undefined;
|
|
31
31
|
tabIndex?: number | undefined;
|
|
32
32
|
dataTestId?: string | undefined;
|
|
33
|
+
ariaLabelledby?: string | undefined;
|
|
33
34
|
// Force single-column (1 item per row). Default: responsive 1/2/3 grid.
|
|
34
35
|
singleColumn?: boolean | undefined;
|
|
35
36
|
}
|
|
@@ -67,7 +68,11 @@ const CardSelect: FunctionComponent<ComponentProps> = (
|
|
|
67
68
|
|
|
68
69
|
return (
|
|
69
70
|
<div data-testid={props.dataTestId}>
|
|
70
|
-
<div
|
|
71
|
+
<div
|
|
72
|
+
role="radiogroup"
|
|
73
|
+
aria-label="Select an option"
|
|
74
|
+
aria-labelledby={props.ariaLabelledby}
|
|
75
|
+
>
|
|
71
76
|
{groups.map((group: RenderGroup, groupIndex: number) => {
|
|
72
77
|
return (
|
|
73
78
|
<div key={groupIndex} className={groupIndex > 0 ? "mt-8" : ""}>
|
|
@@ -19,6 +19,7 @@ export interface CategoryCheckboxProps
|
|
|
19
19
|
initialValue?: undefined | Array<CategoryCheckboxValue | BaseModel>;
|
|
20
20
|
error?: string | undefined;
|
|
21
21
|
dataTestId?: string | undefined;
|
|
22
|
+
ariaLabelledby?: string | undefined;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
const CategoryCheckbox: FunctionComponent<CategoryCheckboxProps> = (
|
|
@@ -152,7 +153,7 @@ const CategoryCheckbox: FunctionComponent<CategoryCheckboxProps> = (
|
|
|
152
153
|
};
|
|
153
154
|
|
|
154
155
|
return (
|
|
155
|
-
<div>
|
|
156
|
+
<div role="group" aria-labelledby={props.ariaLabelledby}>
|
|
156
157
|
{getCategory(undefined, categories.length === 0)}
|
|
157
158
|
{categories.map((category: CheckboxCategory, i: number) => {
|
|
158
159
|
return (
|
|
@@ -108,11 +108,6 @@ const AreaChartElement: FunctionComponent<AreaInternalProps> = (
|
|
|
108
108
|
});
|
|
109
109
|
}, [props.exemplarPoints, props.xAxis]);
|
|
110
110
|
|
|
111
|
-
const className: string = props.heightInPx ? `` : "h-80";
|
|
112
|
-
const style: React.CSSProperties = props.heightInPx
|
|
113
|
-
? { height: `${props.heightInPx}px` }
|
|
114
|
-
: {};
|
|
115
|
-
|
|
116
111
|
const hasNoData: boolean =
|
|
117
112
|
!props.data ||
|
|
118
113
|
props.data.length === 0 ||
|
|
@@ -134,10 +129,11 @@ const AreaChartElement: FunctionComponent<AreaInternalProps> = (
|
|
|
134
129
|
typeof yAxisMaxOption === "number" ? { maxValue: yAxisMaxOption } : {};
|
|
135
130
|
|
|
136
131
|
return (
|
|
137
|
-
<div
|
|
132
|
+
<div
|
|
133
|
+
className="relative flex flex-1"
|
|
134
|
+
style={props.heightInPx ? { height: `${props.heightInPx}px` } : undefined}
|
|
135
|
+
>
|
|
138
136
|
<AreaChart
|
|
139
|
-
className={className}
|
|
140
|
-
style={style}
|
|
141
137
|
data={records}
|
|
142
138
|
tickGap={30}
|
|
143
139
|
index={"Time"}
|
|
@@ -54,11 +54,6 @@ const BarChartElement: FunctionComponent<BarInternalProps> = (
|
|
|
54
54
|
setRecords(records);
|
|
55
55
|
}, [props.data]);
|
|
56
56
|
|
|
57
|
-
const className: string = props.heightInPx ? `` : "h-80";
|
|
58
|
-
const style: React.CSSProperties = props.heightInPx
|
|
59
|
-
? { height: `${props.heightInPx}px` }
|
|
60
|
-
: {};
|
|
61
|
-
|
|
62
57
|
const hasNoData: boolean =
|
|
63
58
|
!props.data ||
|
|
64
59
|
props.data.length === 0 ||
|
|
@@ -67,10 +62,11 @@ const BarChartElement: FunctionComponent<BarInternalProps> = (
|
|
|
67
62
|
});
|
|
68
63
|
|
|
69
64
|
return (
|
|
70
|
-
<div
|
|
65
|
+
<div
|
|
66
|
+
className="relative flex flex-1"
|
|
67
|
+
style={props.heightInPx ? { height: `${props.heightInPx}px` } : undefined}
|
|
68
|
+
>
|
|
71
69
|
<BarChart
|
|
72
|
-
className={className}
|
|
73
|
-
style={style}
|
|
74
70
|
data={records}
|
|
75
71
|
tickGap={30}
|
|
76
72
|
index={"Time"}
|
|
@@ -49,7 +49,6 @@ export interface Chart {
|
|
|
49
49
|
export interface ComponentProps {
|
|
50
50
|
charts: Array<Chart>;
|
|
51
51
|
hideCard?: boolean | undefined;
|
|
52
|
-
heightInPx?: number | undefined;
|
|
53
52
|
chartCssClass?: string | undefined;
|
|
54
53
|
}
|
|
55
54
|
|
|
@@ -80,7 +79,6 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
|
|
|
80
79
|
key={index}
|
|
81
80
|
{...(chart.props as LineChartProps)}
|
|
82
81
|
syncid={syncId}
|
|
83
|
-
heightInPx={props.heightInPx}
|
|
84
82
|
exemplarPoints={chart.exemplarPoints}
|
|
85
83
|
onExemplarClick={chart.onExemplarClick}
|
|
86
84
|
showLegend={showLegend}
|
|
@@ -92,7 +90,6 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
|
|
|
92
90
|
key={index}
|
|
93
91
|
{...(chart.props as BarChartProps)}
|
|
94
92
|
syncid={syncId}
|
|
95
|
-
heightInPx={props.heightInPx}
|
|
96
93
|
showLegend={showLegend}
|
|
97
94
|
/>
|
|
98
95
|
);
|
|
@@ -102,7 +99,6 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
|
|
|
102
99
|
key={index}
|
|
103
100
|
{...(chart.props as AreaChartProps)}
|
|
104
101
|
syncid={syncId}
|
|
105
|
-
heightInPx={props.heightInPx}
|
|
106
102
|
exemplarPoints={chart.exemplarPoints}
|
|
107
103
|
onExemplarClick={chart.onExemplarClick}
|
|
108
104
|
showLegend={showLegend}
|
|
@@ -244,14 +240,14 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
|
|
|
244
240
|
return (
|
|
245
241
|
<>
|
|
246
242
|
{renderMetricInfoModal()}
|
|
247
|
-
<div className="space-y-3">
|
|
243
|
+
<div className="space-y-3 flex-col flex flex-1 w-full">
|
|
248
244
|
{props.charts.map((chart: Chart, index: number) => {
|
|
249
245
|
return (
|
|
250
246
|
<div
|
|
251
247
|
key={index}
|
|
252
|
-
className={`bg-white ${props.chartCssClass || ""}`}
|
|
248
|
+
className={`bg-white ${props.chartCssClass || ""} flex-1 flex-col flex w-full`}
|
|
253
249
|
>
|
|
254
|
-
<div className="px-5 pt-4 pb-4">
|
|
250
|
+
<div className="px-5 pt-4 pb-4 flex flex-col flex-1">
|
|
255
251
|
<div className="mb-3 pb-3 border-b border-gray-100">
|
|
256
252
|
<div className="flex items-center">
|
|
257
253
|
<h3 className="text-sm font-semibold text-gray-800 tracking-tight">
|
|
@@ -265,10 +261,10 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
|
|
|
265
261
|
</p>
|
|
266
262
|
)}
|
|
267
263
|
</div>
|
|
264
|
+
{getChartContent(chart, index)}
|
|
268
265
|
{chart.seriesControls ? (
|
|
269
266
|
<div className="mb-3">{chart.seriesControls}</div>
|
|
270
267
|
) : null}
|
|
271
|
-
{getChartContent(chart, index)}
|
|
272
268
|
</div>
|
|
273
269
|
</div>
|
|
274
270
|
);
|
|
@@ -689,7 +689,7 @@ const AreaChart: React.ForwardRefExoticComponent<
|
|
|
689
689
|
}
|
|
690
690
|
|
|
691
691
|
return (
|
|
692
|
-
<div ref={ref} className={cx("
|
|
692
|
+
<div ref={ref} className={cx("flex-1 w-full", className)} {...other}>
|
|
693
693
|
<ResponsiveContainer>
|
|
694
694
|
<RechartsAreaChart
|
|
695
695
|
data={data}
|
|
@@ -695,7 +695,7 @@ const LineChart: React.ForwardRefExoticComponent<
|
|
|
695
695
|
}
|
|
696
696
|
|
|
697
697
|
return (
|
|
698
|
-
<div ref={ref} className={cx("
|
|
698
|
+
<div ref={ref} className={cx("flex-1 w-full", className)} {...other}>
|
|
699
699
|
<ResponsiveContainer>
|
|
700
700
|
<RechartsLineChart
|
|
701
701
|
data={data}
|
|
@@ -86,11 +86,6 @@ const LineChartElement: FunctionComponent<LineInternalProps> = (
|
|
|
86
86
|
});
|
|
87
87
|
}, [props.exemplarPoints, props.xAxis]);
|
|
88
88
|
|
|
89
|
-
const className: string = props.heightInPx ? `` : "h-80";
|
|
90
|
-
const style: React.CSSProperties = props.heightInPx
|
|
91
|
-
? { height: `${props.heightInPx}px` }
|
|
92
|
-
: {};
|
|
93
|
-
|
|
94
89
|
const hasNoData: boolean =
|
|
95
90
|
!props.data ||
|
|
96
91
|
props.data.length === 0 ||
|
|
@@ -114,10 +109,11 @@ const LineChartElement: FunctionComponent<LineInternalProps> = (
|
|
|
114
109
|
typeof yAxisMaxOption === "number" ? { maxValue: yAxisMaxOption } : {};
|
|
115
110
|
|
|
116
111
|
return (
|
|
117
|
-
<div
|
|
112
|
+
<div
|
|
113
|
+
className="relative flex flex-1"
|
|
114
|
+
style={props.heightInPx ? { height: `${props.heightInPx}px` } : undefined}
|
|
115
|
+
>
|
|
118
116
|
<LineChart
|
|
119
|
-
className={className}
|
|
120
|
-
style={style}
|
|
121
117
|
data={records}
|
|
122
118
|
tickGap={30}
|
|
123
119
|
index={"Time"}
|
|
@@ -25,6 +25,7 @@ export interface ComponentProps {
|
|
|
25
25
|
value?: string | undefined;
|
|
26
26
|
showLineNumbers?: boolean | undefined;
|
|
27
27
|
disableSpellCheck?: boolean | undefined;
|
|
28
|
+
ariaLabelledby?: string | undefined;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
const CodeEditor: FunctionComponent<ComponentProps> = (
|
|
@@ -119,6 +120,7 @@ const CodeEditor: FunctionComponent<ComponentProps> = (
|
|
|
119
120
|
return (
|
|
120
121
|
<div
|
|
121
122
|
data-testid={props.dataTestId}
|
|
123
|
+
aria-labelledby={props.ariaLabelledby}
|
|
122
124
|
onClick={() => {
|
|
123
125
|
if (props.onClick) {
|
|
124
126
|
props.onClick();
|
|
@@ -46,6 +46,9 @@ const CollapsibleSection: FunctionComponent<ComponentProps> = (
|
|
|
46
46
|
|
|
47
47
|
const variant: CollapsibleSectionVariant = props.variant || "default";
|
|
48
48
|
|
|
49
|
+
// Associate the role="button" header with its visible title text (WCAG 4.1.2).
|
|
50
|
+
const collapsibleTitleId: string = `collapsible-title-${React.useId()}`;
|
|
51
|
+
|
|
49
52
|
const getContainerClassName: () => string = (): string => {
|
|
50
53
|
const baseClassName: string = props.className || "";
|
|
51
54
|
|
|
@@ -97,6 +100,7 @@ const CollapsibleSection: FunctionComponent<ComponentProps> = (
|
|
|
97
100
|
}
|
|
98
101
|
}}
|
|
99
102
|
aria-expanded={!isCollapsed}
|
|
103
|
+
aria-labelledby={collapsibleTitleId}
|
|
100
104
|
>
|
|
101
105
|
<div className="flex items-center flex-1 min-w-0">
|
|
102
106
|
<Icon
|
|
@@ -105,7 +109,10 @@ const CollapsibleSection: FunctionComponent<ComponentProps> = (
|
|
|
105
109
|
/>
|
|
106
110
|
<div className="flex-1 min-w-0">
|
|
107
111
|
<div className="flex items-center">
|
|
108
|
-
<span
|
|
112
|
+
<span
|
|
113
|
+
id={collapsibleTitleId}
|
|
114
|
+
className="text-sm font-medium text-gray-900 truncate"
|
|
115
|
+
>
|
|
109
116
|
{props.title}
|
|
110
117
|
</span>
|
|
111
118
|
{isCollapsed && props.badge && (
|
|
@@ -59,6 +59,7 @@ export interface ComponentProps {
|
|
|
59
59
|
id?: string | undefined;
|
|
60
60
|
dataTestId?: string | undefined;
|
|
61
61
|
ariaLabel?: string | undefined;
|
|
62
|
+
ariaLabelledby?: string | undefined;
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
const Dropdown: FunctionComponent<ComponentProps> = (
|
|
@@ -537,6 +538,7 @@ const Dropdown: FunctionComponent<ComponentProps> = (
|
|
|
537
538
|
props.onFocus?.();
|
|
538
539
|
}}
|
|
539
540
|
aria-label={props.ariaLabel}
|
|
541
|
+
aria-labelledby={props.ariaLabelledby}
|
|
540
542
|
aria-invalid={props.error ? true : undefined}
|
|
541
543
|
aria-describedby={props.error ? errorId : undefined}
|
|
542
544
|
classNames={{
|
|
@@ -45,6 +45,7 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
45
45
|
const [validationError, setValidationError] = useState<string>("");
|
|
46
46
|
const [successMessage, setSuccessMessage] = useState<string>("");
|
|
47
47
|
const [isValidating, setIsValidating] = useState<boolean>(false);
|
|
48
|
+
const [isChangingLicense, setIsChangingLicense] = useState<boolean>(false);
|
|
48
49
|
const licenseInputEditedRef: React.MutableRefObject<boolean> =
|
|
49
50
|
useRef<boolean>(false);
|
|
50
51
|
|
|
@@ -318,6 +319,22 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
318
319
|
setIsDialogOpen(false);
|
|
319
320
|
setValidationError("");
|
|
320
321
|
setSuccessMessage("");
|
|
322
|
+
setIsChangingLicense(false);
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
const handleStartChangingLicense: () => void = () => {
|
|
326
|
+
setIsChangingLicense(true);
|
|
327
|
+
setValidationError("");
|
|
328
|
+
setSuccessMessage("");
|
|
329
|
+
setLicenseKeyInput("");
|
|
330
|
+
licenseInputEditedRef.current = true;
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
const handleCancelChangingLicense: () => void = () => {
|
|
334
|
+
setIsChangingLicense(false);
|
|
335
|
+
setValidationError("");
|
|
336
|
+
licenseInputEditedRef.current = false;
|
|
337
|
+
setLicenseKeyInput(globalConfig?.enterpriseLicenseKey || "");
|
|
321
338
|
};
|
|
322
339
|
|
|
323
340
|
const handlePrimaryAction: () => void = () => {
|
|
@@ -371,6 +388,7 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
371
388
|
licenseInputEditedRef.current = false;
|
|
372
389
|
setLicenseKeyInput((payload["licenseKey"] as string) || trimmedKey);
|
|
373
390
|
setSuccessMessage("License validated successfully.");
|
|
391
|
+
setIsChangingLicense(false);
|
|
374
392
|
|
|
375
393
|
await fetchGlobalConfig();
|
|
376
394
|
} catch (err) {
|
|
@@ -396,8 +414,10 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
396
414
|
}
|
|
397
415
|
};
|
|
398
416
|
|
|
399
|
-
const
|
|
400
|
-
IS_ENTERPRISE_EDITION && !licenseValid;
|
|
417
|
+
const showLicenseKeyInput: boolean =
|
|
418
|
+
IS_ENTERPRISE_EDITION && (!licenseValid || isChangingLicense);
|
|
419
|
+
|
|
420
|
+
const shouldShowEnterpriseValidationButton: boolean = showLicenseKeyInput;
|
|
401
421
|
|
|
402
422
|
const modalSubmitButtonText: string | undefined = IS_ENTERPRISE_EDITION
|
|
403
423
|
? shouldShowEnterpriseValidationButton
|
|
@@ -611,6 +631,20 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
611
631
|
</div>
|
|
612
632
|
)}
|
|
613
633
|
|
|
634
|
+
{!configError &&
|
|
635
|
+
!isConfigLoading &&
|
|
636
|
+
licenseValid &&
|
|
637
|
+
!isChangingLicense && (
|
|
638
|
+
<div className="-ml-3">
|
|
639
|
+
<Button
|
|
640
|
+
title="Change license key"
|
|
641
|
+
icon={IconProp.Edit}
|
|
642
|
+
buttonStyle={ButtonStyleType.NORMAL}
|
|
643
|
+
onClick={handleStartChangingLicense}
|
|
644
|
+
/>
|
|
645
|
+
</div>
|
|
646
|
+
)}
|
|
647
|
+
|
|
614
648
|
{!configError &&
|
|
615
649
|
!isConfigLoading &&
|
|
616
650
|
!licenseValid &&
|
|
@@ -632,11 +666,13 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
632
666
|
<Alert type={AlertType.SUCCESS} title={successMessage} />
|
|
633
667
|
)}
|
|
634
668
|
|
|
635
|
-
{
|
|
669
|
+
{showLicenseKeyInput && (
|
|
636
670
|
<>
|
|
637
671
|
<div>
|
|
638
672
|
<label className="text-sm font-medium text-gray-700">
|
|
639
|
-
|
|
673
|
+
{isChangingLicense
|
|
674
|
+
? "New License Key"
|
|
675
|
+
: "License Key"}
|
|
640
676
|
</label>
|
|
641
677
|
<Input
|
|
642
678
|
value={licenseKeyInput}
|
|
@@ -656,18 +692,34 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
656
692
|
/>
|
|
657
693
|
)}
|
|
658
694
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
695
|
+
{isChangingLicense ? (
|
|
696
|
+
<div className="flex items-center justify-between gap-3">
|
|
697
|
+
<p className="text-xs text-gray-500">
|
|
698
|
+
Enter the new enterprise license key and validate
|
|
699
|
+
it to replace the current one. Your existing
|
|
700
|
+
license stays active until the new key is
|
|
701
|
+
validated.
|
|
702
|
+
</p>
|
|
703
|
+
<Button
|
|
704
|
+
title="Cancel"
|
|
705
|
+
buttonStyle={ButtonStyleType.NORMAL}
|
|
706
|
+
onClick={handleCancelChangingLicense}
|
|
707
|
+
/>
|
|
708
|
+
</div>
|
|
709
|
+
) : (
|
|
710
|
+
<p className="text-xs text-gray-500">
|
|
711
|
+
You have installed Enterprise Edition of OneUptime.
|
|
712
|
+
You need to validate your license key. Need a
|
|
713
|
+
license key? Contact our sales team at{" "}
|
|
714
|
+
<a
|
|
715
|
+
href="mailto:sales@oneuptime.com"
|
|
716
|
+
className="font-medium text-indigo-600 hover:text-indigo-700"
|
|
717
|
+
>
|
|
718
|
+
sales@oneuptime.com
|
|
719
|
+
</a>
|
|
720
|
+
.
|
|
721
|
+
</p>
|
|
722
|
+
)}
|
|
671
723
|
</>
|
|
672
724
|
)}
|
|
673
725
|
</>
|
|
@@ -86,6 +86,7 @@ export interface EntityDropdownProps {
|
|
|
86
86
|
id?: string | undefined;
|
|
87
87
|
dataTestId?: string | undefined;
|
|
88
88
|
ariaLabel?: string | undefined;
|
|
89
|
+
ariaLabelledby?: string | undefined;
|
|
89
90
|
disabled?: boolean | undefined;
|
|
90
91
|
|
|
91
92
|
/*
|
|
@@ -1115,6 +1116,7 @@ const EntityDropdown: FunctionComponent<EntityDropdownProps> = (
|
|
|
1115
1116
|
<button
|
|
1116
1117
|
type="button"
|
|
1117
1118
|
disabled={props.disabled}
|
|
1119
|
+
aria-labelledby={props.ariaLabelledby}
|
|
1118
1120
|
onClick={(): void => {
|
|
1119
1121
|
if (props.disabled) {
|
|
1120
1122
|
return;
|
|
@@ -1204,6 +1206,7 @@ const EntityDropdown: FunctionComponent<EntityDropdownProps> = (
|
|
|
1204
1206
|
aria-autocomplete="list"
|
|
1205
1207
|
aria-expanded={isOpen}
|
|
1206
1208
|
aria-label={props.ariaLabel}
|
|
1209
|
+
aria-labelledby={props.ariaLabelledby}
|
|
1207
1210
|
aria-invalid={props.error ? true : undefined}
|
|
1208
1211
|
data-testid={props.dataTestId}
|
|
1209
1212
|
role="combobox"
|
|
@@ -32,6 +32,7 @@ export interface ComponentProps {
|
|
|
32
32
|
isMultiFilePicker?: boolean | undefined;
|
|
33
33
|
tabIndex?: number | undefined;
|
|
34
34
|
error?: string | undefined;
|
|
35
|
+
ariaLabelledby?: string | undefined;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
type UploadStatus = {
|
|
@@ -449,6 +450,7 @@ const FilePicker: FunctionComponent<ComponentProps> = (
|
|
|
449
450
|
id="file-upload"
|
|
450
451
|
name="file-upload"
|
|
451
452
|
type="file"
|
|
453
|
+
aria-labelledby={props.ariaLabelledby}
|
|
452
454
|
className="sr-only"
|
|
453
455
|
/>
|
|
454
456
|
</label>
|
|
@@ -24,6 +24,7 @@ export interface ComponentProps {
|
|
|
24
24
|
dataTestId?: string | undefined;
|
|
25
25
|
onEnterPress?: (() => void) | undefined;
|
|
26
26
|
error?: string | undefined;
|
|
27
|
+
ariaLabelledby?: string | undefined;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
const ColorPicker: FunctionComponent<ComponentProps> = (
|
|
@@ -83,6 +84,7 @@ const ColorPicker: FunctionComponent<ComponentProps> = (
|
|
|
83
84
|
readOnly={true}
|
|
84
85
|
type={InputType.TEXT}
|
|
85
86
|
tabIndex={props.tabIndex}
|
|
87
|
+
ariaLabelledby={props.ariaLabelledby}
|
|
86
88
|
onChange={(value: string) => {
|
|
87
89
|
if (!value) {
|
|
88
90
|
return handleChange("");
|
|
@@ -5,6 +5,8 @@ import React, { FunctionComponent, ReactElement } from "react";
|
|
|
5
5
|
|
|
6
6
|
export interface ComponentProps {
|
|
7
7
|
title: string;
|
|
8
|
+
id?: string | undefined;
|
|
9
|
+
htmlFor?: string | undefined;
|
|
8
10
|
required?: boolean | undefined;
|
|
9
11
|
sideLink?: FormFieldSideLink | undefined;
|
|
10
12
|
description?: string | ReactElement | undefined;
|
|
@@ -26,6 +28,8 @@ const FieldLabelElement: FunctionComponent<ComponentProps> = (
|
|
|
26
28
|
return (
|
|
27
29
|
<>
|
|
28
30
|
<label
|
|
31
|
+
id={props.id}
|
|
32
|
+
htmlFor={props.htmlFor}
|
|
29
33
|
className={
|
|
30
34
|
props.className ||
|
|
31
35
|
`block ${
|