1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-29 06:33:40 +00:00

Apply Prettier (#1347)

This commit is contained in:
Oscar Hinton
2021-12-17 15:57:11 +01:00
committed by GitHub
parent 2b0a9d995e
commit 56477eb39c
414 changed files with 33390 additions and 26857 deletions

View File

@@ -1,57 +1,64 @@
<div class="page-header">
<h1>{{'dataBreachReport' | i18n}}</h1>
<h1>{{ "dataBreachReport" | i18n }}</h1>
</div>
<p>{{'breachDesc' | i18n}}</p>
<p>{{ "breachDesc" | i18n }}</p>
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate>
<div class="row">
<div class="form-group col-6">
<label for="username">{{'username' | i18n}}</label>
<input id="username" type="text" name="Username" class="form-control" [(ngModel)]="username" required>
<small class="form-text text-muted">{{'breachCheckUsernameEmail' | i18n}}</small>
</div>
<div class="row">
<div class="form-group col-6">
<label for="username">{{ "username" | i18n }}</label>
<input
id="username"
type="text"
name="Username"
class="form-control"
[(ngModel)]="username"
required
/>
<small class="form-text text-muted">{{ "breachCheckUsernameEmail" | i18n }}</small>
</div>
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading">
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}" aria-hidden="true"></i>
<span>{{'checkBreaches' | i18n}}</span>
</button>
</div>
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading">
<i class="fa fa-spinner fa-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
<span>{{ "checkBreaches" | i18n }}</span>
</button>
</form>
<div class="mt-4" *ngIf="!form.loading && checkedUsername">
<p *ngIf="error">{{'reportError' | i18n}}...</p>
<ng-container *ngIf="!error">
<app-callout type="success" title="{{'goodNews' | i18n}}" *ngIf="!breachedAccounts.length">
{{'breachUsernameNotFound' | i18n : checkedUsername}}
</app-callout>
<app-callout type="danger" title="{{'breachFound' | i18n}}" *ngIf="breachedAccounts.length">
{{'breachUsernameFound' | i18n : checkedUsername : breachedAccounts.length}}
</app-callout>
<ul class="list-group list-group-breach" *ngIf="breachedAccounts.length">
<li *ngFor="let a of breachedAccounts" class="list-group-item min-height-fix">
<div class="row">
<div class="col-2 text-center">
<img [src]="a.logoPath" alt="" class="img-fluid">
</div>
<div class="col-7">
<h3 class="text-lg">{{a.title}}</h3>
<p [innerHTML]="a.description"></p>
<p class="mb-1">{{'compromisedData' | i18n}}:</p>
<ul>
<li *ngFor="let d of a.dataClasses">{{d}}</li>
</ul>
</div>
<div class="col-3">
<dl>
<dt>{{'website' | i18n}}</dt>
<dd>{{a.domain}}</dd>
<dt>{{'affectedUsers' | i18n}}</dt>
<dd>{{a.pwnCount | number}}</dd>
<dt>{{'breachOccurred' | i18n}}</dt>
<dd>{{a.breachDate | date: 'mediumDate'}}</dd>
<dt>{{'breachReported' | i18n}}</dt>
<dd>{{a.addedDate | date: 'mediumDate'}}</dd>
</dl>
</div>
</div>
</li>
</ul>
</ng-container>
<p *ngIf="error">{{ "reportError" | i18n }}...</p>
<ng-container *ngIf="!error">
<app-callout type="success" title="{{ 'goodNews' | i18n }}" *ngIf="!breachedAccounts.length">
{{ "breachUsernameNotFound" | i18n: checkedUsername }}
</app-callout>
<app-callout type="danger" title="{{ 'breachFound' | i18n }}" *ngIf="breachedAccounts.length">
{{ "breachUsernameFound" | i18n: checkedUsername:breachedAccounts.length }}
</app-callout>
<ul class="list-group list-group-breach" *ngIf="breachedAccounts.length">
<li *ngFor="let a of breachedAccounts" class="list-group-item min-height-fix">
<div class="row">
<div class="col-2 text-center">
<img [src]="a.logoPath" alt="" class="img-fluid" />
</div>
<div class="col-7">
<h3 class="text-lg">{{ a.title }}</h3>
<p [innerHTML]="a.description"></p>
<p class="mb-1">{{ "compromisedData" | i18n }}:</p>
<ul>
<li *ngFor="let d of a.dataClasses">{{ d }}</li>
</ul>
</div>
<div class="col-3">
<dl>
<dt>{{ "website" | i18n }}</dt>
<dd>{{ a.domain }}</dd>
<dt>{{ "affectedUsers" | i18n }}</dt>
<dd>{{ a.pwnCount | number }}</dd>
<dt>{{ "breachOccurred" | i18n }}</dt>
<dd>{{ a.breachDate | date: "mediumDate" }}</dd>
<dt>{{ "breachReported" | i18n }}</dt>
<dd>{{ a.addedDate | date: "mediumDate" }}</dd>
</dl>
</div>
</div>
</li>
</ul>
</ng-container>
</div>

View File

