diff --git a/README.md b/README.md index 22c8d329f1c..cdeaa4c8cf7 100644 --- a/README.md +++ b/README.md @@ -13,15 +13,15 @@ # Bitwarden Client Applications -This repository houses all Bitwarden client applications except the [Mobile application](https://github.com/bitwarden/mobile). +This repository houses all Bitwarden client applications except the mobile applications ([iOS](https://github.com/bitwarden/ios) | [android](https://github.com/bitwarden/android)). Please refer to the [Clients section](https://contributing.bitwarden.com/getting-started/clients/) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started. ## Related projects: - [bitwarden/server](https://github.com/bitwarden/server): The core infrastructure backend (API, database, Docker, etc). -- [bitwarden/ios](https://github.com/bitwarden/ios): Bitwarden mobile app for iOS. -- [bitwarden/android](https://github.com/bitwarden/android): Bitwarden mobile app for Android. +- [bitwarden/ios](https://github.com/bitwarden/ios): Bitwarden iOS Password Manager & Authenticator apps. +- [bitwarden/android](https://github.com/bitwarden/android): Bitwarden Android Password Manager & Authenticator apps. - [bitwarden/directory-connector](https://github.com/bitwarden/directory-connector): A tool for syncing a directory (AD, LDAP, Azure, G Suite, Okta) to an organization. # We're Hiring! diff --git a/apps/browser/package.json b/apps/browser/package.json index 5a8ddd03b41..6ef35d88c10 100644 --- a/apps/browser/package.json +++ b/apps/browser/package.json @@ -1,6 +1,6 @@ { "name": "@bitwarden/browser", - "version": "2025.3.1", + "version": "2025.3.2", "scripts": { "build": "npm run build:chrome", "build:chrome": "cross-env BROWSER=chrome MANIFEST_VERSION=3 NODE_OPTIONS=\"--max-old-space-size=8192\" webpack", diff --git a/apps/browser/src/auth/popup/settings/account-security.component.html b/apps/browser/src/auth/popup/settings/account-security.component.html index 3c5fd7a6af8..b8252aa6e13 100644 --- a/apps/browser/src/auth/popup/settings/account-security.component.html +++ b/apps/browser/src/auth/popup/settings/account-security.component.html @@ -95,7 +95,7 @@ - +

{{ "otherOptions" | i18n }}

diff --git a/apps/browser/src/autofill/popup/settings/autofill.component.html b/apps/browser/src/autofill/popup/settings/autofill.component.html index 340197f6bf3..c690eb3d2ca 100644 --- a/apps/browser/src/autofill/popup/settings/autofill.component.html +++ b/apps/browser/src/autofill/popup/settings/autofill.component.html @@ -209,7 +209,7 @@
- +

{{ "additionalOptions" | i18n }}

