diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index a9d2d75d64c..2d29efcc89e 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -1922,6 +1922,9 @@ "typeSshKey": { "message": "SSH key" }, + "typeNote": { + "message": "Note" + }, "newItemHeader": { "message": "New $TYPE$", "placeholders": { diff --git a/apps/browser/src/autofill/popup/settings/autofill.component.ts b/apps/browser/src/autofill/popup/settings/autofill.component.ts index 2b58c32c926..852b79cad1d 100644 --- a/apps/browser/src/autofill/popup/settings/autofill.component.ts +++ b/apps/browser/src/autofill/popup/settings/autofill.component.ts @@ -45,6 +45,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service"; import { CipherType } from "@bitwarden/common/vault/enums"; +import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; import { CardComponent, CheckboxModule, @@ -58,7 +59,6 @@ import { SelectModule, TypographyModule, } from "@bitwarden/components"; -import { RestrictedItemTypesService } from "@bitwarden/vault"; import { AutofillBrowserSettingsService } from "../../../autofill/services/autofill-browser-settings.service"; import { BrowserApi } from "../../../platform/browser/browser-api"; diff --git a/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.spec.ts index cc97027c82e..7e3db27640e 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.spec.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.spec.ts @@ -12,8 +12,11 @@ import { Utils } from "@bitwarden/common/platform/misc/utils"; import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { CipherType } from "@bitwarden/common/vault/enums"; +import { + RestrictedCipherType, + RestrictedItemTypesService, +} from "@bitwarden/common/vault/services/restricted-item-types.service"; import { ButtonModule, DialogService, MenuModule, NoItemsModule } from "@bitwarden/components"; -import { RestrictedCipherType, RestrictedItemTypesService } from "@bitwarden/vault"; import { BrowserApi } from "../../../../../platform/browser/browser-api"; import BrowserPopupUtils from "../../../../../platform/popup/browser-popup-utils"; diff --git a/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.ts index caffd5e7119..fd7a0c4672b 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.ts @@ -8,9 +8,10 @@ import { map, Observable } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid"; import { CipherType } from "@bitwarden/common/vault/enums"; +import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; import { CipherMenuItem, CIPHER_MENU_ITEMS } from "@bitwarden/common/vault/types/cipher-menu-items"; import { ButtonModule, DialogService, MenuModule, NoItemsModule } from "@bitwarden/components"; -import { AddEditFolderDialogComponent, RestrictedItemTypesService } from "@bitwarden/vault"; +import { AddEditFolderDialogComponent } from "@bitwarden/vault"; import { BrowserApi } from "../../../../../platform/browser/browser-api"; import BrowserPopupUtils from "../../../../../platform/popup/browser-popup-utils"; diff --git a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts index f8351fe0f61..8b2786fab77 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts @@ -20,7 +20,10 @@ import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folde import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; -import { RestrictedCipherType, RestrictedItemTypesService } from "@bitwarden/vault"; +import { + RestrictedCipherType, + RestrictedItemTypesService, +} from "@bitwarden/common/vault/services/restricted-item-types.service"; import { CachedFilterState, diff --git a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts index a3e5fc4c2bd..9f7363afd7e 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts @@ -39,9 +39,12 @@ import { ITreeNodeObject, TreeNode } from "@bitwarden/common/vault/models/domain import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { ServiceUtils } from "@bitwarden/common/vault/service-utils"; +import { + isCipherViewRestricted, + RestrictedItemTypesService, +} from "@bitwarden/common/vault/services/restricted-item-types.service"; import { CIPHER_MENU_ITEMS } from "@bitwarden/common/vault/types/cipher-menu-items"; import { ChipSelectOption } from "@bitwarden/components"; -import { RestrictedItemTypesService } from "@bitwarden/vault"; const FILTER_VISIBILITY_KEY = new KeyDefinition(VAULT_SETTINGS_DISK, "filterVisibility", { deserializer: (obj) => obj, @@ -227,18 +230,8 @@ export class VaultPopupListFiltersService { } // Check if cipher type is restricted (with organization exemptions) - if (restrictions && restrictions.length > 0) { - const isRestricted = restrictions.some( - (restrictedType) => - restrictedType.cipherType === cipher.type && - (cipher.organizationId - ? !restrictedType.allowViewOrgIds.includes(cipher.organizationId) - : restrictedType.allowViewOrgIds.length === 0), - ); - - if (isRestricted) { - return false; - } + if (isCipherViewRestricted(cipher, restrictions)) { + return false; } if (filters.cipherType !== null && cipher.type !== filters.cipherType) { diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index 1685de7d8d4..1431ab72020 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -23,6 +23,9 @@ "typeIdentity": { "message": "Identity" }, + "typeNote": { + "message": "Note" + }, "typeSecureNote": { "message": "Secure note" }, diff --git a/apps/desktop/src/vault/app/vault/add-edit.component.html b/apps/desktop/src/vault/app/vault/add-edit.component.html index 9c316813d1d..2cd384885ce 100644 --- a/apps/desktop/src/vault/app/vault/add-edit.component.html +++ b/apps/desktop/src/vault/app/vault/add-edit.component.html @@ -12,7 +12,9 @@
diff --git a/apps/desktop/src/vault/app/vault/add-edit.component.ts b/apps/desktop/src/vault/app/vault/add-edit.component.ts index eb04003a418..e9b18270f2d 100644 --- a/apps/desktop/src/vault/app/vault/add-edit.component.ts +++ b/apps/desktop/src/vault/app/vault/add-edit.component.ts @@ -3,6 +3,7 @@ import { DatePipe } from "@angular/common"; import { Component, NgZone, OnChanges, OnDestroy, OnInit, ViewChild } from "@angular/core"; import { NgForm } from "@angular/forms"; +import { map, shareReplay } from "rxjs"; import { CollectionService } from "@bitwarden/admin-console/common"; import { AddEditComponent as BaseAddEditComponent } from "@bitwarden/angular/vault/components/add-edit.component"; @@ -22,6 +23,8 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; +import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; +import { CIPHER_MENU_ITEMS } from "@bitwarden/common/vault/types/cipher-menu-items"; import { DialogService, ToastService } from "@bitwarden/components"; import { PasswordRepromptService, SshImportPromptService } from "@bitwarden/vault"; @@ -35,6 +38,18 @@ const BroadcasterSubscriptionId = "AddEditComponent"; export class AddEditComponent extends BaseAddEditComponent implements OnInit, OnChanges, OnDestroy { @ViewChild("form") private form: NgForm; + menuItems$ = this.restrictedItemTypesService.restricted$.pipe( + map((restrictedItemTypes) => + // Filter out restricted item types from the default CIPHER_MENU_ITEMS array + CIPHER_MENU_ITEMS.filter( + (typeOption) => + !restrictedItemTypes.some( + (restrictedType) => restrictedType.cipherType === typeOption.type, + ), + ), + ), + shareReplay({ bufferSize: 1, refCount: true }), + ); constructor( cipherService: CipherService, @@ -59,6 +74,7 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On cipherAuthorizationService: CipherAuthorizationService, sdkService: SdkService, sshImportPromptService: SshImportPromptService, + protected restrictedItemTypesService: RestrictedItemTypesService, ) { super( cipherService, diff --git a/apps/desktop/src/vault/app/vault/vault-filter/filters/type-filter.component.html b/apps/desktop/src/vault/app/vault/vault-filter/filters/type-filter.component.html index c3dcd191dfc..f8a83e01266 100644 --- a/apps/desktop/src/vault/app/vault/vault-filter/filters/type-filter.component.html +++ b/apps/desktop/src/vault/app/vault/vault-filter/filters/type-filter.component.html @@ -20,78 +20,20 @@
diff --git a/apps/desktop/src/vault/app/vault/vault-filter/filters/type-filter.component.ts b/apps/desktop/src/vault/app/vault/vault-filter/filters/type-filter.component.ts index 5920233b206..27e7d5c5ecb 100644 --- a/apps/desktop/src/vault/app/vault/vault-filter/filters/type-filter.component.ts +++ b/apps/desktop/src/vault/app/vault/vault-filter/filters/type-filter.component.ts @@ -1,6 +1,9 @@ import { Component } from "@angular/core"; +import { map, shareReplay } from "rxjs"; import { TypeFilterComponent as BaseTypeFilterComponent } from "@bitwarden/angular/vault/vault-filter/components/type-filter.component"; +import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; +import { CIPHER_MENU_ITEMS } from "@bitwarden/common/vault/types/cipher-menu-items"; @Component({ selector: "app-type-filter", @@ -8,7 +11,22 @@ import { TypeFilterComponent as BaseTypeFilterComponent } from "@bitwarden/angul standalone: false, }) export class TypeFilterComponent extends BaseTypeFilterComponent { - constructor() { + protected typeFilters$ = this.restrictedItemTypesService.restricted$.pipe( + map((restrictedItemTypes) => + // Filter out restricted item types from the typeFilters array + CIPHER_MENU_ITEMS.filter( + (typeFilter) => + !restrictedItemTypes.some( + (restrictedType) => + restrictedType.allowViewOrgIds.length === 0 && + restrictedType.cipherType === typeFilter.type, + ), + ), + ), + shareReplay({ bufferSize: 1, refCount: true }), + ); + + constructor(private restrictedItemTypesService: RestrictedItemTypesService) { super(); } } diff --git a/apps/desktop/src/vault/app/vault/vault-items-v2.component.html b/apps/desktop/src/vault/app/vault/vault-items-v2.component.html index 63e648e3cf3..fcf38ee39bc 100644 --- a/apps/desktop/src/vault/app/vault/vault-items-v2.component.html +++ b/apps/desktop/src/vault/app/vault/vault-items-v2.component.html @@ -72,25 +72,11 @@ - - - - - + @for (itemTypes of itemTypes$ | async; track itemTypes.type) { + + } diff --git a/apps/desktop/src/vault/app/vault/vault-items-v2.component.ts b/apps/desktop/src/vault/app/vault/vault-items-v2.component.ts index 5a832ed79b0..1256c9e52e8 100644 --- a/apps/desktop/src/vault/app/vault/vault-items-v2.component.ts +++ b/apps/desktop/src/vault/app/vault/vault-items-v2.component.ts @@ -10,6 +10,7 @@ import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; import { MenuModule } from "@bitwarden/components"; import { SearchBarService } from "../../../app/layout/search/search-bar.service"; @@ -25,8 +26,9 @@ export class VaultItemsV2Component extends BaseVaultItemsComponent { private readonly searchBarService: SearchBarService, cipherService: CipherService, accountService: AccountService, + restrictedItemTypesService: RestrictedItemTypesService, ) { - super(searchService, cipherService, accountService); + super(searchService, cipherService, accountService, restrictedItemTypesService); this.searchBarService.searchText$ .pipe(distinctUntilChanged(), takeUntilDestroyed()) diff --git a/apps/desktop/src/vault/app/vault/vault-items.component.ts b/apps/desktop/src/vault/app/vault/vault-items.component.ts index 2d1ba784753..8bf4955343d 100644 --- a/apps/desktop/src/vault/app/vault/vault-items.component.ts +++ b/apps/desktop/src/vault/app/vault/vault-items.component.ts @@ -8,6 +8,7 @@ import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; import { SearchBarService } from "../../../app/layout/search/search-bar.service"; @@ -22,8 +23,9 @@ export class VaultItemsComponent extends BaseVaultItemsComponent { searchBarService: SearchBarService, cipherService: CipherService, accountService: AccountService, + protected restrictedItemTypesService: RestrictedItemTypesService, ) { - super(searchService, cipherService, accountService); + super(searchService, cipherService, accountService, restrictedItemTypesService); // eslint-disable-next-line rxjs-angular/prefer-takeuntil searchBarService.searchText$.pipe(distinctUntilChanged()).subscribe((searchText) => { 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 ff6ec9af0af..49bf43d60bf 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 @@ -11,8 +11,8 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; +import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; import { DialogService, ToastService } from "@bitwarden/components"; -import { RestrictedItemTypesService } from "@bitwarden/vault"; import { VaultFilterComponent as BaseVaultFilterComponent } from "../../../../vault/individual-vault/vault-filter/components/vault-filter.component"; import { VaultFilterService } from "../../../../vault/individual-vault/vault-filter/services/abstractions/vault-filter.service"; diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts b/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts index 62b53d71e84..e65d423a57b 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts @@ -35,8 +35,8 @@ import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view"; import { LoginView } from "@bitwarden/common/vault/models/view/login.view"; import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; +import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; import { LayoutComponent } from "@bitwarden/components"; -import { RestrictedItemTypesService } from "@bitwarden/vault"; import { GroupView } from "../../../admin-console/organizations/core"; import { PreloadedEnglishI18nModule } from "../../../core/tests"; diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.ts b/apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.ts index 8987fff04cf..72766817eeb 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.ts @@ -22,8 +22,8 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; +import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; import { DialogService, ToastService } from "@bitwarden/components"; -import { RestrictedItemTypesService } from "@bitwarden/vault"; import { TrialFlowService } from "../../../../billing/services/trial-flow.service"; import { VaultFilterService } from "../services/abstractions/vault-filter.service"; diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/filter-function.spec.ts b/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/filter-function.spec.ts index 660aeb293a4..2ec2b2c40a9 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/filter-function.spec.ts +++ b/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/filter-function.spec.ts @@ -3,7 +3,7 @@ import { Unassigned } from "@bitwarden/admin-console/common"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; -import { RestrictedCipherType } from "@bitwarden/vault"; +import { RestrictedCipherType } from "@bitwarden/common/vault/services/restricted-item-types.service"; import { createFilterFunction } from "./filter-function"; import { All } from "./routed-vault-filter.model"; diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/filter-function.ts b/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/filter-function.ts index 61305fa5e49..93071aecae3 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/filter-function.ts +++ b/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/filter-function.ts @@ -1,7 +1,10 @@ import { Unassigned } from "@bitwarden/admin-console/common"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; -import { RestrictedCipherType } from "@bitwarden/vault"; +import { + isCipherViewRestricted, + RestrictedCipherType, +} from "@bitwarden/common/vault/services/restricted-item-types.service"; import { All, RoutedVaultFilterModel } from "./routed-vault-filter.model"; @@ -83,24 +86,9 @@ export function createFilterFunction( ) { return false; } - // Restricted types - if (restrictedTypes && restrictedTypes.length > 0) { - // Filter the cipher if that type is restricted unless - // - The cipher belongs to an organization and that organization allows viewing the cipher type - // OR - // - The cipher belongs to the user's personal vault and at least one other organization does not restrict that type - if ( - restrictedTypes.some( - (restrictedType) => - restrictedType.cipherType === cipher.type && - (cipher.organizationId - ? !restrictedType.allowViewOrgIds.includes(cipher.organizationId) - : restrictedType.allowViewOrgIds.length === 0), - ) - ) { - return false; - } + if (restrictedTypes && isCipherViewRestricted(cipher, restrictedTypes)) { + return false; } return true; }; diff --git a/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.ts b/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.ts index 48bc3a4268b..49e159143dd 100644 --- a/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.ts @@ -18,13 +18,13 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; +import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; import { BreadcrumbsModule, DialogService, MenuModule, SimpleDialogOptions, } from "@bitwarden/components"; -import { RestrictedItemTypesService } from "@bitwarden/vault"; import { CollectionDialogTabType } from "../../../admin-console/organizations/shared/components/collection-dialog"; import { HeaderModule } from "../../../layouts/header/header.module"; diff --git a/apps/web/src/app/vault/individual-vault/vault.component.ts b/apps/web/src/app/vault/individual-vault/vault.component.ts index 2c9079c7279..3d59a186705 100644 --- a/apps/web/src/app/vault/individual-vault/vault.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault.component.ts @@ -66,6 +66,7 @@ import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-repromp import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { ServiceUtils } from "@bitwarden/common/vault/service-utils"; +import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; import { filterOutNullish } from "@bitwarden/common/vault/utils/observable-utilities"; import { DialogRef, DialogService, Icons, ToastService } from "@bitwarden/components"; import { @@ -79,7 +80,6 @@ import { DecryptionFailureDialogComponent, DefaultCipherFormConfigService, PasswordRepromptService, - RestrictedItemTypesService, } from "@bitwarden/vault"; import { diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 873cb4f9b63..c1c4844a61d 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -294,6 +294,7 @@ import { DefaultCipherEncryptionService } from "@bitwarden/common/vault/services import { CipherFileUploadService } from "@bitwarden/common/vault/services/file-upload/cipher-file-upload.service"; import { FolderApiService } from "@bitwarden/common/vault/services/folder/folder-api.service"; import { FolderService } from "@bitwarden/common/vault/services/folder/folder.service"; +import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; import { TotpService } from "@bitwarden/common/vault/services/totp.service"; import { VaultSettingsService } from "@bitwarden/common/vault/services/vault-settings/vault-settings.service"; import { DefaultTaskService, TaskService } from "@bitwarden/common/vault/tasks"; @@ -680,6 +681,11 @@ const safeProviders: SafeProvider[] = [ KdfConfigService, ], }), + safeProvider({ + provide: RestrictedItemTypesService, + useClass: RestrictedItemTypesService, + deps: [ConfigService, AccountService, OrganizationServiceAbstraction, PolicyServiceAbstraction], + }), safeProvider({ provide: PasswordStrengthServiceAbstraction, useClass: PasswordStrengthService, diff --git a/libs/angular/src/vault/components/add-edit.component.ts b/libs/angular/src/vault/components/add-edit.component.ts index 5d6343b0b3c..ec79ac9ef18 100644 --- a/libs/angular/src/vault/components/add-edit.component.ts +++ b/libs/angular/src/vault/components/add-edit.component.ts @@ -84,7 +84,6 @@ export class AddEditComponent implements OnInit, OnDestroy { showCardNumber = false; showCardCode = false; cipherType = CipherType; - typeOptions: any[]; cardBrandOptions: any[]; cardExpMonthOptions: any[]; identityTitleOptions: any[]; @@ -139,13 +138,6 @@ export class AddEditComponent implements OnInit, OnDestroy { protected sdkService: SdkService, private sshImportPromptService: SshImportPromptService, ) { - this.typeOptions = [ - { name: i18nService.t("typeLogin"), value: CipherType.Login }, - { name: i18nService.t("typeCard"), value: CipherType.Card }, - { name: i18nService.t("typeIdentity"), value: CipherType.Identity }, - { name: i18nService.t("typeSecureNote"), value: CipherType.SecureNote }, - ]; - this.cardBrandOptions = [ { name: "-- " + i18nService.t("select") + " --", value: null }, { name: "Visa", value: "Visa" }, @@ -215,8 +207,6 @@ export class AddEditComponent implements OnInit, OnDestroy { this.writeableCollections = await this.loadCollections(); this.canUseReprompt = await this.passwordRepromptService.enabled(); - - this.typeOptions.push({ name: this.i18nService.t("typeSshKey"), value: CipherType.SshKey }); } ngOnDestroy() { diff --git a/libs/angular/src/vault/components/vault-items.component.ts b/libs/angular/src/vault/components/vault-items.component.ts index c34816994be..db9ac581d41 100644 --- a/libs/angular/src/vault/components/vault-items.component.ts +++ b/libs/angular/src/vault/components/vault-items.component.ts @@ -8,7 +8,9 @@ import { combineLatest, filter, from, + map, of, + shareReplay, switchMap, takeUntil, } from "rxjs"; @@ -20,6 +22,11 @@ import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { + isCipherViewRestricted, + RestrictedItemTypesService, +} from "@bitwarden/common/vault/services/restricted-item-types.service"; +import { CIPHER_MENU_ITEMS } from "@bitwarden/common/vault/types/cipher-menu-items"; @Directive() export class VaultItemsComponent implements OnInit, OnDestroy { @@ -35,6 +42,19 @@ export class VaultItemsComponent implements OnInit, OnDestroy { organization: Organization; CipherType = CipherType; + protected itemTypes$ = this.restrictedItemTypesService.restricted$.pipe( + map((restrictedItemTypes) => + // Filter out restricted item types + CIPHER_MENU_ITEMS.filter( + (itemType) => + !restrictedItemTypes.some( + (restrictedType) => restrictedType.cipherType === itemType.type, + ), + ), + ), + shareReplay({ bufferSize: 1, refCount: true }), + ); + protected searchPending = false; /** Construct filters as an observable so it can be appended to the cipher stream. */ @@ -62,6 +82,7 @@ export class VaultItemsComponent implements OnInit, OnDestroy { protected searchService: SearchService, protected cipherService: CipherService, protected accountService: AccountService, + protected restrictedItemTypesService: RestrictedItemTypesService, ) { this.subscribeToCiphers(); } @@ -143,18 +164,22 @@ export class VaultItemsComponent implements OnInit, OnDestroy { this._searchText$, this._filter$, of(userId), + this.restrictedItemTypesService.restricted$, ]), ), - switchMap(([indexedCiphers, failedCiphers, searchText, filter, userId]) => { + switchMap(([indexedCiphers, failedCiphers, searchText, filter, userId, restricted]) => { let allCiphers = indexedCiphers ?? []; const _failedCiphers = failedCiphers ?? []; allCiphers = [..._failedCiphers, ...allCiphers]; + const restrictedTypeFilter = (cipher: CipherView) => + isCipherViewRestricted(cipher, restricted); + return this.searchService.searchCiphers( userId, searchText, - [filter, this.deletedFilter], + [filter, this.deletedFilter, restrictedTypeFilter], allCiphers, ); }), diff --git a/libs/common/src/vault/service-utils.ts b/libs/common/src/vault/service-utils.ts index 96ae406fae4..9595434223f 100644 --- a/libs/common/src/vault/service-utils.ts +++ b/libs/common/src/vault/service-utils.ts @@ -1,5 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore + import { ITreeNodeObject, TreeNode } from "./models/domain/tree-node"; export class ServiceUtils { diff --git a/libs/vault/src/services/restricted-item-types.service.spec.ts b/libs/common/src/vault/services/restricted-item-types.service.spec.ts similarity index 89% rename from libs/vault/src/services/restricted-item-types.service.spec.ts rename to libs/common/src/vault/services/restricted-item-types.service.spec.ts index 7ff48f0642b..9b549665184 100644 --- a/libs/vault/src/services/restricted-item-types.service.spec.ts +++ b/libs/common/src/vault/services/restricted-item-types.service.spec.ts @@ -1,4 +1,3 @@ -import { TestBed } from "@angular/core/testing"; import { mock, MockProxy } from "jest-mock-extended"; import { firstValueFrom, of } from "rxjs"; @@ -49,19 +48,16 @@ describe("RestrictedItemTypesService", () => { fakeAccount = { id: Utils.newGuid() as UserId } as Account; accountService.activeAccount$ = of(fakeAccount); - TestBed.configureTestingModule({ - providers: [ - { provide: PolicyService, useValue: policyService }, - { provide: OrganizationService, useValue: organizationService }, - { provide: AccountService, useValue: accountService }, - { provide: ConfigService, useValue: configService }, - ], - }); - configService.getFeatureFlag$.mockReturnValue(of(true)); organizationService.organizations$.mockReturnValue(of([org1, org2])); policyService.policiesByType$.mockReturnValue(of([])); - service = TestBed.inject(RestrictedItemTypesService); + + service = new RestrictedItemTypesService( + configService, + accountService, + organizationService, + policyService, + ); }); it("emits empty array when feature flag is disabled", async () => { @@ -106,7 +102,6 @@ describe("RestrictedItemTypesService", () => { }); it("returns empty allowViewOrgIds when all orgs restrict the same type", async () => { - configService.getFeatureFlag$.mockReturnValue(of(true)); organizationService.organizations$.mockReturnValue(of([org1, org2])); policyService.policiesByType$.mockReturnValue(of([policyOrg1, policyOrg2])); @@ -117,7 +112,6 @@ describe("RestrictedItemTypesService", () => { }); it("aggregates multiple types and computes allowViewOrgIds correctly", async () => { - configService.getFeatureFlag$.mockReturnValue(of(true)); organizationService.organizations$.mockReturnValue(of([org1, org2])); policyService.policiesByType$.mockReturnValue( of([ diff --git a/libs/vault/src/services/restricted-item-types.service.ts b/libs/common/src/vault/services/restricted-item-types.service.ts similarity index 79% rename from libs/vault/src/services/restricted-item-types.service.ts rename to libs/common/src/vault/services/restricted-item-types.service.ts index b24533fb2f6..63c9577bc09 100644 --- a/libs/vault/src/services/restricted-item-types.service.ts +++ b/libs/common/src/vault/services/restricted-item-types.service.ts @@ -1,4 +1,3 @@ -import { Injectable } from "@angular/core"; import { combineLatest, map, of, Observable } from "rxjs"; import { switchMap, distinctUntilChanged, shareReplay } from "rxjs/operators"; @@ -10,13 +9,13 @@ import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { CipherType } from "@bitwarden/common/vault/enums"; +import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; export type RestrictedCipherType = { cipherType: CipherType; allowViewOrgIds: string[]; }; -@Injectable({ providedIn: "root" }) export class RestrictedItemTypesService { /** * Emits an array of RestrictedCipherType objects: @@ -78,3 +77,25 @@ export class RestrictedItemTypesService { private policyService: PolicyService, ) {} } + +/** + * Filter that returns whether a cipher is restricted from being viewed by the user + * Criteria: + * - the cipher's type is restricted by at least one org + * UNLESS + * - the cipher belongs to an organization and that organization does not restrict that type + * OR + * - the cipher belongs to the user's personal vault and at least one other organization does not restrict that type + */ +export function isCipherViewRestricted( + cipher: CipherView, + restrictedTypes: RestrictedCipherType[], +) { + return restrictedTypes.some( + (restrictedType) => + restrictedType.cipherType === cipher.type && + (cipher.organizationId + ? !restrictedType.allowViewOrgIds.includes(cipher.organizationId) + : restrictedType.allowViewOrgIds.length === 0), + ); +} diff --git a/libs/common/src/vault/types/cipher-menu-items.ts b/libs/common/src/vault/types/cipher-menu-items.ts index e88c0457081..7108d0d0bd6 100644 --- a/libs/common/src/vault/types/cipher-menu-items.ts +++ b/libs/common/src/vault/types/cipher-menu-items.ts @@ -19,6 +19,6 @@ export const CIPHER_MENU_ITEMS = Object.freeze([ { type: CipherType.Login, icon: "bwi-globe", labelKey: "typeLogin" }, { type: CipherType.Card, icon: "bwi-credit-card", labelKey: "typeCard" }, { type: CipherType.Identity, icon: "bwi-id-card", labelKey: "typeIdentity" }, - { type: CipherType.SecureNote, icon: "bwi-sticky-note", labelKey: "note" }, + { type: CipherType.SecureNote, icon: "bwi-sticky-note", labelKey: "typeNote" }, { type: CipherType.SshKey, icon: "bwi-key", labelKey: "typeSshKey" }, ] as const) satisfies readonly CipherMenuItem[]; diff --git a/libs/vault/src/index.ts b/libs/vault/src/index.ts index 7229b558f30..b39bb85ab30 100644 --- a/libs/vault/src/index.ts +++ b/libs/vault/src/index.ts @@ -24,10 +24,6 @@ export * as VaultIcons from "./icons"; export { DefaultSshImportPromptService } from "./services/default-ssh-import-prompt.service"; export { SshImportPromptService } from "./services/ssh-import-prompt.service"; -export { - RestrictedItemTypesService, - RestrictedCipherType, -} from "./services/restricted-item-types.service"; export * from "./abstractions/change-login-password.service"; export * from "./services/default-change-login-password.service";