@@ -1,38 +1,35 @@
import {
Component,
OnInit,
} from '@angular/core';
import { Component, OnInit } from "@angular/core";
import { AuditService } from 'jslib-common/abstractions/audit.service';
import { StateService } from 'jslib-common/abstractions/state.service';
import { BreachAccountResponse } from 'jslib-common/models/response/breachAccountResponse';
import { AuditService } from "jslib-common/abstractions/audit.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { BreachAccountResponse } from "jslib-common/models/response/breachAccountResponse";
@Component({
selector: 'app-breach-report',
templateUrl: 'breach-report.component.html',
selector: "app-breach-report",
templateUrl: "breach-report.component.html",
})
export class BreachReportComponent implements OnInit {
error = false;
username: string;
checkedUsername: string;
breachedAccounts: BreachAccountResponse[] = [];
formPromise: Promise<BreachAccountResponse[]>;
error = false;
username: string;
checkedUsername: string;
breachedAccounts: BreachAccountResponse[] = [];
formPromise: Promise<BreachAccountResponse[]>;
constructor(private auditService: AuditService, private stateService: StateService) { }
constructor(private auditService: AuditService, private stateService: StateService) {}
async ngOnInit() {
this.username = await this.stateService.getEmail();
}
async submit() {
this.error = false;
this.username = this.username.toLowerCase();
try {
this.formPromise = this.auditService.breachedAccounts(this.username);
this.breachedAccounts = await this.formPromise;
} catch {
this.error = true;
}
this.checkedUsername = this.username;
async ngOnInit() {
this.username = await this.stateService.getEmail();
}
async submit() {
this.error = false;
this.username = this.username.toLowerCase();
try {
this.formPromise = this.auditService.breachedAccounts(this.username);
this.breachedAccounts = await this.formPromise;
} catch {
this.error = true;
}
this.checkedUsername = this.username;
}
}

View File

@@ -1,100 +1,107 @@
import {
Directive,
ViewChild,
ViewContainerRef,
} from '@angular/core';
import { Directive, ViewChild, ViewContainerRef } from "@angular/core";
import { CipherView } from 'jslib-common/models/view/cipherView';
import { CipherView } from "jslib-common/models/view/cipherView";
import { Organization } from 'jslib-common/models/domain/organization';
import { Organization } from "jslib-common/models/domain/organization";
import { AddEditComponent as OrgAddEditComponent } from '../organizations/vault/add-edit.component';
import { AddEditComponent } from '../vault/add-edit.component';
import { AddEditComponent as OrgAddEditComponent } from "../organizations/vault/add-edit.component";
import { AddEditComponent } from "../vault/add-edit.component";
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
import { PasswordRepromptService } from 'jslib-common/abstractions/passwordReprompt.service';
import { StateService } from 'jslib-common/abstractions/state.service';
import { MessagingService } from "jslib-common/abstractions/messaging.service";
import { PasswordRepromptService } from "jslib-common/abstractions/passwordReprompt.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { CipherRepromptType } from 'jslib-common/enums/cipherRepromptType';
import { CipherRepromptType } from "jslib-common/enums/cipherRepromptType";
import { ModalService } from 'jslib-angular/services/modal.service';
import { ModalService } from "jslib-angular/services/modal.service";
@Directive()
export class CipherReportComponent {
@ViewChild('cipherAddEdit', { read: ViewContainerRef, static: true }) cipherAddEditModalRef: ViewContainerRef;
@ViewChild("cipherAddEdit", { read: ViewContainerRef, static: true })
cipherAddEditModalRef: ViewContainerRef;
loading = false;
hasLoaded = false;
ciphers: CipherView[] = [];
organization: Organization;
loading = false;
hasLoaded = false;
ciphers: CipherView[] = [];
organization: Organization;
constructor(private modalService: ModalService, protected messagingService: MessagingService,
public requiresPaid: boolean, private stateService: StateService,
protected passwordRepromptService: PasswordRepromptService) { }
constructor(
private modalService: ModalService,
protected messagingService: MessagingService,
public requiresPaid: boolean,
private stateService: StateService,
protected passwordRepromptService: PasswordRepromptService
) {}
async load() {
this.loading = true;
await this.setCiphers();
this.loading = false;
this.hasLoaded = true;
async load() {
this.loading = true;
await this.setCiphers();
this.loading = false;
this.hasLoaded = true;
}
async selectCipher(cipher: CipherView) {
if (!(await this.repromptCipher(cipher))) {
return;
}
async selectCipher(cipher: CipherView) {
if (!await this.repromptCipher(cipher)) {
return;
}
const type = this.organization != null ? OrgAddEditComponent : AddEditComponent;
const type = this.organization != null ? OrgAddEditComponent : AddEditComponent;
const [modal, childComponent] = await this.modalService.openViewRef(type, this.cipherAddEditModalRef, (comp: OrgAddEditComponent | AddEditComponent) => {
if (this.organization != null) {
(comp as OrgAddEditComponent).organization = this.organization;
comp.organizationId = this.organization.id;
}
comp.cipherId = cipher == null ? null : cipher.id;
comp.onSavedCipher.subscribe(async (c: CipherView) => {
modal.close();
await this.load();
});
comp.onDeletedCipher.subscribe(async (c: CipherView) => {
modal.close();
await this.load();
});
comp.onRestoredCipher.subscribe(async (c: CipherView) => {
modal.close();
await this.load();
});
});
return childComponent;
}
protected async checkAccess(): Promise<boolean> {
const [modal, childComponent] = await this.modalService.openViewRef(
type,
this.cipherAddEditModalRef,
(comp: OrgAddEditComponent | AddEditComponent) => {
if (this.organization != null) {
// TODO: Maybe we want to just make sure they are not on a free plan? Just compare useTotp for now
// since all paid plans include useTotp
if (this.requiresPaid && !this.organization.useTotp) {
this.messagingService.send('upgradeOrganization', { organizationId: this.organization.id });
return false;
}
} else {
const accessPremium = await this.stateService.getCanAccessPremium();
if (this.requiresPaid && !accessPremium) {
this.messagingService.send('premiumRequired');
this.loading = false;
return false;
}
(comp as OrgAddEditComponent).organization = this.organization;
comp.organizationId = this.organization.id;
}
return true;
}
protected async setCiphers() {
this.ciphers = [];
}
comp.cipherId = cipher == null ? null : cipher.id;
comp.onSavedCipher.subscribe(async (c: CipherView) => {
modal.close();
await this.load();
});
comp.onDeletedCipher.subscribe(async (c: CipherView) => {
modal.close();
await this.load();
});
comp.onRestoredCipher.subscribe(async (c: CipherView) => {
modal.close();
await this.load();
});
}
);
protected async repromptCipher(c: CipherView) {
return c.reprompt === CipherRepromptType.None || await this.passwordRepromptService.showPasswordPrompt();
return childComponent;
}
protected async checkAccess(): Promise<boolean> {
if (this.organization != null) {
// TODO: Maybe we want to just make sure they are not on a free plan? Just compare useTotp for now
// since all paid plans include useTotp
if (this.requiresPaid && !this.organization.useTotp) {
this.messagingService.send("upgradeOrganization", { organizationId: this.organization.id });
return false;
}
} else {
const accessPremium = await this.stateService.getCanAccessPremium();
if (this.requiresPaid && !accessPremium) {
this.messagingService.send("premiumRequired");
this.loading = false;
return false;
}
}
return true;
}
protected async setCiphers() {
this.ciphers = [];
}
protected async repromptCipher(c: CipherView) {
return (
c.reprompt === CipherRepromptType.None ||
(await this.passwordRepromptService.showPasswordPrompt())
);
}
}

View File

@@ -1,28 +1,39 @@
<form #form (ngSubmit)="submit()" ngNativeValidate [appApiAction]="formPromise" [formGroup]="exportForm">
<div class="page-header">
<h1>{{'exportVault' | i18n}}</h1>
</div>
<form
#form
(ngSubmit)="submit()"
ngNativeValidate
[appApiAction]="formPromise"
[formGroup]="exportForm"
>
<div class="page-header">
<h1>{{ "exportVault" | i18n }}</h1>
</div>
<app-callout type="error" title="{{'vaultExportDisabled' | i18n}}" *ngIf="disabledByPolicy">
{{'personalVaultExportPolicyInEffect' | i18n}}
</app-callout>
<app-callout type="error" title="{{ 'vaultExportDisabled' | i18n }}" *ngIf="disabledByPolicy">
{{ "personalVaultExportPolicyInEffect" | i18n }}
</app-callout>
<div class="row">
<div class="form-group col-6">
<label for="format">{{'fileFormat' | i18n}}</label>
<select class="form-control" id="format" name="Format" formControlName="format">
<option *ngFor="let f of formatOptions" [value]="f.value">{{f.name}}</option>
</select>
</div>
<div class="row">
<div class="form-group col-6">
<label for="format">{{ "fileFormat" | i18n }}</label>
<select class="form-control" id="format" name="Format" formControlName="format">
<option *ngFor="let f of formatOptions" [value]="f.value">{{ f.name }}</option>
</select>
</div>
<div class="row">
<div class="form-group col-6">
<app-verify-master-password ngDefaultControl formControlName="secret" name="secret">
</app-verify-master-password>
</div>
</div>
<div class="row">
<div class="form-group col-6">
<app-verify-master-password ngDefaultControl formControlName="secret" name="secret">
</app-verify-master-password>
</div>
<button type="submit" class="btn btn-primary" [disabled]="form.loading || exportForm.disabled">
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}" aria-hidden="true" *ngIf="form.loading"></i>
<span *ngIf="!form.loading">{{'exportVault' | i18n}}</span>
</button>
</div>
<button type="submit" class="btn btn-primary" [disabled]="form.loading || exportForm.disabled">
<i
class="fa fa-spinner fa-spin"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
*ngIf="form.loading"
></i>
<span *ngIf="!form.loading">{{ "exportVault" | i18n }}</span>
</button>
</form>

View File

@@ -1,34 +1,51 @@
import { Component } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { Component } from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
import { EventService } from 'jslib-common/abstractions/event.service';
import { ExportService } from 'jslib-common/abstractions/export.service';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { LogService } from 'jslib-common/abstractions/log.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { PolicyService } from 'jslib-common/abstractions/policy.service';
import { UserVerificationService } from 'jslib-common/abstractions/userVerification.service';
import { CryptoService } from "jslib-common/abstractions/crypto.service";
import { EventService } from "jslib-common/abstractions/event.service";
import { ExportService } from "jslib-common/abstractions/export.service";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { LogService } from "jslib-common/abstractions/log.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { PolicyService } from "jslib-common/abstractions/policy.service";
import { UserVerificationService } from "jslib-common/abstractions/userVerification.service";
import { ExportComponent as BaseExportComponent } from 'jslib-angular/components/export.component';
import { ExportComponent as BaseExportComponent } from "jslib-angular/components/export.component";
@Component({
selector: 'app-export',
templateUrl: 'export.component.html',
selector: "app-export",
templateUrl: "export.component.html",
})
export class ExportComponent extends BaseExportComponent {
organizationId: string;
organizationId: string;
constructor(cryptoService: CryptoService, i18nService: I18nService,
platformUtilsService: PlatformUtilsService, exportService: ExportService,
eventService: EventService, policyService: PolicyService, logService: LogService,
userVerificationService: UserVerificationService, fb: FormBuilder) {
super(cryptoService, i18nService, platformUtilsService, exportService, eventService,
policyService, window, logService, userVerificationService, fb);
}
constructor(
cryptoService: CryptoService,
i18nService: I18nService,
platformUtilsService: PlatformUtilsService,
exportService: ExportService,
eventService: EventService,
policyService: PolicyService,
logService: LogService,
userVerificationService: UserVerificationService,
fb: FormBuilder
) {
super(
cryptoService,
i18nService,
platformUtilsService,
exportService,
eventService,
policyService,
window,
logService,
userVerificationService,
fb
);
}
protected saved() {
super.saved();
this.platformUtilsService.showToast('success', null, this.i18nService.t('exportSuccess'));
}
protected saved() {
super.saved();
this.platformUtilsService.showToast("success", null, this.i18nService.t("exportSuccess"));
}
}

View File

@@ -1,52 +1,63 @@
<div class="page-header">
<h1>{{'exposedPasswordsReport' | i18n}}</h1>
<h1>{{ "exposedPasswordsReport" | i18n }}</h1>
</div>
<p>{{'exposedPasswordsReportDesc' | i18n}}</p>
<p>{{ "exposedPasswordsReportDesc" | i18n }}</p>
<button type="button" class="btn btn-primary btn-submit" [disabled]="loading" (click)="load()">
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}" aria-hidden="true"></i>
<span>{{'checkExposedPasswords' | i18n}}</span>
<i class="fa fa-spinner fa-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
<span>{{ "checkExposedPasswords" | i18n }}</span>
</button>
<div class="mt-4" *ngIf="hasLoaded">
<app-callout type="success" title="{{'goodNews' | i18n}}" *ngIf="!ciphers.length">
{{'noExposedPasswords' | i18n}}
<app-callout type="success" title="{{ 'goodNews' | i18n }}" *ngIf="!ciphers.length">
{{ "noExposedPasswords" | i18n }}
</app-callout>
<ng-container *ngIf="ciphers.length">
<app-callout type="danger" title="{{ 'exposedPasswordsFound' | i18n }}" [useAlertRole]="true">
{{ "exposedPasswordsFoundDesc" | i18n: (ciphers.length | number) }}
</app-callout>
<ng-container *ngIf="ciphers.length">
<app-callout type="danger" title="{{'exposedPasswordsFound' | i18n}}" [useAlertRole]="true">
{{'exposedPasswordsFoundDesc' | i18n : (ciphers.length | number)}}
</app-callout>
<table class="table table-hover table-list table-ciphers">
<tbody>
<tr *ngFor="let c of ciphers">
<td class="table-list-icon">
<app-vault-icon [cipher]="c"></app-vault-icon>
</td>
<td class="reduced-lh wrap">
<ng-container *ngIf="!organization || canManageCipher(c) ; else cantManage">
<a href="#" appStopClick (click)="selectCipher(c)" title="{{'editItem' | i18n}}">{{c.name}}</a>
</ng-container>
<ng-template #cantManage>
<span>{{c.name}}</span>
</ng-template>
<ng-container *ngIf="!organization && c.organizationId">
<i class="fa fa-cube" appStopProp title="{{'shared' | i18n}}" aria-hidden="true"></i>
<span class="sr-only">{{'shared' | i18n}}</span>
</ng-container>
<ng-container *ngIf="c.hasAttachments">
<i class="fa fa-paperclip" appStopProp title="{{'attachments' | i18n}}"
aria-hidden="true"></i>
<span class="sr-only">{{'attachments' | i18n}}</span>
</ng-container>
<br>
<small>{{c.subTitle}}</small>
</td>
<td class="text-right">
<span class="badge badge-warning">
{{'exposedXTimes' | i18n : (exposedPasswordMap.get(c.id) | number)}}
</span>
</td>
</tr>
</tbody>
</table>
</ng-container>
<table class="table table-hover table-list table-ciphers">
<tbody>
<tr *ngFor="let c of ciphers">
<td class="table-list-icon">
<app-vault-icon [cipher]="c"></app-vault-icon>
</td>
<td class="reduced-lh wrap">
<ng-container *ngIf="!organization || canManageCipher(c); else cantManage">
<a href="#" appStopClick (click)="selectCipher(c)" title="{{ 'editItem' | i18n }}">{{
c.name
}}</a>
</ng-container>
<ng-template #cantManage>
<span>{{ c.name }}</span>
</ng-template>
<ng-container *ngIf="!organization && c.organizationId">
<i
class="fa fa-cube"
appStopProp
title="{{ 'shared' | i18n }}"
aria-hidden="true"
></i>
<span class="sr-only">{{ "shared" | i18n }}</span>
</ng-container>
<ng-container *ngIf="c.hasAttachments">
<i
class="fa fa-paperclip"
appStopProp
title="{{ 'attachments' | i18n }}"
aria-hidden="true"
></i>
<span class="sr-only">{{ "attachments" | i18n }}</span>
</ng-container>
<br />
<small>{{ c.subTitle }}</small>
</td>
<td class="text-right">
<span class="badge badge-warning">
{{ "exposedXTimes" | i18n: (exposedPasswordMap.get(c.id) | number) }}
</span>
</td>
</tr>
</tbody>
</table>
</ng-container>
</div>
<ng-template #cipherAddEdit></ng-template>

View File

@@ -1,71 +1,78 @@
import {
Component,
OnInit,
} from '@angular/core';
import { Component, OnInit } from "@angular/core";
import { AuditService } from 'jslib-common/abstractions/audit.service';
import { CipherService } from 'jslib-common/abstractions/cipher.service';
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
import { PasswordRepromptService } from 'jslib-common/abstractions/passwordReprompt.service';
import { StateService } from 'jslib-common/abstractions/state.service';
import { AuditService } from "jslib-common/abstractions/audit.service";
import { CipherService } from "jslib-common/abstractions/cipher.service";
import { MessagingService } from "jslib-common/abstractions/messaging.service";
import { PasswordRepromptService } from "jslib-common/abstractions/passwordReprompt.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { ModalService } from 'jslib-angular/services/modal.service';
import { ModalService } from "jslib-angular/services/modal.service";
import { CipherView } from 'jslib-common/models/view/cipherView';
import { CipherView } from "jslib-common/models/view/cipherView";
import { CipherType } from 'jslib-common/enums/cipherType';
import { CipherType } from "jslib-common/enums/cipherType";
import { CipherReportComponent } from './cipher-report.component';
import { CipherReportComponent } from "./cipher-report.component";
@Component({
selector: 'app-exposed-passwords-report',
templateUrl: 'exposed-passwords-report.component.html',
selector: "app-exposed-passwords-report",
templateUrl: "exposed-passwords-report.component.html",
})
export class ExposedPasswordsReportComponent extends CipherReportComponent implements OnInit {
exposedPasswordMap = new Map<string, number>();
exposedPasswordMap = new Map<string, number>();
constructor(protected cipherService: CipherService, protected auditService: AuditService,
modalService: ModalService, messagingService: MessagingService,
stateService: StateService, passwordRepromptService: PasswordRepromptService) {
super(modalService, messagingService, true, stateService, passwordRepromptService);
constructor(
protected cipherService: CipherService,
protected auditService: AuditService,
modalService: ModalService,
messagingService: MessagingService,
stateService: StateService,
passwordRepromptService: PasswordRepromptService
) {
super(modalService, messagingService, true, stateService, passwordRepromptService);
}
ngOnInit() {
this.checkAccess();
}
async load() {
if (await this.checkAccess()) {
super.load();
}
}
ngOnInit() {
this.checkAccess();
}
async load() {
if (await this.checkAccess()) {
super.load();
async setCiphers() {
const allCiphers = await this.getAllCiphers();
const exposedPasswordCiphers: CipherView[] = [];
const promises: Promise<void>[] = [];
allCiphers.forEach((c) => {
if (
c.type !== CipherType.Login ||
c.login.password == null ||
c.login.password === "" ||
c.isDeleted
) {
return;
}
const promise = this.auditService.passwordLeaked(c.login.password).then((exposedCount) => {
if (exposedCount > 0) {
exposedPasswordCiphers.push(c);
this.exposedPasswordMap.set(c.id, exposedCount);
}
}
});
promises.push(promise);
});
await Promise.all(promises);
this.ciphers = exposedPasswordCiphers;
}
async setCiphers() {
const allCiphers = await this.getAllCiphers();
const exposedPasswordCiphers: CipherView[] = [];
const promises: Promise<void>[] = [];
allCiphers.forEach(c => {
if (c.type !== CipherType.Login || c.login.password == null || c.login.password === '' || c.isDeleted) {
return;
}
const promise = this.auditService.passwordLeaked(c.login.password).then(exposedCount => {
if (exposedCount > 0) {
exposedPasswordCiphers.push(c);
this.exposedPasswordMap.set(c.id, exposedCount);
}
});
promises.push(promise);
});
await Promise.all(promises);
this.ciphers = exposedPasswordCiphers;
}
protected getAllCiphers(): Promise<CipherView[]> {
return this.cipherService.getAllDecrypted();
}
protected getAllCiphers(): Promise<CipherView[]> {
return this.cipherService.getAllDecrypted();
}
protected canManageCipher(c: CipherView): boolean {
// this will only ever be false from the org view;
return true;
}
protected canManageCipher(c: CipherView): boolean {
// this will only ever be false from the org view;
return true;
}
}

View File

@@ -1,266 +1,329 @@
<div class="page-header">
<h1>{{'importData' | i18n}}</h1>
<h1>{{ "importData" | i18n }}</h1>
</div>
<app-callout type="info" *ngIf="importBlockedByPolicy">
{{'personalOwnershipPolicyInEffectImports' | i18n}}
{{ "personalOwnershipPolicyInEffectImports" | i18n }}
</app-callout>
<form #form (ngSubmit)="submit()" ngNativeValidate>
<div class="row">
<div class="col-6">
<div class="form-group">
<label for="type">1. {{'selectFormat' | i18n}}</label>
<select id="type" name="Format" [(ngModel)]="format" class="form-control"
[disabled]="importBlockedByPolicy" required>
<option *ngFor="let o of featuredImportOptions" [ngValue]="o.id">{{o.name}}</option>
<ng-container *ngIf="importOptions && importOptions.length">
<option value="-" disabled></option>
<option *ngFor="let o of importOptions" [ngValue]="o.id">{{o.name}}</option>
</ng-container>
</select>
</div>
</div>
<div class="row">
<div class="col-6">
<div class="form-group">
<label for="type">1. {{ "selectFormat" | i18n }}</label>
<select
id="type"
name="Format"
[(ngModel)]="format"
class="form-control"
[disabled]="importBlockedByPolicy"
required
>
<option *ngFor="let o of featuredImportOptions" [ngValue]="o.id">{{ o.name }}</option>
<ng-container *ngIf="importOptions && importOptions.length">
<option value="-" disabled></option>
<option *ngFor="let o of importOptions" [ngValue]="o.id">{{ o.name }}</option>
</ng-container>
</select>
</div>
</div>
<app-callout type="info" title="{{getFormatInstructionTitle()}}" *ngIf="format">
<ng-container *ngIf="format === 'bitwardencsv' || format === 'bitwardenjson'">
See detailed instructions on our help site at
<a target="_blank" rel="noopener" href="https://help.bitwarden.com/article/export-your-data/">
https://help.bitwarden.com/article/export-your-data/</a>
</ng-container>
<ng-container *ngIf="format === 'lastpasscsv'">
See detailed instructions on our help site at
<a target="_blank" rel="noopener" href="https://help.bitwarden.com/article/import-from-lastpass/">
https://help.bitwarden.com/article/import-from-lastpass/</a>
</ng-container>
<ng-container *ngIf="format === 'keepassxcsv'">
Using the KeePassX desktop application, navigate to "Database" &rarr; "Export to CSV file" and save the CSV
file.
</ng-container>
<ng-container *ngIf="format === 'aviracsv'">
In the Avira web vault, go to "Settings" &rarr; "My Data" &rarr; "Export data" and save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'blurcsv'">
In the Blur web vault, click your username at the top and go to "Settings" &rarr; "Export Data", then click
"Export CSV"
for your "Accounts".
</ng-container>
<ng-container *ngIf="format === 'safeincloudxml'">
Using the SaveInCloud desktop application, navigate to "File" &rarr; "Export" &rarr; "As XML" and save the
XML file.
</ng-container>
<ng-container *ngIf="format === 'padlockcsv'">
Using the Padlock desktop application, click the hamburger icon in the top left corner and navigate to
"Settings" &rarr;
"Export" button and save the file "As CSV".
</ng-container>
<ng-container *ngIf="format === 'keepass2xml'">
Using the KeePass 2 desktop application, navigate to "File" &rarr; "Export" and select the "KeePass XML
(2.x)" option.
</ng-container>
<ng-container *ngIf="format === 'upmcsv'">
Using the Universal Password Manager desktop application, navigate to "Database" &rarr; "Export" and save
the CSV file.
</ng-container>
<ng-container *ngIf="format === 'saferpasscsv'">
Using the SaferPass browser extension, click the hamburger icon in the top left corner and navigate to
"Settings". Click the "Export accounts" button to save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'meldiumcsv'">
Using the Meldium web vault, navigate to "Settings". Locate the "Export data" function and click "Show me my
data" to save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'keepercsv'">
Log into the Keeper web vault (keepersecurity.com/vault). Navigate to "Backup" (top right) and find the
"Export to .csv File" option. Click "Export Now" to save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'chromecsv' || format === 'operacsv' || format === 'vivaldicsv'">
<span *ngIf="format !== 'chromecsv'">
The process is exactly the same as importing from Google Chrome.
</span>
See detailed instructions on our help site at
<a target="_blank" rel="noopener" href="https://help.bitwarden.com/article/import-from-chrome/">
https://help.bitwarden.com/article/import-from-chrome/</a>
</ng-container>
<ng-container *ngIf="format === 'firefoxcsv'">
See detailed instructions on our help site at
<a target="_blank" rel="noopener" href="https://bitwarden.com/help/article/import-from-firefox/">
https://bitwarden.com/help/article/import-from-firefox/</a>.
</ng-container>
<ng-container *ngIf="format === 'safaricsv'">
See detailed instructions on our help site at
<a target="_blank" rel="noopener" href="https://bitwarden.com/help/article/import-from-safari/">
https://bitwarden.com/help/article/import-from-safari/</a>.
</ng-container>
<ng-container *ngIf="format === '1password1pif' || format === '1passwordwincsv' || format === '1passwordmaccsv'">
See detailed instructions on our help site at
<a target="_blank" rel="noopener" href="https://help.bitwarden.com/article/import-from-1password/">
https://help.bitwarden.com/article/import-from-1password/</a>.
</ng-container>
<ng-container *ngIf="format === 'passworddragonxml'">
Using the Password Dragon desktop application, navigate to "File" &rarr; "Export" &rarr; "To XML". In the
dialog that pops up select "All Rows" and check all fields. Click the "Export" button and save the XML file.
</ng-container>
<ng-container *ngIf="format === 'enpasscsv'">
Using the Enpass desktop application, navigate to "File" &rarr; "Export" &rarr; "As CSV". Select "OK" to the
warning alert and save the CSV file. Note that the importer only supports files exported while Enpass is set
to the English language, so adjust your settings accordingly.
</ng-container>
<ng-container *ngIf="format === 'enpassjson'">
Using the Enpass 6 desktop application, click the menu button and navigate to "File" &rarr; "Export".
Select the ".json" file format option and save the JSON file.
</ng-container>
<ng-container *ngIf="format === 'pwsafexml'">
Using the Password Safe desktop application, navigate to "File" &rarr; "Export To" &rarr; "XML format..."
and save the XML file.
</ng-container>
<ng-container *ngIf="format === 'dashlanejson'">
Using the Dashlane desktop application, navigate to "File" &rarr; "Export" &rarr; "Unsecured archive
(readable) in JSON format" and save the JSON file.
</ng-container>
<ng-container *ngIf="format === 'msecurecsv'">
Using the mSecure desktop application, navigate to "File" &rarr; "Export" &rarr; "CSV File..." and save the
CSV file.
</ng-container>
<ng-container *ngIf="format === 'stickypasswordxml'">
Using the Sticky Password desktop application, navigate to "Menu" (top right) &rarr; "Export" &rarr; "Export
all". Select the unencrypted format XML option and save the XML file.
</ng-container>
<ng-container *ngIf="format === 'truekeycsv'">
Using the True Key desktop application, click the gear icon (top right) and then navigate to "App Settings".
Click the "Export" button, enter your password and save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'clipperzhtml'">
Log into the Clipperz web application (clipperz.is/app). Click the hamburger menu icon in the top right to
expand the navigation bar. Navigate to "Data" &rarr; "Export". Click the "download HTML+JSON" button to save
the HTML file.
</ng-container>
<ng-container *ngIf="format === 'roboformcsv'">
Using the RoboForm Editor desktop application, navigate to "RoboForm" (top left) &rarr; "Options" &rarr;
"Account &amp; Data" and click the "Export" button. Select all of your data, change the "Format" to "CSV
file" and then click the "Export" button to save the CSV file. Note: RoboForm only allows you to export
Logins. Other items will not be exported.
</ng-container>
<ng-container *ngIf="format === 'passboltcsv'">
Log into the Passbolt web vault and navigate to the "Passwords" listing. Select all of the passwords you
would like to export and click the "Export" button at the top of the listing. Choose the "csv (lastpass)"
export format and click the "Export" button.
</ng-container>
<ng-container *ngIf="format === 'ascendocsv'">
Using the Ascendo DataVault desktop application, navigate to "Tools" &rarr; "Export". In the dialog that
pops up, select the "All Items (DVX, CSV)" option. Click the "Ok" button to save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'passwordbossjson'">
Using the Password Boss desktop application, navigate to "File" &rarr; "Export data" &rarr; "Password Boss
JSON - not encrypted" and save the JSON file.
</ng-container>
<ng-container *ngIf="format === 'zohovaultcsv'">
Log into the Zoho web vault (vault.zoho.com). Navigate to "Tools" &rarr; "Export Secrets". Select "All
Secrets" and click the "Zoho Vault Format CSV" button. Highlight and copy the data from the textarea. Open a
text editor like Notepad and paste the data. Save the data from the text editor as
<code>zoho_export.csv</code>.
</ng-container>
<ng-container *ngIf="format === 'splashidcsv'">
Using the SplashID Safe desktop application, click on the SplashID blue lock logo in the top right corner.
Navigate to "Export" &rarr; "Export as CSV" and save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'passkeepcsv'">
Using the PassKeep mobile app, navigate to "Backup/Restore". Locate the "CSV Backup/Restore" section and
click "Backup to CSV" to save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'gnomejson'">
Make sure you have python-keyring and python-gnomekeyring installed. Save the
<a target="_blank" rel="noopener" href="https://bit.ly/2GpOMTg">GNOME Keyring Import/Export</a> python
script to your desktop as <code>pw_helper.py</code>. Open terminal and run
<code>chmod +rx Desktop/pw_helper.py</code> and then
<code>python Desktop/pw_helper.py export Desktop/my_passwords.json</code>. Then upload the resulting
<code>my_passwords.json</code> file here to Bitwarden.
</ng-container>
<ng-container *ngIf="format === 'passwordagentcsv'">
Using the Password Agent desktop application navigate to "File" &rarr; "Export", select the "Fields to
export" button and check all of the fields, change the "Output format" to "CSV", and then click the "Start"
button to save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'passpackcsv'">
Log into the Passpack website vault and navigate to "Settings" &rarr; "Export", then click the "Download"
button to save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'passmanjson'">
Open your Passman vault and click on "Settings" in the bottom left corner. In the "Settings" window switch
to the "Export credentials" tab and choose "JSON" as the export type. Enter your vault's passphrase and
click the "Export" button to save the JSON file.
</ng-container>
<ng-container *ngIf="format === 'avastcsv'">
Open the Avast Passwords desktop application and navigate to "Settings" &rarr; "Import/export data". Select
the "Export" button for the "Export to CSV file" option to save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'avastjson'">
Open the Avast Passwords desktop application and navigate to "Settings" &rarr; "Import/export data". Select
the "Export" button for the "Export to JSON file" option to save the JSON file.
</ng-container>
<ng-container *ngIf="format === 'fsecurefsk'">
Open the F-Secure KEY desktop application and navigate to "Settings" &rarr; "Export Passwords". Select the
"Export" button, enter your master password, and save the FSK file.
</ng-container>
<ng-container *ngIf="format === 'kasperskytxt'">
Open the Kaspersky Password Manager desktop application and navigate to "Settings" &rarr; "Import/Export".
Locate the "Export to text file" section and select the "Export" button to save the TXT file.
</ng-container>
<ng-container *ngIf="format === 'remembearcsv'">
Open the RememBear desktop application and navigate to "Settings" &rarr; "Account" &rarr; "Export".
Enter your master password and select the "Export Anyway" button to save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'passwordwallettxt'">
Open the PasswordWallet desktop application and navigate to "File" &rarr; "Export" &rarr;
"Visible entries to text file". Enter your password and select the "Ok" button to save the TXT file.
</ng-container>
<ng-container *ngIf="format === 'mykicsv'">
Open the Myki desktop browser extension and navigate to "Advanced" &rarr; "Export Accounts" and then scan
the QR code with your mobile device. Various CSV files will then be saved to your computer's
downloads folder.
</ng-container>
<ng-container *ngIf="format === 'securesafecsv'">
Export your SecureSafe password safe to a CSV file with a comma delimiter.
</ng-container>
<ng-container *ngIf="format === 'logmeoncecsv'">
Open the LogMeOnce browser extension, then navigate to "Open Menu" &rarr; "Export To" and
select "CSV File" to save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'blackberrycsv'">
Open the BlackBerry Password Keeper application, then navigate to "Settings" &rarr; "Import/Export".
Select "Export Passwords" and follow the instructions on screen to save the unencrypted CSV file.
</ng-container>
<ng-container *ngIf="format === 'buttercupcsv'">
Open the Buttercup desktop application and unlock your vault. Right click on your vault's icon and
select "Export" to save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'codebookcsv'">
Open the Codebook desktop application and log in. Navigate to "File" &rarr; "Export all", then click
"Yes" on the dialog and save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'encryptrcsv'">
Open the newest version of the Encryptr desktop application and allow all of your data to sync.
Once syncing of your data is complete, the download icon in the top right corner will turn pink. Click
the download icon and save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'yoticsv'">
From the Yoti browser extension, click on "Settings", then "Export Saved Logins" and save the CSV file.
</ng-container>
</app-callout>
<div class="row">
<div class="col-6">
<div class="form-group">
<label for="file">2. {{'selectImportFile' | i18n}}</label>
<input type="file" id="file" class="form-control-file" name="file" [disabled]="importBlockedByPolicy">
</div>
</div>
</div>
<app-callout type="info" title="{{ getFormatInstructionTitle() }}" *ngIf="format">
<ng-container *ngIf="format === 'bitwardencsv' || format === 'bitwardenjson'">
See detailed instructions on our help site at
<a target="_blank" rel="noopener" href="https://help.bitwarden.com/article/export-your-data/">
https://help.bitwarden.com/article/export-your-data/</a
>
</ng-container>
<ng-container *ngIf="format === 'lastpasscsv'">
See detailed instructions on our help site at
<a
target="_blank"
rel="noopener"
href="https://help.bitwarden.com/article/import-from-lastpass/"
>
https://help.bitwarden.com/article/import-from-lastpass/</a
>
</ng-container>
<ng-container *ngIf="format === 'keepassxcsv'">
Using the KeePassX desktop application, navigate to "Database" &rarr; "Export to CSV file" and
save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'aviracsv'">
In the Avira web vault, go to "Settings" &rarr; "My Data" &rarr; "Export data" and save the
CSV file.
</ng-container>
<ng-container *ngIf="format === 'blurcsv'">
In the Blur web vault, click your username at the top and go to "Settings" &rarr; "Export
Data", then click "Export CSV" for your "Accounts".
</ng-container>
<ng-container *ngIf="format === 'safeincloudxml'">
Using the SaveInCloud desktop application, navigate to "File" &rarr; "Export" &rarr; "As XML"
and save the XML file.
</ng-container>
<ng-container *ngIf="format === 'padlockcsv'">
Using the Padlock desktop application, click the hamburger icon in the top left corner and
navigate to "Settings" &rarr; "Export" button and save the file "As CSV".
</ng-container>
<ng-container *ngIf="format === 'keepass2xml'">
Using the KeePass 2 desktop application, navigate to "File" &rarr; "Export" and select the
"KeePass XML (2.x)" option.
</ng-container>
<ng-container *ngIf="format === 'upmcsv'">
Using the Universal Password Manager desktop application, navigate to "Database" &rarr;
"Export" and save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'saferpasscsv'">
Using the SaferPass browser extension, click the hamburger icon in the top left corner and
navigate to "Settings". Click the "Export accounts" button to save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'meldiumcsv'">
Using the Meldium web vault, navigate to "Settings". Locate the "Export data" function and
click "Show me my data" to save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'keepercsv'">
Log into the Keeper web vault (keepersecurity.com/vault). Navigate to "Backup" (top right) and
find the "Export to .csv File" option. Click "Export Now" to save the CSV file.
</ng-container>
<ng-container
*ngIf="format === 'chromecsv' || format === 'operacsv' || format === 'vivaldicsv'"
>
<span *ngIf="format !== 'chromecsv'">
The process is exactly the same as importing from Google Chrome.
</span>
See detailed instructions on our help site at
<a
target="_blank"
rel="noopener"
href="https://help.bitwarden.com/article/import-from-chrome/"
>
https://help.bitwarden.com/article/import-from-chrome/</a
>
</ng-container>
<ng-container *ngIf="format === 'firefoxcsv'">
See detailed instructions on our help site at
<a
target="_blank"
rel="noopener"
href="https://bitwarden.com/help/article/import-from-firefox/"
>
https://bitwarden.com/help/article/import-from-firefox/</a
>.
</ng-container>
<ng-container *ngIf="format === 'safaricsv'">
See detailed instructions on our help site at
<a
target="_blank"
rel="noopener"
href="https://bitwarden.com/help/article/import-from-safari/"
>
https://bitwarden.com/help/article/import-from-safari/</a
>.
</ng-container>
<ng-container
*ngIf="
format === '1password1pif' || format === '1passwordwincsv' || format === '1passwordmaccsv'
"
>
See detailed instructions on our help site at
<a
target="_blank"
rel="noopener"
href="https://help.bitwarden.com/article/import-from-1password/"
>
https://help.bitwarden.com/article/import-from-1password/</a
>.
</ng-container>
<ng-container *ngIf="format === 'passworddragonxml'">
Using the Password Dragon desktop application, navigate to "File" &rarr; "Export" &rarr; "To
XML". In the dialog that pops up select "All Rows" and check all fields. Click the "Export"
button and save the XML file.
</ng-container>
<ng-container *ngIf="format === 'enpasscsv'">
Using the Enpass desktop application, navigate to "File" &rarr; "Export" &rarr; "As CSV".
Select "OK" to the warning alert and save the CSV file. Note that the importer only supports
files exported while Enpass is set to the English language, so adjust your settings
accordingly.
</ng-container>
<ng-container *ngIf="format === 'enpassjson'">
Using the Enpass 6 desktop application, click the menu button and navigate to "File" &rarr;
"Export". Select the ".json" file format option and save the JSON file.
</ng-container>
<ng-container *ngIf="format === 'pwsafexml'">
Using the Password Safe desktop application, navigate to "File" &rarr; "Export To" &rarr; "XML
format..." and save the XML file.
</ng-container>
<ng-container *ngIf="format === 'dashlanejson'">
Using the Dashlane desktop application, navigate to "File" &rarr; "Export" &rarr; "Unsecured
archive (readable) in JSON format" and save the JSON file.
</ng-container>
<ng-container *ngIf="format === 'msecurecsv'">
Using the mSecure desktop application, navigate to "File" &rarr; "Export" &rarr; "CSV File..."
and save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'stickypasswordxml'">
Using the Sticky Password desktop application, navigate to "Menu" (top right) &rarr; "Export"
&rarr; "Export all". Select the unencrypted format XML option and save the XML file.
</ng-container>
<ng-container *ngIf="format === 'truekeycsv'">
Using the True Key desktop application, click the gear icon (top right) and then navigate to
"App Settings". Click the "Export" button, enter your password and save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'clipperzhtml'">
Log into the Clipperz web application (clipperz.is/app). Click the hamburger menu icon in the
top right to expand the navigation bar. Navigate to "Data" &rarr; "Export". Click the
"download HTML+JSON" button to save the HTML file.
</ng-container>
<ng-container *ngIf="format === 'roboformcsv'">
Using the RoboForm Editor desktop application, navigate to "RoboForm" (top left) &rarr;
"Options" &rarr; "Account &amp; Data" and click the "Export" button. Select all of your data,
change the "Format" to "CSV file" and then click the "Export" button to save the CSV file.
Note: RoboForm only allows you to export Logins. Other items will not be exported.
</ng-container>
<ng-container *ngIf="format === 'passboltcsv'">
Log into the Passbolt web vault and navigate to the "Passwords" listing. Select all of the
passwords you would like to export and click the "Export" button at the top of the listing.
Choose the "csv (lastpass)" export format and click the "Export" button.
</ng-container>
<ng-container *ngIf="format === 'ascendocsv'">
Using the Ascendo DataVault desktop application, navigate to "Tools" &rarr; "Export". In the
dialog that pops up, select the "All Items (DVX, CSV)" option. Click the "Ok" button to save
the CSV file.
</ng-container>
<ng-container *ngIf="format === 'passwordbossjson'">
Using the Password Boss desktop application, navigate to "File" &rarr; "Export data" &rarr;
"Password Boss JSON - not encrypted" and save the JSON file.
</ng-container>
<ng-container *ngIf="format === 'zohovaultcsv'">
Log into the Zoho web vault (vault.zoho.com). Navigate to "Tools" &rarr; "Export Secrets".
Select "All Secrets" and click the "Zoho Vault Format CSV" button. Highlight and copy the data
from the textarea. Open a text editor like Notepad and paste the data. Save the data from the
text editor as
<code>zoho_export.csv</code>.
</ng-container>
<ng-container *ngIf="format === 'splashidcsv'">
Using the SplashID Safe desktop application, click on the SplashID blue lock logo in the top
right corner. Navigate to "Export" &rarr; "Export as CSV" and save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'passkeepcsv'">
Using the PassKeep mobile app, navigate to "Backup/Restore". Locate the "CSV Backup/Restore"
section and click "Backup to CSV" to save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'gnomejson'">
Make sure you have python-keyring and python-gnomekeyring installed. Save the
<a target="_blank" rel="noopener" href="https://bit.ly/2GpOMTg"
>GNOME Keyring Import/Export</a
>
python script to your desktop as <code>pw_helper.py</code>. Open terminal and run
<code>chmod +rx Desktop/pw_helper.py</code> and then
<code>python Desktop/pw_helper.py export Desktop/my_passwords.json</code>. Then upload the
resulting <code>my_passwords.json</code> file here to Bitwarden.
</ng-container>
<ng-container *ngIf="format === 'passwordagentcsv'">
Using the Password Agent desktop application navigate to "File" &rarr; "Export", select the
"Fields to export" button and check all of the fields, change the "Output format" to "CSV",
and then click the "Start" button to save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'passpackcsv'">
Log into the Passpack website vault and navigate to "Settings" &rarr; "Export", then click the
"Download" button to save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'passmanjson'">
Open your Passman vault and click on "Settings" in the bottom left corner. In the "Settings"
window switch to the "Export credentials" tab and choose "JSON" as the export type. Enter your
vault's passphrase and click the "Export" button to save the JSON file.
</ng-container>
<ng-container *ngIf="format === 'avastcsv'">
Open the Avast Passwords desktop application and navigate to "Settings" &rarr; "Import/export
data". Select the "Export" button for the "Export to CSV file" option to save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'avastjson'">
Open the Avast Passwords desktop application and navigate to "Settings" &rarr; "Import/export
data". Select the "Export" button for the "Export to JSON file" option to save the JSON file.
</ng-container>
<ng-container *ngIf="format === 'fsecurefsk'">
Open the F-Secure KEY desktop application and navigate to "Settings" &rarr; "Export
Passwords". Select the "Export" button, enter your master password, and save the FSK file.
</ng-container>
<ng-container *ngIf="format === 'kasperskytxt'">
Open the Kaspersky Password Manager desktop application and navigate to "Settings" &rarr;
"Import/Export". Locate the "Export to text file" section and select the "Export" button to
save the TXT file.
</ng-container>
<ng-container *ngIf="format === 'remembearcsv'">
Open the RememBear desktop application and navigate to "Settings" &rarr; "Account" &rarr;
"Export". Enter your master password and select the "Export Anyway" button to save the CSV
file.
</ng-container>
<ng-container *ngIf="format === 'passwordwallettxt'">
Open the PasswordWallet desktop application and navigate to "File" &rarr; "Export" &rarr;
"Visible entries to text file". Enter your password and select the "Ok" button to save the TXT
file.
</ng-container>
<ng-container *ngIf="format === 'mykicsv'">
Open the Myki desktop browser extension and navigate to "Advanced" &rarr; "Export Accounts"
and then scan the QR code with your mobile device. Various CSV files will then be saved to
your computer's downloads folder.
</ng-container>
<ng-container *ngIf="format === 'securesafecsv'">
Export your SecureSafe password safe to a CSV file with a comma delimiter.
</ng-container>
<ng-container *ngIf="format === 'logmeoncecsv'">
Open the LogMeOnce browser extension, then navigate to "Open Menu" &rarr; "Export To" and
select "CSV File" to save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'blackberrycsv'">
Open the BlackBerry Password Keeper application, then navigate to "Settings" &rarr;
"Import/Export". Select "Export Passwords" and follow the instructions on screen to save the
unencrypted CSV file.
</ng-container>
<ng-container *ngIf="format === 'buttercupcsv'">
Open the Buttercup desktop application and unlock your vault. Right click on your vault's icon
and select "Export" to save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'codebookcsv'">
Open the Codebook desktop application and log in. Navigate to "File" &rarr; "Export all", then
click "Yes" on the dialog and save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'encryptrcsv'">
Open the newest version of the Encryptr desktop application and allow all of your data to
sync. Once syncing of your data is complete, the download icon in the top right corner will
turn pink. Click the download icon and save the CSV file.
</ng-container>
<ng-container *ngIf="format === 'yoticsv'">
From the Yoti browser extension, click on "Settings", then "Export Saved Logins" and save the
CSV file.
</ng-container>
</app-callout>
<div class="row">
<div class="col-6">
<div class="form-group">
<label for="file">2. {{ "selectImportFile" | i18n }}</label>
<input
type="file"
id="file"
class="form-control-file"
name="file"
[disabled]="importBlockedByPolicy"
/>
</div>
</div>
<div class="form-group">
<label for="fileContents">{{'orCopyPasteFileContents' | i18n}}</label>
<textarea id="fileContents" class="form-control" name="FileContents" [(ngModel)]="fileContents"
[disabled]="importBlockedByPolicy"></textarea>
</div>
<button type="submit" class="btn btn-primary btn-submit" [disabled]="loading || importBlockedByPolicy"
[ngClass]="{manual:importBlockedByPolicy}">
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}" aria-hidden="true"></i>
<span>{{'importData' | i18n}}</span>
</button>
</div>
<div class="form-group">
<label for="fileContents">{{ "orCopyPasteFileContents" | i18n }}</label>
<textarea
id="fileContents"
class="form-control"
name="FileContents"
[(ngModel)]="fileContents"
[disabled]="importBlockedByPolicy"
></textarea>
</div>
<button
type="submit"
class="btn btn-primary btn-submit"
[disabled]="loading || importBlockedByPolicy"
[ngClass]="{ manual: importBlockedByPolicy }"
>
<i class="fa fa-spinner fa-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
<span>{{ "importData" | i18n }}</span>
</button>
</form>

View File

@@ -1,185 +1,209 @@
import {
Component,
OnInit,
} from '@angular/core';
import { Router } from '@angular/router';
import { Component, OnInit } from "@angular/core";
import { Router } from "@angular/router";
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { ImportOption, ImportService } from 'jslib-common/abstractions/import.service';
import { LogService } from 'jslib-common/abstractions/log.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { PolicyService } from 'jslib-common/abstractions/policy.service';
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { ImportOption, ImportService } from "jslib-common/abstractions/import.service";
import { LogService } from "jslib-common/abstractions/log.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { PolicyService } from "jslib-common/abstractions/policy.service";
import { PolicyType } from 'jslib-common/enums/policyType';
import { PolicyType } from "jslib-common/enums/policyType";
import Swal, { SweetAlertIcon } from 'sweetalert2';
import Swal, { SweetAlertIcon } from "sweetalert2";
@Component({
selector: 'app-import',
templateUrl: 'import.component.html',
selector: "app-import",
templateUrl: "import.component.html",
})
export class ImportComponent implements OnInit {
featuredImportOptions: ImportOption[];
importOptions: ImportOption[];
format: string = null;
fileContents: string;
formPromise: Promise<Error>;
loading: boolean = false;
importBlockedByPolicy: boolean = false;
featuredImportOptions: ImportOption[];
importOptions: ImportOption[];
format: string = null;
fileContents: string;
formPromise: Promise<Error>;
loading: boolean = false;
importBlockedByPolicy: boolean = false;
protected organizationId: string = null;
protected successNavigate: any[] = ['vault'];
protected organizationId: string = null;
protected successNavigate: any[] = ["vault"];
constructor(protected i18nService: I18nService,
protected importService: ImportService, protected router: Router,
protected platformUtilsService: PlatformUtilsService, protected policyService: PolicyService,
private logService: LogService) { }
constructor(
protected i18nService: I18nService,
protected importService: ImportService,
protected router: Router,
protected platformUtilsService: PlatformUtilsService,
protected policyService: PolicyService,
private logService: LogService
) {}
async ngOnInit() {
this.setImportOptions();
this.importOptions.sort((a, b) => {
if (a.name == null && b.name != null) {
return -1;
}
if (a.name != null && b.name == null) {
return 1;
}
if (a.name == null && b.name == null) {
return 0;
}
async ngOnInit() {
this.setImportOptions();
this.importOptions.sort((a, b) => {
if (a.name == null && b.name != null) {
return -1;
}
if (a.name != null && b.name == null) {
return 1;
}
if (a.name == null && b.name == null) {
return 0;
}
return this.i18nService.collator ? this.i18nService.collator.compare(a.name, b.name) :
a.name.localeCompare(b.name);
});
return this.i18nService.collator
? this.i18nService.collator.compare(a.name, b.name)
: a.name.localeCompare(b.name);
});
this.importBlockedByPolicy = await this.policyService.policyAppliesToUser(PolicyType.PersonalOwnership);
this.importBlockedByPolicy = await this.policyService.policyAppliesToUser(
PolicyType.PersonalOwnership
);
}
async submit() {
if (this.importBlockedByPolicy) {
this.platformUtilsService.showToast(
"error",
null,
this.i18nService.t("personalOwnershipPolicyInEffectImports")
);
return;
}
async submit() {
if (this.importBlockedByPolicy) {
this.platformUtilsService.showToast('error', null,
this.i18nService.t('personalOwnershipPolicyInEffectImports'));
return;
this.loading = true;
const importer = this.importService.getImporter(this.format, this.organizationId);
if (importer === null) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("selectFormat")
);
this.loading = false;
return;
}
const fileEl = document.getElementById("file") as HTMLInputElement;
const files = fileEl.files;
if (
(files == null || files.length === 0) &&
(this.fileContents == null || this.fileContents === "")
) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("selectFile")
);
this.loading = false;
return;
}
let fileContents = this.fileContents;
if (files != null && files.length > 0) {
try {
const content = await this.getFileContents(files[0]);
if (content != null) {
fileContents = content;
}
} catch (e) {
this.logService.error(e);
}
}
this.loading = true;
const importer = this.importService.getImporter(this.format, this.organizationId);
if (importer === null) {
this.platformUtilsService.showToast('error', this.i18nService.t('errorOccurred'),
this.i18nService.t('selectFormat'));
this.loading = false;
return;
}
const fileEl = document.getElementById('file') as HTMLInputElement;
const files = fileEl.files;
if ((files == null || files.length === 0) && (this.fileContents == null || this.fileContents === '')) {
this.platformUtilsService.showToast('error', this.i18nService.t('errorOccurred'),
this.i18nService.t('selectFile'));
this.loading = false;
return;
}
let fileContents = this.fileContents;
if (files != null && files.length > 0) {
try {
const content = await this.getFileContents(files[0]);
if (content != null) {
fileContents = content;
}
} catch (e) {
this.logService.error(e);
}
}
if (fileContents == null || fileContents === '') {
this.platformUtilsService.showToast('error', this.i18nService.t('errorOccurred'),
this.i18nService.t('selectFile'));
this.loading = false;
return;
}
try {
this.formPromise = this.importService.import(importer, fileContents, this.organizationId);
const error = await this.formPromise;
if (error != null) {
this.error(error);
this.loading = false;
return;
}
this.platformUtilsService.showToast('success', null, this.i18nService.t('importSuccess'));
this.router.navigate(this.successNavigate);
} catch (e) {
this.logService.error(e);
}
if (fileContents == null || fileContents === "") {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("selectFile")
);
this.loading = false;
return;
}
try {
this.formPromise = this.importService.import(importer, fileContents, this.organizationId);
const error = await this.formPromise;
if (error != null) {
this.error(error);
this.loading = false;
return;
}
this.platformUtilsService.showToast("success", null, this.i18nService.t("importSuccess"));
this.router.navigate(this.successNavigate);
} catch (e) {
this.logService.error(e);
}
getFormatInstructionTitle() {
if (this.format == null) {
return null;
this.loading = false;
}
getFormatInstructionTitle() {
if (this.format == null) {
return null;
}
const results = this.featuredImportOptions
.concat(this.importOptions)
.filter((o) => o.id === this.format);
if (results.length > 0) {
return this.i18nService.t("instructionsFor", results[0].name);
}
return null;
}
protected setImportOptions() {
this.featuredImportOptions = [
{
id: null,
name: "-- " + this.i18nService.t("select") + " --",
},
...this.importService.featuredImportOptions,
];
this.importOptions = this.importService.regularImportOptions;
}
private async error(error: Error) {
await Swal.fire({
heightAuto: false,
buttonsStyling: false,
icon: "error" as SweetAlertIcon,
iconHtml: `<i class="swal-custom-icon fa fa-bolt text-danger"></i>`,
input: "textarea",
inputValue: error.message,
inputAttributes: {
readonly: "true",
},
titleText: this.i18nService.t("importError"),
text: this.i18nService.t("importErrorDesc"),
showConfirmButton: true,
confirmButtonText: this.i18nService.t("ok"),
onOpen: (popupEl) => {
popupEl.querySelector(".swal2-textarea").scrollTo(0, 0);
},
});
}
private getFileContents(file: File): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsText(file, "utf-8");
reader.onload = (evt) => {
if (this.format === "lastpasscsv" && file.type === "text/html") {
const parser = new DOMParser();
const doc = parser.parseFromString((evt.target as any).result, "text/html");
const pre = doc.querySelector("pre");
if (pre != null) {
resolve(pre.textContent);
return;
}
reject();
return;
}
const results = this.featuredImportOptions.concat(this.importOptions).filter(o => o.id === this.format);
if (results.length > 0) {
return this.i18nService.t('instructionsFor', results[0].name);
}
return null;
}
protected setImportOptions() {
this.featuredImportOptions = [{
id: null,
name: '-- ' + this.i18nService.t('select') + ' --',
}, ...this.importService.featuredImportOptions];
this.importOptions = this.importService.regularImportOptions;
}
private async error(error: Error) {
await Swal.fire({
heightAuto: false,
buttonsStyling: false,
icon: 'error' as SweetAlertIcon,
iconHtml: `<i class="swal-custom-icon fa fa-bolt text-danger"></i>`,
input: 'textarea',
inputValue: error.message,
inputAttributes: {
'readonly': 'true',
},
titleText: this.i18nService.t('importError'),
text: this.i18nService.t('importErrorDesc'),
showConfirmButton: true,
confirmButtonText: this.i18nService.t('ok'),
onOpen: popupEl => {
popupEl.querySelector('.swal2-textarea').scrollTo(0, 0);
},
});
}
private getFileContents(file: File): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsText(file, 'utf-8');
reader.onload = evt => {
if (this.format === 'lastpasscsv' && file.type === 'text/html') {
const parser = new DOMParser();
const doc = parser.parseFromString((evt.target as any).result, 'text/html');
const pre = doc.querySelector('pre');
if (pre != null) {
resolve(pre.textContent);
return;
}
reject();
return;
}
resolve((evt.target as any).result);
};
reader.onerror = () => {
reject();
};
});
}
resolve((evt.target as any).result);
};
reader.onerror = () => {
reject();
};
});
}
}