@@ -270,7 +270,7 @@
- + {{ "blockedDomains" | i18n }} diff --git a/apps/browser/src/autofill/popup/settings/notifications.component.html b/apps/browser/src/autofill/popup/settings/notifications.component.html index c6446012d0c..385db8c059b 100644 --- a/apps/browser/src/autofill/popup/settings/notifications.component.html +++ b/apps/browser/src/autofill/popup/settings/notifications.component.html @@ -47,7 +47,7 @@ - + {{ "excludedDomains" | i18n }} diff --git a/apps/browser/src/manifest.json b/apps/browser/src/manifest.json index 4510c2f342d..af2bf72b7b5 100644 --- a/apps/browser/src/manifest.json +++ b/apps/browser/src/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "__MSG_extName__", "short_name": "__MSG_appName__", - "version": "2025.3.1", + "version": "2025.3.2", "description": "__MSG_extDesc__", "default_locale": "en", "author": "Bitwarden Inc.", diff --git a/apps/browser/src/manifest.v3.json b/apps/browser/src/manifest.v3.json index fc897c1b1c3..131724e38f9 100644 --- a/apps/browser/src/manifest.v3.json +++ b/apps/browser/src/manifest.v3.json @@ -3,7 +3,7 @@ "minimum_chrome_version": "102.0", "name": "__MSG_extName__", "short_name": "__MSG_appName__", - "version": "2025.3.1", + "version": "2025.3.2", "description": "__MSG_extDesc__", "default_locale": "en", "author": "Bitwarden Inc.", diff --git a/apps/browser/src/platform/popup/layout/popup-page.component.html b/apps/browser/src/platform/popup/layout/popup-page.component.html index 94f0846a852..2313b942a38 100644 --- a/apps/browser/src/platform/popup/layout/popup-page.component.html +++ b/apps/browser/src/platform/popup/layout/popup-page.component.html @@ -19,7 +19,7 @@ [ngClass]="{ 'tw-invisible': loading }" >
diff --git a/apps/desktop/src/autofill/services/ssh-agent.service.ts b/apps/desktop/src/autofill/services/ssh-agent.service.ts index bf7167c0240..6522ef19500 100644 --- a/apps/desktop/src/autofill/services/ssh-agent.service.ts +++ b/apps/desktop/src/autofill/services/ssh-agent.service.ts @@ -143,7 +143,10 @@ export class SshAgentService implements OnDestroy { if (isListRequest) { const sshCiphers = ciphers.filter( - (cipher) => cipher.type === CipherType.SshKey && !cipher.isDeleted, + (cipher) => + cipher.type === CipherType.SshKey && + !cipher.isDeleted && + cipher.organizationId == null, ); const keys = sshCiphers.map((cipher) => { return { @@ -247,7 +250,7 @@ export class SshAgentService implements OnDestroy { (cipher) => cipher.type === CipherType.SshKey && !cipher.isDeleted && - cipher.organizationId === null, + cipher.organizationId == null, ); const keys = sshCiphers.map((cipher) => { return { diff --git a/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.component.ts b/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.component.ts index 43d8f910d0f..384390d738e 100644 --- a/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.component.ts +++ b/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.component.ts @@ -89,8 +89,8 @@ export class VaultFilterComponent const collapsedNodes = await firstValueFrom(this.vaultFilterService.collapsedFilterNodes$); collapsedNodes.delete("AllCollections"); - - await this.vaultFilterService.setCollapsedFilterNodes(collapsedNodes); + const userId = await firstValueFrom(this.activeUserId$); + await this.vaultFilterService.setCollapsedFilterNodes(collapsedNodes, userId); } protected async addCollectionFilter(): Promise { diff --git a/apps/web/src/app/admin-console/organizations/collections/vault.component.html b/apps/web/src/app/admin-console/organizations/collections/vault.component.html index 65cd26bafee..604d326bf37 100644 --- a/apps/web/src/app/admin-console/organizations/collections/vault.component.html +++ b/apps/web/src/app/admin-console/organizations/collections/vault.component.html @@ -54,7 +54,7 @@ (searchTextChanged)="filterSearchText($event)" >
-
+
(null); private destroy$ = new Subject(); protected addAccessStatus$ = new BehaviorSubject(0); - private resellerManagedOrgAlert: boolean; private vaultItemDialogRef?: DialogRef | undefined; private readonly unpaidSubscriptionDialog$ = this.accountService.activeAccount$.pipe( @@ -264,10 +262,6 @@ export class VaultComponent implements OnInit, OnDestroy { async ngOnInit() { this.userId = await firstValueFrom(getUserId(this.accountService.activeAccount$)); - this.resellerManagedOrgAlert = await this.configService.getFeatureFlag( - FeatureFlag.ResellerManagedOrgAlert, - ); - this.trashCleanupWarning = this.i18nService.t( this.platformUtilsService.isSelfHost() ? "trashCleanupWarningSelfHosted" @@ -654,7 +648,7 @@ export class VaultComponent implements OnInit, OnDestroy { ); this.resellerWarning$ = organization$.pipe( - filter((org) => org.isOwner && this.resellerManagedOrgAlert), + filter((org) => org.isOwner), switchMap((org) => from(this.billingApiService.getOrganizationBillingMetadata(org.id)).pipe( map((metadata) => ({ org, metadata })), diff --git a/apps/web/src/app/admin-console/organizations/manage/events.component.html b/apps/web/src/app/admin-console/organizations/manage/events.component.html index 4a48ae1ead7..adadec5075a 100644 --- a/apps/web/src/app/admin-console/organizations/manage/events.component.html +++ b/apps/web/src/app/admin-console/organizations/manage/events.component.html @@ -111,7 +111,7 @@
{{ "characterMaximum" | i18n: 100 }} - + {{ "externalId" | i18n }} {{ "externalIdDesc" | i18n }} diff --git a/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts b/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts index 330ffe86f0b..d1a9d4919e5 100644 --- a/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts +++ b/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts @@ -29,7 +29,9 @@ import { } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; @@ -215,6 +217,10 @@ export class GroupAddEditComponent implements OnInit, OnDestroy { this.groupDetails$, ]).pipe(map(([allowAdminAccess, groupDetails]) => !allowAdminAccess && groupDetails != null)); + protected isExternalIdVisible$ = this.configService + .getFeatureFlag$(FeatureFlag.SsoExternalIdVisibility) + .pipe(map((isEnabled) => !isEnabled || !!this.groupForm.get("externalId")?.value)); + constructor( @Inject(DIALOG_DATA) private params: GroupAddEditDialogParams, private dialogRef: DialogRef, @@ -231,6 +237,7 @@ export class GroupAddEditComponent implements OnInit, OnDestroy { private accountService: AccountService, private collectionAdminService: CollectionAdminService, private toastService: ToastService, + private configService: ConfigService, ) { this.tabIndex = params.initialTab ?? GroupAddEditTabType.Info; } diff --git a/apps/web/src/app/auth/core/services/index.ts b/apps/web/src/app/auth/core/services/index.ts index 1e8eec759b1..11c8dd98872 100644 --- a/apps/web/src/app/auth/core/services/index.ts +++ b/apps/web/src/app/auth/core/services/index.ts @@ -4,3 +4,4 @@ export * from "./webauthn-login"; export * from "./set-password-jit"; export * from "./registration"; export * from "./two-factor-auth"; +export * from "./link-sso.service"; diff --git a/apps/web/src/app/auth/core/services/link-sso.service.spec.ts b/apps/web/src/app/auth/core/services/link-sso.service.spec.ts new file mode 100644 index 00000000000..70b52999875 --- /dev/null +++ b/apps/web/src/app/auth/core/services/link-sso.service.spec.ts @@ -0,0 +1,154 @@ +import { mock, MockProxy } from "jest-mock-extended"; +import { BehaviorSubject } from "rxjs"; + +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; +import { SsoPreValidateResponse } from "@bitwarden/common/auth/models/response/sso-pre-validate.response"; +import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; +import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { + PasswordGenerationServiceAbstraction, + PasswordGeneratorOptions, +} from "@bitwarden/generator-legacy"; + +import { LinkSsoService } from "./link-sso.service"; + +describe("LinkSsoService", () => { + let sut: LinkSsoService; + + let mockSsoLoginService: MockProxy; + let mockApiService: MockProxy; + let mockCryptoFunctionService: MockProxy; + let mockEnvironmentService: MockProxy; + let mockPasswordGenerationService: MockProxy; + let mockPlatformUtilsService: MockProxy; + + const mockEnvironment$ = new BehaviorSubject({ + getIdentityUrl: jest.fn().mockReturnValue("https://identity.bitwarden.com"), + }); + + beforeEach(() => { + // Create mock implementations + mockSsoLoginService = mock(); + mockApiService = mock(); + mockCryptoFunctionService = mock(); + mockEnvironmentService = mock(); + mockPasswordGenerationService = mock(); + mockPlatformUtilsService = mock(); + + // Set up environment service to return our mock environment + mockEnvironmentService.environment$ = mockEnvironment$; + + // Set up API service mocks + const mockResponse = { Token: "mockSsoToken" }; + mockApiService.preValidateSso.mockResolvedValue(new SsoPreValidateResponse(mockResponse)); + mockApiService.getSsoUserIdentifier.mockResolvedValue("mockUserIdentifier"); + + // Set up password generation service mock + mockPasswordGenerationService.generatePassword.mockImplementation( + async (options: PasswordGeneratorOptions) => { + return "mockGeneratedPassword"; + }, + ); + + // Set up crypto function service mock + mockCryptoFunctionService.hash.mockResolvedValue(new Uint8Array([1, 2, 3, 4])); + + // Create the service under test with mock dependencies + sut = new LinkSsoService( + mockSsoLoginService, + mockApiService, + mockCryptoFunctionService, + mockEnvironmentService, + mockPasswordGenerationService, + mockPlatformUtilsService, + ); + + // Mock Utils.fromBufferToUrlB64 + jest.spyOn(Utils, "fromBufferToUrlB64").mockReturnValue("mockCodeChallenge"); + + // Mock window.location + Object.defineProperty(window, "location", { + value: { + origin: "https://bitwarden.com", + }, + writable: true, + }); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe("linkSso", () => { + it("throws an error when identifier is null", async () => { + await expect(sut.linkSso(null as unknown as string)).rejects.toThrow( + "SSO identifier is required", + ); + }); + + it("throws an error when identifier is empty", async () => { + await expect(sut.linkSso("")).rejects.toThrow("SSO identifier is required"); + }); + + it("calls preValidateSso with the provided identifier", async () => { + await sut.linkSso("org123"); + + expect(mockApiService.preValidateSso).toHaveBeenCalledWith("org123"); + }); + + it("generates a password for code verifier", async () => { + await sut.linkSso("org123"); + + expect(mockPasswordGenerationService.generatePassword).toHaveBeenCalledWith({ + type: "password", + length: 64, + uppercase: true, + lowercase: true, + number: true, + special: false, + }); + }); + + it("sets the code verifier in the ssoLoginService", async () => { + await sut.linkSso("org123"); + + expect(mockSsoLoginService.setCodeVerifier).toHaveBeenCalledWith("mockGeneratedPassword"); + }); + + it("generates a state and sets it in the ssoLoginService", async () => { + await sut.linkSso("org123"); + + const expectedState = + "mockGeneratedPassword_returnUri='/settings/organizations'_identifier=org123"; + expect(mockSsoLoginService.setSsoState).toHaveBeenCalledWith(expectedState); + }); + + it("gets the SSO user identifier from the API", async () => { + await sut.linkSso("org123"); + + expect(mockApiService.getSsoUserIdentifier).toHaveBeenCalled(); + }); + + it("launches the authorize URL with the correct parameters", async () => { + await sut.linkSso("org123"); + + expect(mockPlatformUtilsService.launchUri).toHaveBeenCalledWith( + expect.stringContaining("https://identity.bitwarden.com/connect/authorize"), + { sameWindow: true }, + ); + + const launchUriArg = mockPlatformUtilsService.launchUri.mock.calls[0][0]; + expect(launchUriArg).toContain("client_id=web"); + expect(launchUriArg).toContain( + "redirect_uri=https%3A%2F%2Fbitwarden.com%2Fsso-connector.html", + ); + expect(launchUriArg).toContain("response_type=code"); + expect(launchUriArg).toContain("code_challenge=mockCodeChallenge"); + expect(launchUriArg).toContain("ssoToken=mockSsoToken"); + expect(launchUriArg).toContain("user_identifier=mockUserIdentifier"); + }); + }); +}); diff --git a/apps/web/src/app/auth/core/services/link-sso.service.ts b/apps/web/src/app/auth/core/services/link-sso.service.ts new file mode 100644 index 00000000000..3d51525add1 --- /dev/null +++ b/apps/web/src/app/auth/core/services/link-sso.service.ts @@ -0,0 +1,91 @@ +import { firstValueFrom } from "rxjs"; + +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; +import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; +import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { + PasswordGenerationServiceAbstraction, + PasswordGeneratorOptions, +} from "@bitwarden/generator-legacy"; + +/** + * Provides a service for linking SSO. + */ +export class LinkSsoService { + constructor( + private ssoLoginService: SsoLoginServiceAbstraction, + private apiService: ApiService, + private cryptoFunctionService: CryptoFunctionService, + private environmentService: EnvironmentService, + private passwordGenerationService: PasswordGenerationServiceAbstraction, + private platformUtilsService: PlatformUtilsService, + ) {} + + /** + * Links SSO to an organization. + * Ported from the SsoComponent + * @param identifier The identifier of the organization to link to. + */ + async linkSso(identifier: string) { + if (identifier == null || identifier === "") { + throw new Error("SSO identifier is required"); + } + + const redirectUri = window.location.origin + "/sso-connector.html"; + const clientId = "web"; + const returnUri = "/settings/organizations"; + + const response = await this.apiService.preValidateSso(identifier); + + const passwordOptions: PasswordGeneratorOptions = { + type: "password", + length: 64, + uppercase: true, + lowercase: true, + number: true, + special: false, + }; + + const codeVerifier = await this.passwordGenerationService.generatePassword(passwordOptions); + const codeVerifierHash = await this.cryptoFunctionService.hash(codeVerifier, "sha256"); + const codeChallenge = Utils.fromBufferToUrlB64(codeVerifierHash); + await this.ssoLoginService.setCodeVerifier(codeVerifier); + + let state = await this.passwordGenerationService.generatePassword(passwordOptions); + state += `_returnUri='${returnUri}'`; + state += `_identifier=${identifier}`; + + // Save state + await this.ssoLoginService.setSsoState(state); + + const env = await firstValueFrom(this.environmentService.environment$); + + let authorizeUrl = + env.getIdentityUrl() + + "/connect/authorize?" + + "client_id=" + + clientId + + "&redirect_uri=" + + encodeURIComponent(redirectUri) + + "&" + + "response_type=code&scope=api offline_access&" + + "state=" + + state + + "&code_challenge=" + + codeChallenge + + "&" + + "code_challenge_method=S256&response_mode=query&" + + "domain_hint=" + + encodeURIComponent(identifier) + + "&ssoToken=" + + encodeURIComponent(response.token); + + const userIdentifier = await this.apiService.getSsoUserIdentifier(); + authorizeUrl += `&user_identifier=${encodeURIComponent(userIdentifier)}`; + + this.platformUtilsService.launchUri(authorizeUrl, { sameWindow: true }); + } +} diff --git a/apps/web/src/app/billing/services/trial-flow.service.ts b/apps/web/src/app/billing/services/trial-flow.service.ts index eb08e5bd7ad..979fc29aed7 100644 --- a/apps/web/src/app/billing/services/trial-flow.service.ts +++ b/apps/web/src/app/billing/services/trial-flow.service.ts @@ -11,8 +11,6 @@ import { BillingSourceResponse } from "@bitwarden/common/billing/models/response import { OrganizationBillingMetadataResponse } from "@bitwarden/common/billing/models/response/organization-billing-metadata.response"; import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response"; import { PaymentSourceResponse } from "@bitwarden/common/billing/models/response/payment-source.response"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { DialogService } from "@bitwarden/components"; @@ -24,15 +22,12 @@ import { FreeTrial } from "../types/free-trial"; @Injectable({ providedIn: "root" }) export class TrialFlowService { - private resellerManagedOrgAlert: boolean; - constructor( private i18nService: I18nService, protected dialogService: DialogService, private router: Router, protected billingApiService: BillingApiServiceAbstraction, private organizationApiService: OrganizationApiServiceAbstraction, - private configService: ConfigService, ) {} checkForOrgsWithUpcomingPaymentIssues( organization: Organization, @@ -98,10 +93,6 @@ export class TrialFlowService { isCanceled: boolean, isUnpaid: boolean, ): Promise { - this.resellerManagedOrgAlert = await this.configService.getFeatureFlag( - FeatureFlag.ResellerManagedOrgAlert, - ); - if (!org?.isOwner && !org.providerId) { await this.dialogService.openSimpleDialog({ title: this.i18nService.t("suspendedOrganizationTitle", org?.name), @@ -113,7 +104,7 @@ export class TrialFlowService { return false; } - if (org.providerId && this.resellerManagedOrgAlert) { + if (org.providerId) { await this.dialogService.openSimpleDialog({ title: this.i18nService.t("suspendedOrganizationTitle", org.name), content: { key: "suspendedManagedOrgMessage", placeholders: [org.providerName] }, @@ -134,7 +125,7 @@ export class TrialFlowService { }); } - if (org.isOwner && isCanceled && this.resellerManagedOrgAlert) { + if (org.isOwner && isCanceled) { await this.changePlan(org); } } diff --git a/apps/web/src/app/core/core.module.ts b/apps/web/src/app/core/core.module.ts index 856ca798c8d..694aa2d4325 100644 --- a/apps/web/src/app/core/core.module.ts +++ b/apps/web/src/app/core/core.module.ts @@ -117,6 +117,7 @@ import { WebLoginDecryptionOptionsService, WebTwoFactorAuthComponentService, WebTwoFactorAuthDuoComponentService, + LinkSsoService, } from "../auth"; import { WebSsoComponentService } from "../auth/core/services/login/web-sso-component.service"; import { WebTwoFactorFormCacheService } from "../auth/core/services/two-factor-auth/web-two-factor-form-cache.service"; @@ -352,6 +353,18 @@ const safeProviders: SafeProvider[] = [ useClass: WebSsoComponentService, deps: [I18nServiceAbstraction], }), + safeProvider({ + provide: LinkSsoService, + useClass: LinkSsoService, + deps: [ + SsoLoginServiceAbstraction, + ApiService, + CryptoFunctionService, + EnvironmentService, + PasswordGenerationServiceAbstraction, + PlatformUtilsService, + ], + }), safeProvider({ provide: TwoFactorAuthDuoComponentService, useClass: WebTwoFactorAuthDuoComponentService, diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/components/link-sso.directive.ts b/apps/web/src/app/vault/individual-vault/vault-filter/components/link-sso.directive.ts deleted file mode 100644 index a1781889c49..00000000000 --- a/apps/web/src/app/vault/individual-vault/vault-filter/components/link-sso.directive.ts +++ /dev/null @@ -1,26 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { AfterContentInit, Directive, HostListener, Input } from "@angular/core"; - -import { SsoComponent } from "@bitwarden/angular/auth/components/sso.component"; -import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; - -@Directive({ - selector: "[app-link-sso]", -}) -export class LinkSsoDirective extends SsoComponent implements AfterContentInit { - @Input() organization: Organization; - returnUri = "/settings/organizations"; - redirectUri = window.location.origin + "/sso-connector.html"; - clientId = "web"; - - @HostListener("click", ["$event"]) - async onClick($event: MouseEvent) { - $event.preventDefault(); - await this.submit(this.returnUri, true); - } - - async ngAfterContentInit() { - this.identifier = this.organization.identifier; - } -} diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/components/organization-options.component.html b/apps/web/src/app/vault/individual-vault/vault-filter/components/organization-options.component.html index 0b94b6e2be2..0fe243ed20a 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/components/organization-options.component.html +++ b/apps/web/src/app/vault/individual-vault/vault-filter/components/organization-options.component.html @@ -50,10 +50,10 @@ {{ "unlinkSso" | i18n }} - +