1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-12 14:23:32 +00:00

[PM-24232] - [Defect][Web] Admin Console - SSH key and Folder should not show as options from New button (#15834)

* properly call input functions

* don't enable sshkey form

* fix logic for disabling cipher form

* prefer use of observable

* use destroyRef. change to enabled status only
This commit is contained in:
Jordan Aasen
2025-07-30 12:15:59 -07:00
committed by GitHub
parent 5dae5267d9
commit 1c5095806f
6 changed files with 32 additions and 5 deletions

View File

@@ -78,6 +78,7 @@
<vault-new-cipher-menu <vault-new-cipher-menu
[canCreateCipher]="true" [canCreateCipher]="true"
[canCreateFolder]="true" [canCreateFolder]="true"
[canCreateSshKey]="true"
[canCreateCollection]="canCreateCollections" [canCreateCollection]="canCreateCollections"
(cipherAdded)="addCipher($event)" (cipherAdded)="addCipher($event)"
(folderAdded)="addFolder()" (folderAdded)="addFolder()"

View File

@@ -1,5 +1,7 @@
// FIXME: Update this file to be type safe and remove this and next line // FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore // @ts-strict-ignore
import { Observable } from "rxjs";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { CipherFormConfig } from "@bitwarden/vault"; import { CipherFormConfig } from "@bitwarden/vault";
@@ -74,4 +76,10 @@ export abstract class CipherFormContainer {
abstract disableFormFields(): void; abstract disableFormFields(): void;
abstract enableFormFields(): void; abstract enableFormFields(): void;
/**
* An observable that emits when the form status changes to enabled.
* This can be used to disable child forms when the parent form is enabled.
*/
formEnabled$: Observable<void>;
} }

View File

@@ -112,6 +112,12 @@ export class CipherFormComponent implements AfterViewInit, OnInit, OnChanges, Ci
@Output() formReady = this.formReadySubject.asObservable(); @Output() formReady = this.formReadySubject.asObservable();
/**
* Emitted when the form is enabled
*/
private formEnabledSubject = new Subject<void>();
formEnabled$ = this.formEnabledSubject.asObservable();
/** /**
* The original cipher being edited or cloned. Null for add mode. * The original cipher being edited or cloned. Null for add mode.
*/ */
@@ -156,6 +162,7 @@ export class CipherFormComponent implements AfterViewInit, OnInit, OnChanges, Ci
enableFormFields(): void { enableFormFields(): void {
this.cipherForm.enable({ emitEvent: false }); this.cipherForm.enable({ emitEvent: false });
this.formEnabledSubject.next();
} }
/** /**

View File

@@ -1,7 +1,7 @@
// FIXME: Update this file to be type safe and remove this and next line // FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore // @ts-strict-ignore
import { CommonModule } from "@angular/common"; import { CommonModule } from "@angular/common";
import { Component, Input, OnInit } from "@angular/core"; import { Component, DestroyRef, inject, Input, OnInit } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { FormBuilder, ReactiveFormsModule } from "@angular/forms"; import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { firstValueFrom } from "rxjs"; import { firstValueFrom } from "rxjs";
@@ -60,6 +60,7 @@ export class SshKeySectionComponent implements OnInit {
}); });
showImport = false; showImport = false;
private destroyRef = inject(DestroyRef);
constructor( constructor(
private cipherFormContainer: CipherFormContainer, private cipherFormContainer: CipherFormContainer,
@@ -94,6 +95,12 @@ export class SshKeySectionComponent implements OnInit {
if (this.platformUtilsService.getClientType() !== ClientType.Web) { if (this.platformUtilsService.getClientType() !== ClientType.Web) {
this.showImport = true; this.showImport = true;
} }
// Disable the form if the cipher form container is enabled
// to prevent user interaction
this.cipherFormContainer.formEnabled$
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => this.sshKeyForm.disable());
} }
/** Set form initial form values from the current cipher */ /** Set form initial form values from the current cipher */

View File

@@ -1,4 +1,4 @@
<ng-container *ngIf="canCreateCipher || canCreateCollection || canCreateFolder"> <ng-container *ngIf="canCreateCipher() || canCreateCollection() || canCreateFolder()">
<div> <div>
<button <button
bitButton bitButton
@@ -18,13 +18,13 @@
{{ item.labelKey | i18n }} {{ item.labelKey | i18n }}
</button> </button>
} }
<bit-menu-divider *ngIf="canCreateCipher"></bit-menu-divider> <bit-menu-divider *ngIf="canCreateCipher()"></bit-menu-divider>
<button *ngIf="canCreateFolder" type="button" bitMenuItem (click)="folderAdded.emit()"> <button *ngIf="canCreateFolder()" type="button" bitMenuItem (click)="folderAdded.emit()">
<i class="bwi bwi-fw bwi-folder" aria-hidden="true"></i> <i class="bwi bwi-fw bwi-folder" aria-hidden="true"></i>
{{ "folder" | i18n }} {{ "folder" | i18n }}
</button> </button>
<button <button
*ngIf="canCreateCollection" *ngIf="canCreateCollection()"
type="button" type="button"
bitMenuItem bitMenuItem
(click)="collectionAdded.emit()" (click)="collectionAdded.emit()"

View File

@@ -18,6 +18,7 @@ export class NewCipherMenuComponent {
canCreateCipher = input(false); canCreateCipher = input(false);
canCreateFolder = input(false); canCreateFolder = input(false);
canCreateCollection = input(false); canCreateCollection = input(false);
canCreateSshKey = input(false);
folderAdded = output(); folderAdded = output();
collectionAdded = output(); collectionAdded = output();
cipherAdded = output<CipherType>(); cipherAdded = output<CipherType>();
@@ -30,6 +31,9 @@ export class NewCipherMenuComponent {
cipherMenuItems$ = this.restrictedItemTypesService.restricted$.pipe( cipherMenuItems$ = this.restrictedItemTypesService.restricted$.pipe(
map((restrictedTypes) => { map((restrictedTypes) => {
return CIPHER_MENU_ITEMS.filter((item) => { return CIPHER_MENU_ITEMS.filter((item) => {
if (!this.canCreateSshKey() && item.type === CipherType.SshKey) {
return false;
}
return !restrictedTypes.some((restrictedType) => restrictedType.cipherType === item.type); return !restrictedTypes.some((restrictedType) => restrictedType.cipherType === item.type);
}); });
}), }),