View File

@@ -1,53 +1,74 @@
<div class="page-header">
<h1>
{{'inactive2faReport' | i18n}}
<small *ngIf="hasLoaded && loading">
<i class="fa fa-spinner fa-spin text-muted" title="{{'loading' | i18n}}" aria-hidden="true"></i>
<span class="sr-only">{{'loading' | i18n}}</span>
</small>
</h1>
<h1>
{{ "inactive2faReport" | i18n }}
<small *ngIf="hasLoaded && loading">
<i
class="fa fa-spinner fa-spin text-muted"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
></i>
<span class="sr-only">{{ "loading" | i18n }}</span>
</small>
</h1>
</div>
<p>{{'inactive2faReportDesc' | i18n}}</p>
<p>{{ "inactive2faReportDesc" | i18n }}</p>
<div *ngIf="!hasLoaded && loading">
<i class="fa fa-spinner fa-spin text-muted" title="{{'loading' | i18n}}" aria-hidden="true"></i>
<span class="sr-only">{{'loading' | i18n}}</span>
<i class="fa fa-spinner fa-spin text-muted" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
<span class="sr-only">{{ "loading" | i18n }}</span>
</div>
<div class="mt-4" *ngIf="hasLoaded">
<app-callout type="success" title="{{'goodNews' | i18n}}" *ngIf="!ciphers.length">
{{'noInactive2fa' | i18n}}
<app-callout type="success" title="{{ 'goodNews' | i18n }}" *ngIf="!ciphers.length">
{{ "noInactive2fa" | i18n }}
</app-callout>
<ng-container *ngIf="ciphers.length">
<app-callout type="danger" title="{{ 'inactive2faFound' | i18n }}">
{{ "inactive2faFoundDesc" | i18n: (ciphers.length | number) }}
</app-callout>
<ng-container *ngIf="ciphers.length">
<app-callout type="danger" title="{{'inactive2faFound' | i18n}}">
{{'inactive2faFoundDesc' | i18n : (ciphers.length | number)}}
</app-callout>
<table class="table table-hover table-list table-ciphers">
<tbody>
<tr *ngFor="let c of ciphers">
<td class="table-list-icon">
<app-vault-icon [cipher]="c"></app-vault-icon>
</td>
<td class="reduced-lh wrap">
<a href="#" appStopClick (click)="selectCipher(c)" title="{{'editItem' | i18n}}">{{c.name}}</a>
<ng-container *ngIf="!organization && c.organizationId">
<i class="fa fa-cube" appStopProp title="{{'shared' | i18n}}" aria-hidden="true"></i>
<span class="sr-only">{{'shared' | i18n}}</span>
</ng-container>
<ng-container *ngIf="c.hasAttachments">
<i class="fa fa-paperclip" appStopProp title="{{'attachments' | i18n}}"
aria-hidden="true"></i>
<span class="sr-only">{{'attachments' | i18n}}</span>
</ng-container>
<br>
<small>{{c.subTitle}}</small>
</td>
<td class="text-right">
<a class="badge badge-primary" href="{{cipherDocs.get(c.id)}}" target="_blank" rel="noopener"
*ngIf="cipherDocs.has(c.id)">
{{'instructions' | i18n}}</a>
</td>
</tr>
</tbody>
</table>
</ng-container>
<table class="table table-hover table-list table-ciphers">
<tbody>
<tr *ngFor="let c of ciphers">
<td class="table-list-icon">
<app-vault-icon [cipher]="c"></app-vault-icon>
</td>
<td class="reduced-lh wrap">
<a href="#" appStopClick (click)="selectCipher(c)" title="{{ 'editItem' | i18n }}">{{
c.name
}}</a>
<ng-container *ngIf="!organization && c.organizationId">
<i
class="fa fa-cube"
appStopProp
title="{{ 'shared' | i18n }}"
aria-hidden="true"
></i>
<span class="sr-only">{{ "shared" | i18n }}</span>
</ng-container>
<ng-container *ngIf="c.hasAttachments">
<i
class="fa fa-paperclip"
appStopProp
title="{{ 'attachments' | i18n }}"
aria-hidden="true"
></i>
<span class="sr-only">{{ "attachments" | i18n }}</span>
</ng-container>
<br />
<small>{{ c.subTitle }}</small>
</td>
<td class="text-right">
<a
class="badge badge-primary"
href="{{ cipherDocs.get(c.id) }}"
target="_blank"
rel="noopener"
*ngIf="cipherDocs.has(c.id)"
>
{{ "instructions" | i18n }}</a
>
</td>
</tr>
</tbody>
</table>
</ng-container>
</div>
<ng-template #cipherAddEdit></ng-template>

