From c11f429ddb6a48fb6018777041decc35b09ad32c Mon Sep 17 00:00:00 2001 From: Brandon Treston Date: Thu, 5 Dec 2024 10:09:40 -0500 Subject: [PATCH] [PM-12273] Admin Console Integration Page (#11883) * Integration page initial implementation * replace placeholder integrations * fix linting and tests * fix locales * update locale * Change logos, add link to SCIM page * refactor to standalone components, add integration filtering pipe * refactor modules and imports. Remove hyperlink text from integration card template * refactor i18n usage to be more generic * Add storybooks * fix tests * minify svgs, include spec files in TS config, fix stories * Update apps/web/src/locales/en/messages.json Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> * fix imports, add static dir for stories * Add darkmode svgs for integrations * hide nav link for non enterprise orgs * add router guard * change rxjs selector * Remove tailwind class causing style issues --------- Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> --- .storybook/main.ts | 1 + .../guards/is-enterprise-org.guard.spec.ts | 26 ++- .../guards/is-enterprise-org.guard.ts | 4 +- .../integrations/integrations.component.html | 66 ++++++ .../integrations/integrations.component.ts | 207 ++++++++++++++++++ .../organization-layout.component.html | 6 + .../layouts/organization-layout.component.ts | 14 ++ .../organization-routing.module.ts | 15 ++ apps/web/src/app/shared/components/index.ts | 4 + .../integration-card.component.html | 10 +- .../integration-card.component.spec.ts | 23 +- .../integration-card.component.ts | 7 +- .../integration-card.stories.ts | 63 ++++++ .../integration-grid.component.html | 11 +- .../integration-grid.component.spec.ts | 39 +++- .../integration-grid.component.ts | 22 ++ .../integration-grid.stories.ts | 77 +++++++ .../integrations/integrations.pipe.ts | 15 ++ .../shared/components/integrations/models.ts | 1 - apps/web/src/app/shared/index.ts | 1 + .../web/src/images/integrations/aws-color.svg | 1 + .../src/images/integrations/aws-darkmode.svg | 1 + .../integrations/azure-active-directory.svg | 1 + .../integrations/bitwarden-vertical-blue.svg | 1 + .../integrations/jumpcloud-darkmode.svg | 1 + .../integrations/logo-auth0-badge-color.svg | 1 + .../images/integrations/logo-duo-color.svg | 1 + .../integrations/logo-elastic-badge-color.svg | 1 + .../integrations/logo-google-badge-color.svg | 1 + .../logo-jumpcloud-badge-color.svg | 1 + .../integrations/logo-keycloak-icon.svg | 23 ++ .../logo-microsoft-entra-id-color.svg | 1 + .../logo-microsoft-intune-color.svg | 1 + .../logo-microsoft-sentinel-color.svg | 1 + .../integrations/logo-okta-symbol-black.svg | 1 + .../logo-onelogin-badge-color.svg | 1 + .../integrations/logo-panther-round-color.svg | 1 + .../logo-ping-identity-badge-color.svg | 1 + .../images/integrations/logo-rapid7-black.svg | 1 + .../images/integrations/logo-splunk-black.svg | 1 + .../src/images/integrations/okta-darkmode.svg | 1 + .../images/integrations/onelogin-darkmode.svg | 1 + .../images/integrations/rapid7-darkmode.svg | 1 + .../images/integrations/splunk-darkmode.svg | 1 + apps/web/src/locales/en/messages.json | 115 +++++++--- .../organizations/manage/scim.component.html | 7 +- .../integration-grid.component.ts | 15 -- .../integrations/integrations.component.html | 12 +- .../integrations.component.spec.ts | 21 +- .../integrations/integrations.component.ts | 19 +- .../integrations/integrations.module.ts | 17 +- bitwarden_license/bit-web/tsconfig.json | 3 +- libs/common/src/enums/feature-flag.enum.ts | 2 + .../common/src/enums/integration-type.enum.ts | 5 + 54 files changed, 764 insertions(+), 110 deletions(-) create mode 100644 apps/web/src/app/admin-console/organizations/integrations/integrations.component.html create mode 100644 apps/web/src/app/admin-console/organizations/integrations/integrations.component.ts create mode 100644 apps/web/src/app/shared/components/index.ts rename {bitwarden_license/bit-web/src/app/secrets-manager => apps/web/src/app/shared/components}/integrations/integration-card/integration-card.component.html (64%) rename {bitwarden_license/bit-web/src/app/secrets-manager => apps/web/src/app/shared/components}/integrations/integration-card/integration-card.component.spec.ts (86%) rename {bitwarden_license/bit-web/src/app/secrets-manager => apps/web/src/app/shared/components}/integrations/integration-card/integration-card.component.ts (95%) create mode 100644 apps/web/src/app/shared/components/integrations/integration-card/integration-card.stories.ts rename {bitwarden_license/bit-web/src/app/secrets-manager => apps/web/src/app/shared/components}/integrations/integration-grid/integration-grid.component.html (66%) rename {bitwarden_license/bit-web/src/app/secrets-manager => apps/web/src/app/shared/components}/integrations/integration-grid/integration-grid.component.spec.ts (61%) create mode 100644 apps/web/src/app/shared/components/integrations/integration-grid/integration-grid.component.ts create mode 100644 apps/web/src/app/shared/components/integrations/integration-grid/integration-grid.stories.ts create mode 100644 apps/web/src/app/shared/components/integrations/integrations.pipe.ts rename bitwarden_license/bit-web/src/app/secrets-manager/integrations/models/integration.ts => apps/web/src/app/shared/components/integrations/models.ts (95%) create mode 100644 apps/web/src/images/integrations/aws-color.svg create mode 100644 apps/web/src/images/integrations/aws-darkmode.svg create mode 100644 apps/web/src/images/integrations/azure-active-directory.svg create mode 100644 apps/web/src/images/integrations/bitwarden-vertical-blue.svg create mode 100644 apps/web/src/images/integrations/jumpcloud-darkmode.svg create mode 100644 apps/web/src/images/integrations/logo-auth0-badge-color.svg create mode 100644 apps/web/src/images/integrations/logo-duo-color.svg create mode 100644 apps/web/src/images/integrations/logo-elastic-badge-color.svg create mode 100644 apps/web/src/images/integrations/logo-google-badge-color.svg create mode 100644 apps/web/src/images/integrations/logo-jumpcloud-badge-color.svg create mode 100644 apps/web/src/images/integrations/logo-keycloak-icon.svg create mode 100644 apps/web/src/images/integrations/logo-microsoft-entra-id-color.svg create mode 100644 apps/web/src/images/integrations/logo-microsoft-intune-color.svg create mode 100644 apps/web/src/images/integrations/logo-microsoft-sentinel-color.svg create mode 100644 apps/web/src/images/integrations/logo-okta-symbol-black.svg create mode 100644 apps/web/src/images/integrations/logo-onelogin-badge-color.svg create mode 100644 apps/web/src/images/integrations/logo-panther-round-color.svg create mode 100644 apps/web/src/images/integrations/logo-ping-identity-badge-color.svg create mode 100644 apps/web/src/images/integrations/logo-rapid7-black.svg create mode 100644 apps/web/src/images/integrations/logo-splunk-black.svg create mode 100644 apps/web/src/images/integrations/okta-darkmode.svg create mode 100644 apps/web/src/images/integrations/onelogin-darkmode.svg create mode 100644 apps/web/src/images/integrations/rapid7-darkmode.svg create mode 100644 apps/web/src/images/integrations/splunk-darkmode.svg delete mode 100644 bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-grid/integration-grid.component.ts diff --git a/.storybook/main.ts b/.storybook/main.ts index 454da4377d..b48a86ba2b 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -57,6 +57,7 @@ const config: StorybookConfig = { return config; }, docs: {}, + staticDirs: ["../apps/web/src/images"], }; export default config; diff --git a/apps/web/src/app/admin-console/organizations/guards/is-enterprise-org.guard.spec.ts b/apps/web/src/app/admin-console/organizations/guards/is-enterprise-org.guard.spec.ts index 75e63d4242..5d138e8137 100644 --- a/apps/web/src/app/admin-console/organizations/guards/is-enterprise-org.guard.spec.ts +++ b/apps/web/src/app/admin-console/organizations/guards/is-enterprise-org.guard.spec.ts @@ -59,8 +59,14 @@ describe("Is Enterprise Org Guard", () => { { path: "organizations/:organizationId/enterpriseOrgsOnly", component: IsEnterpriseOrganizationComponent, - canActivate: [isEnterpriseOrgGuard()], + canActivate: [isEnterpriseOrgGuard(true)], }, + { + path: "organizations/:organizationId/enterpriseOrgsOnlyNoError", + component: IsEnterpriseOrganizationComponent, + canActivate: [isEnterpriseOrgGuard(false)], + }, + { path: "organizations/:organizationId/billing/subscription", component: OrganizationUpgradeScreenComponent, @@ -115,6 +121,24 @@ describe("Is Enterprise Org Guard", () => { ); }); + it.each([ + ProductTierType.Free, + ProductTierType.Families, + ProductTierType.Teams, + ProductTierType.TeamsStarter, + ])("does not proceed with the navigation for productTierType '%s'", async (productTierType) => { + const org = orgFactory({ + type: OrganizationUserType.User, + productTierType: productTierType, + }); + organizationService.get.calledWith(org.id).mockResolvedValue(org); + await routerHarness.navigateByUrl(`organizations/${org.id}/enterpriseOrgsOnlyNoError`); + expect(dialogService.openSimpleDialog).not.toHaveBeenCalled(); + expect( + routerHarness.routeNativeElement?.querySelector("h1")?.textContent?.trim() ?? "", + ).not.toBe("This component can only be accessed by a enterprise organization!"); + }); + it("proceeds with navigation if the organization in question is a enterprise organization", async () => { const org = orgFactory({ productTierType: ProductTierType.Enterprise }); organizationService.get.calledWith(org.id).mockResolvedValue(org); diff --git a/apps/web/src/app/admin-console/organizations/guards/is-enterprise-org.guard.ts b/apps/web/src/app/admin-console/organizations/guards/is-enterprise-org.guard.ts index 8a0d374997..5eab89ae68 100644 --- a/apps/web/src/app/admin-console/organizations/guards/is-enterprise-org.guard.ts +++ b/apps/web/src/app/admin-console/organizations/guards/is-enterprise-org.guard.ts @@ -17,7 +17,7 @@ import { DialogService } from "@bitwarden/components"; * if they have access to upgrade the organization. If the organization is * enterprise routing proceeds." */ -export function isEnterpriseOrgGuard(): CanActivateFn { +export function isEnterpriseOrgGuard(showError: boolean = true): CanActivateFn { return async (route: ActivatedRouteSnapshot, _state: RouterStateSnapshot) => { const router = inject(Router); const organizationService = inject(OrganizationService); @@ -29,7 +29,7 @@ export function isEnterpriseOrgGuard(): CanActivateFn { return router.createUrlTree(["/"]); } - if (org.productTierType != ProductTierType.Enterprise) { + if (org.productTierType != ProductTierType.Enterprise && showError) { // Users without billing permission can't access billing if (!org.canEditSubscription) { await dialogService.openSimpleDialog({ diff --git a/apps/web/src/app/admin-console/organizations/integrations/integrations.component.html b/apps/web/src/app/admin-console/organizations/integrations/integrations.component.html new file mode 100644 index 0000000000..61e7996bec --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/integrations/integrations.component.html @@ -0,0 +1,66 @@ + + + + +
+

{{ "singleSignOn" | i18n }}

+

+ {{ "ssoDescStart" | i18n }} + {{ "singleSignOn" | i18n }} + {{ "ssoDescEnd" | i18n }} +

+ +
+
+ + +
+

+ {{ "scimIntegration" | i18n }} +

+

+ {{ "scimIntegrationDescStart" | i18n }} + {{ "scimIntegration" | i18n }} + {{ "scimIntegrationDescEnd" | i18n }} +

+ +
+
+

+ {{ "bwdc" | i18n }} +

+

{{ "bwdcDesc" | i18n }}

+ +
+
+ + +
+

+ {{ "eventManagement" | i18n }} +

+

{{ "eventManagementDesc" | i18n }}

+ +
+
+ + +
+

+ {{ "deviceManagement" | i18n }} +

+

{{ "deviceManagementDesc" | i18n }}

+ +
+
+
diff --git a/apps/web/src/app/admin-console/organizations/integrations/integrations.component.ts b/apps/web/src/app/admin-console/organizations/integrations/integrations.component.ts new file mode 100644 index 0000000000..4b8822da7c --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/integrations/integrations.component.ts @@ -0,0 +1,207 @@ +import { Component } from "@angular/core"; + +import { IntegrationType } from "@bitwarden/common/enums"; + +import { HeaderModule } from "../../../layouts/header/header.module"; +import { FilterIntegrationsPipe, IntegrationGridComponent, Integration } from "../../../shared/"; +import { SharedModule } from "../../../shared/shared.module"; +import { SharedOrganizationModule } from "../shared"; + +@Component({ + selector: "ac-integrations", + templateUrl: "./integrations.component.html", + standalone: true, + imports: [ + SharedModule, + SharedOrganizationModule, + IntegrationGridComponent, + HeaderModule, + FilterIntegrationsPipe, + ], +}) +export class AdminConsoleIntegrationsComponent { + integrationsList: Integration[] = []; + tabIndex: number; + + constructor() { + this.integrationsList = [ + { + name: "AD FS", + linkURL: "https://bitwarden.com/help/saml-adfs/", + image: "../../../../../../../images/integrations/azure-active-directory.svg", + type: IntegrationType.SSO, + }, + { + name: "Auth0", + linkURL: "https://bitwarden.com/help/saml-auth0/", + image: "../../../../../../../images/integrations/logo-auth0-badge-color.svg", + type: IntegrationType.SSO, + }, + { + name: "AWS", + linkURL: "https://bitwarden.com/help/saml-aws/", + image: "../../../../../../../images/integrations/aws-color.svg", + imageDarkMode: "../../../../../../../images/integrations/aws-darkmode.svg", + type: IntegrationType.SSO, + }, + { + name: "Microsoft Entra ID", + linkURL: "https://bitwarden.com/help/saml-azure/", + image: "../../../../../../../images/integrations/logo-microsoft-entra-id-color.svg", + type: IntegrationType.SSO, + }, + { + name: "Duo", + linkURL: "https://bitwarden.com/help/saml-duo/", + image: "../../../../../../../images/integrations/logo-duo-color.svg", + type: IntegrationType.SSO, + }, + { + name: "Google", + linkURL: "https://bitwarden.com/help/saml-google/", + image: "../../../../../../../images/integrations/logo-google-badge-color.svg", + type: IntegrationType.SSO, + }, + { + name: "JumpCloud", + linkURL: "https://bitwarden.com/help/saml-jumpcloud/", + image: "../../../../../../../images/integrations/logo-jumpcloud-badge-color.svg", + imageDarkMode: "../../../../../../../images/integrations/jumpcloud-darkmode.svg", + type: IntegrationType.SSO, + }, + { + name: "KeyCloak", + linkURL: "https://bitwarden.com/help/saml-keycloak/", + image: "../../../../../../../images/integrations/logo-keycloak-icon.svg", + type: IntegrationType.SSO, + }, + { + name: "Okta", + linkURL: "https://bitwarden.com/help/saml-okta/", + image: "../../../../../../../images/integrations/logo-okta-symbol-black.svg", + imageDarkMode: "../../../../../../../images/integrations/okta-darkmode.svg", + type: IntegrationType.SSO, + }, + { + name: "OneLogin", + linkURL: "https://bitwarden.com/help/saml-onelogin/", + image: "../../../../../../../images/integrations/logo-onelogin-badge-color.svg", + imageDarkMode: "../../../../../../../images/integrations/onelogin-darkmode.svg", + type: IntegrationType.SSO, + }, + { + name: "PingFederate", + linkURL: "https://bitwarden.com/help/saml-pingfederate/", + image: "../../../../../../../images/integrations/logo-ping-identity-badge-color.svg", + type: IntegrationType.SSO, + }, + { + name: "Microsoft Entra ID", + linkURL: "https://bitwarden.com/help/microsoft-entra-id-scim-integration/", + image: "../../../../../../../images/integrations/logo-microsoft-entra-id-color.svg", + type: IntegrationType.SCIM, + }, + { + name: "Okta", + linkURL: "https://bitwarden.com/help/okta-scim-integration/", + image: "../../../../../../../images/integrations/logo-okta-symbol-black.svg", + imageDarkMode: "../../../../../../../images/integrations/okta-darkmode.svg", + type: IntegrationType.SCIM, + }, + { + name: "OneLogin", + linkURL: "https://bitwarden.com/help/onelogin-scim-integration/", + image: "../../../../../../../images/integrations/logo-onelogin-badge-color.svg", + imageDarkMode: "../../../../../../../images/integrations/onelogin-darkmode.svg", + type: IntegrationType.SCIM, + }, + { + name: "JumpCloud", + linkURL: "https://bitwarden.com/help/jumpcloud-scim-integration/", + image: "../../../../../../../images/integrations/logo-jumpcloud-badge-color.svg", + imageDarkMode: "../../../../../../../images/integrations/jumpcloud-darkmode.svg", + type: IntegrationType.SCIM, + }, + { + name: "Ping Identity", + linkURL: "https://bitwarden.com/help/ping-identity-scim-integration/", + image: "../../../../../../../images/integrations/logo-ping-identity-badge-color.svg", + type: IntegrationType.SCIM, + }, + { + name: "Active Directory", + linkURL: "https://bitwarden.com/help/ldap-directory/", + image: "../../../../../../../images/integrations/azure-active-directory.svg", + type: IntegrationType.BWDC, + }, + { + name: "Microsoft Entra ID", + linkURL: "https://bitwarden.com/help/microsoft-entra-id/", + image: "../../../../../../../images/integrations/logo-microsoft-entra-id-color.svg", + type: IntegrationType.BWDC, + }, + { + name: "Google Workspace", + linkURL: "https://bitwarden.com/help/workspace-directory/", + image: "../../../../../../../images/integrations/logo-google-badge-color.svg", + type: IntegrationType.BWDC, + }, + { + name: "Okta", + linkURL: "https://bitwarden.com/help/okta-directory/", + image: "../../../../../../../images/integrations/logo-okta-symbol-black.svg", + imageDarkMode: "../../../../../../../images/integrations/okta-darkmode.svg", + type: IntegrationType.BWDC, + }, + { + name: "OneLogin", + linkURL: "https://bitwarden.com/help/onelogin-directory/", + image: "../../../../../../../images/integrations/logo-onelogin-badge-color.svg", + imageDarkMode: "../../../../../../../images/integrations/onelogin-darkmode.svg", + type: IntegrationType.BWDC, + }, + { + name: "Splunk", + linkURL: "https://bitwarden.com/help/splunk-siem/", + image: "../../../../../../../images/integrations/logo-splunk-black.svg", + imageDarkMode: "../../../../../../../images/integrations/splunk-darkmode.svg", + type: IntegrationType.EVENT, + }, + { + name: "Microsoft Sentinel", + linkURL: "https://bitwarden.com/help/microsoft-sentinel-siem/", + image: "../../../../../../../images/integrations/logo-microsoft-sentinel-color.svg", + type: IntegrationType.EVENT, + }, + { + name: "Rapid7", + linkURL: "https://bitwarden.com/help/rapid7-siem/", + image: "../../../../../../../images/integrations/logo-rapid7-black.svg", + imageDarkMode: "../../../../../../../images/integrations/rapid7-darkmode.svg", + type: IntegrationType.EVENT, + }, + { + name: "Elastic", + linkURL: "https://bitwarden.com/help/elastic-siem/", + image: "../../../../../../../images/integrations/logo-elastic-badge-color.svg", + type: IntegrationType.EVENT, + }, + { + name: "Panther", + linkURL: "https://bitwarden.com/help/panther-siem/", + image: "../../../../../../../images/integrations/logo-panther-round-color.svg", + type: IntegrationType.EVENT, + }, + { + name: "Microsoft Intune", + linkURL: "https://bitwarden.com/help/deploy-browser-extensions-with-intune/", + image: "../../../../../../../images/integrations/logo-microsoft-intune-color.svg", + type: IntegrationType.DEVICE, + }, + ]; + } + + get IntegrationType(): typeof IntegrationType { + return IntegrationType; + } +} diff --git a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html index 1e811b6c29..ce832ef06e 100644 --- a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html +++ b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html @@ -60,6 +60,12 @@ + canAccessOrgAdmin(org); + protected integrationPageEnabled$: Observable; + organization$: Observable; canAccessExport$: Observable; showPaymentAndHistory$: Observable; hideNewOrgButton$: Observable; organizationIsUnmanaged$: Observable; isAccessIntelligenceFeatureEnabled = false; + enterpriseOrganization$: Observable; constructor( private route: ActivatedRoute, @@ -104,6 +108,16 @@ export class OrganizationLayoutComponent implements OnInit { provider.providerStatus !== ProviderStatusType.Billable, ), ); + + this.integrationPageEnabled$ = combineLatest( + this.organization$, + this.configService.getFeatureFlag$(FeatureFlag.PM14505AdminConsoleIntegrationPage), + ).pipe( + map( + ([org, featureFlagEnabled]) => + org.productTierType === ProductTierType.Enterprise && featureFlagEnabled, + ), + ); } canShowVaultTab(organization: Organization): boolean { diff --git a/apps/web/src/app/admin-console/organizations/organization-routing.module.ts b/apps/web/src/app/admin-console/organizations/organization-routing.module.ts index 538cc45ac6..31544968dd 100644 --- a/apps/web/src/app/admin-console/organizations/organization-routing.module.ts +++ b/apps/web/src/app/admin-console/organizations/organization-routing.module.ts @@ -2,6 +2,7 @@ import { NgModule } from "@angular/core"; import { RouterModule, Routes } from "@angular/router"; import { authGuard } from "@bitwarden/angular/auth/guards"; +import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard"; import { canAccessOrgAdmin, canAccessGroupsTab, @@ -11,6 +12,7 @@ import { canAccessSettingsTab, } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { organizationPermissionsGuard } from "../../admin-console/organizations/guards/org-permissions.guard"; import { organizationRedirectGuard } from "../../admin-console/organizations/guards/org-redirect.guard"; @@ -18,6 +20,8 @@ import { OrganizationLayoutComponent } from "../../admin-console/organizations/l import { deepLinkGuard } from "../../auth/guards/deep-link.guard"; import { VaultModule } from "../../vault/org-vault/vault.module"; +import { isEnterpriseOrgGuard } from "./guards/is-enterprise-org.guard"; +import { AdminConsoleIntegrationsComponent } from "./integrations/integrations.component"; import { GroupsComponent } from "./manage/groups.component"; const routes: Routes = [ @@ -36,6 +40,17 @@ const routes: Routes = [ path: "vault", loadChildren: () => VaultModule, }, + { + path: "integrations", + canActivate: [ + canAccessFeature(FeatureFlag.PM14505AdminConsoleIntegrationPage), + isEnterpriseOrgGuard(false), + ], + component: AdminConsoleIntegrationsComponent, + data: { + titleId: "integrations", + }, + }, { path: "settings", loadChildren: () => diff --git a/apps/web/src/app/shared/components/index.ts b/apps/web/src/app/shared/components/index.ts new file mode 100644 index 0000000000..5745a7827f --- /dev/null +++ b/apps/web/src/app/shared/components/index.ts @@ -0,0 +1,4 @@ +export * from "./integrations/integration-card/integration-card.component"; +export * from "./integrations/integration-grid/integration-grid.component"; +export * from "./integrations/integrations.pipe"; +export * from "./integrations/models"; diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-card/integration-card.component.html b/apps/web/src/app/shared/components/integrations/integration-card/integration-card.component.html similarity index 64% rename from bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-card/integration-card.component.html rename to apps/web/src/app/shared/components/integrations/integration-card/integration-card.component.html index 5bb9ed425f..4801c39297 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-card/integration-card.component.html +++ b/apps/web/src/app/shared/components/integrations/integration-card/integration-card.component.html @@ -1,8 +1,11 @@
+
+ +
-

{{ name }}

+

{{ name }}

- {{ linkText }} {{ "new" | i18n }} diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-card/integration-card.component.spec.ts b/apps/web/src/app/shared/components/integrations/integration-card/integration-card.component.spec.ts similarity index 86% rename from bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-card/integration-card.component.spec.ts rename to apps/web/src/app/shared/components/integrations/integration-card/integration-card.component.spec.ts index 94cec5f627..c8b6a29042 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-card/integration-card.component.spec.ts +++ b/apps/web/src/app/shared/components/integrations/integration-card/integration-card.component.spec.ts @@ -1,9 +1,13 @@ import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { mock } from "jest-mock-extended"; import { BehaviorSubject } from "rxjs"; -import { SYSTEM_THEME_OBSERVABLE } from "../../../../../../../libs/angular/src/services/injection-tokens"; -import { ThemeType } from "../../../../../../../libs/common/src/platform/enums"; -import { ThemeStateService } from "../../../../../../../libs/common/src/platform/theming/theme-state.service"; +import { SYSTEM_THEME_OBSERVABLE } from "@bitwarden/angular/services/injection-tokens"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { ThemeType } from "@bitwarden/common/platform/enums"; +import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service"; +import { SharedModule } from "@bitwarden/components/src/shared"; +import { I18nPipe } from "@bitwarden/components/src/shared/i18n.pipe"; import { IntegrationCardComponent } from "./integration-card.component"; @@ -19,7 +23,7 @@ describe("IntegrationCardComponent", () => { systemTheme$.next(ThemeType.Light); await TestBed.configureTestingModule({ - declarations: [IntegrationCardComponent], + imports: [IntegrationCardComponent, SharedModule], providers: [ { provide: ThemeStateService, @@ -29,6 +33,14 @@ describe("IntegrationCardComponent", () => { provide: SYSTEM_THEME_OBSERVABLE, useValue: systemTheme$, }, + { + provide: I18nPipe, + useValue: mock(), + }, + { + provide: I18nService, + useValue: mock(), + }, ], }).compileComponents(); }); @@ -39,7 +51,6 @@ describe("IntegrationCardComponent", () => { component.name = "Integration Name"; component.image = "test-image.png"; - component.linkText = "Get started with integration"; component.linkURL = "https://example.com/"; fixture.detectChanges(); @@ -53,10 +64,8 @@ describe("IntegrationCardComponent", () => { it("renders card body", () => { const name = fixture.nativeElement.querySelector("h3"); - const link = fixture.nativeElement.querySelector("a"); expect(name.textContent).toBe("Integration Name"); - expect(link.textContent.trim()).toBe("Get started with integration"); }); it("assigns external rel attribute", () => { diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-card/integration-card.component.ts b/apps/web/src/app/shared/components/integrations/integration-card/integration-card.component.ts similarity index 95% rename from bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-card/integration-card.component.ts rename to apps/web/src/app/shared/components/integrations/integration-card/integration-card.component.ts index bf5f5bd311..a33fbd28c9 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-card/integration-card.component.ts +++ b/apps/web/src/app/shared/components/integrations/integration-card/integration-card.component.ts @@ -13,9 +13,13 @@ import { SYSTEM_THEME_OBSERVABLE } from "@bitwarden/angular/services/injection-t import { ThemeType } from "@bitwarden/common/platform/enums"; import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service"; +import { SharedModule } from "../../../shared.module"; + @Component({ - selector: "sm-integration-card", + selector: "app-integration-card", templateUrl: "./integration-card.component.html", + standalone: true, + imports: [SharedModule], }) export class IntegrationCardComponent implements AfterViewInit, OnDestroy { private destroyed$: Subject = new Subject(); @@ -24,7 +28,6 @@ export class IntegrationCardComponent implements AfterViewInit, OnDestroy { @Input() name: string; @Input() image: string; @Input() imageDarkMode?: string; - @Input() linkText: string; @Input() linkURL: string; /** Adds relevant `rel` attribute to external links */ diff --git a/apps/web/src/app/shared/components/integrations/integration-card/integration-card.stories.ts b/apps/web/src/app/shared/components/integrations/integration-card/integration-card.stories.ts new file mode 100644 index 0000000000..1d1e229740 --- /dev/null +++ b/apps/web/src/app/shared/components/integrations/integration-card/integration-card.stories.ts @@ -0,0 +1,63 @@ +import { Meta, StoryObj, moduleMetadata } from "@storybook/angular"; +import { of } from "rxjs"; + +import { SYSTEM_THEME_OBSERVABLE } from "@bitwarden/angular/services/injection-tokens"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { ThemeTypes } from "@bitwarden/common/platform/enums"; +import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service"; +import { I18nMockService } from "@bitwarden/components"; + +import { SharedModule } from "../../../shared.module"; + +import { IntegrationCardComponent } from "./integration-card.component"; + +class MockThemeService implements Partial {} + +export default { + title: "Web/Integration Layout/Integration Card", + component: IntegrationCardComponent, + decorators: [ + moduleMetadata({ + imports: [SharedModule], + providers: [ + { + provide: I18nService, + useFactory: () => { + return new I18nMockService({}); + }, + }, + { + provide: ThemeStateService, + useClass: MockThemeService, + }, + { + provide: SYSTEM_THEME_OBSERVABLE, + useValue: of(ThemeTypes.Light), + }, + ], + }), + ], + args: { + integrations: [], + }, +} as Meta; + +type Story = StoryObj; + +export const Default: Story = { + render: (args) => ({ + props: args, + template: /*html*/ ` + + `, + }), + args: { + name: "Bitwarden", + image: "/integrations/bitwarden-vertical-blue.svg", + linkURL: "https://bitwarden.com", + }, +}; diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-grid/integration-grid.component.html b/apps/web/src/app/shared/components/integrations/integration-grid/integration-grid.component.html similarity index 66% rename from bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-grid/integration-grid.component.html rename to apps/web/src/app/shared/components/integrations/integration-grid/integration-grid.component.html index a0c82d2f34..4b4b3ac972 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-grid/integration-grid.component.html +++ b/apps/web/src/app/shared/components/integrations/integration-grid/integration-grid.component.html @@ -1,15 +1,18 @@
    -
  • - + + >
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-grid/integration-grid.component.spec.ts b/apps/web/src/app/shared/components/integrations/integration-grid/integration-grid.component.spec.ts similarity index 61% rename from bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-grid/integration-grid.component.spec.ts rename to apps/web/src/app/shared/components/integrations/integration-grid/integration-grid.component.spec.ts index e74e057e06..c77ec455e0 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-grid/integration-grid.component.spec.ts +++ b/apps/web/src/app/shared/components/integrations/integration-grid/integration-grid.component.spec.ts @@ -3,12 +3,16 @@ import { By } from "@angular/platform-browser"; import { mock } from "jest-mock-extended"; import { of } from "rxjs"; -import { SYSTEM_THEME_OBSERVABLE } from "../../../../../../../libs/angular/src/services/injection-tokens"; -import { IntegrationType } from "../../../../../../../libs/common/src/enums"; -import { ThemeType } from "../../../../../../../libs/common/src/platform/enums"; -import { ThemeStateService } from "../../../../../../../libs/common/src/platform/theming/theme-state.service"; +import { SYSTEM_THEME_OBSERVABLE } from "@bitwarden/angular/services/injection-tokens"; +import { IntegrationType } from "@bitwarden/common/enums"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { ThemeTypes } from "@bitwarden/common/platform/enums"; +import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service"; +import { SharedModule } from "@bitwarden/components/src/shared"; +import { I18nPipe } from "@bitwarden/components/src/shared/i18n.pipe"; + import { IntegrationCardComponent } from "../integration-card/integration-card.component"; -import { Integration } from "../models/integration"; +import { Integration } from "../models"; import { IntegrationGridComponent } from "./integration-grid.component"; @@ -19,14 +23,12 @@ describe("IntegrationGridComponent", () => { { name: "Integration 1", image: "test-image1.png", - linkText: "Get started with integration 1", linkURL: "https://example.com/1", type: IntegrationType.Integration, }, { name: "SDK 2", image: "test-image2.png", - linkText: "View SDK 2", linkURL: "https://example.com/2", type: IntegrationType.SDK, }, @@ -34,7 +36,7 @@ describe("IntegrationGridComponent", () => { beforeEach(() => { TestBed.configureTestingModule({ - declarations: [IntegrationGridComponent, IntegrationCardComponent], + imports: [IntegrationGridComponent, IntegrationCardComponent, SharedModule], providers: [ { provide: ThemeStateService, @@ -42,7 +44,15 @@ describe("IntegrationGridComponent", () => { }, { provide: SYSTEM_THEME_OBSERVABLE, - useValue: of(ThemeType.Light), + useValue: of(ThemeTypes.Light), + }, + { + provide: I18nPipe, + useValue: mock(), + }, + { + provide: I18nService, + useValue: mock({ t: (key, p1) => key + " " + p1 }), }, ], }); @@ -50,6 +60,8 @@ describe("IntegrationGridComponent", () => { fixture = TestBed.createComponent(IntegrationGridComponent); component = fixture.componentInstance; component.integrations = integrations; + component.ariaI18nKey = "integrationCardAriaLabel"; + component.tooltipI18nKey = "integrationCardTooltip"; fixture.detectChanges(); }); @@ -68,7 +80,6 @@ describe("IntegrationGridComponent", () => { expect(card.componentInstance.name).toBe("SDK 2"); expect(card.componentInstance.image).toBe("test-image2.png"); - expect(card.componentInstance.linkText).toBe("View SDK 2"); expect(card.componentInstance.linkURL).toBe("https://example.com/2"); }); @@ -78,4 +89,12 @@ describe("IntegrationGridComponent", () => { expect(card[0].componentInstance.externalURL).toBe(false); expect(card[1].componentInstance.externalURL).toBe(true); }); + + it("has a tool tip and aria label attributes", () => { + const card: HTMLElement = fixture.debugElement.queryAll(By.css("li"))[0].nativeElement; + expect(card.title).toBe("integrationCardTooltip" + " " + integrations[0].name); + expect(card.getAttribute("aria-label")).toBe( + "integrationCardAriaLabel" + " " + integrations[0].name, + ); + }); }); diff --git a/apps/web/src/app/shared/components/integrations/integration-grid/integration-grid.component.ts b/apps/web/src/app/shared/components/integrations/integration-grid/integration-grid.component.ts new file mode 100644 index 0000000000..7660162f87 --- /dev/null +++ b/apps/web/src/app/shared/components/integrations/integration-grid/integration-grid.component.ts @@ -0,0 +1,22 @@ +import { Component, Input } from "@angular/core"; + +import { IntegrationType } from "@bitwarden/common/enums"; + +import { SharedModule } from "../../../shared.module"; +import { IntegrationCardComponent } from "../integration-card/integration-card.component"; +import { Integration } from "../models"; + +@Component({ + selector: "app-integration-grid", + templateUrl: "./integration-grid.component.html", + standalone: true, + imports: [IntegrationCardComponent, SharedModule], +}) +export class IntegrationGridComponent { + @Input() integrations: Integration[]; + + @Input() ariaI18nKey: string = "integrationCardAriaLabel"; + @Input() tooltipI18nKey: string = "integrationCardTooltip"; + + protected IntegrationType = IntegrationType; +} diff --git a/apps/web/src/app/shared/components/integrations/integration-grid/integration-grid.stories.ts b/apps/web/src/app/shared/components/integrations/integration-grid/integration-grid.stories.ts new file mode 100644 index 0000000000..2ec0bccec3 --- /dev/null +++ b/apps/web/src/app/shared/components/integrations/integration-grid/integration-grid.stories.ts @@ -0,0 +1,77 @@ +import { Meta, StoryObj, moduleMetadata } from "@storybook/angular"; +import { of } from "rxjs"; + +import { SYSTEM_THEME_OBSERVABLE } from "@bitwarden/angular/services/injection-tokens"; +import { IntegrationType } from "@bitwarden/common/enums"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { ThemeTypes } from "@bitwarden/common/platform/enums"; +import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service"; +import { I18nMockService } from "@bitwarden/components"; + +import { SharedModule } from "../../../shared.module"; +import { IntegrationCardComponent } from "../integration-card/integration-card.component"; +import { IntegrationGridComponent } from "../integration-grid/integration-grid.component"; + +class MockThemeService implements Partial {} + +export default { + title: "Web/Integration Layout/Integration Grid", + component: IntegrationGridComponent, + decorators: [ + moduleMetadata({ + imports: [IntegrationCardComponent, SharedModule], + providers: [ + { + provide: I18nService, + useFactory: () => { + return new I18nMockService({ + integrationCardAriaLabel: "Go to integration", + integrationCardTooltip: "Go to integration", + }); + }, + }, + { + provide: ThemeStateService, + useClass: MockThemeService, + }, + { + provide: SYSTEM_THEME_OBSERVABLE, + useValue: of(ThemeTypes.Dark), + }, + ], + }), + ], +} as Meta; + +type Story = StoryObj; + +export const Default: Story = { + render: (args) => ({ + props: args, + template: /*html*/ ` + + `, + }), + args: { + integrations: [ + { + name: "Card 1", + linkURL: "https://bitwarden.com", + image: "/integrations/bitwarden-vertical-blue.svg", + type: IntegrationType.SSO, + }, + { + name: "Card 2", + linkURL: "https://bitwarden.com", + image: "/integrations/bitwarden-vertical-blue.svg", + type: IntegrationType.SDK, + }, + { + name: "Card 3", + linkURL: "https://bitwarden.com", + image: "/integrations/bitwarden-vertical-blue.svg", + type: IntegrationType.SCIM, + }, + ], + }, +}; diff --git a/apps/web/src/app/shared/components/integrations/integrations.pipe.ts b/apps/web/src/app/shared/components/integrations/integrations.pipe.ts new file mode 100644 index 0000000000..760d9913e9 --- /dev/null +++ b/apps/web/src/app/shared/components/integrations/integrations.pipe.ts @@ -0,0 +1,15 @@ +import { Pipe, PipeTransform } from "@angular/core"; + +import { IntegrationType } from "@bitwarden/common/enums"; + +import { Integration } from "../../../shared/components/integrations/models"; + +@Pipe({ + name: "filterIntegrations", + standalone: true, +}) +export class FilterIntegrationsPipe implements PipeTransform { + transform(integrations: Integration[], type: IntegrationType): Integration[] { + return integrations.filter((integration) => integration.type === type); + } +} diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/models/integration.ts b/apps/web/src/app/shared/components/integrations/models.ts similarity index 95% rename from bitwarden_license/bit-web/src/app/secrets-manager/integrations/models/integration.ts rename to apps/web/src/app/shared/components/integrations/models.ts index 51ca79b30f..765b1d44a2 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/models/integration.ts +++ b/apps/web/src/app/shared/components/integrations/models.ts @@ -9,7 +9,6 @@ export type Integration = { */ imageDarkMode?: string; linkURL: string; - linkText: string; type: IntegrationType; /** * Shows the "New" badge until the defined date. diff --git a/apps/web/src/app/shared/index.ts b/apps/web/src/app/shared/index.ts index 7defcdedfd..f57648c0e4 100644 --- a/apps/web/src/app/shared/index.ts +++ b/apps/web/src/app/shared/index.ts @@ -1,2 +1,3 @@ export * from "./shared.module"; export * from "./loose-components.module"; +export * from "./components/index"; diff --git a/apps/web/src/images/integrations/aws-color.svg b/apps/web/src/images/integrations/aws-color.svg new file mode 100644 index 0000000000..963b65027d --- /dev/null +++ b/apps/web/src/images/integrations/aws-color.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/images/integrations/aws-darkmode.svg b/apps/web/src/images/integrations/aws-darkmode.svg new file mode 100644 index 0000000000..64c9ba3cf9 --- /dev/null +++ b/apps/web/src/images/integrations/aws-darkmode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/images/integrations/azure-active-directory.svg b/apps/web/src/images/integrations/azure-active-directory.svg new file mode 100644 index 0000000000..22ea64f1f0 --- /dev/null +++ b/apps/web/src/images/integrations/azure-active-directory.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/images/integrations/bitwarden-vertical-blue.svg b/apps/web/src/images/integrations/bitwarden-vertical-blue.svg new file mode 100644 index 0000000000..5d5200364d --- /dev/null +++ b/apps/web/src/images/integrations/bitwarden-vertical-blue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/images/integrations/jumpcloud-darkmode.svg b/apps/web/src/images/integrations/jumpcloud-darkmode.svg new file mode 100644 index 0000000000..6969fceeb8 --- /dev/null +++ b/apps/web/src/images/integrations/jumpcloud-darkmode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/images/integrations/logo-auth0-badge-color.svg b/apps/web/src/images/integrations/logo-auth0-badge-color.svg new file mode 100644 index 0000000000..24887cc751 --- /dev/null +++ b/apps/web/src/images/integrations/logo-auth0-badge-color.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/images/integrations/logo-duo-color.svg b/apps/web/src/images/integrations/logo-duo-color.svg new file mode 100644 index 0000000000..0959a21570 --- /dev/null +++ b/apps/web/src/images/integrations/logo-duo-color.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/images/integrations/logo-elastic-badge-color.svg b/apps/web/src/images/integrations/logo-elastic-badge-color.svg new file mode 100644 index 0000000000..f6e00f3d40 --- /dev/null +++ b/apps/web/src/images/integrations/logo-elastic-badge-color.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/images/integrations/logo-google-badge-color.svg b/apps/web/src/images/integrations/logo-google-badge-color.svg new file mode 100644 index 0000000000..c5a8fe5036 --- /dev/null +++ b/apps/web/src/images/integrations/logo-google-badge-color.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/images/integrations/logo-jumpcloud-badge-color.svg b/apps/web/src/images/integrations/logo-jumpcloud-badge-color.svg new file mode 100644 index 0000000000..9349186d8a --- /dev/null +++ b/apps/web/src/images/integrations/logo-jumpcloud-badge-color.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/images/integrations/logo-keycloak-icon.svg b/apps/web/src/images/integrations/logo-keycloak-icon.svg new file mode 100644 index 0000000000..862ffcb6c2 --- /dev/null +++ b/apps/web/src/images/integrations/logo-keycloak-icon.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/src/images/integrations/logo-microsoft-entra-id-color.svg b/apps/web/src/images/integrations/logo-microsoft-entra-id-color.svg new file mode 100644 index 0000000000..a6150c29c6 --- /dev/null +++ b/apps/web/src/images/integrations/logo-microsoft-entra-id-color.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/images/integrations/logo-microsoft-intune-color.svg b/apps/web/src/images/integrations/logo-microsoft-intune-color.svg new file mode 100644 index 0000000000..2611cf4b3b --- /dev/null +++ b/apps/web/src/images/integrations/logo-microsoft-intune-color.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/images/integrations/logo-microsoft-sentinel-color.svg b/apps/web/src/images/integrations/logo-microsoft-sentinel-color.svg new file mode 100644 index 0000000000..93135526c6 --- /dev/null +++ b/apps/web/src/images/integrations/logo-microsoft-sentinel-color.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/images/integrations/logo-okta-symbol-black.svg b/apps/web/src/images/integrations/logo-okta-symbol-black.svg new file mode 100644 index 0000000000..876727ad56 --- /dev/null +++ b/apps/web/src/images/integrations/logo-okta-symbol-black.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/images/integrations/logo-onelogin-badge-color.svg b/apps/web/src/images/integrations/logo-onelogin-badge-color.svg new file mode 100644 index 0000000000..e2d9ccbc0c --- /dev/null +++ b/apps/web/src/images/integrations/logo-onelogin-badge-color.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/images/integrations/logo-panther-round-color.svg b/apps/web/src/images/integrations/logo-panther-round-color.svg new file mode 100644 index 0000000000..bed0550768 --- /dev/null +++ b/apps/web/src/images/integrations/logo-panther-round-color.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/images/integrations/logo-ping-identity-badge-color.svg b/apps/web/src/images/integrations/logo-ping-identity-badge-color.svg new file mode 100644 index 0000000000..e34762c249 --- /dev/null +++ b/apps/web/src/images/integrations/logo-ping-identity-badge-color.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/images/integrations/logo-rapid7-black.svg b/apps/web/src/images/integrations/logo-rapid7-black.svg new file mode 100644 index 0000000000..e2bb7a6f4a --- /dev/null +++ b/apps/web/src/images/integrations/logo-rapid7-black.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/images/integrations/logo-splunk-black.svg b/apps/web/src/images/integrations/logo-splunk-black.svg new file mode 100644 index 0000000000..d25247bfca --- /dev/null +++ b/apps/web/src/images/integrations/logo-splunk-black.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/images/integrations/okta-darkmode.svg b/apps/web/src/images/integrations/okta-darkmode.svg new file mode 100644 index 0000000000..e16e0d3c70 --- /dev/null +++ b/apps/web/src/images/integrations/okta-darkmode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/images/integrations/onelogin-darkmode.svg b/apps/web/src/images/integrations/onelogin-darkmode.svg new file mode 100644 index 0000000000..764b1684fa --- /dev/null +++ b/apps/web/src/images/integrations/onelogin-darkmode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/images/integrations/rapid7-darkmode.svg b/apps/web/src/images/integrations/rapid7-darkmode.svg new file mode 100644 index 0000000000..b5f25aae8b --- /dev/null +++ b/apps/web/src/images/integrations/rapid7-darkmode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/images/integrations/splunk-darkmode.svg b/apps/web/src/images/integrations/splunk-darkmode.svg new file mode 100644 index 0000000000..a4515c0a18 --- /dev/null +++ b/apps/web/src/images/integrations/splunk-darkmode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index cab0e703a7..163c63a6d2 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -6831,6 +6831,10 @@ "message": "Automatically provision users and groups with your preferred identity provider via SCIM provisioning", "description": "the text, 'SCIM', is an acronym and should not be translated." }, + "scimIntegrationDescription": { + "message": "Automatically provision users and groups with your preferred identity provider via SCIM provisioning. Find supported integrations", + "description": "the text, 'SCIM', is an acronym and should not be translated." + }, "scimEnabledCheckboxDesc": { "message": "Enable SCIM", "description": "the text, 'SCIM', is an acronym and should not be translated." @@ -9025,44 +9029,103 @@ "sdksDesc": { "message": "Use Bitwarden Secrets Manager SDK in the following programming languages to build your own applications." }, - "setUpGithubActions": { - "message": "Set up Github Actions" + "singleSignOn": { + "message": "Single sign-on" }, - "setUpKubernetes": { - "message": "Set up Kubernetes" + "ssoDescStart": { + "message": "Configure", + "description": "This represents the beginning of a sentence, broken up to include links. The full sentence will be 'Configure single sign-on for Bitwarden using the implementation guide for your Identity Provider." }, - "setUpGitlabCICD": { - "message": "Set up GitLab CI/CD" + "ssoDescEnd": { + "message": "for Bitwarden using the implementation guide for your Identity Provider.", + "description": "This represents the end of a sentence, broken up to include links. The full sentence will be 'Configure single sign-on for Bitwarden using the implementation guide for your Identity Provider." }, - "setUpAnsible": { - "message": "Set up Ansible" + "userProvisioning":{ + "message": "User provisioning" }, - "rustSDKRepo": { - "message": "View Rust repository" + "scimIntegration": { + "message": "SCIM" }, - "cSharpSDKRepo": { - "message": "View C# repository" + "scimIntegrationDescStart": { + "message": "Configure ", + "description": "This represents the beginning of a sentence, broken up to include links. The full sentence will be 'Configure SCIM (System for Cross-domain Identity Management) to automatically provision users and groups to Bitwarden using the implementation guide for your Identity Provider" }, - "cPlusPlusSDKRepo": { - "message": "View C++ repository" + "scimIntegrationDescEnd": { + "message": "(System for Cross-domain Identity Management) to automatically provision users and groups to Bitwarden using the implementation guide for your Identity Provider.", + "description": "This represents the end of a sentence, broken up to include links. The full sentence will be 'Configure SCIM (System for Cross-domain Identity Management) to automatically provision users and groups to Bitwarden using the implementation guide for your Identity Provider" }, - "jsWebAssemblySDKRepo": { - "message": "View JS WebAssembly repository" + "bwdc":{ + "message": "Bitwarden Directory Connector" }, - "javaSDKRepo": { - "message": "View Java repository" + "bwdcDesc": { + "message": "Configure Bitwarden Directory Connector to automatically provision users and groups using the implementation guide for your Identity Provider." }, - "pythonSDKRepo": { - "message": "View Python repository" + "eventManagement":{ + "message": "Event management" }, - "phpSDKRepo": { - "message": "View php repository" + "eventManagementDesc":{ + "message": "Integrate Bitwarden event logs with your SIEM (system information and event management) system by using the implementation guide for your platform." }, - "rubySDKRepo": { - "message": "View Ruby repository" + "deviceManagement":{ + "message": "Device management" }, - "goSDKRepo": { - "message": "View Go repository" + "deviceManagementDesc":{ + "message": "Configure device management for Bitwarden using the implementation guide for your platform." + + }, + "integrationCardTooltip":{ + "message": "Launch $INTEGRATION$ implementation guide.", + "placeholders": { + "integration": { + "content": "$1", + "example": "Google" + } + } + }, + "smIntegrationTooltip":{ + "message": "Set up $INTEGRATION$.", + "placeholders": { + "integration": { + "content": "$1", + "example": "Google" + } + } + }, + "smSdkTooltip":{ + "message": "View $SDK$ repository", + "placeholders": { + "sdk": { + "content": "$1", + "example": "Rust" + } + } + }, + "integrationCardAriaLabel":{ + "message": "open $INTEGRATION$ implementation guide in a new tab.", + "placeholders": { + "integration": { + "content": "$1", + "example": "google" + } + } + }, + "smSdkAriaLabel":{ + "message": "view $SDK$ repository in a new tab.", + "placeholders": { + "sdk": { + "content": "$1", + "example": "rust" + } + } + }, + "smIntegrationCardAriaLabel":{ + "message": "set up $INTEGRATION$ implementation guide in a new tab.", + "placeholders": { + "integration": { + "content": "$1", + "example": "google" + } + } }, "createNewClientToManageAsProvider": { "message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle." diff --git a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/scim.component.html b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/scim.component.html index e30883515e..7ade2e6c63 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/scim.component.html +++ b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/scim.component.html @@ -1,6 +1,11 @@ -

{{ "scimDescription" | i18n }}

+

+ {{ "scimIntegrationDescription" | i18n }} + +

{{ "integrationsDesc" | i18n }}

- +
@@ -12,5 +16,9 @@ {{ "sdks" | i18n }}

{{ "sdksDesc" | i18n }}

- +
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.spec.ts b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.spec.ts index 6c8ea28bc2..e4a65f7ddd 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.spec.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.spec.ts @@ -4,14 +4,17 @@ import { By } from "@angular/platform-browser"; import { mock } from "jest-mock-extended"; import { of } from "rxjs"; +import { SharedModule } from "@bitwarden/components/src/shared"; +import { + IntegrationCardComponent, + IntegrationGridComponent, +} from "@bitwarden/web-vault/app/shared"; + import { SYSTEM_THEME_OBSERVABLE } from "../../../../../../libs/angular/src/services/injection-tokens"; import { I18nService } from "../../../../../../libs/common/src/platform/abstractions/i18n.service"; import { ThemeType } from "../../../../../../libs/common/src/platform/enums"; import { ThemeStateService } from "../../../../../../libs/common/src/platform/theming/theme-state.service"; -import { I18nPipe } from "../../../../../../libs/components/src/shared/i18n.pipe"; -import { IntegrationCardComponent } from "./integration-card/integration-card.component"; -import { IntegrationGridComponent } from "./integration-grid/integration-grid.component"; import { IntegrationsComponent } from "./integrations.component"; @Component({ @@ -31,18 +34,12 @@ describe("IntegrationsComponent", () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ - IntegrationsComponent, - IntegrationGridComponent, - IntegrationCardComponent, - MockHeaderComponent, - MockNewMenuComponent, - I18nPipe, - ], + declarations: [IntegrationsComponent, MockHeaderComponent, MockNewMenuComponent], + imports: [IntegrationGridComponent, IntegrationCardComponent, SharedModule], providers: [ { provide: I18nService, - useValue: mock({ t: (key) => key }), + useValue: mock(), }, { provide: ThemeStateService, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.ts index 9e846d4503..b8f9386d71 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.ts @@ -1,9 +1,7 @@ import { Component } from "@angular/core"; import { IntegrationType } from "@bitwarden/common/enums"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; - -import { Integration } from "./models/integration"; +import { Integration } from "@bitwarden/web-vault/app/shared"; @Component({ selector: "sm-integrations", @@ -12,11 +10,10 @@ import { Integration } from "./models/integration"; export class IntegrationsComponent { private integrationsAndSdks: Integration[] = []; - constructor(i18nService: I18nService) { + constructor() { this.integrationsAndSdks = [ { name: "Rust", - linkText: i18nService.t("rustSDKRepo"), linkURL: "https://github.com/bitwarden/sdk", image: "../../../../../../../images/secrets-manager/sdks/rust.svg", imageDarkMode: "../../../../../../../images/secrets-manager/sdks/rust-white.svg", @@ -24,7 +21,6 @@ export class IntegrationsComponent { }, { name: "GitHub Actions", - linkText: i18nService.t("setUpGithubActions"), linkURL: "https://bitwarden.com/help/github-actions-integration/", image: "../../../../../../../images/secrets-manager/integrations/github.svg", imageDarkMode: "../../../../../../../images/secrets-manager/integrations/github-white.svg", @@ -32,7 +28,6 @@ export class IntegrationsComponent { }, { name: "GitLab CI/CD", - linkText: i18nService.t("setUpGitlabCICD"), linkURL: "https://bitwarden.com/help/gitlab-integration/", image: "../../../../../../../images/secrets-manager/integrations/gitlab.svg", imageDarkMode: "../../../../../../../images/secrets-manager/integrations/gitlab-white.svg", @@ -40,35 +35,30 @@ export class IntegrationsComponent { }, { name: "Ansible", - linkText: i18nService.t("setUpAnsible"), linkURL: "https://bitwarden.com/help/ansible-integration/", image: "../../../../../../../images/secrets-manager/integrations/ansible.svg", type: IntegrationType.Integration, }, { name: "C#", - linkText: i18nService.t("cSharpSDKRepo"), linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/csharp", image: "../../../../../../../images/secrets-manager/sdks/c-sharp.svg", type: IntegrationType.SDK, }, { name: "C++", - linkText: i18nService.t("cPlusPlusSDKRepo"), linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/cpp", image: "../../../../../../../images/secrets-manager/sdks/c-plus-plus.png", type: IntegrationType.SDK, }, { name: "Go", - linkText: i18nService.t("goSDKRepo"), linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/go", image: "../../../../../../../images/secrets-manager/sdks/go.svg", type: IntegrationType.SDK, }, { name: "Java", - linkText: i18nService.t("javaSDKRepo"), linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/java", image: "../../../../../../../images/secrets-manager/sdks/java.svg", imageDarkMode: "../../../../../../../images/secrets-manager/sdks/java-white.svg", @@ -76,35 +66,30 @@ export class IntegrationsComponent { }, { name: "JS WebAssembly", - linkText: i18nService.t("jsWebAssemblySDKRepo"), linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/js", image: "../../../../../../../images/secrets-manager/sdks/wasm.svg", type: IntegrationType.SDK, }, { name: "php", - linkText: i18nService.t("phpSDKRepo"), linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/php", image: "../../../../../../../images/secrets-manager/sdks/php.svg", type: IntegrationType.SDK, }, { name: "Python", - linkText: i18nService.t("pythonSDKRepo"), linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/python", image: "../../../../../../../images/secrets-manager/sdks/python.svg", type: IntegrationType.SDK, }, { name: "Ruby", - linkText: i18nService.t("rubySDKRepo"), linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/ruby", image: "../../../../../../../images/secrets-manager/sdks/ruby.png", type: IntegrationType.SDK, }, { name: "Kubernetes Operator", - linkText: i18nService.t("setUpKubernetes"), linkURL: "https://bitwarden.com/help/secrets-manager-kubernetes-operator/", image: "../../../../../../../images/secrets-manager/integrations/kubernetes.svg", type: IntegrationType.Integration, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.module.ts b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.module.ts index 0d26b626f1..b79892f5ed 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.module.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.module.ts @@ -1,15 +1,22 @@ import { NgModule } from "@angular/core"; +import { + IntegrationCardComponent, + IntegrationGridComponent, +} from "@bitwarden/web-vault/app/shared"; + import { SecretsManagerSharedModule } from "../shared/sm-shared.module"; -import { IntegrationCardComponent } from "./integration-card/integration-card.component"; -import { IntegrationGridComponent } from "./integration-grid/integration-grid.component"; import { IntegrationsRoutingModule } from "./integrations-routing.module"; import { IntegrationsComponent } from "./integrations.component"; @NgModule({ - imports: [SecretsManagerSharedModule, IntegrationsRoutingModule], - declarations: [IntegrationsComponent, IntegrationGridComponent, IntegrationCardComponent], - providers: [], + imports: [ + SecretsManagerSharedModule, + IntegrationsRoutingModule, + IntegrationCardComponent, + IntegrationGridComponent, + ], + declarations: [IntegrationsComponent], }) export class IntegrationsModule {} diff --git a/bitwarden_license/bit-web/tsconfig.json b/bitwarden_license/bit-web/tsconfig.json index 3ccdade273..09de92d355 100644 --- a/bitwarden_license/bit-web/tsconfig.json +++ b/bitwarden_license/bit-web/tsconfig.json @@ -46,6 +46,7 @@ "../../apps/web/src/**/*.spec.ts", "../../libs/common/src/platform/services/**/*.worker.ts", - "src/**/*.stories.ts" + "src/**/*.stories.ts", + "src/**/*.spec.ts" ] } diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index f9630aba04..96283b0000 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -32,6 +32,7 @@ export enum FeatureFlag { VerifiedSsoDomainEndpoint = "pm-12337-refactor-sso-details-endpoint", PM11901_RefactorSelfHostingLicenseUploader = "PM-11901-refactor-self-hosting-license-uploader", AccessIntelligence = "pm-13227-access-intelligence", + PM14505AdminConsoleIntegrationPage = "pm-14505-admin-console-integration-page", CriticalApps = "pm-14466-risk-insights-critical-application", TrialPaymentOptional = "PM-8163-trial-payment", SecurityTasks = "security-tasks", @@ -81,6 +82,7 @@ export const DefaultFeatureFlagValue = { [FeatureFlag.VerifiedSsoDomainEndpoint]: FALSE, [FeatureFlag.PM11901_RefactorSelfHostingLicenseUploader]: FALSE, [FeatureFlag.AccessIntelligence]: FALSE, + [FeatureFlag.PM14505AdminConsoleIntegrationPage]: FALSE, [FeatureFlag.CriticalApps]: FALSE, [FeatureFlag.TrialPaymentOptional]: FALSE, [FeatureFlag.SecurityTasks]: FALSE, diff --git a/libs/common/src/enums/integration-type.enum.ts b/libs/common/src/enums/integration-type.enum.ts index acb9510697..42c385fe71 100644 --- a/libs/common/src/enums/integration-type.enum.ts +++ b/libs/common/src/enums/integration-type.enum.ts @@ -1,4 +1,9 @@ export enum IntegrationType { Integration = "integration", SDK = "sdk", + SSO = "sso", + SCIM = "scim", + BWDC = "bwdc", + EVENT = "event", + DEVICE = "device", }