mirror of
https://github.com/bitwarden/browser
synced 2025-12-06 00:13:28 +00:00
do not show copy password button on the web for users that do not have access (#17635)
This commit is contained in:
@@ -109,10 +109,12 @@
|
||||
<i class="bwi bwi-fw bwi-clone" aria-hidden="true"></i>
|
||||
{{ "copyUsername" | i18n }}
|
||||
</button>
|
||||
<button bitMenuItem type="button" appCopyField="password" [cipher]="cipher">
|
||||
<i class="bwi bwi-fw bwi-clone" aria-hidden="true"></i>
|
||||
{{ "copyPassword" | i18n }}
|
||||
</button>
|
||||
@if (cipher.viewPassword) {
|
||||
<button bitMenuItem type="button" appCopyField="password" [cipher]="cipher">
|
||||
<i class="bwi bwi-fw bwi-clone" aria-hidden="true"></i>
|
||||
{{ "copyPassword" | i18n }}
|
||||
</button>
|
||||
}
|
||||
<button bitMenuItem type="button" appCopyField="totp" [cipher]="cipher">
|
||||
<i class="bwi bwi-fw bwi-clone" aria-hidden="true"></i>
|
||||
{{ "copyVerificationCode" | i18n }}
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
import { OverlayContainer } from "@angular/cdk/overlay";
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||
import { RouterModule } from "@angular/router";
|
||||
import { mock } from "jest-mock-extended";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.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 { LoginView } from "@bitwarden/common/vault/models/view/login.view";
|
||||
import { CipherViewLike } from "@bitwarden/common/vault/utils/cipher-view-like-utils";
|
||||
import { IconButtonModule, MenuModule } from "@bitwarden/components";
|
||||
import { CopyCipherFieldDirective, CopyCipherFieldService } from "@bitwarden/vault";
|
||||
|
||||
import { OrganizationNameBadgeComponent } from "../../individual-vault/organization-badge/organization-name-badge.component";
|
||||
|
||||
import { VaultCipherRowComponent } from "./vault-cipher-row.component";
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
const originalError = console.error;
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.error = (...args) => {
|
||||
if (
|
||||
typeof args[0] === "object" &&
|
||||
(args[0] as Error).message.includes("Could not parse CSS stylesheet")
|
||||
) {
|
||||
// Opening the overlay container in tests causes stylesheets to be parsed,
|
||||
// which can lead to JSDOM unable to parse CSS errors. These can be ignored safely.
|
||||
return;
|
||||
}
|
||||
originalError(...args);
|
||||
};
|
||||
|
||||
describe("VaultCipherRowComponent", () => {
|
||||
let component: VaultCipherRowComponent<CipherViewLike>;
|
||||
let fixture: ComponentFixture<VaultCipherRowComponent<CipherViewLike>>;
|
||||
let overlayContainer: OverlayContainer;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [VaultCipherRowComponent, OrganizationNameBadgeComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
RouterModule.forRoot([]),
|
||||
MenuModule,
|
||||
IconButtonModule,
|
||||
JslibModule,
|
||||
CopyCipherFieldDirective,
|
||||
],
|
||||
providers: [
|
||||
{ provide: I18nService, useValue: { t: (key: string) => key } },
|
||||
{
|
||||
provide: EnvironmentService,
|
||||
useValue: { environment$: new BehaviorSubject({}).asObservable() },
|
||||
},
|
||||
{
|
||||
provide: DomainSettingsService,
|
||||
useValue: { showFavicons$: new BehaviorSubject(false).asObservable() },
|
||||
},
|
||||
{ provide: CopyCipherFieldService, useValue: mock<CopyCipherFieldService>() },
|
||||
{ provide: AccountService, useValue: mock<AccountService>() },
|
||||
{ provide: CipherService, useValue: mock<CipherService>() },
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(VaultCipherRowComponent);
|
||||
component = fixture.componentInstance;
|
||||
overlayContainer = TestBed.inject(OverlayContainer);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
overlayContainer?.ngOnDestroy();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error = originalError;
|
||||
});
|
||||
|
||||
describe("copy password visibility", () => {
|
||||
let loginCipher: CipherView;
|
||||
|
||||
beforeEach(() => {
|
||||
loginCipher = new CipherView();
|
||||
loginCipher.id = "cipher-1";
|
||||
loginCipher.name = "Test Login";
|
||||
loginCipher.type = CipherType.Login;
|
||||
loginCipher.login = new LoginView();
|
||||
loginCipher.login.password = "test-password";
|
||||
loginCipher.organizationId = undefined;
|
||||
loginCipher.deletedDate = null;
|
||||
loginCipher.archivedDate = null;
|
||||
|
||||
component.cipher = loginCipher;
|
||||
component.disabled = false;
|
||||
});
|
||||
|
||||
const openMenuAndGetContent = (): string => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const menuTrigger = fixture.nativeElement.querySelector(
|
||||
'button[biticonbutton="bwi-ellipsis-v"]',
|
||||
) as HTMLButtonElement;
|
||||
expect(menuTrigger).toBeTruthy();
|
||||
|
||||
menuTrigger.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
return overlayContainer.getContainerElement().innerHTML;
|
||||
};
|
||||
|
||||
it("renders copy password button in menu when viewPassword is true", () => {
|
||||
component.cipher.viewPassword = true;
|
||||
|
||||
const overlayContent = openMenuAndGetContent();
|
||||
|
||||
expect(overlayContent).toContain('appcopyfield="password"');
|
||||
expect(overlayContent).toContain("copyPassword");
|
||||
});
|
||||
|
||||
it("does not render copy password button in menu when viewPassword is false", () => {
|
||||
component.cipher.viewPassword = false;
|
||||
|
||||
const overlayContent = openMenuAndGetContent();
|
||||
|
||||
expect(overlayContent).not.toContain('appcopyfield="password"');
|
||||
});
|
||||
|
||||
it("does not render copy password button in menu when viewPassword is undefined", () => {
|
||||
component.cipher.viewPassword = undefined;
|
||||
|
||||
const overlayContent = openMenuAndGetContent();
|
||||
|
||||
expect(overlayContent).not.toContain('appcopyfield="password"');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user