View File

@@ -1,108 +1,114 @@
import {
Component,
OnInit,
} from '@angular/core';
import { Component, OnInit } from "@angular/core";
import { CipherService } from 'jslib-common/abstractions/cipher.service';
import { LogService } from 'jslib-common/abstractions/log.service';
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
import { PasswordRepromptService } from 'jslib-common/abstractions/passwordReprompt.service';
import { StateService } from 'jslib-common/abstractions/state.service';
import { CipherService } from "jslib-common/abstractions/cipher.service";
import { LogService } from "jslib-common/abstractions/log.service";
import { MessagingService } from "jslib-common/abstractions/messaging.service";
import { PasswordRepromptService } from "jslib-common/abstractions/passwordReprompt.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { ModalService } from 'jslib-angular/services/modal.service';
import { ModalService } from "jslib-angular/services/modal.service";
import { CipherView } from 'jslib-common/models/view/cipherView';
import { CipherView } from "jslib-common/models/view/cipherView";
import { CipherType } from 'jslib-common/enums/cipherType';
import { CipherType } from "jslib-common/enums/cipherType";
import { Utils } from 'jslib-common/misc/utils';
import { Utils } from "jslib-common/misc/utils";
import { CipherReportComponent } from './cipher-report.component';
import { CipherReportComponent } from "./cipher-report.component";
@Component({
selector: 'app-inactive-two-factor-report',
templateUrl: 'inactive-two-factor-report.component.html',
selector: "app-inactive-two-factor-report",
templateUrl: "inactive-two-factor-report.component.html",
})
export class InactiveTwoFactorReportComponent extends CipherReportComponent implements OnInit {
services = new Map<string, string>();
cipherDocs = new Map<string, string>();
services = new Map<string, string>();
cipherDocs = new Map<string, string>();
constructor(protected cipherService: CipherService, modalService: ModalService,
messagingService: MessagingService, stateService: StateService, private logService: LogService,
passwordRepromptService: PasswordRepromptService) {
super(modalService, messagingService, true, stateService, passwordRepromptService);
constructor(
protected cipherService: CipherService,
modalService: ModalService,
messagingService: MessagingService,
stateService: StateService,
private logService: LogService,
passwordRepromptService: PasswordRepromptService
) {
super(modalService, messagingService, true, stateService, passwordRepromptService);
}
async ngOnInit() {
if (await this.checkAccess()) {
await super.load();
}
}
async setCiphers() {
try {
await this.load2fa();
} catch (e) {
this.logService.error(e);
}
async ngOnInit() {
if (await this.checkAccess()) {
await super.load();
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) => {
if (
c.type !== CipherType.Login ||
(c.login.totp != null && c.login.totp !== "") ||
!c.login.hasUris ||
c.isDeleted
) {
return;
}
}
async setCiphers() {
try {
await this.load2fa();
} catch (e) {
this.logService.error(e);
}
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 => {
if (c.type !== CipherType.Login || (c.login.totp != null && c.login.totp !== '') || !c.login.hasUris ||
c.isDeleted) {
return;
}
for (let i = 0; i < c.login.uris.length; i++) {
const u = c.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));
}
inactive2faCiphers.push(c);
}
}
}
});
await Promise.all(promises);
this.ciphers = inactive2faCiphers;
this.cipherDocs = docs;
}
}
protected getAllCiphers(): Promise<CipherView[]> {
return this.cipherService.getAllDecrypted();
}
private async load2fa() {
if (this.services.size > 0) {
return;
}
const response = await fetch(new Request('https://2fa.directory/api/v3/totp.json'));
if (response.status !== 200) {
throw new Error();
}
const responseJson = await response.json();
for (const service of responseJson) {
const serviceData = service[1];
if (serviceData.domain == null) {
continue;
for (let i = 0; i < c.login.uris.length; i++) {
const u = c.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));
}
inactive2faCiphers.push(c);
}
if (serviceData.documentation == null) {
continue;
}
if (serviceData['additional-domains'] != null) {
for (const additionalDomain of serviceData['additional-domains']) {
this.services.set(additionalDomain, serviceData.documentation);
}
}
this.services.set(serviceData.domain, serviceData.documentation);
}
}
});
await Promise.all(promises);
this.ciphers = inactive2faCiphers;
this.cipherDocs = docs;
}
}
protected getAllCiphers(): Promise<CipherView[]> {
return this.cipherService.getAllDecrypted();
}
private async load2fa() {
if (this.services.size > 0) {
return;
}
const response = await fetch(new Request("https://2fa.directory/api/v3/totp.json"));
if (response.status !== 200) {
throw new Error();
}
const responseJson = await response.json();
for (const service of responseJson) {
const serviceData = service[1];
if (serviceData.domain == null) {
continue;
}
if (serviceData.documentation == null) {
continue;
}
if (serviceData["additional-domains"] != null) {
for (const additionalDomain of serviceData["additional-domains"]) {
this.services.set(additionalDomain, serviceData.documentation);
}
}
this.services.set(serviceData.domain, serviceData.documentation);
}
}
}

