mirror of
https://github.com/bitwarden/browser.git
synced 2025-03-02 03:41:09 +01:00
AC-1333 vault report org ciphers (#5998)
* updated report components to only show can edit ciphers, added badges, spec files --------- Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
This commit is contained in:
parent
3952af058c
commit
a141890b09
@ -4,7 +4,6 @@ import { ActivatedRoute } from "@angular/router";
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
@ -25,12 +24,11 @@ export class ExposedPasswordsReportComponent extends BaseExposedPasswordsReportC
|
||||
cipherService: CipherService,
|
||||
auditService: AuditService,
|
||||
modalService: ModalService,
|
||||
messagingService: MessagingService,
|
||||
private organizationService: OrganizationService,
|
||||
organizationService: OrganizationService,
|
||||
private route: ActivatedRoute,
|
||||
passwordRepromptService: PasswordRepromptService
|
||||
) {
|
||||
super(cipherService, auditService, modalService, messagingService, passwordRepromptService);
|
||||
super(cipherService, auditService, organizationService, modalService, passwordRepromptService);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
|
@ -4,7 +4,6 @@ import { ActivatedRoute } from "@angular/router";
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import { PasswordRepromptService } from "@bitwarden/vault";
|
||||
@ -21,13 +20,12 @@ export class InactiveTwoFactorReportComponent extends BaseInactiveTwoFactorRepor
|
||||
constructor(
|
||||
cipherService: CipherService,
|
||||
modalService: ModalService,
|
||||
messagingService: MessagingService,
|
||||
private route: ActivatedRoute,
|
||||
logService: LogService,
|
||||
passwordRepromptService: PasswordRepromptService,
|
||||
private organizationService: OrganizationService
|
||||
organizationService: OrganizationService
|
||||
) {
|
||||
super(cipherService, modalService, messagingService, logService, passwordRepromptService);
|
||||
super(cipherService, organizationService, modalService, logService, passwordRepromptService);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
|
@ -3,8 +3,6 @@ import { ActivatedRoute } from "@angular/router";
|
||||
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
@ -24,13 +22,11 @@ export class ReusedPasswordsReportComponent extends BaseReusedPasswordsReportCom
|
||||
constructor(
|
||||
cipherService: CipherService,
|
||||
modalService: ModalService,
|
||||
messagingService: MessagingService,
|
||||
stateService: StateService,
|
||||
private route: ActivatedRoute,
|
||||
private organizationService: OrganizationService,
|
||||
organizationService: OrganizationService,
|
||||
passwordRepromptService: PasswordRepromptService
|
||||
) {
|
||||
super(cipherService, modalService, messagingService, stateService, passwordRepromptService);
|
||||
super(cipherService, organizationService, modalService, passwordRepromptService);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
|
@ -3,7 +3,6 @@ import { ActivatedRoute } from "@angular/router";
|
||||
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import { PasswordRepromptService } from "@bitwarden/vault";
|
||||
@ -20,12 +19,11 @@ export class UnsecuredWebsitesReportComponent extends BaseUnsecuredWebsitesRepor
|
||||
constructor(
|
||||
cipherService: CipherService,
|
||||
modalService: ModalService,
|
||||
messagingService: MessagingService,
|
||||
private route: ActivatedRoute,
|
||||
private organizationService: OrganizationService,
|
||||
organizationService: OrganizationService,
|
||||
passwordRepromptService: PasswordRepromptService
|
||||
) {
|
||||
super(cipherService, modalService, messagingService, passwordRepromptService);
|
||||
super(cipherService, organizationService, modalService, passwordRepromptService);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
|
@ -3,7 +3,6 @@ import { ActivatedRoute } from "@angular/router";
|
||||
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
|
||||
@ -25,16 +24,15 @@ export class WeakPasswordsReportComponent extends BaseWeakPasswordsReportCompone
|
||||
cipherService: CipherService,
|
||||
passwordStrengthService: PasswordStrengthServiceAbstraction,
|
||||
modalService: ModalService,
|
||||
messagingService: MessagingService,
|
||||
private route: ActivatedRoute,
|
||||
private organizationService: OrganizationService,
|
||||
organizationService: OrganizationService,
|
||||
passwordRepromptService: PasswordRepromptService
|
||||
) {
|
||||
super(
|
||||
cipherService,
|
||||
passwordStrengthService,
|
||||
organizationService,
|
||||
modalService,
|
||||
messagingService,
|
||||
passwordRepromptService
|
||||
);
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { Directive, ViewChild, ViewContainerRef } from "@angular/core";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import { PasswordRepromptService } from "@bitwarden/vault";
|
||||
@ -19,13 +20,15 @@ export class CipherReportComponent {
|
||||
hasLoaded = false;
|
||||
ciphers: CipherView[] = [];
|
||||
organization: Organization;
|
||||
organizations$: Observable<Organization[]>;
|
||||
|
||||
constructor(
|
||||
private modalService: ModalService,
|
||||
protected messagingService: MessagingService,
|
||||
public requiresPaid: boolean,
|
||||
protected passwordRepromptService: PasswordRepromptService
|
||||
) {}
|
||||
protected passwordRepromptService: PasswordRepromptService,
|
||||
protected organizationService: OrganizationService
|
||||
) {
|
||||
this.organizations$ = this.organizationService.organizations$;
|
||||
}
|
||||
|
||||
async load() {
|
||||
this.loading = true;
|
||||
|
@ -49,6 +49,16 @@
|
||||
<br />
|
||||
<small>{{ c.subTitle }}</small>
|
||||
</td>
|
||||
<td>
|
||||
<app-org-badge
|
||||
*ngIf="!organization"
|
||||
[disabled]="disabled"
|
||||
[organizationId]="c.organizationId"
|
||||
[organizationName]="c.organizationId | orgNameFromId : (organizations$ | async)"
|
||||
appStopProp
|
||||
>
|
||||
</app-org-badge>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span bitBadge badgeType="warning">
|
||||
{{ "exposedXTimes" | i18n : (exposedPasswordMap.get(c.id) | number) }}
|
||||
|
@ -0,0 +1,79 @@
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||
import { mock, MockProxy } from "jest-mock-extended";
|
||||
|
||||
import { I18nPipe } from "@bitwarden/angular/platform/pipes/i18n.pipe";
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { PasswordRepromptService } from "@bitwarden/vault";
|
||||
|
||||
import { ExposedPasswordsReportComponent } from "./exposed-passwords-report.component";
|
||||
import { cipherData } from "./reports-ciphers.mock";
|
||||
|
||||
describe("ExposedPasswordsReportComponent", () => {
|
||||
let component: ExposedPasswordsReportComponent;
|
||||
let fixture: ComponentFixture<ExposedPasswordsReportComponent>;
|
||||
let auditService: MockProxy<AuditService>;
|
||||
|
||||
beforeEach(() => {
|
||||
auditService = mock<AuditService>();
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ExposedPasswordsReportComponent, I18nPipe],
|
||||
providers: [
|
||||
{
|
||||
provide: CipherService,
|
||||
useValue: mock<CipherService>(),
|
||||
},
|
||||
{
|
||||
provide: AuditService,
|
||||
useValue: auditService,
|
||||
},
|
||||
{
|
||||
provide: OrganizationService,
|
||||
useValue: mock<OrganizationService>(),
|
||||
},
|
||||
{
|
||||
provide: ModalService,
|
||||
useValue: mock<ModalService>(),
|
||||
},
|
||||
{
|
||||
provide: PasswordRepromptService,
|
||||
useValue: mock<PasswordRepromptService>(),
|
||||
},
|
||||
{
|
||||
provide: I18nService,
|
||||
useValue: mock<I18nService>(),
|
||||
},
|
||||
],
|
||||
schemas: [],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ExposedPasswordsReportComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it("should initialize component", () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should get only ciphers with exposed passwords that the user has "Can Edit" access to', async () => {
|
||||
const expectedIdOne: any = "cbea34a8-bde4-46ad-9d19-b05001228ab2";
|
||||
const expectedIdTwo = "cbea34a8-bde4-46ad-9d19-b05001228cd3";
|
||||
|
||||
jest.spyOn(auditService, "passwordLeaked").mockReturnValue(Promise.resolve<any>(1234));
|
||||
jest.spyOn(component as any, "getAllCiphers").mockReturnValue(Promise.resolve<any>(cipherData));
|
||||
await component.setCiphers();
|
||||
|
||||
expect(component.ciphers.length).toEqual(2);
|
||||
expect(component.ciphers[0].id).toEqual(expectedIdOne);
|
||||
expect(component.ciphers[0].edit).toEqual(true);
|
||||
expect(component.ciphers[1].id).toEqual(expectedIdTwo);
|
||||
expect(component.ciphers[1].edit).toEqual(true);
|
||||
});
|
||||
});
|
@ -2,7 +2,7 @@ import { Component, OnInit } from "@angular/core";
|
||||
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
@ -16,15 +16,16 @@ import { CipherReportComponent } from "./cipher-report.component";
|
||||
})
|
||||
export class ExposedPasswordsReportComponent extends CipherReportComponent implements OnInit {
|
||||
exposedPasswordMap = new Map<string, number>();
|
||||
disabled = true;
|
||||
|
||||
constructor(
|
||||
protected cipherService: CipherService,
|
||||
protected auditService: AuditService,
|
||||
protected organizationService: OrganizationService,
|
||||
modalService: ModalService,
|
||||
messagingService: MessagingService,
|
||||
passwordRepromptService: PasswordRepromptService
|
||||
) {
|
||||
super(modalService, messagingService, true, passwordRepromptService);
|
||||
super(modalService, passwordRepromptService, organizationService);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
@ -35,25 +36,28 @@ export class ExposedPasswordsReportComponent extends CipherReportComponent imple
|
||||
const allCiphers = await this.getAllCiphers();
|
||||
const exposedPasswordCiphers: CipherView[] = [];
|
||||
const promises: Promise<void>[] = [];
|
||||
allCiphers.forEach((c) => {
|
||||
allCiphers.forEach((ciph) => {
|
||||
const { type, login, isDeleted, edit, viewPassword, id } = ciph;
|
||||
if (
|
||||
c.type !== CipherType.Login ||
|
||||
c.login.password == null ||
|
||||
c.login.password === "" ||
|
||||
c.isDeleted
|
||||
type !== CipherType.Login ||
|
||||
login.password == null ||
|
||||
login.password === "" ||
|
||||
isDeleted ||
|
||||
(!this.organization && !edit) ||
|
||||
!viewPassword
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const promise = this.auditService.passwordLeaked(c.login.password).then((exposedCount) => {
|
||||
const promise = this.auditService.passwordLeaked(login.password).then((exposedCount) => {
|
||||
if (exposedCount > 0) {
|
||||
exposedPasswordCiphers.push(c);
|
||||
this.exposedPasswordMap.set(c.id, exposedCount);
|
||||
exposedPasswordCiphers.push(ciph);
|
||||
this.exposedPasswordMap.set(id, exposedCount);
|
||||
}
|
||||
});
|
||||
promises.push(promise);
|
||||
});
|
||||
await Promise.all(promises);
|
||||
this.ciphers = exposedPasswordCiphers;
|
||||
this.ciphers = [...exposedPasswordCiphers];
|
||||
}
|
||||
|
||||
protected getAllCiphers(): Promise<CipherView[]> {
|
||||
|
@ -59,6 +59,16 @@
|
||||
<br />
|
||||
<small>{{ c.subTitle }}</small>
|
||||
</td>
|
||||
<td>
|
||||
<app-org-badge
|
||||
*ngIf="!organization"
|
||||
[disabled]="disabled"
|
||||
[organizationId]="c.organizationId"
|
||||
[organizationName]="c.organizationId | orgNameFromId : (organizations$ | async)"
|
||||
appStopProp
|
||||
>
|
||||
</app-org-badge>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<a
|
||||
bitBadge
|
||||
|
@ -0,0 +1,84 @@
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||
import { mock } from "jest-mock-extended";
|
||||
|
||||
import { I18nPipe } from "@bitwarden/angular/platform/pipes/i18n.pipe";
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { PasswordRepromptService } from "@bitwarden/vault";
|
||||
|
||||
import { InactiveTwoFactorReportComponent } from "./inactive-two-factor-report.component";
|
||||
import { cipherData } from "./reports-ciphers.mock";
|
||||
|
||||
describe("InactiveTwoFactorReportComponent", () => {
|
||||
let component: InactiveTwoFactorReportComponent;
|
||||
let fixture: ComponentFixture<InactiveTwoFactorReportComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [InactiveTwoFactorReportComponent, I18nPipe],
|
||||
providers: [
|
||||
{
|
||||
provide: CipherService,
|
||||
useValue: mock<CipherService>(),
|
||||
},
|
||||
{
|
||||
provide: OrganizationService,
|
||||
useValue: mock<OrganizationService>(),
|
||||
},
|
||||
{
|
||||
provide: ModalService,
|
||||
useValue: mock<ModalService>(),
|
||||
},
|
||||
{
|
||||
provide: LogService,
|
||||
useValue: mock<LogService>(),
|
||||
},
|
||||
{
|
||||
provide: PasswordRepromptService,
|
||||
useValue: mock<PasswordRepromptService>(),
|
||||
},
|
||||
{
|
||||
provide: I18nService,
|
||||
useValue: mock<I18nService>(),
|
||||
},
|
||||
],
|
||||
schemas: [],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(InactiveTwoFactorReportComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it("should initialize component", () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should get only ciphers with domains in the 2fa directory that they have "Can Edit" access to', async () => {
|
||||
const expectedIdOne: any = "cbea34a8-bde4-46ad-9d19-b05001228xy4";
|
||||
const expectedIdTwo: any = "cbea34a8-bde4-46ad-9d19-b05001227nm5";
|
||||
component.services.set(
|
||||
"101domain.com",
|
||||
"https://help.101domain.com/account-management/account-security/enabling-disabling-two-factor-verification"
|
||||
);
|
||||
component.services.set(
|
||||
"123formbuilder.com",
|
||||
"https://www.123formbuilder.com/docs/multi-factor-authentication-login"
|
||||
);
|
||||
|
||||
jest.spyOn(component as any, "getAllCiphers").mockReturnValue(Promise.resolve<any>(cipherData));
|
||||
await component.setCiphers();
|
||||
|
||||
expect(component.ciphers.length).toEqual(2);
|
||||
expect(component.ciphers[0].id).toEqual(expectedIdOne);
|
||||
expect(component.ciphers[0].edit).toEqual(true);
|
||||
expect(component.ciphers[1].id).toEqual(expectedIdTwo);
|
||||
expect(component.ciphers[1].edit).toEqual(true);
|
||||
});
|
||||
});
|
@ -1,8 +1,8 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
|
||||
@ -18,15 +18,16 @@ import { CipherReportComponent } from "./cipher-report.component";
|
||||
export class InactiveTwoFactorReportComponent extends CipherReportComponent implements OnInit {
|
||||
services = new Map<string, string>();
|
||||
cipherDocs = new Map<string, string>();
|
||||
disabled = true;
|
||||
|
||||
constructor(
|
||||
protected cipherService: CipherService,
|
||||
protected organizationService: OrganizationService,
|
||||
modalService: ModalService,
|
||||
messagingService: MessagingService,
|
||||
private logService: LogService,
|
||||
passwordRepromptService: PasswordRepromptService
|
||||
) {
|
||||
super(modalService, messagingService, true, passwordRepromptService);
|
||||
super(modalService, passwordRepromptService, organizationService);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
@ -43,33 +44,34 @@ export class InactiveTwoFactorReportComponent extends CipherReportComponent impl
|
||||
if (this.services.size > 0) {
|
||||
const allCiphers = await this.getAllCiphers();
|
||||
const inactive2faCiphers: CipherView[] = [];
|
||||
const promises: Promise<void>[] = [];
|
||||
const docs = new Map<string, string>();
|
||||
allCiphers.forEach((c) => {
|
||||
|
||||
allCiphers.forEach((ciph) => {
|
||||
const { type, login, isDeleted, edit, id } = ciph;
|
||||
if (
|
||||
c.type !== CipherType.Login ||
|
||||
(c.login.totp != null && c.login.totp !== "") ||
|
||||
!c.login.hasUris ||
|
||||
c.isDeleted
|
||||
type !== CipherType.Login ||
|
||||
(login.totp != null && login.totp !== "") ||
|
||||
!login.hasUris ||
|
||||
isDeleted ||
|
||||
(!this.organization && !edit)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < c.login.uris.length; i++) {
|
||||
const u = c.login.uris[i];
|
||||
for (let i = 0; i < login.uris.length; i++) {
|
||||
const u = login.uris[i];
|
||||
if (u.uri != null && u.uri !== "") {
|
||||
const uri = u.uri.replace("www.", "");
|
||||
const domain = Utils.getDomain(uri);
|
||||
if (domain != null && this.services.has(domain)) {
|
||||
if (this.services.get(domain) != null) {
|
||||
docs.set(c.id, this.services.get(domain));
|
||||
docs.set(id, this.services.get(domain));
|
||||
}
|
||||
inactive2faCiphers.push(c);
|
||||
inactive2faCiphers.push(ciph);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
await Promise.all(promises);
|
||||
this.ciphers = inactive2faCiphers;
|
||||
this.ciphers = [...inactive2faCiphers];
|
||||
this.cipherDocs = docs;
|
||||
}
|
||||
}
|
||||
|
128
apps/web/src/app/reports/pages/reports-ciphers.mock.ts
Normal file
128
apps/web/src/app/reports/pages/reports-ciphers.mock.ts
Normal file
@ -0,0 +1,128 @@
|
||||
export const cipherData: any[] = [
|
||||
{
|
||||
initializerKey: 1,
|
||||
id: "cbea34a8-bde4-46ad-9d19-b05001228ab1",
|
||||
organizationId: null,
|
||||
folderId: null,
|
||||
name: "Cannot Be Edited",
|
||||
notes: null,
|
||||
isDeleted: false,
|
||||
type: 1,
|
||||
favorite: false,
|
||||
organizationUseTotp: false,
|
||||
login: {
|
||||
password: "123",
|
||||
},
|
||||
edit: false,
|
||||
viewPassword: true,
|
||||
collectionIds: [],
|
||||
revisionDate: "2023-08-03T17:40:59.793Z",
|
||||
creationDate: "2023-08-03T17:40:59.793Z",
|
||||
deletedDate: null,
|
||||
reprompt: 0,
|
||||
localData: null,
|
||||
},
|
||||
{
|
||||
initializerKey: 1,
|
||||
id: "cbea34a8-bde4-46ad-9d19-b05001228ab2",
|
||||
organizationId: null,
|
||||
folderId: null,
|
||||
name: "Can Be Edited id ending 2",
|
||||
notes: null,
|
||||
isDeleted: false,
|
||||
type: 1,
|
||||
favorite: false,
|
||||
organizationUseTotp: false,
|
||||
login: {
|
||||
password: "123",
|
||||
hasUris: true,
|
||||
uris: [
|
||||
{
|
||||
uri: "http://nothing.com",
|
||||
},
|
||||
],
|
||||
},
|
||||
edit: true,
|
||||
viewPassword: true,
|
||||
collectionIds: [],
|
||||
revisionDate: "2023-08-03T17:40:59.793Z",
|
||||
creationDate: "2023-08-03T17:40:59.793Z",
|
||||
deletedDate: null,
|
||||
reprompt: 0,
|
||||
localData: null,
|
||||
},
|
||||
{
|
||||
initializerKey: 1,
|
||||
id: "cbea34a8-bde4-46ad-9d19-b05001228cd3",
|
||||
organizationId: null,
|
||||
folderId: null,
|
||||
name: "Can Be Edited id ending 3",
|
||||
notes: null,
|
||||
type: 1,
|
||||
favorite: false,
|
||||
organizationUseTotp: false,
|
||||
login: {
|
||||
password: "123",
|
||||
hasUris: true,
|
||||
uris: [
|
||||
{
|
||||
uri: "http://example.com",
|
||||
},
|
||||
],
|
||||
},
|
||||
edit: true,
|
||||
viewPassword: true,
|
||||
collectionIds: [],
|
||||
revisionDate: "2023-08-03T17:40:59.793Z",
|
||||
creationDate: "2023-08-03T17:40:59.793Z",
|
||||
deletedDate: null,
|
||||
reprompt: 0,
|
||||
localData: null,
|
||||
},
|
||||
{
|
||||
initializerKey: 1,
|
||||
id: "cbea34a8-bde4-46ad-9d19-b05001228xy4",
|
||||
organizationId: null,
|
||||
folderId: null,
|
||||
name: "Can Be Edited id ending 4",
|
||||
notes: null,
|
||||
type: 1,
|
||||
favorite: false,
|
||||
organizationUseTotp: false,
|
||||
login: {
|
||||
hasUris: true,
|
||||
uris: [{ uri: "101domain.com" }],
|
||||
},
|
||||
edit: true,
|
||||
viewPassword: true,
|
||||
collectionIds: [],
|
||||
revisionDate: "2023-08-03T17:40:59.793Z",
|
||||
creationDate: "2023-08-03T17:40:59.793Z",
|
||||
deletedDate: null,
|
||||
reprompt: 0,
|
||||
localData: null,
|
||||
},
|
||||
{
|
||||
initializerKey: 1,
|
||||
id: "cbea34a8-bde4-46ad-9d19-b05001227nm5",
|
||||
organizationId: null,
|
||||
folderId: null,
|
||||
name: "Can Be Edited id ending 5",
|
||||
notes: null,
|
||||
type: 1,
|
||||
favorite: false,
|
||||
organizationUseTotp: false,
|
||||
login: {
|
||||
hasUris: true,
|
||||
uris: [{ uri: "123formbuilder.com" }],
|
||||
},
|
||||
edit: true,
|
||||
viewPassword: true,
|
||||
collectionIds: [],
|
||||
revisionDate: "2023-08-03T17:40:59.793Z",
|
||||
creationDate: "2023-08-03T17:40:59.793Z",
|
||||
deletedDate: null,
|
||||
reprompt: 0,
|
||||
localData: null,
|
||||
},
|
||||
];
|
@ -64,6 +64,16 @@
|
||||
<br />
|
||||
<small>{{ c.subTitle }}</small>
|
||||
</td>
|
||||
<td>
|
||||
<app-org-badge
|
||||
*ngIf="!organization"
|
||||
[disabled]="disabled"
|
||||
[organizationId]="c.organizationId"
|
||||
[organizationName]="c.organizationId | orgNameFromId : (organizations$ | async)"
|
||||
appStopProp
|
||||
>
|
||||
</app-org-badge>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span bitBadge badgeType="warning">
|
||||
{{ "reusedXTimes" | i18n : passwordUseMap.get(c.login.password) }}
|
||||
|
@ -0,0 +1,70 @@
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||
import { mock } from "jest-mock-extended";
|
||||
|
||||
import { I18nPipe } from "@bitwarden/angular/platform/pipes/i18n.pipe";
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { PasswordRepromptService } from "@bitwarden/vault";
|
||||
|
||||
import { cipherData } from "./reports-ciphers.mock";
|
||||
import { ReusedPasswordsReportComponent } from "./reused-passwords-report.component";
|
||||
|
||||
describe("ReusedPasswordsReportComponent", () => {
|
||||
let component: ReusedPasswordsReportComponent;
|
||||
let fixture: ComponentFixture<ReusedPasswordsReportComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ReusedPasswordsReportComponent, I18nPipe],
|
||||
providers: [
|
||||
{
|
||||
provide: CipherService,
|
||||
useValue: mock<CipherService>(),
|
||||
},
|
||||
{
|
||||
provide: OrganizationService,
|
||||
useValue: mock<OrganizationService>(),
|
||||
},
|
||||
{
|
||||
provide: ModalService,
|
||||
useValue: mock<ModalService>(),
|
||||
},
|
||||
{
|
||||
provide: PasswordRepromptService,
|
||||
useValue: mock<PasswordRepromptService>(),
|
||||
},
|
||||
{
|
||||
provide: I18nService,
|
||||
useValue: mock<I18nService>(),
|
||||
},
|
||||
],
|
||||
schemas: [],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ReusedPasswordsReportComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it("should initialize component", () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should get ciphers with reused passwords that the user has "Can Edit" access to', async () => {
|
||||
const expectedIdOne: any = "cbea34a8-bde4-46ad-9d19-b05001228ab2";
|
||||
const expectedIdTwo = "cbea34a8-bde4-46ad-9d19-b05001228cd3";
|
||||
jest.spyOn(component as any, "getAllCiphers").mockReturnValue(Promise.resolve<any>(cipherData));
|
||||
await component.setCiphers();
|
||||
|
||||
expect(component.ciphers.length).toEqual(2);
|
||||
expect(component.ciphers[0].id).toEqual(expectedIdOne);
|
||||
expect(component.ciphers[0].edit).toEqual(true);
|
||||
expect(component.ciphers[1].id).toEqual(expectedIdTwo);
|
||||
expect(component.ciphers[1].edit).toEqual(true);
|
||||
});
|
||||
});
|
@ -1,8 +1,7 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
@ -16,15 +15,15 @@ import { CipherReportComponent } from "./cipher-report.component";
|
||||
})
|
||||
export class ReusedPasswordsReportComponent extends CipherReportComponent implements OnInit {
|
||||
passwordUseMap: Map<string, number>;
|
||||
disabled = true;
|
||||
|
||||
constructor(
|
||||
protected cipherService: CipherService,
|
||||
protected organizationService: OrganizationService,
|
||||
modalService: ModalService,
|
||||
messagingService: MessagingService,
|
||||
stateService: StateService,
|
||||
passwordRepromptService: PasswordRepromptService
|
||||
) {
|
||||
super(modalService, messagingService, true, passwordRepromptService);
|
||||
super(modalService, passwordRepromptService, organizationService);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
@ -35,20 +34,23 @@ export class ReusedPasswordsReportComponent extends CipherReportComponent implem
|
||||
const allCiphers = await this.getAllCiphers();
|
||||
const ciphersWithPasswords: CipherView[] = [];
|
||||
this.passwordUseMap = new Map<string, number>();
|
||||
allCiphers.forEach((c) => {
|
||||
allCiphers.forEach((ciph) => {
|
||||
const { type, login, isDeleted, edit, viewPassword } = ciph;
|
||||
if (
|
||||
c.type !== CipherType.Login ||
|
||||
c.login.password == null ||
|
||||
c.login.password === "" ||
|
||||
c.isDeleted
|
||||
type !== CipherType.Login ||
|
||||
login.password == null ||
|
||||
login.password === "" ||
|
||||
isDeleted ||
|
||||
(!this.organization && !edit) ||
|
||||
!viewPassword
|
||||
) {
|
||||
return;
|
||||
}
|
||||
ciphersWithPasswords.push(c);
|
||||
if (this.passwordUseMap.has(c.login.password)) {
|
||||
this.passwordUseMap.set(c.login.password, this.passwordUseMap.get(c.login.password) + 1);
|
||||
ciphersWithPasswords.push(ciph);
|
||||
if (this.passwordUseMap.has(login.password)) {
|
||||
this.passwordUseMap.set(login.password, this.passwordUseMap.get(login.password) + 1);
|
||||
} else {
|
||||
this.passwordUseMap.set(c.login.password, 1);
|
||||
this.passwordUseMap.set(login.password, 1);
|
||||
}
|
||||
});
|
||||
const reusedPasswordCiphers = ciphersWithPasswords.filter(
|
||||
|
@ -59,6 +59,16 @@
|
||||
<br />
|
||||
<small>{{ c.subTitle }}</small>
|
||||
</td>
|
||||
<td>
|
||||
<app-org-badge
|
||||
*ngIf="!organization"
|
||||
[disabled]="disabled"
|
||||
[organizationId]="c.organizationId"
|
||||
[organizationName]="c.organizationId | orgNameFromId : (organizations$ | async)"
|
||||
appStopProp
|
||||
>
|
||||
</app-org-badge>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -0,0 +1,70 @@
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||
import { mock } from "jest-mock-extended";
|
||||
|
||||
import { I18nPipe } from "@bitwarden/angular/platform/pipes/i18n.pipe";
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { PasswordRepromptService } from "@bitwarden/vault";
|
||||
|
||||
import { cipherData } from "./reports-ciphers.mock";
|
||||
import { UnsecuredWebsitesReportComponent } from "./unsecured-websites-report.component";
|
||||
|
||||
describe("UnsecuredWebsitesReportComponent", () => {
|
||||
let component: UnsecuredWebsitesReportComponent;
|
||||
let fixture: ComponentFixture<UnsecuredWebsitesReportComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [UnsecuredWebsitesReportComponent, I18nPipe],
|
||||
providers: [
|
||||
{
|
||||
provide: CipherService,
|
||||
useValue: mock<CipherService>(),
|
||||
},
|
||||
{
|
||||
provide: OrganizationService,
|
||||
useValue: mock<OrganizationService>(),
|
||||
},
|
||||
{
|
||||
provide: ModalService,
|
||||
useValue: mock<ModalService>(),
|
||||
},
|
||||
{
|
||||
provide: PasswordRepromptService,
|
||||
useValue: mock<PasswordRepromptService>(),
|
||||
},
|
||||
{
|
||||
provide: I18nService,
|
||||
useValue: mock<I18nService>(),
|
||||
},
|
||||
],
|
||||
schemas: [],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(UnsecuredWebsitesReportComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it("should initialize component", () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should get only unsecured ciphers that the user has "Can Edit" access to', async () => {
|
||||
const expectedIdOne: any = "cbea34a8-bde4-46ad-9d19-b05001228ab2";
|
||||
const expectedIdTwo = "cbea34a8-bde4-46ad-9d19-b05001228cd3";
|
||||
jest.spyOn(component as any, "getAllCiphers").mockReturnValue(Promise.resolve<any>(cipherData));
|
||||
await component.setCiphers();
|
||||
|
||||
expect(component.ciphers.length).toEqual(2);
|
||||
expect(component.ciphers[0].id).toEqual(expectedIdOne);
|
||||
expect(component.ciphers[0].edit).toEqual(true);
|
||||
expect(component.ciphers[1].id).toEqual(expectedIdTwo);
|
||||
expect(component.ciphers[1].edit).toEqual(true);
|
||||
});
|
||||
});
|
@ -1,7 +1,7 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
@ -14,13 +14,15 @@ import { CipherReportComponent } from "./cipher-report.component";
|
||||
templateUrl: "unsecured-websites-report.component.html",
|
||||
})
|
||||
export class UnsecuredWebsitesReportComponent extends CipherReportComponent implements OnInit {
|
||||
disabled = true;
|
||||
|
||||
constructor(
|
||||
protected cipherService: CipherService,
|
||||
protected organizationService: OrganizationService,
|
||||
modalService: ModalService,
|
||||
messagingService: MessagingService,
|
||||
passwordRepromptService: PasswordRepromptService
|
||||
) {
|
||||
super(modalService, messagingService, true, passwordRepromptService);
|
||||
super(modalService, passwordRepromptService, organizationService);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
@ -35,7 +37,9 @@ export class UnsecuredWebsitesReportComponent extends CipherReportComponent impl
|
||||
}
|
||||
return c.login.uris.some((u) => u.uri != null && u.uri.indexOf("http://") === 0);
|
||||
});
|
||||
this.ciphers = unsecuredCiphers;
|
||||
this.ciphers = unsecuredCiphers.filter(
|
||||
(c) => (!this.organization && c.edit) || (this.organization && !c.edit)
|
||||
);
|
||||
}
|
||||
|
||||
protected getAllCiphers(): Promise<CipherView[]> {
|
||||
|
@ -64,6 +64,16 @@
|
||||
<br />
|
||||
<small>{{ c.subTitle }}</small>
|
||||
</td>
|
||||
<td>
|
||||
<app-org-badge
|
||||
*ngIf="!organization"
|
||||
[disabled]="disabled"
|
||||
[organizationId]="c.organizationId"
|
||||
[organizationName]="c.organizationId | orgNameFromId : (organizations$ | async)"
|
||||
appStopProp
|
||||
>
|
||||
</app-org-badge>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span bitBadge [badgeType]="passwordStrengthMap.get(c.id)[1]">
|
||||
{{ passwordStrengthMap.get(c.id)[0] | i18n }}
|
||||
|
@ -0,0 +1,82 @@
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||
import { mock, MockProxy } from "jest-mock-extended";
|
||||
|
||||
import { I18nPipe } from "@bitwarden/angular/platform/pipes/i18n.pipe";
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { PasswordRepromptService } from "@bitwarden/vault";
|
||||
|
||||
import { cipherData } from "./reports-ciphers.mock";
|
||||
import { WeakPasswordsReportComponent } from "./weak-passwords-report.component";
|
||||
|
||||
describe("WeakPasswordsReportComponent", () => {
|
||||
let component: WeakPasswordsReportComponent;
|
||||
let fixture: ComponentFixture<WeakPasswordsReportComponent>;
|
||||
let passwordStrengthService: MockProxy<PasswordStrengthServiceAbstraction>;
|
||||
|
||||
beforeEach(() => {
|
||||
passwordStrengthService = mock<PasswordStrengthServiceAbstraction>();
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [WeakPasswordsReportComponent, I18nPipe],
|
||||
providers: [
|
||||
{
|
||||
provide: CipherService,
|
||||
useValue: mock<CipherService>(),
|
||||
},
|
||||
{
|
||||
provide: PasswordStrengthServiceAbstraction,
|
||||
useValue: passwordStrengthService,
|
||||
},
|
||||
{
|
||||
provide: OrganizationService,
|
||||
useValue: mock<OrganizationService>(),
|
||||
},
|
||||
{
|
||||
provide: ModalService,
|
||||
useValue: mock<ModalService>(),
|
||||
},
|
||||
{
|
||||
provide: PasswordRepromptService,
|
||||
useValue: mock<PasswordRepromptService>(),
|
||||
},
|
||||
{
|
||||
provide: I18nService,
|
||||
useValue: mock<I18nService>(),
|
||||
},
|
||||
],
|
||||
schemas: [],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(WeakPasswordsReportComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it("should initialize component", () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should get only ciphers with weak passwords that the user has "Can Edit" access to', async () => {
|
||||
const expectedIdOne: any = "cbea34a8-bde4-46ad-9d19-b05001228ab2";
|
||||
const expectedIdTwo = "cbea34a8-bde4-46ad-9d19-b05001228cd3";
|
||||
|
||||
jest.spyOn(passwordStrengthService, "getPasswordStrength").mockReturnValue({
|
||||
password: "123",
|
||||
score: 0,
|
||||
} as any);
|
||||
jest.spyOn(component as any, "getAllCiphers").mockReturnValue(Promise.resolve<any>(cipherData));
|
||||
await component.setCiphers();
|
||||
|
||||
expect(component.ciphers.length).toEqual(2);
|
||||
expect(component.ciphers[0].id).toEqual(expectedIdOne);
|
||||
expect(component.ciphers[0].edit).toEqual(true);
|
||||
expect(component.ciphers[1].id).toEqual(expectedIdTwo);
|
||||
expect(component.ciphers[1].edit).toEqual(true);
|
||||
});
|
||||
});
|
@ -1,7 +1,8 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
|
||||
@ -17,17 +18,19 @@ import { CipherReportComponent } from "./cipher-report.component";
|
||||
})
|
||||
export class WeakPasswordsReportComponent extends CipherReportComponent implements OnInit {
|
||||
passwordStrengthMap = new Map<string, [string, BadgeTypes]>();
|
||||
disabled = true;
|
||||
|
||||
private passwordStrengthCache = new Map<string, number>();
|
||||
weakPasswordCiphers: CipherView[] = [];
|
||||
|
||||
constructor(
|
||||
protected cipherService: CipherService,
|
||||
protected passwordStrengthService: PasswordStrengthServiceAbstraction,
|
||||
protected organizationService: OrganizationService,
|
||||
modalService: ModalService,
|
||||
messagingService: MessagingService,
|
||||
passwordRepromptService: PasswordRepromptService
|
||||
) {
|
||||
super(modalService, messagingService, true, passwordRepromptService);
|
||||
super(modalService, passwordRepromptService, organizationService);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
@ -36,33 +39,32 @@ export class WeakPasswordsReportComponent extends CipherReportComponent implemen
|
||||
|
||||
async setCiphers() {
|
||||
const allCiphers = await this.getAllCiphers();
|
||||
const weakPasswordCiphers: CipherView[] = [];
|
||||
const isUserNameNotEmpty = (c: CipherView): boolean => {
|
||||
return c.login.username != null && c.login.username.trim() !== "";
|
||||
};
|
||||
const getCacheKey = (c: CipherView): string => {
|
||||
return c.login.password + "_____" + (isUserNameNotEmpty(c) ? c.login.username : "");
|
||||
};
|
||||
this.findWeakPasswords(allCiphers);
|
||||
}
|
||||
|
||||
allCiphers.forEach((c) => {
|
||||
protected findWeakPasswords(ciphers: any[]): void {
|
||||
ciphers.forEach((ciph) => {
|
||||
const { type, login, isDeleted, edit, viewPassword, id } = ciph;
|
||||
if (
|
||||
c.type !== CipherType.Login ||
|
||||
c.login.password == null ||
|
||||
c.login.password === "" ||
|
||||
c.isDeleted
|
||||
type !== CipherType.Login ||
|
||||
login.password == null ||
|
||||
login.password === "" ||
|
||||
isDeleted ||
|
||||
(!this.organization && !edit) ||
|
||||
!viewPassword
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const hasUserName = isUserNameNotEmpty(c);
|
||||
const cacheKey = getCacheKey(c);
|
||||
const hasUserName = this.isUserNameNotEmpty(ciph);
|
||||
const cacheKey = this.getCacheKey(ciph);
|
||||
if (!this.passwordStrengthCache.has(cacheKey)) {
|
||||
let userInput: string[] = [];
|
||||
if (hasUserName) {
|
||||
const atPosition = c.login.username.indexOf("@");
|
||||
const atPosition = login.username.indexOf("@");
|
||||
if (atPosition > -1) {
|
||||
userInput = userInput
|
||||
.concat(
|
||||
c.login.username
|
||||
login.username
|
||||
.substr(0, atPosition)
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
@ -70,15 +72,15 @@ export class WeakPasswordsReportComponent extends CipherReportComponent implemen
|
||||
)
|
||||
.filter((i) => i.length >= 3);
|
||||
} else {
|
||||
userInput = c.login.username
|
||||
userInput = login.username
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.split(/[^A-Za-z0-9]/)
|
||||
.filter((i) => i.length >= 3);
|
||||
.filter((i: any) => i.length >= 3);
|
||||
}
|
||||
}
|
||||
const result = this.passwordStrengthService.getPasswordStrength(
|
||||
c.login.password,
|
||||
login.password,
|
||||
null,
|
||||
userInput.length > 0 ? userInput : null
|
||||
);
|
||||
@ -86,17 +88,17 @@ export class WeakPasswordsReportComponent extends CipherReportComponent implemen
|
||||
}
|
||||
const score = this.passwordStrengthCache.get(cacheKey);
|
||||
if (score != null && score <= 2) {
|
||||
this.passwordStrengthMap.set(c.id, this.scoreKey(score));
|
||||
weakPasswordCiphers.push(c);
|
||||
this.passwordStrengthMap.set(id, this.scoreKey(score));
|
||||
this.weakPasswordCiphers.push(ciph);
|
||||
}
|
||||
});
|
||||
weakPasswordCiphers.sort((a, b) => {
|
||||
this.weakPasswordCiphers.sort((a, b) => {
|
||||
return (
|
||||
this.passwordStrengthCache.get(getCacheKey(a)) -
|
||||
this.passwordStrengthCache.get(getCacheKey(b))
|
||||
this.passwordStrengthCache.get(this.getCacheKey(a)) -
|
||||
this.passwordStrengthCache.get(this.getCacheKey(b))
|
||||
);
|
||||
});
|
||||
this.ciphers = weakPasswordCiphers;
|
||||
this.ciphers = [...this.weakPasswordCiphers];
|
||||
}
|
||||
|
||||
protected getAllCiphers(): Promise<CipherView[]> {
|
||||
@ -108,6 +110,14 @@ export class WeakPasswordsReportComponent extends CipherReportComponent implemen
|
||||
return true;
|
||||
}
|
||||
|
||||
private isUserNameNotEmpty(c: CipherView): boolean {
|
||||
return !Utils.isNullOrWhitespace(c.login.username);
|
||||
}
|
||||
|
||||
private getCacheKey(c: CipherView): string {
|
||||
return c.login.password + "_____" + (this.isUserNameNotEmpty(c) ? c.login.username : "");
|
||||
}
|
||||
|
||||
private scoreKey(score: number): [string, BadgeTypes] {
|
||||
switch (score) {
|
||||
case 4:
|
||||
|
@ -2,6 +2,8 @@ import { CommonModule } from "@angular/common";
|
||||
import { NgModule } from "@angular/core";
|
||||
|
||||
import { SharedModule } from "../shared";
|
||||
import { OrganizationBadgeModule } from "../vault/individual-vault/organization-badge/organization-badge.module";
|
||||
import { PipesModule } from "../vault/individual-vault/pipes/pipes.module";
|
||||
|
||||
import { BreachReportComponent } from "./pages/breach-report.component";
|
||||
import { ExposedPasswordsReportComponent } from "./pages/exposed-passwords-report.component";
|
||||
@ -15,7 +17,14 @@ import { ReportsRoutingModule } from "./reports-routing.module";
|
||||
import { ReportsSharedModule } from "./shared";
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, SharedModule, ReportsSharedModule, ReportsRoutingModule],
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
ReportsSharedModule,
|
||||
ReportsRoutingModule,
|
||||
OrganizationBadgeModule,
|
||||
PipesModule,
|
||||
],
|
||||
declarations: [
|
||||
BreachReportComponent,
|
||||
ExposedPasswordsReportComponent,
|
||||
@ -25,7 +34,6 @@ import { ReportsSharedModule } from "./shared";
|
||||
ReusedPasswordsReportComponent,
|
||||
UnsecuredWebsitesReportComponent,
|
||||
WeakPasswordsReportComponent,
|
||||
WeakPasswordsReportComponent,
|
||||
],
|
||||
})
|
||||
export class ReportsModule {}
|
||||
|
@ -81,6 +81,8 @@ import { AddEditComponent } from "../vault/individual-vault/add-edit.component";
|
||||
import { AttachmentsComponent } from "../vault/individual-vault/attachments.component";
|
||||
import { CollectionsComponent } from "../vault/individual-vault/collections.component";
|
||||
import { FolderAddEditComponent } from "../vault/individual-vault/folder-add-edit.component";
|
||||
import { OrganizationBadgeModule } from "../vault/individual-vault/organization-badge/organization-badge.module";
|
||||
import { PipesModule } from "../vault/individual-vault/pipes/pipes.module";
|
||||
import { ShareComponent } from "../vault/individual-vault/share.component";
|
||||
import { AddEditComponent as OrgAddEditComponent } from "../vault/org-vault/add-edit.component";
|
||||
import { AttachmentsComponent as OrgAttachmentsComponent } from "../vault/org-vault/attachments.component";
|
||||
@ -102,6 +104,8 @@ import { SharedModule } from "./shared.module";
|
||||
DynamicAvatarComponent,
|
||||
EnvironmentSelectorModule,
|
||||
AccountFingerprintComponent,
|
||||
OrganizationBadgeModule,
|
||||
PipesModule,
|
||||
PasswordCalloutComponent,
|
||||
],
|
||||
declarations: [
|
||||
|
Loading…
Reference in New Issue
Block a user