1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-06 00:13:28 +00:00

[PM-23825] setup crowdstrike card (#15728)

This commit is contained in:
Vijay Oommen
2025-07-24 08:53:03 -05:00
committed by GitHub
parent d0d1359ff4
commit df8e0ed094
9 changed files with 153 additions and 16 deletions

View File

@@ -1,8 +1,8 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Component, OnInit } from "@angular/core";
import { Component, OnDestroy, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { Observable, switchMap } from "rxjs";
import { Observable, Subject, switchMap, takeUntil } from "rxjs";
import {
getOrganizationById,
@@ -11,6 +11,8 @@ import {
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { IntegrationType } from "@bitwarden/common/enums";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { HeaderModule } from "../../../layouts/header/header.module";
import { SharedModule } from "../../../shared/shared.module";
@@ -30,10 +32,12 @@ import { Integration } from "../shared/components/integrations/models";
FilterIntegrationsPipe,
],
})
export class AdminConsoleIntegrationsComponent implements OnInit {
export class AdminConsoleIntegrationsComponent implements OnInit, OnDestroy {
integrationsList: Integration[] = [];
tabIndex: number;
organization$: Observable<Organization>;
isEventBasedIntegrationsEnabled: boolean = false;
private destroy$ = new Subject<void>();
ngOnInit(): void {
this.organization$ = this.route.params.pipe(
@@ -53,7 +57,15 @@ export class AdminConsoleIntegrationsComponent implements OnInit {
private route: ActivatedRoute,
private organizationService: OrganizationService,
private accountService: AccountService,
private configService: ConfigService,
) {
this.configService
.getFeatureFlag$(FeatureFlag.EventBasedOrganizationIntegrations)
.pipe(takeUntil(this.destroy$))
.subscribe((isEnabled) => {
this.isEventBasedIntegrationsEnabled = isEnabled;
});
this.integrationsList = [
{
name: "AD FS",
@@ -229,6 +241,22 @@ export class AdminConsoleIntegrationsComponent implements OnInit {
type: IntegrationType.DEVICE,
},
];
if (this.isEventBasedIntegrationsEnabled) {
this.integrationsList.push({
name: "Crowdstrike",
linkURL: "",
image: "../../../../../../../images/integrations/logo-crowdstrike-black.svg",
type: IntegrationType.EVENT,
description: "crowdstrikeEventIntegrationDesc",
isConnected: false,
canSetupConnection: true,
});
}
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
get IntegrationType(): typeof IntegrationType {

View File

@@ -17,16 +17,40 @@
</div>
</div>
<div class="tw-p-5">
<h3 class="tw-text-main tw-text-lg tw-font-semibold">{{ name }}</h3>
<a
class="tw-block tw-mb-0 tw-font-bold hover:tw-no-underline focus:tw-outline-none after:tw-content-[''] after:tw-block after:tw-absolute after:tw-size-full after:tw-left-0 after:tw-top-0"
[href]="linkURL"
rel="noopener noreferrer"
target="_blank"
>
</a>
<span *ngIf="showNewBadge()" bitBadge class="tw-mt-3" variant="secondary">
{{ "new" | i18n }}
</span>
<h3 class="tw-text-main tw-text-lg tw-font-semibold">
{{ name }}
@if (showConnectedBadge()) {
<span class="tw-ml-3">
@if (isConnected) {
<span bitBadge variant="success">{{ "on" | i18n }}</span>
}
@if (!isConnected) {
<span bitBadge>{{ "off" | i18n }}</span>
}
</span>
}
</h3>
<p class="tw-mb-0">{{ description }}</p>
@if (canSetupConnection) {
<button type="button" class="tw-mt-3" bitButton (click)="setupConnection(name)">
<span>{{ "connectIntegrationButtonDesc" | i18n: name }}</span>
</button>
}
@if (linkURL) {
<a
class="tw-block tw-mb-0 tw-font-bold hover:tw-no-underline focus:tw-outline-none after:tw-content-[''] after:tw-block after:tw-absolute after:tw-size-full after:tw-left-0 after:tw-top-0"
[href]="linkURL"
rel="noopener noreferrer"
target="_blank"
>
</a>
}
@if (showNewBadge()) {
<span bitBadge class="tw-mt-3" variant="secondary">
{{ "new" | i18n }}
</span>
}
</div>
</div>

View File

@@ -16,6 +16,7 @@ import { IntegrationCardComponent } from "./integration-card.component";
describe("IntegrationCardComponent", () => {
let component: IntegrationCardComponent;
let fixture: ComponentFixture<IntegrationCardComponent>;
const mockI18nService = mock<I18nService>();
const systemTheme$ = new BehaviorSubject<ThemeType>(ThemeType.Light);
const usersPreferenceTheme$ = new BehaviorSubject<ThemeType>(ThemeType.Light);
@@ -41,7 +42,7 @@ describe("IntegrationCardComponent", () => {
},
{
provide: I18nService,
useValue: mock<I18nService>(),
useValue: mockI18nService,
},
],
}).compileComponents();
@@ -55,6 +56,7 @@ describe("IntegrationCardComponent", () => {
component.image = "test-image.png";
component.linkURL = "https://example.com/";
mockI18nService.t.mockImplementation((key) => key);
fixture.detectChanges();
});
@@ -67,7 +69,7 @@ describe("IntegrationCardComponent", () => {
it("renders card body", () => {
const name = fixture.nativeElement.querySelector("h3");
expect(name.textContent).toBe("Integration Name");
expect(name.textContent).toContain("Integration Name");
});
it("assigns external rel attribute", () => {
@@ -182,4 +184,28 @@ describe("IntegrationCardComponent", () => {
});
});
});
describe("connected badge", () => {
it("shows connected badge when isConnected is true", () => {
component.isConnected = true;
expect(component.showConnectedBadge()).toBe(true);
});
it("does not show connected badge when isConnected is false", () => {
component.isConnected = false;
fixture.detectChanges();
const name = fixture.nativeElement.querySelector("h3 > span > span > span");
expect(name.textContent).toContain("off");
// when isConnected is true/false, the badge should be shown as on/off
// when isConnected is undefined, the badge should not be shown
expect(component.showConnectedBadge()).toBe(true);
});
it("does not show connected badge when isConnected is undefined", () => {
component.isConnected = undefined;
expect(component.showConnectedBadge()).toBe(false);
});
});
});

View File

@@ -41,6 +41,9 @@ export class IntegrationCardComponent implements AfterViewInit, OnDestroy {
* @example "2024-12-31"
*/
@Input() newBadgeExpiration?: string;
@Input() description?: string;
@Input() isConnected?: boolean;
@Input() canSetupConnection?: boolean;
constructor(
private themeStateService: ThemeStateService,
@@ -93,4 +96,14 @@ export class IntegrationCardComponent implements AfterViewInit, OnDestroy {
return expirationDate > new Date();
}
showConnectedBadge(): boolean {
return this.isConnected !== undefined;
}
setupConnection(app: string) {
// This method can be used to handle the connection logic for the integration
// For example, it could open a modal or redirect to a setup page
this.isConnected = !this.isConnected; // Toggle connection state for demonstration
}
}

View File

@@ -13,6 +13,9 @@
[imageDarkMode]="integration.imageDarkMode"
[externalURL]="integration.type === IntegrationType.SDK"
[newBadgeExpiration]="integration.newBadgeExpiration"
[description]="integration.description | i18n"
[isConnected]="integration.isConnected"
[canSetupConnection]="integration.canSetupConnection"
></app-integration-card>
</li>
</ul>

View File

@@ -17,4 +17,7 @@ export type Integration = {
* @example "2024-12-31"
*/
newBadgeExpiration?: string;
description?: string;
isConnected?: boolean;
canSetupConnection?: boolean;
};

View File

@@ -0,0 +1,22 @@
<svg width="185" height="41" viewBox="0 0 185 41" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_265_4075)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M29.944 23.2703V23.6027C28.8221 24.9324 27.3635 25.8189 25.7928 26.2621C24.1099 24.9324 22.4269 23.7135 20.5196 22.4946C19.9586 22.1621 19.5099 21.8297 18.8367 21.3865C17.8269 20.7216 16.5928 19.9459 15.3586 19.0594C15.3586 19.0594 15.3586 18.9486 15.3586 18.8378C15.3586 18.8378 15.3586 18.6162 15.3586 18.5054C15.3586 13.9621 18.9489 10.6378 23.6611 10.6378C26.1294 10.6378 28.3733 11.7459 29.944 13.5189V13.8513L27.1391 16.1784H26.8025C26.0172 15.2919 24.783 14.7378 23.5489 14.7378C22.5391 14.7378 21.6416 15.0703 20.9684 15.8459C20.2952 16.5108 19.9586 17.5081 19.9586 18.5054C19.9586 19.5027 20.2952 20.3892 20.9684 21.1648C21.6416 21.8297 22.5391 22.273 23.5489 22.273C24.783 22.273 25.905 21.7189 26.8025 20.8324H27.1391L29.944 23.2703ZM21.5294 26.2621C19.7343 25.9297 18.1635 24.9324 17.0416 23.4919C17.1538 23.4919 17.266 23.6027 17.3782 23.7135C17.6025 23.8243 17.7147 23.9351 17.9391 24.0459C19.0611 24.8216 20.2952 25.4865 21.5294 26.1513V26.2621Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M46.1002 22.273L46.7734 25.2649V25.5973C46.1002 26.0406 45.427 26.2622 44.6416 26.2622C42.7343 26.2622 41.7246 25.819 40.3782 23.8244C39.7051 22.8271 38.4709 20.7217 37.349 20.7217H36.9002V26.0406H32.188V10.8595H39.3685C43.5197 10.8595 45.7636 12.8541 45.7636 15.9568C45.7636 17.8406 44.6416 19.5027 42.8465 20.1676C43.2953 20.5 43.7441 20.9433 44.0807 21.3865C44.4173 21.8298 44.6416 22.3838 45.2026 22.3838C45.427 22.3838 45.6514 22.3838 45.8758 22.273H46.1002ZM40.827 16.0676C40.827 16.5108 40.7148 16.8433 40.3782 17.1757C40.0416 17.5081 39.5929 17.619 39.1441 17.619H36.9002V14.5163H39.1441C39.5929 14.5163 40.0416 14.6271 40.3782 14.9595C40.7148 15.2919 40.827 15.7352 40.827 16.0676Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M55.1878 10.7487C60.0122 10.7487 63.6025 14.073 63.6025 18.5054C63.6025 22.9378 60.0122 26.2622 55.1878 26.2622C50.3634 26.2622 46.7732 22.9378 46.7732 18.5054C46.7732 14.073 50.3634 10.7487 55.1878 10.7487ZM57.0951 21.8297C58.2171 21.1649 59.0025 19.946 58.8903 18.5054C58.8903 17.1757 58.2171 15.9568 57.0951 15.1811C55.9732 14.5162 54.5147 14.5162 53.2805 15.1811C52.1586 15.846 51.3732 17.0649 51.4854 18.5054C51.4854 19.8351 52.1586 21.0541 53.2805 21.8297C54.4025 22.4946 55.861 22.4946 57.0951 21.8297Z" fill="black"/>
<path d="M85.8172 10.7487H81.5538L78.9733 19.3919L76.1684 10.7487H73.4757L70.6708 19.2811L68.0903 10.7487H63.8269L63.6025 11.0811L69.1001 26.2622H71.7928L74.8221 17.8405L77.9635 26.2622H80.6562L86.1538 11.0811L85.9294 10.7487H85.8172Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M95.1293 10.7487C99.9537 10.7487 102.871 13.8514 102.871 18.5054C102.871 23.1595 99.9537 26.2622 95.1293 26.2622H88.2854V10.7487H95.1293ZM94.9049 22.4946C96.8122 22.4946 98.2708 21.0541 98.2708 18.5054C98.2708 15.9568 96.8122 14.5162 94.9049 14.5162H92.661V22.3838H94.9049V22.4946Z" fill="black"/>
<path d="M128.339 14.6271H133.163V10.8595H118.578V14.6271H123.403V26.373H128.227V14.6271H128.339Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M141.241 10.7487C145.281 10.7487 147.412 12.7433 147.412 15.9568C147.412 17.9514 146.29 19.3919 144.607 20.1676L147.749 25.9297L147.524 26.2622H142.924L140.007 20.8324H138.885V26.2622H134.285V10.7487H141.241ZM142.7 16.1784C142.7 16.6216 142.588 17.0649 142.251 17.2865C141.915 17.6189 141.466 17.7297 141.129 17.7297H138.998V14.627H141.129C141.578 14.627 142.027 14.7378 142.251 15.0703C142.588 15.4027 142.7 15.846 142.7 16.2892V16.1784Z" fill="black"/>
<path d="M149.993 10.7487H154.48V26.2622H149.993V10.7487Z" fill="black"/>
<path d="M165.588 17.7298L170.861 11.1919L170.637 10.8595H165.7L161.325 16.4V10.8595H156.612V26.373H161.325V21.6081L162.222 20.6108L166.149 26.373H171.085L171.31 26.0406L165.588 17.8406V17.7298Z" fill="black"/>
<path d="M184.661 14.6271V10.8595H173.554V26.373H184.773V22.6054H178.042V20.5H183.09V16.9541H178.042V14.8487H184.773L184.661 14.6271Z" fill="black"/>
<path d="M110.949 26.2622C114.427 26.2622 117.456 24.6 117.456 21.4973C117.456 18.3946 114.427 17.2865 111.734 16.5108C110.724 16.1784 109.602 15.846 109.602 15.1811C109.602 14.5162 110.163 14.2946 111.173 14.2946C112.744 14.2946 114.09 15.1811 114.763 15.846H115.1L117.344 13.2973V12.9649C116.11 11.746 113.754 10.7487 111.061 10.7487C108.368 10.7487 104.778 12.6324 104.778 15.4027C104.778 18.173 107.583 19.6135 110.051 20.2784C111.622 20.7216 112.632 20.7216 112.632 21.6081C112.632 22.4946 111.846 22.4946 110.724 22.4946C109.602 22.4946 107.471 21.6081 106.573 20.8324H106.237L103.993 23.4919V23.8243C105.451 25.3757 107.92 26.373 110.949 26.373" fill="black"/>
<path d="M32.1878 40.6675C31.0659 38.2297 28.822 35.0162 20.0707 30.4729C16.0317 28.2567 9.18782 24.9324 3.01709 18.5054C3.57807 20.8324 6.49514 25.9297 18.8366 32.2459C22.2025 34.0189 28.0366 35.7919 32.1878 40.6675Z" fill="black"/>
<path d="M31.0659 36.2352C30.0561 33.2433 28.261 29.4757 19.5098 23.9352C15.2464 21.1649 8.96344 17.619 0.773193 8.53247C1.33417 10.9703 3.91466 17.2865 17.0415 25.4865C21.3049 28.4784 26.9147 30.2514 31.0659 36.2352Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_265_4075">
<rect width="184" height="41" fill="white" transform="translate(0.773193)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -9478,6 +9478,9 @@
"deviceManagementDesc": {
"message": "Configure device management for Bitwarden using the implementation guide for your platform."
},
"crowdstrikeEventIntegrationDesc": {
"message": "Send event data to your Logscale instance"
},
"deviceIdMissing": {
"message": "Device ID is missing"
},
@@ -9493,6 +9496,15 @@
"reopenLinkOnDesktop": {
"message": "Reopen this link from your email on a desktop."
},
"connectIntegrationButtonDesc": {
"message": "Connect $INTEGRATION$",
"placeholders": {
"integration": {
"content": "$1",
"example": "Crowdstrike"
}
}
},
"integrationCardTooltip": {
"message": "Launch $INTEGRATION$ implementation guide.",
"placeholders": {

View File

@@ -46,6 +46,9 @@ export enum FeatureFlag {
/* Tools */
DesktopSendUIRefresh = "desktop-send-ui-refresh",
/* DIRT */
EventBasedOrganizationIntegrations = "event-based-organization-integrations",
/* Vault */
PM8851_BrowserOnboardingNudge = "pm-8851-browser-onboarding-nudge",
PM9111ExtensionPersistAddEditForm = "pm-9111-extension-persist-add-edit-form",
@@ -89,6 +92,9 @@ export const DefaultFeatureFlagValue = {
/* Tools */
[FeatureFlag.DesktopSendUIRefresh]: FALSE,
/* DIRT */
[FeatureFlag.EventBasedOrganizationIntegrations]: FALSE,
/* Vault */
[FeatureFlag.PM8851_BrowserOnboardingNudge]: FALSE,
[FeatureFlag.PM9111ExtensionPersistAddEditForm]: FALSE,