View File

@@ -1,43 +1,58 @@
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="passHistoryTitle">
<div class="modal-dialog modal-dialog-scrollable" role="document">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title" id="passHistoryTitle">{{'passwordHistory' | i18n}}</h2>
<button type="button" class="close" data-dismiss="modal" appA11yTitle="{{'close' | i18n}}">
<span aria-hidden="true">&times;</span>
</button>
<div class="modal-dialog modal-dialog-scrollable" role="document">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title" id="passHistoryTitle">{{ "passwordHistory" | i18n }}</h2>
<button
type="button"
class="close"
data-dismiss="modal"
appA11yTitle="{{ 'close' | i18n }}"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body" *ngIf="history.length">
<ul class="list-group list-group-flush">
<li class="list-group-item d-flex" *ngFor="let h of history">
<div class="password-row">
<div
class="text-monospace password-wrapper"
[innerHTML]="h.password | colorPassword"
appSelectCopy
></div>
<small class="text-muted">{{ h.date | date: "medium" }}</small>
</div>
<div class="modal-body" *ngIf="history.length">
<ul class="list-group list-group-flush">
<li class="list-group-item d-flex" *ngFor="let h of history">
<div class="password-row">
<div class="text-monospace password-wrapper" [innerHTML]="h.password | colorPassword"
appSelectCopy></div>
<small class="text-muted">{{h.date | date:'medium'}}</small>
</div>
<div class="ml-auto">
<button class="btn btn-link" appA11yTitle="{{'copyPassword' | i18n}}"
(click)="copy(h.password)">
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
</button>
</div>
</li>
</ul>
</div>
<div class="modal-body" *ngIf="!history.length">
{{'noPasswordsInList' | i18n}}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
{{'close' | i18n}}
</button>
<div class="ml-auto">
<button type="button" (click)="clear()" class="btn btn-outline-danger"
appA11yTitle="{{'clear' | i18n}}">
<i class="fa fa-trash-o fa-lg fa-fw" aria-hidden="true"></i>
</button>
</div>
<div class="ml-auto">
<button
class="btn btn-link"
appA11yTitle="{{ 'copyPassword' | i18n }}"
(click)="copy(h.password)"
>
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
</button>
</div>
</li>
</ul>
</div>
<div class="modal-body" *ngIf="!history.length">
{{ "noPasswordsInList" | i18n }}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
{{ "close" | i18n }}
</button>
<div class="ml-auto">
<button
type="button"
(click)="clear()"
class="btn btn-outline-danger"
appA11yTitle="{{ 'clear' | i18n }}"
>
<i class="fa fa-trash-o fa-lg fa-fw" aria-hidden="true"></i>
</button>
</div>
</div>
</div>
</div>
</div>

View File

@@ -1,20 +1,21 @@
import { Component } from '@angular/core';
import { Component } from "@angular/core";
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import {
PasswordGeneratorHistoryComponent as BasePasswordGeneratorHistoryComponent,
} from 'jslib-angular/components/password-generator-history.component';
import { PasswordGeneratorHistoryComponent as BasePasswordGeneratorHistoryComponent } from "jslib-angular/components/password-generator-history.component";
@Component({
selector: 'app-password-generator-history',
templateUrl: 'password-generator-history.component.html',
selector: "app-password-generator-history",
templateUrl: "password-generator-history.component.html",
})
export class PasswordGeneratorHistoryComponent extends BasePasswordGeneratorHistoryComponent {
constructor(passwordGenerationService: PasswordGenerationService, platformUtilsService: PlatformUtilsService,
i18nService: I18nService) {
super(passwordGenerationService, platformUtilsService, i18nService, window);
}
constructor(
passwordGenerationService: PasswordGenerationService,
platformUtilsService: PlatformUtilsService,
i18nService: I18nService
) {
super(passwordGenerationService, platformUtilsService, i18nService, window);
}
}

View File

@@ -1,109 +1,199 @@
<div class="page-header">
<h1>{{'passwordGenerator' | i18n}}</h1>
<h1>{{ "passwordGenerator" | i18n }}</h1>
</div>
<app-callout type="info" *ngIf="enforcedPolicyOptions?.inEffect()">
{{'passwordGeneratorPolicyInEffect' | i18n}}
{{ "passwordGeneratorPolicyInEffect" | i18n }}
</app-callout>
<div class="card card-password bg-light my-4">
<div class="card-body">
<div class="password-wrapper" [innerHTML]="password | colorPassword" appSelectCopy></div>
</div>
<div class="card-body">
<div class="password-wrapper" [innerHTML]="password | colorPassword" appSelectCopy></div>
</div>
</div>
<div class="form-group">
<div class="form-check form-check-inline" *ngFor="let o of passTypeOptions">
<input class="form-check-input" type="radio" [(ngModel)]="options.type" name="Type_{{o.value}}"
id="type_{{o.value}}" [value]="o.value" (change)="saveOptions()" [checked]="options.type === o.value">
<label class="form-check-label" for="type_{{o.value}}">
{{o.name}}
</label>
</div>
<div class="form-check form-check-inline" *ngFor="let o of passTypeOptions">
<input
class="form-check-input"
type="radio"
[(ngModel)]="options.type"
name="Type_{{ o.value }}"
id="type_{{ o.value }}"
[value]="o.value"
(change)="saveOptions()"
[checked]="options.type === o.value"
/>
<label class="form-check-label" for="type_{{ o.value }}">
{{ o.name }}
</label>
</div>
</div>
<ng-container *ngIf="options.type === 'passphrase'">
<div class="row">
<div class="form-group col-4">
<label for="num-words">{{'numWords' | i18n}}</label>
<input id="num-words" class="form-control" type="number" min="3" max="20" [(ngModel)]="options.numWords"
(blur)="saveOptions()">
</div>
<div class="form-group col-4">
<label for="word-separator">{{'wordSeparator' | i18n}}</label>
<input id="word-separator" class="form-control" type="text" maxlength="1"
[(ngModel)]="options.wordSeparator" (blur)="saveOptions()">
</div>
<div class="row">
<div class="form-group col-4">
<label for="num-words">{{ "numWords" | i18n }}</label>
<input
id="num-words"
class="form-control"
type="number"
min="3"
max="20"
[(ngModel)]="options.numWords"
(blur)="saveOptions()"
/>
</div>
<div class="form-group">
<div class="form-check">
<input id="capitalize" class="form-check-input" type="checkbox" (change)="saveOptions()"
[(ngModel)]="options.capitalize" [disabled]="enforcedPolicyOptions?.capitalize">
<label for="capitalize" class="form-check-label">{{'capitalize' | i18n}}</label>
</div>
<div class="form-check">
<input id="include-number" class="form-check-input" type="checkbox" (change)="saveOptions()"
[(ngModel)]="options.includeNumber" [disabled]="enforcedPolicyOptions?.includeNumber">
<label for="include-number" class="form-check-label">{{'includeNumber' | i18n}}</label>
</div>
<div class="form-group col-4">
<label for="word-separator">{{ "wordSeparator" | i18n }}</label>
<input
id="word-separator"
class="form-control"
type="text"
maxlength="1"
[(ngModel)]="options.wordSeparator"
(blur)="saveOptions()"
/>
</div>
</div>
<div class="form-group">
<div class="form-check">
<input
id="capitalize"
class="form-check-input"
type="checkbox"
(change)="saveOptions()"
[(ngModel)]="options.capitalize"
[disabled]="enforcedPolicyOptions?.capitalize"
/>
<label for="capitalize" class="form-check-label">{{ "capitalize" | i18n }}</label>
</div>
<div class="form-check">
<input
id="include-number"
class="form-check-input"
type="checkbox"
(change)="saveOptions()"
[(ngModel)]="options.includeNumber"
[disabled]="enforcedPolicyOptions?.includeNumber"
/>
<label for="include-number" class="form-check-label">{{ "includeNumber" | i18n }}</label>
</div>
</div>
</ng-container>
<ng-container *ngIf="options.type === 'password'">
<div class="row">
<div class="form-group col-4">
<label for="length">{{'length' | i18n}}</label>
<input id="length" class="form-control" type="number" min="5" max="128" [(ngModel)]="options.length"
(blur)="saveOptions()" (change)="lengthChanged()">
</div>
<div class="form-group col-4">
<label for="min-number">{{'minNumbers' | i18n}}</label>
<input id="min-number" class="form-control" type="number" min="0" max="9" (blur)="saveOptions()"
[(ngModel)]="options.minNumber" (change)="minNumberChanged()">
</div>
<div class="form-group col-4">
<label for="min-special">{{'minSpecial' | i18n}}</label>
<input id="min-special" class="form-control" type="number" min="0" max="9" (blur)="saveOptions()"
[(ngModel)]="options.minSpecial" (change)="minSpecialChanged()">
</div>
<div class="row">
<div class="form-group col-4">
<label for="length">{{ "length" | i18n }}</label>
<input
id="length"
class="form-control"
type="number"
min="5"
max="128"
[(ngModel)]="options.length"
(blur)="saveOptions()"
(change)="lengthChanged()"
/>
</div>
<div class="form-group">
<div class="form-check">
<input id="uppercase" class="form-check-input" type="checkbox" (change)="saveOptions()"
[(ngModel)]="options.uppercase" [disabled]="enforcedPolicyOptions?.useUppercase">
<label for="uppercase" class="form-check-label">A-Z</label>
</div>
<div class="form-check">
<input id="lowercase" class="form-check-input" type="checkbox" (change)="saveOptions()"
[(ngModel)]="options.lowercase" [disabled]="enforcedPolicyOptions?.useLowercase">
<label for="lowercase" class="form-check-label">a-z</label>
</div>
<div class="form-check">
<input id="numbers" class="form-check-input" type="checkbox" (change)="saveOptions()"
[(ngModel)]="options.number" [disabled]="enforcedPolicyOptions?.useNumbers">
<label for="numbers" class="form-check-label">0-9</label>
</div>
<div class="form-check">
<input id="special" class="form-check-input" type="checkbox" (change)="saveOptions()"
[(ngModel)]="options.special" [disabled]="enforcedPolicyOptions?.useSpecial">
<label for="special" class="form-check-label">!@#$%^&amp;*</label>
</div>
<div class="form-check">
<input id="ambiguous" class="form-check-input" type="checkbox" (change)="saveOptions()"
[(ngModel)]="avoidAmbiguous">
<label for="ambiguous" class="form-check-label">{{'ambiguous' | i18n}}</label>
</div>
<div class="form-group col-4">
<label for="min-number">{{ "minNumbers" | i18n }}</label>
<input
id="min-number"
class="form-control"
type="number"
min="0"
max="9"
(blur)="saveOptions()"
[(ngModel)]="options.minNumber"
(change)="minNumberChanged()"
/>
</div>
<div class="form-group col-4">
<label for="min-special">{{ "minSpecial" | i18n }}</label>
<input
id="min-special"
class="form-control"
type="number"
min="0"
max="9"
(blur)="saveOptions()"
[(ngModel)]="options.minSpecial"
(change)="minSpecialChanged()"
/>
</div>
</div>
<div class="form-group">
<div class="form-check">
<input
id="uppercase"
class="form-check-input"
type="checkbox"
(change)="saveOptions()"
[(ngModel)]="options.uppercase"
[disabled]="enforcedPolicyOptions?.useUppercase"
/>
<label for="uppercase" class="form-check-label">A-Z</label>
</div>
<div class="form-check">
<input
id="lowercase"
class="form-check-input"
type="checkbox"
(change)="saveOptions()"
[(ngModel)]="options.lowercase"
[disabled]="enforcedPolicyOptions?.useLowercase"
/>
<label for="lowercase" class="form-check-label">a-z</label>
</div>
<div class="form-check">
<input
id="numbers"
class="form-check-input"
type="checkbox"
(change)="saveOptions()"
[(ngModel)]="options.number"
[disabled]="enforcedPolicyOptions?.useNumbers"
/>
<label for="numbers" class="form-check-label">0-9</label>
</div>
<div class="form-check">
<input
id="special"
class="form-check-input"
type="checkbox"
(change)="saveOptions()"
[(ngModel)]="options.special"
[disabled]="enforcedPolicyOptions?.useSpecial"
/>
<label for="special" class="form-check-label">!@#$%^&amp;*</label>
</div>
<div class="form-check">
<input
id="ambiguous"
class="form-check-input"
type="checkbox"
(change)="saveOptions()"
[(ngModel)]="avoidAmbiguous"
/>
<label for="ambiguous" class="form-check-label">{{ "ambiguous" | i18n }}</label>
</div>
</div>
</ng-container>
<div class="d-flex">
<div>
<button type="button" class="btn btn-primary" (click)="regenerate()">
{{'regeneratePassword' | i18n}}
</button>
<button type="button" class="btn btn-outline-secondary" (click)="copy()">
{{'copyPassword' | i18n}}
</button>
</div>
<div class="ml-auto">
<button type="button" class="btn btn-outline-secondary" (click)="history()"
appA11yTitle="{{'passwordHistory' | i18n}}">
<i class="fa fa-clock-o fa-lg" aria-hidden="true"></i>
</button>
</div>
<div>
<button type="button" class="btn btn-primary" (click)="regenerate()">
{{ "regeneratePassword" | i18n }}
</button>
<button type="button" class="btn btn-outline-secondary" (click)="copy()">
{{ "copyPassword" | i18n }}
</button>
</div>
<div class="ml-auto">
<button
type="button"
class="btn btn-outline-secondary"
(click)="history()"
appA11yTitle="{{ 'passwordHistory' | i18n }}"
>
<i class="fa fa-clock-o fa-lg" aria-hidden="true"></i>
</button>
</div>
</div>
<ng-template #historyTemplate></ng-template>

View File

@@ -1,45 +1,44 @@
import {
Component,
ViewChild,
ViewContainerRef,
} from '@angular/core';
import { Component, ViewChild, ViewContainerRef } from "@angular/core";
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import {
PasswordGeneratorComponent as BasePasswordGeneratorComponent,
} from 'jslib-angular/components/password-generator.component';
import { PasswordGeneratorComponent as BasePasswordGeneratorComponent } from "jslib-angular/components/password-generator.component";
import { ModalService } from 'jslib-angular/services/modal.service';
import { PasswordGeneratorHistoryComponent } from './password-generator-history.component';
import { ModalService } from "jslib-angular/services/modal.service";
import { PasswordGeneratorHistoryComponent } from "./password-generator-history.component";
@Component({
selector: 'app-password-generator',
templateUrl: 'password-generator.component.html',
selector: "app-password-generator",
templateUrl: "password-generator.component.html",
})
export class PasswordGeneratorComponent extends BasePasswordGeneratorComponent {
@ViewChild('historyTemplate', { read: ViewContainerRef, static: true }) historyModalRef: ViewContainerRef;
@ViewChild("historyTemplate", { read: ViewContainerRef, static: true })
historyModalRef: ViewContainerRef;
constructor(passwordGenerationService: PasswordGenerationService, platformUtilsService: PlatformUtilsService,
i18nService: I18nService, private modalService: ModalService) {
super(passwordGenerationService, platformUtilsService, i18nService, window);
}
constructor(
passwordGenerationService: PasswordGenerationService,
platformUtilsService: PlatformUtilsService,
i18nService: I18nService,
private modalService: ModalService
) {
super(passwordGenerationService, platformUtilsService, i18nService, window);
}
async history() {
await this.modalService.openViewRef(PasswordGeneratorHistoryComponent, this.historyModalRef);
}
async history() {
await this.modalService.openViewRef(PasswordGeneratorHistoryComponent, this.historyModalRef);
}
lengthChanged() {
document.getElementById('length').focus();
}
lengthChanged() {
document.getElementById("length").focus();
}
minNumberChanged() {
document.getElementById('min-number').focus();
}
minNumberChanged() {
document.getElementById("min-number").focus();
}
minSpecialChanged() {
document.getElementById('min-special').focus();
}
minSpecialChanged() {
document.getElementById("min-special").focus();
}
}

View File

@@ -1,58 +1,73 @@
<div class="page-header">
<h1>
{{'reusedPasswordsReport' | i18n}}
<small *ngIf="hasLoaded && loading">
<i class="fa fa-spinner fa-spin text-muted" title="{{'loading' | i18n}}" aria-hidden="true"></i>
<span class="sr-only">{{'loading' | i18n}}</span>
</small>
</h1>
<h1>
{{ "reusedPasswordsReport" | i18n }}
<small *ngIf="hasLoaded && loading">
<i
class="fa fa-spinner fa-spin text-muted"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
></i>
<span class="sr-only">{{ "loading" | i18n }}</span>
</small>
</h1>
</div>
<p>{{'reusedPasswordsReportDesc' | i18n}}</p>
<p>{{ "reusedPasswordsReportDesc" | i18n }}</p>
<div *ngIf="!hasLoaded && loading">
<i class="fa fa-spinner fa-spin text-muted" title="{{'loading' | i18n}}" aria-hidden="true"></i>
<span class="sr-only">{{'loading' | i18n}}</span>
<i class="fa fa-spinner fa-spin text-muted" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
<span class="sr-only">{{ "loading" | i18n }}</span>
</div>
<div class="mt-4" *ngIf="hasLoaded">
<app-callout type="success" title="{{'goodNews' | i18n}}" *ngIf="!ciphers.length">
{{'noReusedPasswords' | i18n}}
<app-callout type="success" title="{{ 'goodNews' | i18n }}" *ngIf="!ciphers.length">
{{ "noReusedPasswords" | i18n }}
</app-callout>
<ng-container *ngIf="ciphers.length">
<app-callout type="danger" title="{{ 'reusedPasswordsFound' | i18n }}">
{{ "reusedPasswordsFoundDesc" | i18n: (ciphers.length | number) }}
</app-callout>
<ng-container *ngIf="ciphers.length">
<app-callout type="danger" title="{{'reusedPasswordsFound' | i18n}}">
{{'reusedPasswordsFoundDesc' | i18n : (ciphers.length | number)}}
</app-callout>
<table class="table table-hover table-list table-ciphers">
<tbody>
<tr *ngFor="let c of ciphers">
<td class="table-list-icon">
<app-vault-icon [cipher]="c"></app-vault-icon>
</td>
<td class="reduced-lh wrap">
<ng-container *ngIf="!organization || canManageCipher(c) ; else cantManage">
<a href="#" appStopClick (click)="selectCipher(c)" title="{{'editItem' | i18n}}">{{c.name}}</a>
</ng-container>
<ng-template #cantManage>
<span>{{c.name}}</span>
</ng-template>
<ng-container *ngIf="!organization && c.organizationId">
<i class="fa fa-cube" appStopProp title="{{'shared' | i18n}}" aria-hidden="true"></i>
<span class="sr-only">{{'shared' | i18n}}</span>
</ng-container>
<ng-container *ngIf="c.hasAttachments">
<i class="fa fa-paperclip" appStopProp title="{{'attachments' | i18n}}"
aria-hidden="true"></i>
<span class="sr-only">{{'attachments' | i18n}}</span>
</ng-container>
<br>
<small>{{c.subTitle}}</small>
</td>
<td class="text-right">
<span class="badge badge-warning">
{{'reusedXTimes' | i18n : passwordUseMap.get(c.login.password)}}
</span>
</td>
</tr>
</tbody>
</table>
</ng-container>
<table class="table table-hover table-list table-ciphers">
<tbody>
<tr *ngFor="let c of ciphers">
<td class="table-list-icon">
<app-vault-icon [cipher]="c"></app-vault-icon>
</td>
<td class="reduced-lh wrap">
<ng-container *ngIf="!organization || canManageCipher(c); else cantManage">
<a href="#" appStopClick (click)="selectCipher(c)" title="{{ 'editItem' | i18n }}">{{
c.name
}}</a>
</ng-container>
<ng-template #cantManage>
<span>{{ c.name }}</span>
</ng-template>
<ng-container *ngIf="!organization && c.organizationId">
<i
class="fa fa-cube"
appStopProp
title="{{ 'shared' | i18n }}"
aria-hidden="true"
></i>
<span class="sr-only">{{ "shared" | i18n }}</span>
</ng-container>
<ng-container *ngIf="c.hasAttachments">
<i
class="fa fa-paperclip"
appStopProp
title="{{ 'attachments' | i18n }}"
aria-hidden="true"
></i>
<span class="sr-only">{{ "attachments" | i18n }}</span>
</ng-container>
<br />
<small>{{ c.subTitle }}</small>
</td>
<td class="text-right">
<span class="badge badge-warning">
{{ "reusedXTimes" | i18n: passwordUseMap.get(c.login.password) }}
</span>
</td>
</tr>
</tbody>
</table>
</ng-container>
</div>
<ng-template #cipherAddEdit></ng-template>

View File

@@ -1,66 +1,74 @@
import {
Component,
OnInit,
} from '@angular/core';
import { Component, OnInit } from "@angular/core";
import { CipherService } from 'jslib-common/abstractions/cipher.service';
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
import { PasswordRepromptService } from 'jslib-common/abstractions/passwordReprompt.service';
import { StateService } from 'jslib-common/abstractions/state.service';
import { CipherService } from "jslib-common/abstractions/cipher.service";
import { MessagingService } from "jslib-common/abstractions/messaging.service";
import { PasswordRepromptService } from "jslib-common/abstractions/passwordReprompt.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { ModalService } from 'jslib-angular/services/modal.service';
import { ModalService } from "jslib-angular/services/modal.service";
import { CipherView } from 'jslib-common/models/view/cipherView';
import { CipherView } from "jslib-common/models/view/cipherView";
import { CipherType } from 'jslib-common/enums/cipherType';
import { CipherType } from "jslib-common/enums/cipherType";
import { CipherReportComponent } from './cipher-report.component';
import { CipherReportComponent } from "./cipher-report.component";
@Component({
selector: 'app-reused-passwords-report',
templateUrl: 'reused-passwords-report.component.html',
selector: "app-reused-passwords-report",
templateUrl: "reused-passwords-report.component.html",
})
export class ReusedPasswordsReportComponent extends CipherReportComponent implements OnInit {
passwordUseMap: Map<string, number>;
passwordUseMap: Map<string, number>;
constructor(protected cipherService: CipherService, modalService: ModalService,
messagingService: MessagingService, stateService: StateService,
passwordRepromptService: PasswordRepromptService) {
super(modalService, messagingService, true, stateService, passwordRepromptService);
}
constructor(
protected cipherService: CipherService,
modalService: ModalService,
messagingService: MessagingService,
stateService: StateService,
passwordRepromptService: PasswordRepromptService
) {
super(modalService, messagingService, true, stateService, passwordRepromptService);
}
async ngOnInit() {
if (await this.checkAccess()) {
await super.load();
}
async ngOnInit() {
if (await this.checkAccess()) {
await super.load();
}
}
async setCiphers() {
const allCiphers = await this.getAllCiphers();
const ciphersWithPasswords: CipherView[] = [];
this.passwordUseMap = new Map<string, number>();
allCiphers.forEach(c => {
if (c.type !== CipherType.Login || c.login.password == null || c.login.password === '' || c.isDeleted) {
return;
}
ciphersWithPasswords.push(c);
if (this.passwordUseMap.has(c.login.password)) {
this.passwordUseMap.set(c.login.password, this.passwordUseMap.get(c.login.password) + 1);
} else {
this.passwordUseMap.set(c.login.password, 1);
}
});
const reusedPasswordCiphers = ciphersWithPasswords.filter(c =>
this.passwordUseMap.has(c.login.password) && this.passwordUseMap.get(c.login.password) > 1);
this.ciphers = reusedPasswordCiphers;
}
async setCiphers() {
const allCiphers = await this.getAllCiphers();
const ciphersWithPasswords: CipherView[] = [];
this.passwordUseMap = new Map<string, number>();
allCiphers.forEach((c) => {
if (
c.type !== CipherType.Login ||
c.login.password == null ||
c.login.password === "" ||
c.isDeleted
) {
return;
}
ciphersWithPasswords.push(c);
if (this.passwordUseMap.has(c.login.password)) {
this.passwordUseMap.set(c.login.password, this.passwordUseMap.get(c.login.password) + 1);
} else {
this.passwordUseMap.set(c.login.password, 1);
}
});
const reusedPasswordCiphers = ciphersWithPasswords.filter(
(c) =>
this.passwordUseMap.has(c.login.password) && this.passwordUseMap.get(c.login.password) > 1
);
this.ciphers = reusedPasswordCiphers;
}
protected getAllCiphers(): Promise<CipherView[]> {
return this.cipherService.getAllDecrypted();
}
protected getAllCiphers(): Promise<CipherView[]> {
return this.cipherService.getAllDecrypted();
}
protected canManageCipher(c: CipherView): boolean {
// this will only ever be false from an organization view
return true;
}
protected canManageCipher(c: CipherView): boolean {
// this will only ever be false from an organization view
return true;
}
}

View File

@@ -1,59 +1,76 @@
<div class="container page-content">
<div class="row">
<div class="col-3">
<div class="card mb-4">
<div class="card-header">{{'tools' | i18n}}</div>
<div class="list-group list-group-flush">
<a routerLink="generator" class="list-group-item" routerLinkActive="active">
{{'passwordGenerator' | i18n}}
</a>
<a routerLink="import" class="list-group-item" routerLinkActive="active">
{{'importData' | i18n}}
</a>
<a routerLink="export" class="list-group-item" routerLinkActive="active">
{{'exportVault' | i18n}}
</a>
</div>
</div>
<div class="card">
<div class="card-header d-flex">
{{'reports' | i18n}}
<div class="ml-auto">
<a href="#" appStopClick class="badge badge-primary" *ngIf="!canAccessPremium"
(click)="premiumRequired()">
{{'premium' | i18n}}
</a>
</div>
</div>
<div class="list-group list-group-flush">
<a routerLink="exposed-passwords-report" class="list-group-item" routerLinkActive="active">
{{'exposedPasswordsReport' | i18n}}
</a>
<a routerLink="reused-passwords-report" class="list-group-item" routerLinkActive="active">
{{'reusedPasswordsReport' | i18n}}
</a>
<a routerLink="weak-passwords-report" class="list-group-item" routerLinkActive="active">
{{'weakPasswordsReport' | i18n}}
</a>
<a routerLink="unsecured-websites-report" class="list-group-item" routerLinkActive="active">
{{'unsecuredWebsitesReport' | i18n}}
</a>
<a routerLink="inactive-two-factor-report" class="list-group-item" routerLinkActive="active">
{{'inactive2faReport' | i18n}}
</a>
<a routerLink="breach-report" class="list-group-item d-flex" routerLinkActive="active">
{{'dataBreachReport' | i18n}}
<div class="ml-auto">
<span class="badge badge-success" *ngIf="!canAccessPremium">
{{'free' | i18n | uppercase}}
</span>
</div>
</a>
</div>
</div>
<div class="row">
<div class="col-3">
<div class="card mb-4">
<div class="card-header">{{ "tools" | i18n }}</div>
<div class="list-group list-group-flush">
<a routerLink="generator" class="list-group-item" routerLinkActive="active">
{{ "passwordGenerator" | i18n }}
</a>
<a routerLink="import" class="list-group-item" routerLinkActive="active">
{{ "importData" | i18n }}
</a>
<a routerLink="export" class="list-group-item" routerLinkActive="active">
{{ "exportVault" | i18n }}
</a>
</div>
<div class="col-9">
<router-outlet></router-outlet>
</div>
<div class="card">
<div class="card-header d-flex">
{{ "reports" | i18n }}
<div class="ml-auto">
<a
href="#"
appStopClick
class="badge badge-primary"
*ngIf="!canAccessPremium"
(click)="premiumRequired()"
>
{{ "premium" | i18n }}
</a>
</div>
</div>
<div class="list-group list-group-flush">
<a
routerLink="exposed-passwords-report"
class="list-group-item"
routerLinkActive="active"
>
{{ "exposedPasswordsReport" | i18n }}
</a>
<a routerLink="reused-passwords-report" class="list-group-item" routerLinkActive="active">
{{ "reusedPasswordsReport" | i18n }}
</a>
<a routerLink="weak-passwords-report" class="list-group-item" routerLinkActive="active">
{{ "weakPasswordsReport" | i18n }}
</a>
<a
routerLink="unsecured-websites-report"
class="list-group-item"
routerLinkActive="active"
>
{{ "unsecuredWebsitesReport" | i18n }}
</a>
<a
routerLink="inactive-two-factor-report"
class="list-group-item"
routerLinkActive="active"
>
{{ "inactive2faReport" | i18n }}
</a>
<a routerLink="breach-report" class="list-group-item d-flex" routerLinkActive="active">
{{ "dataBreachReport" | i18n }}
<div class="ml-auto">
<span class="badge badge-success" *ngIf="!canAccessPremium">
{{ "free" | i18n | uppercase }}
</span>
</div>
</a>
</div>
</div>
</div>
<div class="col-9">
<router-outlet></router-outlet>
</div>
</div>
</div>

View File

@@ -1,28 +1,25 @@
import {
Component,
OnInit,
} from '@angular/core';
import { Component, OnInit } from "@angular/core";
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
import { StateService } from 'jslib-common/abstractions/state.service';
import { MessagingService } from "jslib-common/abstractions/messaging.service";
import { StateService } from "jslib-common/abstractions/state.service";
@Component({
selector: 'app-tools',
templateUrl: 'tools.component.html',
selector: "app-tools",
templateUrl: "tools.component.html",
})
export class ToolsComponent implements OnInit {
canAccessPremium = false;
canAccessPremium = false;
constructor(private stateService: StateService, private messagingService: MessagingService) { }
constructor(private stateService: StateService, private messagingService: MessagingService) {}
async ngOnInit() {
this.canAccessPremium = await this.stateService.getCanAccessPremium();
}
premiumRequired() {
if (!this.canAccessPremium) {
this.messagingService.send('premiumRequired');
return;
}
async ngOnInit() {
this.canAccessPremium = await this.stateService.getCanAccessPremium();
}
premiumRequired() {
if (!this.canAccessPremium) {
this.messagingService.send("premiumRequired");
return;
}
}
}

View File

@@ -1,48 +1,63 @@
<div class="page-header">
<h1>
{{'unsecuredWebsitesReport' | i18n}}
<small *ngIf="hasLoaded && loading">
<i class="fa fa-spinner fa-spin text-muted" title="{{'loading' | i18n}}" aria-hidden="true"></i>
<span class="sr-only">{{'loading' | i18n}}</span>
</small>
</h1>
<h1>
{{ "unsecuredWebsitesReport" | i18n }}
<small *ngIf="hasLoaded && loading">
<i
class="fa fa-spinner fa-spin text-muted"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
></i>
<span class="sr-only">{{ "loading" | i18n }}</span>
</small>
</h1>
</div>
<p>{{'unsecuredWebsitesReportDesc' | i18n}}</p>
<p>{{ "unsecuredWebsitesReportDesc" | i18n }}</p>
<div *ngIf="!hasLoaded && loading">
<i class="fa fa-spinner fa-spin text-muted" title="{{'loading' | i18n}}" aria-hidden="true"></i>
<span class="sr-only">{{'loading' | i18n}}</span>
<i class="fa fa-spinner fa-spin text-muted" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
<span class="sr-only">{{ "loading" | i18n }}</span>
</div>
<div class="mt-4" *ngIf="hasLoaded">
<app-callout type="success" title="{{'goodNews' | i18n}}" *ngIf="!ciphers.length">
{{'noUnsecuredWebsites' | i18n}}
<app-callout type="success" title="{{ 'goodNews' | i18n }}" *ngIf="!ciphers.length">
{{ "noUnsecuredWebsites" | i18n }}
</app-callout>
<ng-container *ngIf="ciphers.length">
<app-callout type="danger" title="{{ 'unsecuredWebsitesFound' | i18n }}">
{{ "unsecuredWebsitesFoundDesc" | i18n: (ciphers.length | number) }}
</app-callout>
<ng-container *ngIf="ciphers.length">
<app-callout type="danger" title="{{'unsecuredWebsitesFound' | i18n}}">
{{'unsecuredWebsitesFoundDesc' | i18n : (ciphers.length | number)}}
</app-callout>
<table class="table table-hover table-list table-ciphers">
<tbody>
<tr *ngFor="let c of ciphers">
<td class="table-list-icon">
<app-vault-icon [cipher]="c"></app-vault-icon>
</td>
<td class="reduced-lh wrap">
<a href="#" appStopClick (click)="selectCipher(c)" title="{{'editItem' | i18n}}">{{c.name}}</a>
<ng-container *ngIf="!organization && c.organizationId">
<i class="fa fa-cube" appStopProp title="{{'shared' | i18n}}" aria-hidden="true"></i>
<span class="sr-only">{{'shared' | i18n}}</span>
</ng-container>
<ng-container *ngIf="c.hasAttachments">
<i class="fa fa-paperclip" appStopProp title="{{'attachments' | i18n}}"
aria-hidden="true"></i>
<span class="sr-only">{{'attachments' | i18n}}</span>
</ng-container>
<br>
<small>{{c.subTitle}}</small>
</td>
</tr>
</tbody>
</table>
</ng-container>
<table class="table table-hover table-list table-ciphers">
<tbody>
<tr *ngFor="let c of ciphers">
<td class="table-list-icon">
<app-vault-icon [cipher]="c"></app-vault-icon>
</td>
<td class="reduced-lh wrap">
<a href="#" appStopClick (click)="selectCipher(c)" title="{{ 'editItem' | i18n }}">{{
c.name
}}</a>
<ng-container *ngIf="!organization && c.organizationId">
<i
class="fa fa-cube"
appStopProp
title="{{ 'shared' | i18n }}"
aria-hidden="true"
></i>
<span class="sr-only">{{ "shared" | i18n }}</span>
</ng-container>
<ng-container *ngIf="c.hasAttachments">
<i
class="fa fa-paperclip"
appStopProp
title="{{ 'attachments' | i18n }}"
aria-hidden="true"
></i>
<span class="sr-only">{{ "attachments" | i18n }}</span>
</ng-container>
<br />
<small>{{ c.subTitle }}</small>
</td>
</tr>
</tbody>
</table>
</ng-container>
</div>
<ng-template #cipherAddEdit></ng-template>

View File

@@ -1,50 +1,51 @@
import {
Component,
OnInit,
} from '@angular/core';
import { Component, OnInit } from "@angular/core";
import { CipherService } from 'jslib-common/abstractions/cipher.service';
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
import { PasswordRepromptService } from 'jslib-common/abstractions/passwordReprompt.service';
import { StateService } from 'jslib-common/abstractions/state.service';
import { CipherService } from "jslib-common/abstractions/cipher.service";
import { MessagingService } from "jslib-common/abstractions/messaging.service";
import { PasswordRepromptService } from "jslib-common/abstractions/passwordReprompt.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { ModalService } from 'jslib-angular/services/modal.service';
import { ModalService } from "jslib-angular/services/modal.service";
import { CipherType } from 'jslib-common/enums/cipherType';
import { CipherType } from "jslib-common/enums/cipherType";
import { CipherView } from 'jslib-common/models/view/cipherView';
import { CipherView } from "jslib-common/models/view/cipherView";
import { CipherReportComponent } from './cipher-report.component';
import { CipherReportComponent } from "./cipher-report.component";
@Component({
selector: 'app-unsecured-websites-report',
templateUrl: 'unsecured-websites-report.component.html',
selector: "app-unsecured-websites-report",
templateUrl: "unsecured-websites-report.component.html",
})
export class UnsecuredWebsitesReportComponent extends CipherReportComponent implements OnInit {
constructor(protected cipherService: CipherService, modalService: ModalService,
messagingService: MessagingService, stateService: StateService,
passwordRepromptService: PasswordRepromptService) {
super(modalService, messagingService, true, stateService, passwordRepromptService);
}
constructor(
protected cipherService: CipherService,
modalService: ModalService,
messagingService: MessagingService,
stateService: StateService,
passwordRepromptService: PasswordRepromptService
) {
super(modalService, messagingService, true, stateService, passwordRepromptService);
}
async ngOnInit() {
if (await this.checkAccess()) {
await super.load();
}
async ngOnInit() {
if (await this.checkAccess()) {
await super.load();
}
}
async setCiphers() {
const allCiphers = await this.getAllCiphers();
const unsecuredCiphers = allCiphers.filter(c => {
if (c.type !== CipherType.Login || !c.login.hasUris || c.isDeleted) {
return false;
}
return c.login.uris.some(u => u.uri != null && u.uri.indexOf('http://') === 0);
});
this.ciphers = unsecuredCiphers;
}
async setCiphers() {
const allCiphers = await this.getAllCiphers();
const unsecuredCiphers = allCiphers.filter((c) => {
if (c.type !== CipherType.Login || !c.login.hasUris || c.isDeleted) {
return false;
}
return c.login.uris.some((u) => u.uri != null && u.uri.indexOf("http://") === 0);
});
this.ciphers = unsecuredCiphers;
}
protected getAllCiphers(): Promise<CipherView[]> {
return this.cipherService.getAllDecrypted();
}
protected getAllCiphers(): Promise<CipherView[]> {
return this.cipherService.getAllDecrypted();
}
}

View File

@@ -1,58 +1,73 @@
<div class="page-header">
<h1>
{{'weakPasswordsReport' | i18n}}
<small *ngIf="hasLoaded && loading">
<i class="fa fa-spinner fa-spin text-muted" title="{{'loading' | i18n}}" aria-hidden="true"></i>
<span class="sr-only">{{'loading' | i18n}}</span>
</small>
</h1>
<h1>
{{ "weakPasswordsReport" | i18n }}
<small *ngIf="hasLoaded && loading">
<i
class="fa fa-spinner fa-spin text-muted"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
></i>
<span class="sr-only">{{ "loading" | i18n }}</span>
</small>
</h1>
</div>
<p>{{'weakPasswordsReportDesc' | i18n}}</p>
<p>{{ "weakPasswordsReportDesc" | i18n }}</p>
<div *ngIf="!hasLoaded && loading">
<i class="fa fa-spinner fa-spin text-muted" title="{{'loading' | i18n}}" aria-hidden="true"></i>
<span class="sr-only">{{'loading' | i18n}}</span>
<i class="fa fa-spinner fa-spin text-muted" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
<span class="sr-only">{{ "loading" | i18n }}</span>
</div>
<div class="mt-4" *ngIf="hasLoaded">
<app-callout type="success" title="{{'goodNews' | i18n}}" *ngIf="!ciphers.length">
{{'noWeakPasswords' | i18n}}
<app-callout type="success" title="{{ 'goodNews' | i18n }}" *ngIf="!ciphers.length">
{{ "noWeakPasswords" | i18n }}
</app-callout>
<ng-container *ngIf="ciphers.length">
<app-callout type="danger" title="{{ 'weakPasswordsFound' | i18n }}">
{{ "weakPasswordsFoundDesc" | i18n: (ciphers.length | number) }}
</app-callout>
<ng-container *ngIf="ciphers.length">
<app-callout type="danger" title="{{'weakPasswordsFound' | i18n}}">
{{'weakPasswordsFoundDesc' | i18n : (ciphers.length | number)}}
</app-callout>
<table class="table table-hover table-list table-ciphers">
<tbody>
<tr *ngFor="let c of ciphers">
<td class="table-list-icon">
<app-vault-icon [cipher]="c"></app-vault-icon>
</td>
<td class="reduced-lh wrap">
<ng-container *ngIf="!organization || canManageCipher(c) ; else cantManage">
<a href="#" appStopClick (click)="selectCipher(c)" title="{{'editItem' | i18n}}">{{c.name}}</a>
</ng-container>
<ng-template #cantManage>
<span>{{c.name}}</span>
</ng-template>
<ng-container *ngIf="!organization && c.organizationId">
<i class="fa fa-cube" appStopProp title="{{'shared' | i18n}}" aria-hidden="true"></i>
<span class="sr-only">{{'shared' | i18n}}</span>
</ng-container>
<ng-container *ngIf="c.hasAttachments">
<i class="fa fa-paperclip" appStopProp title="{{'attachments' | i18n}}"
aria-hidden="true"></i>
<span class="sr-only">{{'attachments' | i18n}}</span>
</ng-container>
<br>
<small>{{c.subTitle}}</small>
</td>
<td class="text-right">
<span class="badge badge-{{passwordStrengthMap.get(c.id)[1]}}">
{{passwordStrengthMap.get(c.id)[0] | i18n}}
</span>
</td>
</tr>
</tbody>
</table>
</ng-container>
<table class="table table-hover table-list table-ciphers">
<tbody>
<tr *ngFor="let c of ciphers">
<td class="table-list-icon">
<app-vault-icon [cipher]="c"></app-vault-icon>
</td>
<td class="reduced-lh wrap">
<ng-container *ngIf="!organization || canManageCipher(c); else cantManage">
<a href="#" appStopClick (click)="selectCipher(c)" title="{{ 'editItem' | i18n }}">{{
c.name
}}</a>
</ng-container>
<ng-template #cantManage>
<span>{{ c.name }}</span>
</ng-template>
<ng-container *ngIf="!organization && c.organizationId">
<i
class="fa fa-cube"
appStopProp
title="{{ 'shared' | i18n }}"
aria-hidden="true"
></i>
<span class="sr-only">{{ "shared" | i18n }}</span>
</ng-container>
<ng-container *ngIf="c.hasAttachments">
<i
class="fa fa-paperclip"
appStopProp
title="{{ 'attachments' | i18n }}"
aria-hidden="true"
></i>
<span class="sr-only">{{ "attachments" | i18n }}</span>
</ng-container>
<br />
<small>{{ c.subTitle }}</small>
</td>
<td class="text-right">
<span class="badge badge-{{ passwordStrengthMap.get(c.id)[1] }}">
{{ passwordStrengthMap.get(c.id)[0] | i18n }}
</span>
</td>
</tr>
</tbody>
</table>
</ng-container>
</div>
<ng-template #cipherAddEdit></ng-template>

View File

@@ -1,109 +1,128 @@
import {
Component,
OnInit,
} from '@angular/core';
import { Component, OnInit } from "@angular/core";
import { CipherService } from 'jslib-common/abstractions/cipher.service';
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
import { PasswordRepromptService } from 'jslib-common/abstractions/passwordReprompt.service';
import { StateService } from 'jslib-common/abstractions/state.service';
import { CipherService } from "jslib-common/abstractions/cipher.service";
import { MessagingService } from "jslib-common/abstractions/messaging.service";
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
import { PasswordRepromptService } from "jslib-common/abstractions/passwordReprompt.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { ModalService } from 'jslib-angular/services/modal.service';
import { ModalService } from "jslib-angular/services/modal.service";
import { CipherView } from 'jslib-common/models/view/cipherView';
import { CipherView } from "jslib-common/models/view/cipherView";
import { CipherType } from 'jslib-common/enums/cipherType';
import { CipherType } from "jslib-common/enums/cipherType";
import { CipherReportComponent } from './cipher-report.component';
import { CipherReportComponent } from "./cipher-report.component";
@Component({
selector: 'app-weak-passwords-report',
templateUrl: 'weak-passwords-report.component.html',
selector: "app-weak-passwords-report",
templateUrl: "weak-passwords-report.component.html",
})
export class WeakPasswordsReportComponent extends CipherReportComponent implements OnInit {
passwordStrengthMap = new Map<string, [string, string]>();
passwordStrengthMap = new Map<string, [string, string]>();
private passwordStrengthCache = new Map<string, number>();
private passwordStrengthCache = new Map<string, number>();
constructor(
protected cipherService: CipherService,
protected passwordGenerationService: PasswordGenerationService,
modalService: ModalService,
messagingService: MessagingService,
stateService: StateService,
passwordRepromptService: PasswordRepromptService
) {
super(modalService, messagingService, true, stateService, passwordRepromptService);
}
constructor(protected cipherService: CipherService, protected passwordGenerationService: PasswordGenerationService,
modalService: ModalService, messagingService: MessagingService,
stateService: StateService, passwordRepromptService: PasswordRepromptService) {
super(modalService, messagingService, true, stateService, passwordRepromptService);
async ngOnInit() {
if (await this.checkAccess()) {
await super.load();
}
}
async ngOnInit() {
if (await this.checkAccess()) {
await super.load();
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 : "");
};
allCiphers.forEach((c) => {
if (
c.type !== CipherType.Login ||
c.login.password == null ||
c.login.password === "" ||
c.isDeleted
) {
return;
}
const hasUserName = isUserNameNotEmpty(c);
const cacheKey = getCacheKey(c);
if (!this.passwordStrengthCache.has(cacheKey)) {
let userInput: string[] = [];
if (hasUserName) {
const atPosition = c.login.username.indexOf("@");
if (atPosition > -1) {
userInput = userInput
.concat(
c.login.username
.substr(0, atPosition)
.trim()
.toLowerCase()
.split(/[^A-Za-z0-9]/)
)
.filter((i) => i.length >= 3);
} else {
userInput = c.login.username
.trim()
.toLowerCase()
.split(/[^A-Za-z0-9]/)
.filter((i) => i.length >= 3);
}
}
}
const result = this.passwordGenerationService.passwordStrength(
c.login.password,
userInput.length > 0 ? userInput : null
);
this.passwordStrengthCache.set(cacheKey, result.score);
}
const score = this.passwordStrengthCache.get(cacheKey);
if (score != null && score <= 2) {
this.passwordStrengthMap.set(c.id, this.scoreKey(score));
weakPasswordCiphers.push(c);
}
});
weakPasswordCiphers.sort((a, b) => {
return (
this.passwordStrengthCache.get(getCacheKey(a)) -
this.passwordStrengthCache.get(getCacheKey(b))
);
});
this.ciphers = weakPasswordCiphers;
}
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 : '');
};
protected getAllCiphers(): Promise<CipherView[]> {
return this.cipherService.getAllDecrypted();
}
allCiphers.forEach(c => {
if (c.type !== CipherType.Login || c.login.password == null || c.login.password === '' || c.isDeleted) {
return;
}
const hasUserName = isUserNameNotEmpty(c);
const cacheKey = getCacheKey(c);
if (!this.passwordStrengthCache.has(cacheKey)) {
let userInput: string[] = [];
if (hasUserName) {
const atPosition = c.login.username.indexOf('@');
if (atPosition > -1) {
userInput = userInput.concat(
c.login.username.substr(0, atPosition).trim().toLowerCase().split(/[^A-Za-z0-9]/))
.filter(i => i.length >= 3);
} else {
userInput = c.login.username.trim().toLowerCase().split(/[^A-Za-z0-9]/)
.filter(i => i.length >= 3);
}
}
const result = this.passwordGenerationService.passwordStrength(c.login.password,
userInput.length > 0 ? userInput : null);
this.passwordStrengthCache.set(cacheKey, result.score);
}
const score = this.passwordStrengthCache.get(cacheKey);
if (score != null && score <= 2) {
this.passwordStrengthMap.set(c.id, this.scoreKey(score));
weakPasswordCiphers.push(c);
}
});
weakPasswordCiphers.sort((a, b) => {
return this.passwordStrengthCache.get(getCacheKey(a)) -
this.passwordStrengthCache.get(getCacheKey(b));
});
this.ciphers = weakPasswordCiphers;
}
protected canManageCipher(c: CipherView): boolean {
// this will only ever be false from the org view;
return true;
}
protected getAllCiphers(): Promise<CipherView[]> {
return this.cipherService.getAllDecrypted();
}
protected canManageCipher(c: CipherView): boolean {
// this will only ever be false from the org view;
return true;
}
private scoreKey(score: number): [string, string] {
switch (score) {
case 4:
return ['strong', 'success'];
case 3:
return ['good', 'primary'];
case 2:
return ['weak', 'warning'];
default:
return ['veryWeak', 'danger'];
}
private scoreKey(score: number): [string, string] {
switch (score) {
case 4:
return ["strong", "success"];
case 3:
return ["good", "primary"];
case 2:
return ["weak", "warning"];
default:
return ["veryWeak", "danger"];
}
}
}