mirror of
https://github.com/bitwarden/browser
synced 2025-12-23 11:43:46 +00:00
Vertical Vault Navigation (#6957)
* WIP admin console layout * Update icons * Migrate more things * Migrate the last pages * Move header to web * Fix story not working * Convert header component to standalone * Migrate org layout to standalone * Enable org switcher * Add AC to product switcher * Migrate provider portal to vertical nav * Migrate PM * Prettier fixes * Change AC and PP to use secondary variant layout & update logos * Remove full width setting * Remove commented code * Add header to report pages * Add provider portal banner * Fix banner for billing pages * Move vault title to header * Prevent scrollbar jumping * Move send button to header * Replace search input with bit-search * Remove unused files and css * Add banner * Tweak storage option * Fix duplicate nav item after merge * Migrate banner state to state provider framework * [AC-2078] Fix device approvals header * [PM-5861] Hide AC from product switcher for users that do not have access * [PM-5860] Fix Vault and Send page headers * [AC-2075] Fix missing link on reporting nav group * [AC-2079] Hide Payment Method and Billing History pages for self-hosted instances * [AC-2090] Hide reports/event log nav items for users that do not have permission * [AC-2092] Fix missing provider portal option in product switcher on page load * Add null check for organization in org layout component * [AC-2094] Fix missing page header for new client orgs page * [AC-2093] Update New client button styling * Fix failing test after merge * [PM-2087] Use disk-local for web layout banner * [PM-6041] Update banner copy to read "web app" * [PM-6094] Update banner link to marketing URL * [PM-6114] add CL container component to VVR pages (#7802) * create bit-container component * add container to all page components * Fix linting errors after merge with main * Fix product switcher stories * Fix web-header stories * mock org state properly in product switcher stories (#7956) * refactor: move web layout migration banner logic into a service (#7958) * make CL codeowner of web header files * move migration banner logic to service; update stories * [PM-5862] Ensure a sync has run before hiding navigation links * Remove leftover banner global state * Re-add dropped selfHosted ngIf * Add rel noreferrer * Remove comment --------- Co-authored-by: Shane Melton <smelton@bitwarden.com> Co-authored-by: Will Martin <contact@willmartian.com>
This commit is contained in:
@@ -1,476 +1,479 @@
|
||||
<div class="page-header">
|
||||
<h1>{{ "generator" | i18n }}</h1>
|
||||
</div>
|
||||
<app-callout type="info" *ngIf="enforcedPasswordPolicyOptions?.inEffect() && type === 'password'">
|
||||
{{ "passwordGeneratorPolicyInEffect" | i18n }}
|
||||
</app-callout>
|
||||
<div class="card card-generated bg-light my-4">
|
||||
<div class="card-body">
|
||||
<bit-color-password
|
||||
[password]="type === 'password' ? password : username"
|
||||
[appCopyText]="type === 'password' ? password : username"
|
||||
></bit-color-password>
|
||||
<app-header></app-header>
|
||||
|
||||
<bit-container>
|
||||
<app-callout type="info" *ngIf="enforcedPasswordPolicyOptions?.inEffect() && type === 'password'">
|
||||
{{ "passwordGeneratorPolicyInEffect" | i18n }}
|
||||
</app-callout>
|
||||
<div class="card card-generated bg-light my-4">
|
||||
<div class="card-body">
|
||||
<bit-color-password
|
||||
[password]="type === 'password' ? password : username"
|
||||
[appCopyText]="type === 'password' ? password : username"
|
||||
></bit-color-password>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" role="radiogroup" aria-labelledby="typeHeading">
|
||||
<label id="typeHeading" class="d-block">{{ "whatWouldYouLikeToGenerate" | i18n }}</label>
|
||||
<div class="form-check form-check-inline" *ngFor="let o of typeOptions">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
[(ngModel)]="type"
|
||||
name="Type"
|
||||
id="type_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="typeChanged()"
|
||||
[checked]="type === o.value"
|
||||
/>
|
||||
<label class="form-check-label" for="type_{{ o.value }}">
|
||||
{{ o.name }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="type === 'password'">
|
||||
<div aria-labelledby="passwordTypeHeading" class="form-group" role="radiogroup">
|
||||
<label id="passwordTypeHeading" class="d-block">{{ "passwordType" | i18n }}</label>
|
||||
<div class="form-check form-check-inline" *ngFor="let o of passTypeOptions">
|
||||
<div class="form-group" role="radiogroup" aria-labelledby="typeHeading">
|
||||
<label id="typeHeading" class="d-block">{{ "whatWouldYouLikeToGenerate" | i18n }}</label>
|
||||
<div class="form-check form-check-inline" *ngFor="let o of typeOptions">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
[(ngModel)]="passwordOptions.type"
|
||||
name="PasswordType"
|
||||
id="passwordType_{{ o.value }}"
|
||||
[(ngModel)]="type"
|
||||
name="Type"
|
||||
id="type_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="savePasswordOptions()"
|
||||
[checked]="passwordOptions.type === o.value"
|
||||
(change)="typeChanged()"
|
||||
[checked]="type === o.value"
|
||||
/>
|
||||
<label class="form-check-label" for="passwordType_{{ o.value }}">
|
||||
<label class="form-check-label" for="type_{{ o.value }}">
|
||||
{{ o.name }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="passwordOptions.type === 'passphrase'">
|
||||
<div class="row">
|
||||
<div class="form-group col-4">
|
||||
<label for="num-words">{{ "numWords" | i18n }}</label>
|
||||
<ng-container *ngIf="type === 'password'">
|
||||
<div aria-labelledby="passwordTypeHeading" class="form-group" role="radiogroup">
|
||||
<label id="passwordTypeHeading" class="d-block">{{ "passwordType" | i18n }}</label>
|
||||
<div class="form-check form-check-inline" *ngFor="let o of passTypeOptions">
|
||||
<input
|
||||
id="num-words"
|
||||
class="form-control"
|
||||
type="number"
|
||||
min="3"
|
||||
max="20"
|
||||
[(ngModel)]="passwordOptions.numWords"
|
||||
(blur)="savePasswordOptions()"
|
||||
/>
|
||||
</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)]="passwordOptions.wordSeparator"
|
||||
(blur)="savePasswordOptions()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<label class="d-block">{{ "options" | i18n }}</label>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="capitalize"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
type="radio"
|
||||
[(ngModel)]="passwordOptions.type"
|
||||
name="PasswordType"
|
||||
id="passwordType_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="savePasswordOptions()"
|
||||
[(ngModel)]="passwordOptions.capitalize"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.capitalize"
|
||||
[checked]="passwordOptions.type === o.value"
|
||||
/>
|
||||
<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)="savePasswordOptions()"
|
||||
[(ngModel)]="passwordOptions.includeNumber"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.includeNumber"
|
||||
/>
|
||||
<label for="include-number" class="form-check-label">{{ "includeNumber" | i18n }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="passwordOptions.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]="passwordOptions.minLength"
|
||||
max="128"
|
||||
[(ngModel)]="passwordOptions.length"
|
||||
(blur)="savePasswordOptions()"
|
||||
(change)="lengthChanged()"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group col-4">
|
||||
<label for="min-length">{{ "passwordMinLength" | i18n }}</label>
|
||||
<input
|
||||
id="min-length"
|
||||
class="form-control"
|
||||
type="text"
|
||||
readonly="true"
|
||||
[value]="passwordOptions.length"
|
||||
/>
|
||||
<span
|
||||
class="sr-only"
|
||||
attr.aria-label="{{ 'passwordMinLength' | i18n }}"
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
>
|
||||
{{ passwordOptionsMinLengthForReader$ | async }}
|
||||
</span>
|
||||
</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"
|
||||
[(ngModel)]="passwordOptions.minNumber"
|
||||
(input)="onPasswordOptionsMinNumberInput($event)"
|
||||
(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"
|
||||
[(ngModel)]="passwordOptions.minSpecial"
|
||||
(input)="onPasswordOptionsMinSpecialInput($event)"
|
||||
(change)="minSpecialChanged()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<label class="d-block">{{ "options" | i18n }}</label>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="uppercase"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
(change)="savePasswordOptions()"
|
||||
[(ngModel)]="passwordOptions.uppercase"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useUppercase"
|
||||
attr.aria-label="{{ 'uppercase' | i18n }}"
|
||||
/>
|
||||
<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)="savePasswordOptions()"
|
||||
[(ngModel)]="passwordOptions.lowercase"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useLowercase"
|
||||
attr.aria-label="{{ 'lowercase' | i18n }}"
|
||||
/>
|
||||
<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)="savePasswordOptions()"
|
||||
[ngModel]="passwordOptions.number"
|
||||
(ngModelChange)="setPasswordOptionsNumber($event)"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useNumbers"
|
||||
attr.aria-label="{{ 'numbers' | i18n }}"
|
||||
/>
|
||||
<label for="numbers" class="form-check-label">0-9</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="special"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
[ngModel]="passwordOptions.special"
|
||||
(ngModelChange)="setPasswordOptionsSpecial($event)"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useSpecial"
|
||||
attr.aria-label="{{ 'specialCharacters' | i18n }}"
|
||||
/>
|
||||
<label for="special" class="form-check-label">!@#$%^&*</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="ambiguous"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
(change)="savePasswordOptions()"
|
||||
[(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="bwi bwi-clock bwi-lg" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="type === 'username'">
|
||||
<div aria-labelledby="usernameTypeHeading" class="form-group" role="radiogroup">
|
||||
<div class="d-block">
|
||||
<label id="usernameTypeHeading">{{ "usernameType" | i18n }}</label>
|
||||
<a
|
||||
class="ml-auto"
|
||||
href="https://bitwarden.com/help/generator/#username-types"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
appA11yTitle="{{ 'learnMore' | i18n }}"
|
||||
>
|
||||
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="form-check" *ngFor="let o of usernameTypeOptions">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
[(ngModel)]="usernameOptions.type"
|
||||
name="UsernameType"
|
||||
id="usernameType_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="saveUsernameOptions()"
|
||||
[checked]="usernameOptions.type === o.value"
|
||||
/>
|
||||
<label class="form-check-label" for="usernameType_{{ o.value }}">
|
||||
{{ o.name }}
|
||||
<div class="small text-muted">{{ o.desc }}</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="usernameOptions.type === 'forwarded'">
|
||||
<div class="form-group" role="listbox">
|
||||
<label class="d-block">{{ "service" | i18n }}</label>
|
||||
<select
|
||||
id="ForwardTypeDropdown"
|
||||
name="ForwardType"
|
||||
[(ngModel)]="usernameOptions.forwardedService"
|
||||
(change)="saveUsernameOptions()"
|
||||
class="form-control w-auto"
|
||||
>
|
||||
<option *ngFor="let o of forwardOptions" [ngValue]="o.value" role="option">
|
||||
<label class="form-check-label" for="passwordType_{{ o.value }}">
|
||||
{{ o.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="row" *ngIf="usernameOptions.forwardedService === 'simplelogin'">
|
||||
<div class="form-group col-4">
|
||||
<label for="simplelogin-apikey">{{ "apiKey" | i18n }}</label>
|
||||
<input
|
||||
id="simplelogin-apikey"
|
||||
class="form-control"
|
||||
type="password"
|
||||
[(ngModel)]="usernameOptions.forwardedSimpleLoginApiKey"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group col-4" *ngIf="isSelfHosted">
|
||||
<label for="simplelogin-baseUrl">{{ "baseUrl" | i18n }}</label>
|
||||
<input
|
||||
id="simplelogin-baseUrl"
|
||||
class="form-control"
|
||||
type="text"
|
||||
name="SimpleLoginDomain"
|
||||
[(ngModel)]="usernameOptions.forwardedSimpleLoginBaseUrl"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" *ngIf="usernameOptions.forwardedService === 'duckduckgo'">
|
||||
<div class="form-group col-4">
|
||||
<label for="duckduckgo-apikey">{{ "apiKey" | i18n }}</label>
|
||||
<input
|
||||
id="duckduckgo-apikey"
|
||||
class="form-control"
|
||||
type="password"
|
||||
name="DuckDuckGoApiKey"
|
||||
[(ngModel)]="usernameOptions.forwardedDuckDuckGoToken"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
<ng-container *ngIf="passwordOptions.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)]="passwordOptions.numWords"
|
||||
(blur)="savePasswordOptions()"
|
||||
/>
|
||||
</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)]="passwordOptions.wordSeparator"
|
||||
(blur)="savePasswordOptions()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" *ngIf="usernameOptions.forwardedService === 'anonaddy'">
|
||||
<div class="form-group col-4">
|
||||
<label for="anonaddy-apikey">{{ "apiAccessToken" | i18n }}</label>
|
||||
<input
|
||||
id="anonaddy-apikey"
|
||||
class="form-control"
|
||||
type="password"
|
||||
[(ngModel)]="usernameOptions.forwardedAnonAddyApiToken"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group col-4">
|
||||
<label for="anonaddy-domain">{{ "aliasDomain" | i18n }}</label>
|
||||
<input
|
||||
id="anonaddy-domain"
|
||||
class="form-control"
|
||||
type="text"
|
||||
[(ngModel)]="usernameOptions.forwardedAnonAddyDomain"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group col-4" *ngIf="isSelfHosted">
|
||||
<label for="anonaddy-baseUrl">{{ "baseUrl" | i18n }}</label>
|
||||
<input
|
||||
id="anonaddy-baseUrl"
|
||||
class="form-control"
|
||||
type="text"
|
||||
name="AnonAddyDomain"
|
||||
[(ngModel)]="usernameOptions.forwardedAnonAddyBaseUrl"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" *ngIf="usernameOptions.forwardedService === 'firefoxrelay'">
|
||||
<div class="form-group col-4">
|
||||
<label for="firefox-apikey">{{ "apiAccessToken" | i18n }}</label>
|
||||
<input
|
||||
id="firefox-apikey"
|
||||
class="form-control"
|
||||
type="password"
|
||||
[(ngModel)]="usernameOptions.forwardedFirefoxApiToken"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" *ngIf="usernameOptions.forwardedService === 'fastmail'">
|
||||
<div class="form-group col-4">
|
||||
<label for="fastmail-apiToken">{{ "apiAccessToken" | i18n }}</label>
|
||||
<input
|
||||
id="fastmail-apiToken"
|
||||
class="form-control"
|
||||
type="password"
|
||||
[(ngModel)]="usernameOptions.forwardedFastmailApiToken"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" *ngIf="usernameOptions.forwardedService === 'forwardemail'">
|
||||
<div class="form-group col-4">
|
||||
<label for="forwardemail-apikey">{{ "apiAccessToken" | i18n }}</label>
|
||||
<input
|
||||
id="forwardemail-apikey"
|
||||
class="form-control"
|
||||
type="password"
|
||||
[(ngModel)]="usernameOptions.forwardedForwardEmailApiToken"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group col-4">
|
||||
<label for="forwardemail-domain">{{ "aliasDomain" | i18n }}</label>
|
||||
<input
|
||||
id="forwardemail-domain"
|
||||
class="form-control"
|
||||
type="text"
|
||||
[(ngModel)]="usernameOptions.forwardedForwardEmailDomain"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="row" *ngIf="usernameOptions.type === 'subaddress'">
|
||||
<div class="form-group col-4">
|
||||
<label for="subaddress-email">{{ "emailAddress" | i18n }}</label>
|
||||
<input
|
||||
id="subaddress-email"
|
||||
class="form-control"
|
||||
type="text"
|
||||
[(ngModel)]="usernameOptions.subaddressEmail"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" *ngIf="usernameOptions.type === 'catchall'">
|
||||
<div class="form-group col-4">
|
||||
<label for="catchall-domain">{{ "domainName" | i18n }}</label>
|
||||
<input
|
||||
id="catchall-domain"
|
||||
class="form-control"
|
||||
type="text"
|
||||
[(ngModel)]="usernameOptions.catchallDomain"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="usernameOptions.type === 'word'">
|
||||
<label class="d-block">{{ "options" | i18n }}</label>
|
||||
<div class="row">
|
||||
<label class="d-block">{{ "options" | i18n }}</label>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="capitalizeUsername"
|
||||
id="capitalize"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
(change)="saveUsernameOptions()"
|
||||
[(ngModel)]="usernameOptions.wordCapitalize"
|
||||
(change)="savePasswordOptions()"
|
||||
[(ngModel)]="passwordOptions.capitalize"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.capitalize"
|
||||
/>
|
||||
<label for="capitalizeUsername" class="form-check-label">{{ "capitalize" | i18n }}</label>
|
||||
<label for="capitalize" class="form-check-label">{{ "capitalize" | i18n }}</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="includeNumberUsername"
|
||||
id="include-number"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
(change)="saveUsernameOptions()"
|
||||
[(ngModel)]="usernameOptions.wordIncludeNumber"
|
||||
(change)="savePasswordOptions()"
|
||||
[(ngModel)]="passwordOptions.includeNumber"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.includeNumber"
|
||||
/>
|
||||
<label for="includeNumberUsername" class="form-check-label">{{
|
||||
"includeNumber" | i18n
|
||||
}}</label>
|
||||
<label for="include-number" class="form-check-label">{{ "includeNumber" | i18n }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="passwordOptions.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]="passwordOptions.minLength"
|
||||
max="128"
|
||||
[(ngModel)]="passwordOptions.length"
|
||||
(blur)="savePasswordOptions()"
|
||||
(change)="lengthChanged()"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group col-4">
|
||||
<label for="min-length">{{ "passwordMinLength" | i18n }}</label>
|
||||
<input
|
||||
id="min-length"
|
||||
class="form-control"
|
||||
type="text"
|
||||
readonly="true"
|
||||
[value]="passwordOptions.length"
|
||||
/>
|
||||
<span
|
||||
class="sr-only"
|
||||
attr.aria-label="{{ 'passwordMinLength' | i18n }}"
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
>
|
||||
{{ passwordOptionsMinLengthForReader$ | async }}
|
||||
</span>
|
||||
</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"
|
||||
[(ngModel)]="passwordOptions.minNumber"
|
||||
(input)="onPasswordOptionsMinNumberInput($event)"
|
||||
(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"
|
||||
[(ngModel)]="passwordOptions.minSpecial"
|
||||
(input)="onPasswordOptionsMinSpecialInput($event)"
|
||||
(change)="minSpecialChanged()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<label class="d-block">{{ "options" | i18n }}</label>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="uppercase"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
(change)="savePasswordOptions()"
|
||||
[(ngModel)]="passwordOptions.uppercase"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useUppercase"
|
||||
attr.aria-label="{{ 'uppercase' | i18n }}"
|
||||
/>
|
||||
<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)="savePasswordOptions()"
|
||||
[(ngModel)]="passwordOptions.lowercase"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useLowercase"
|
||||
attr.aria-label="{{ 'lowercase' | i18n }}"
|
||||
/>
|
||||
<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)="savePasswordOptions()"
|
||||
[ngModel]="passwordOptions.number"
|
||||
(ngModelChange)="setPasswordOptionsNumber($event)"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useNumbers"
|
||||
attr.aria-label="{{ 'numbers' | i18n }}"
|
||||
/>
|
||||
<label for="numbers" class="form-check-label">0-9</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="special"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
[ngModel]="passwordOptions.special"
|
||||
(ngModelChange)="setPasswordOptionsSpecial($event)"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useSpecial"
|
||||
attr.aria-label="{{ 'specialCharacters' | i18n }}"
|
||||
/>
|
||||
<label for="special" class="form-check-label">!@#$%^&*</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="ambiguous"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
(change)="savePasswordOptions()"
|
||||
[(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="bwi bwi-clock bwi-lg" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div #form [appApiAction]="usernameGeneratingPromise">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-submit btn-primary"
|
||||
(click)="$any(form).loading ? false : regenerate()"
|
||||
[attr.aria-disabled]="$any(form).loading ? 'true' : null"
|
||||
>
|
||||
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||
<span>{{ "regenerateUsername" | i18n }}</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" (click)="copy()">
|
||||
{{ "copyUsername" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-template #historyTemplate></ng-template>
|
||||
<ng-container *ngIf="type === 'username'">
|
||||
<div aria-labelledby="usernameTypeHeading" class="form-group" role="radiogroup">
|
||||
<div class="d-block">
|
||||
<label id="usernameTypeHeading">{{ "usernameType" | i18n }}</label>
|
||||
<a
|
||||
class="ml-auto"
|
||||
href="https://bitwarden.com/help/generator/#username-types"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
appA11yTitle="{{ 'learnMore' | i18n }}"
|
||||
>
|
||||
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="form-check" *ngFor="let o of usernameTypeOptions">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
[(ngModel)]="usernameOptions.type"
|
||||
name="UsernameType"
|
||||
id="usernameType_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="saveUsernameOptions()"
|
||||
[checked]="usernameOptions.type === o.value"
|
||||
/>
|
||||
<label class="form-check-label" for="usernameType_{{ o.value }}">
|
||||
{{ o.name }}
|
||||
<div class="small text-muted">{{ o.desc }}</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="usernameOptions.type === 'forwarded'">
|
||||
<div class="form-group" role="listbox">
|
||||
<label class="d-block">{{ "service" | i18n }}</label>
|
||||
<select
|
||||
id="ForwardTypeDropdown"
|
||||
name="ForwardType"
|
||||
[(ngModel)]="usernameOptions.forwardedService"
|
||||
(change)="saveUsernameOptions()"
|
||||
class="form-control w-auto"
|
||||
>
|
||||
<option *ngFor="let o of forwardOptions" [ngValue]="o.value" role="option">
|
||||
{{ o.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="row" *ngIf="usernameOptions.forwardedService === 'simplelogin'">
|
||||
<div class="form-group col-4">
|
||||
<label for="simplelogin-apikey">{{ "apiKey" | i18n }}</label>
|
||||
<input
|
||||
id="simplelogin-apikey"
|
||||
class="form-control"
|
||||
type="password"
|
||||
[(ngModel)]="usernameOptions.forwardedSimpleLoginApiKey"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group col-4" *ngIf="isSelfHosted">
|
||||
<label for="simplelogin-baseUrl">{{ "baseUrl" | i18n }}</label>
|
||||
<input
|
||||
id="simplelogin-baseUrl"
|
||||
class="form-control"
|
||||
type="text"
|
||||
name="SimpleLoginDomain"
|
||||
[(ngModel)]="usernameOptions.forwardedSimpleLoginBaseUrl"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" *ngIf="usernameOptions.forwardedService === 'duckduckgo'">
|
||||
<div class="form-group col-4">
|
||||
<label for="duckduckgo-apikey">{{ "apiKey" | i18n }}</label>
|
||||
<input
|
||||
id="duckduckgo-apikey"
|
||||
class="form-control"
|
||||
type="password"
|
||||
name="DuckDuckGoApiKey"
|
||||
[(ngModel)]="usernameOptions.forwardedDuckDuckGoToken"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" *ngIf="usernameOptions.forwardedService === 'anonaddy'">
|
||||
<div class="form-group col-4">
|
||||
<label for="anonaddy-apikey">{{ "apiAccessToken" | i18n }}</label>
|
||||
<input
|
||||
id="anonaddy-apikey"
|
||||
class="form-control"
|
||||
type="password"
|
||||
[(ngModel)]="usernameOptions.forwardedAnonAddyApiToken"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group col-4">
|
||||
<label for="anonaddy-domain">{{ "aliasDomain" | i18n }}</label>
|
||||
<input
|
||||
id="anonaddy-domain"
|
||||
class="form-control"
|
||||
type="text"
|
||||
[(ngModel)]="usernameOptions.forwardedAnonAddyDomain"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group col-4" *ngIf="isSelfHosted">
|
||||
<label for="anonaddy-baseUrl">{{ "baseUrl" | i18n }}</label>
|
||||
<input
|
||||
id="anonaddy-baseUrl"
|
||||
class="form-control"
|
||||
type="text"
|
||||
name="AnonAddyDomain"
|
||||
[(ngModel)]="usernameOptions.forwardedAnonAddyBaseUrl"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" *ngIf="usernameOptions.forwardedService === 'firefoxrelay'">
|
||||
<div class="form-group col-4">
|
||||
<label for="firefox-apikey">{{ "apiAccessToken" | i18n }}</label>
|
||||
<input
|
||||
id="firefox-apikey"
|
||||
class="form-control"
|
||||
type="password"
|
||||
[(ngModel)]="usernameOptions.forwardedFirefoxApiToken"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" *ngIf="usernameOptions.forwardedService === 'fastmail'">
|
||||
<div class="form-group col-4">
|
||||
<label for="fastmail-apiToken">{{ "apiAccessToken" | i18n }}</label>
|
||||
<input
|
||||
id="fastmail-apiToken"
|
||||
class="form-control"
|
||||
type="password"
|
||||
[(ngModel)]="usernameOptions.forwardedFastmailApiToken"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" *ngIf="usernameOptions.forwardedService === 'forwardemail'">
|
||||
<div class="form-group col-4">
|
||||
<label for="forwardemail-apikey">{{ "apiAccessToken" | i18n }}</label>
|
||||
<input
|
||||
id="forwardemail-apikey"
|
||||
class="form-control"
|
||||
type="password"
|
||||
[(ngModel)]="usernameOptions.forwardedForwardEmailApiToken"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group col-4">
|
||||
<label for="forwardemail-domain">{{ "aliasDomain" | i18n }}</label>
|
||||
<input
|
||||
id="forwardemail-domain"
|
||||
class="form-control"
|
||||
type="text"
|
||||
[(ngModel)]="usernameOptions.forwardedForwardEmailDomain"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="row" *ngIf="usernameOptions.type === 'subaddress'">
|
||||
<div class="form-group col-4">
|
||||
<label for="subaddress-email">{{ "emailAddress" | i18n }}</label>
|
||||
<input
|
||||
id="subaddress-email"
|
||||
class="form-control"
|
||||
type="text"
|
||||
[(ngModel)]="usernameOptions.subaddressEmail"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" *ngIf="usernameOptions.type === 'catchall'">
|
||||
<div class="form-group col-4">
|
||||
<label for="catchall-domain">{{ "domainName" | i18n }}</label>
|
||||
<input
|
||||
id="catchall-domain"
|
||||
class="form-control"
|
||||
type="text"
|
||||
[(ngModel)]="usernameOptions.catchallDomain"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="usernameOptions.type === 'word'">
|
||||
<label class="d-block">{{ "options" | i18n }}</label>
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="capitalizeUsername"
|
||||
type="checkbox"
|
||||
(change)="saveUsernameOptions()"
|
||||
[(ngModel)]="usernameOptions.wordCapitalize"
|
||||
/>
|
||||
<label for="capitalizeUsername" class="form-check-label">{{
|
||||
"capitalize" | i18n
|
||||
}}</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="includeNumberUsername"
|
||||
type="checkbox"
|
||||
(change)="saveUsernameOptions()"
|
||||
[(ngModel)]="usernameOptions.wordIncludeNumber"
|
||||
/>
|
||||
<label for="includeNumberUsername" class="form-check-label">{{
|
||||
"includeNumber" | i18n
|
||||
}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div #form [appApiAction]="usernameGeneratingPromise">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-submit btn-primary"
|
||||
(click)="$any(form).loading ? false : regenerate()"
|
||||
[attr.aria-disabled]="$any(form).loading ? 'true' : null"
|
||||
>
|
||||
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||
<span>{{ "regenerateUsername" | i18n }}</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" (click)="copy()">
|
||||
{{ "copyUsername" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-template #historyTemplate></ng-template>
|
||||
</bit-container>
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { ImportCollectionServiceAbstraction } from "@bitwarden/importer/core";
|
||||
import { ImportComponent } from "@bitwarden/importer/ui";
|
||||
|
||||
import { SharedModule } from "../../shared";
|
||||
import { CollectionAdminService } from "../../vault/core/collection-admin.service";
|
||||
|
||||
import { ImportCollectionAdminService } from "./import-collection-admin.service";
|
||||
import { ImportWebComponent } from "./import-web.component";
|
||||
|
||||
@Component({
|
||||
templateUrl: "import-web.component.html",
|
||||
standalone: true,
|
||||
imports: [SharedModule, ImportComponent],
|
||||
providers: [
|
||||
{
|
||||
provide: ImportCollectionServiceAbstraction,
|
||||
useClass: ImportCollectionAdminService,
|
||||
deps: [CollectionAdminService],
|
||||
},
|
||||
],
|
||||
})
|
||||
export class AdminImportComponent extends ImportWebComponent {}
|
||||
@@ -1,18 +1,20 @@
|
||||
<h1 bitTypography="h1">{{ "importData" | i18n }}</h1>
|
||||
<tools-import
|
||||
(formDisabled)="this.disabled = $event"
|
||||
(formLoading)="this.loading = $event"
|
||||
(onSuccessfulImport)="this.onSuccessfulImport($event)"
|
||||
organizationId="{{ routeOrgId }}"
|
||||
></tools-import>
|
||||
<button
|
||||
[disabled]="disabled"
|
||||
[loading]="loading"
|
||||
form="import_form_importForm"
|
||||
bitButton
|
||||
type="submit"
|
||||
bitFormButton
|
||||
buttonType="primary"
|
||||
>
|
||||
{{ "importData" | i18n }}
|
||||
</button>
|
||||
<app-header></app-header>
|
||||
|
||||
<bit-container>
|
||||
<tools-import
|
||||
(formDisabled)="this.disabled = $event"
|
||||
(formLoading)="this.loading = $event"
|
||||
(onSuccessfulImport)="this.onSuccessfulImport($event)"
|
||||
></tools-import>
|
||||
<button
|
||||
[disabled]="disabled"
|
||||
[loading]="loading"
|
||||
form="import_form_importForm"
|
||||
bitButton
|
||||
type="submit"
|
||||
bitFormButton
|
||||
buttonType="primary"
|
||||
>
|
||||
{{ "importData" | i18n }}
|
||||
</button>
|
||||
</bit-container>
|
||||
|
||||
@@ -1,51 +1,26 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
import { Component } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import {
|
||||
OrganizationService,
|
||||
canAccessVaultTab,
|
||||
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { ImportComponent } from "@bitwarden/importer/ui";
|
||||
|
||||
import { HeaderModule } from "../../layouts/header/header.module";
|
||||
import { SharedModule } from "../../shared";
|
||||
|
||||
@Component({
|
||||
templateUrl: "import-web.component.html",
|
||||
standalone: true,
|
||||
imports: [SharedModule, ImportComponent],
|
||||
imports: [SharedModule, ImportComponent, HeaderModule],
|
||||
})
|
||||
export class ImportWebComponent implements OnInit {
|
||||
protected routeOrgId: string = null;
|
||||
export class ImportWebComponent {
|
||||
protected loading = false;
|
||||
protected disabled = false;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private organizationService: OrganizationService,
|
||||
private router: Router,
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.routeOrgId = this.route.snapshot.paramMap.get("organizationId");
|
||||
}
|
||||
constructor(private router: Router) {}
|
||||
|
||||
/**
|
||||
* Callback that is called after a successful import.
|
||||
*/
|
||||
protected async onSuccessfulImport(organizationId: string): Promise<void> {
|
||||
if (!this.routeOrgId) {
|
||||
await this.router.navigate(["vault"]);
|
||||
return;
|
||||
}
|
||||
|
||||
const organization = await firstValueFrom(this.organizationService.get$(organizationId));
|
||||
if (organization == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (canAccessVaultTab(organization)) {
|
||||
await this.router.navigate(["organizations", organizationId, "vault"]);
|
||||
}
|
||||
await this.router.navigate(["vault"]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,63 +1,64 @@
|
||||
<div class="page-header">
|
||||
<h1>{{ "dataBreachReport" | i18n }}</h1>
|
||||
</div>
|
||||
<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>
|
||||
<app-header></app-header>
|
||||
|
||||
<bit-container>
|
||||
<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>
|
||||
<button type="submit" buttonType="primary" bitButton [loading]="form.loading">
|
||||
{{ "checkBreaches" | i18n }}
|
||||
</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>
|
||||
</div>
|
||||
<button type="submit" buttonType="primary" bitButton [loading]="form.loading">
|
||||
{{ "checkBreaches" | i18n }}
|
||||
</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>
|
||||
</div>
|
||||
</bit-container>
|
||||
|
||||
@@ -1,72 +1,77 @@
|
||||
<div class="page-header">
|
||||
<h1>{{ "exposedPasswordsReport" | i18n }}</h1>
|
||||
</div>
|
||||
<p>{{ "exposedPasswordsReportDesc" | i18n }}</p>
|
||||
<button type="submit" buttonType="primary" bitButton [loading]="loading" (click)="load()">
|
||||
{{ "checkExposedPasswords" | i18n }}
|
||||
</button>
|
||||
<div class="mt-4" *ngIf="hasLoaded">
|
||||
<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-header></app-header>
|
||||
|
||||
<bit-container>
|
||||
<p>{{ "exposedPasswordsReportDesc" | i18n }}</p>
|
||||
<button type="submit" buttonType="primary" bitButton [loading]="loading" (click)="load()">
|
||||
{{ "checkExposedPasswords" | i18n }}
|
||||
</button>
|
||||
<div class="mt-4" *ngIf="hasLoaded">
|
||||
<app-callout type="success" title="{{ 'goodNews' | i18n }}" *ngIf="!ciphers.length">
|
||||
{{ "noExposedPasswords" | i18n }}
|
||||
</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="bwi bwi-collection"
|
||||
<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="bwi bwi-collection"
|
||||
appStopProp
|
||||
title="{{ 'shared' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "shared" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="c.hasAttachments">
|
||||
<i
|
||||
class="bwi bwi-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>
|
||||
<app-org-badge
|
||||
*ngIf="!organization"
|
||||
[disabled]="disabled"
|
||||
[organizationId]="c.organizationId"
|
||||
[organizationName]="c.organizationId | orgNameFromId: (organizations$ | async)"
|
||||
appStopProp
|
||||
title="{{ 'shared' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "shared" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="c.hasAttachments">
|
||||
<i
|
||||
class="bwi bwi-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>
|
||||
<app-org-badge
|
||||
*ngIf="!organization"
|
||||
[disabled]="disabled"
|
||||
[organizationId]="c.organizationId"
|
||||
[organizationName]="c.organizationId | orgNameFromId: (organizations$ | async)"
|
||||
appStopProp
|
||||
>
|
||||
</app-org-badge>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span bitBadge variant="warning">
|
||||
{{ "exposedXTimes" | i18n: (exposedPasswordMap.get(c.id) | number) }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</ng-container>
|
||||
</div>
|
||||
<ng-template #cipherAddEdit></ng-template>
|
||||
>
|
||||
</app-org-badge>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span bitBadge variant="warning">
|
||||
{{ "exposedXTimes" | i18n: (exposedPasswordMap.get(c.id) | number) }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</ng-container>
|
||||
</div>
|
||||
<ng-template #cipherAddEdit></ng-template>
|
||||
</bit-container>
|
||||
|
||||
@@ -1,88 +1,79 @@
|
||||
<div class="page-header">
|
||||
<h1>
|
||||
{{ "inactive2faReport" | i18n }}
|
||||
<small *ngIf="hasLoaded && loading">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin text-muted"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "loading" | i18n }}</span>
|
||||
</small>
|
||||
</h1>
|
||||
</div>
|
||||
<p>{{ "inactive2faReportDesc" | i18n }}</p>
|
||||
<div *ngIf="!hasLoaded && loading">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-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>
|
||||
<ng-container *ngIf="ciphers.length">
|
||||
<app-callout type="danger" title="{{ 'inactive2faFound' | i18n }}">
|
||||
{{ "inactive2faFoundDesc" | i18n: (ciphers.length | number) }}
|
||||
<app-header></app-header>
|
||||
|
||||
<bit-container>
|
||||
<p>{{ "inactive2faReportDesc" | i18n }}</p>
|
||||
<div *ngIf="!hasLoaded && loading">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-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>
|
||||
<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="bwi bwi-collection"
|
||||
<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="bwi bwi-collection"
|
||||
appStopProp
|
||||
title="{{ 'shared' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "shared" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="c.hasAttachments">
|
||||
<i
|
||||
class="bwi bwi-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>
|
||||
<app-org-badge
|
||||
*ngIf="!organization"
|
||||
[disabled]="disabled"
|
||||
[organizationId]="c.organizationId"
|
||||
[organizationName]="c.organizationId | orgNameFromId: (organizations$ | async)"
|
||||
appStopProp
|
||||
title="{{ 'shared' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "shared" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="c.hasAttachments">
|
||||
<i
|
||||
class="bwi bwi-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>
|
||||
<app-org-badge
|
||||
*ngIf="!organization"
|
||||
[disabled]="disabled"
|
||||
[organizationId]="c.organizationId"
|
||||
[organizationName]="c.organizationId | orgNameFromId: (organizations$ | async)"
|
||||
appStopProp
|
||||
>
|
||||
</app-org-badge>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<a
|
||||
bitBadge
|
||||
href="{{ cipherDocs.get(c.id) }}"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
*ngIf="cipherDocs.has(c.id)"
|
||||
>
|
||||
{{ "instructions" | i18n }}</a
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</ng-container>
|
||||
</div>
|
||||
<ng-template #cipherAddEdit></ng-template>
|
||||
>
|
||||
</app-org-badge>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<a
|
||||
bitBadge
|
||||
href="{{ cipherDocs.get(c.id) }}"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
*ngIf="cipherDocs.has(c.id)"
|
||||
>
|
||||
{{ "instructions" | i18n }}</a
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</ng-container>
|
||||
</div>
|
||||
<ng-template #cipherAddEdit></ng-template>
|
||||
</bit-container>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<div class="page-header">
|
||||
<h1>{{ "reports" | i18n }}</h1>
|
||||
</div>
|
||||
<app-header></app-header>
|
||||
|
||||
<p>{{ "reportsDesc" | i18n }}</p>
|
||||
<bit-container>
|
||||
<p>{{ "reportsDesc" | i18n }}</p>
|
||||
|
||||
<app-report-list [reports]="reports"></app-report-list>
|
||||
<app-report-list [reports]="reports"></app-report-list>
|
||||
</bit-container>
|
||||
|
||||
@@ -1,87 +1,82 @@
|
||||
<div class="page-header">
|
||||
<h1>
|
||||
{{ "reusedPasswordsReport" | i18n }}
|
||||
<small *ngIf="hasLoaded && loading">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin text-muted"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "loading" | i18n }}</span>
|
||||
</small>
|
||||
</h1>
|
||||
</div>
|
||||
<p>{{ "reusedPasswordsReportDesc" | i18n }}</p>
|
||||
<div *ngIf="!hasLoaded && loading">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-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>
|
||||
<ng-container *ngIf="ciphers.length">
|
||||
<app-callout type="danger" title="{{ 'reusedPasswordsFound' | i18n }}">
|
||||
{{ "reusedPasswordsFoundDesc" | i18n: (ciphers.length | number) }}
|
||||
<app-header></app-header>
|
||||
|
||||
<bit-container>
|
||||
<p>{{ "reusedPasswordsReportDesc" | i18n }}</p>
|
||||
<div *ngIf="!hasLoaded && loading">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-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>
|
||||
<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="bwi bwi-collection"
|
||||
<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="bwi bwi-collection"
|
||||
appStopProp
|
||||
title="{{ 'shared' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "shared" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="c.hasAttachments">
|
||||
<i
|
||||
class="bwi bwi-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>
|
||||
<app-org-badge
|
||||
*ngIf="!organization"
|
||||
[disabled]="disabled"
|
||||
[organizationId]="c.organizationId"
|
||||
[organizationName]="c.organizationId | orgNameFromId: (organizations$ | async)"
|
||||
appStopProp
|
||||
title="{{ 'shared' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "shared" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="c.hasAttachments">
|
||||
<i
|
||||
class="bwi bwi-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>
|
||||
<app-org-badge
|
||||
*ngIf="!organization"
|
||||
[disabled]="disabled"
|
||||
[organizationId]="c.organizationId"
|
||||
[organizationName]="c.organizationId | orgNameFromId: (organizations$ | async)"
|
||||
appStopProp
|
||||
>
|
||||
</app-org-badge>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span bitBadge variant="warning">
|
||||
{{ "reusedXTimes" | i18n: passwordUseMap.get(c.login.password) }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</ng-container>
|
||||
</div>
|
||||
<ng-template #cipherAddEdit></ng-template>
|
||||
>
|
||||
</app-org-badge>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span bitBadge variant="warning">
|
||||
{{ "reusedXTimes" | i18n: passwordUseMap.get(c.login.password) }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</ng-container>
|
||||
</div>
|
||||
<ng-template #cipherAddEdit></ng-template>
|
||||
</bit-container>
|
||||
|
||||
@@ -1,77 +1,68 @@
|
||||
<div class="page-header">
|
||||
<h1>
|
||||
{{ "unsecuredWebsitesReport" | i18n }}
|
||||
<small *ngIf="hasLoaded && loading">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin text-muted"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "loading" | i18n }}</span>
|
||||
</small>
|
||||
</h1>
|
||||
</div>
|
||||
<p>{{ "unsecuredWebsitesReportDesc" | i18n }}</p>
|
||||
<div *ngIf="!hasLoaded && loading">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-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>
|
||||
<ng-container *ngIf="ciphers.length">
|
||||
<app-callout type="danger" title="{{ 'unsecuredWebsitesFound' | i18n }}">
|
||||
{{ "unsecuredWebsitesFoundDesc" | i18n: (ciphers.length | number) }}
|
||||
<app-header></app-header>
|
||||
|
||||
<bit-container>
|
||||
<p>{{ "unsecuredWebsitesReportDesc" | i18n }}</p>
|
||||
<div *ngIf="!hasLoaded && loading">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-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>
|
||||
<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="bwi bwi-collection"
|
||||
<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="bwi bwi-collection"
|
||||
appStopProp
|
||||
title="{{ 'shared' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "shared" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="c.hasAttachments">
|
||||
<i
|
||||
class="bwi bwi-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>
|
||||
<app-org-badge
|
||||
*ngIf="!organization"
|
||||
[disabled]="disabled"
|
||||
[organizationId]="c.organizationId"
|
||||
[organizationName]="c.organizationId | orgNameFromId: (organizations$ | async)"
|
||||
appStopProp
|
||||
title="{{ 'shared' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "shared" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="c.hasAttachments">
|
||||
<i
|
||||
class="bwi bwi-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>
|
||||
<app-org-badge
|
||||
*ngIf="!organization"
|
||||
[disabled]="disabled"
|
||||
[organizationId]="c.organizationId"
|
||||
[organizationName]="c.organizationId | orgNameFromId: (organizations$ | async)"
|
||||
appStopProp
|
||||
>
|
||||
</app-org-badge>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</ng-container>
|
||||
</div>
|
||||
<ng-template #cipherAddEdit></ng-template>
|
||||
>
|
||||
</app-org-badge>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</ng-container>
|
||||
</div>
|
||||
<ng-template #cipherAddEdit></ng-template>
|
||||
</bit-container>
|
||||
|
||||
@@ -1,87 +1,82 @@
|
||||
<div class="page-header">
|
||||
<h1>
|
||||
{{ "weakPasswordsReport" | i18n }}
|
||||
<small *ngIf="hasLoaded && loading">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin text-muted"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "loading" | i18n }}</span>
|
||||
</small>
|
||||
</h1>
|
||||
</div>
|
||||
<p>{{ "weakPasswordsReportDesc" | i18n }}</p>
|
||||
<div *ngIf="!hasLoaded && loading">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-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>
|
||||
<ng-container *ngIf="ciphers.length">
|
||||
<app-callout type="danger" title="{{ 'weakPasswordsFound' | i18n }}">
|
||||
{{ "weakPasswordsFoundDesc" | i18n: (ciphers.length | number) }}
|
||||
<app-header></app-header>
|
||||
|
||||
<bit-container>
|
||||
<p>{{ "weakPasswordsReportDesc" | i18n }}</p>
|
||||
<div *ngIf="!hasLoaded && loading">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-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>
|
||||
<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="bwi bwi-collection"
|
||||
<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="bwi bwi-collection"
|
||||
appStopProp
|
||||
title="{{ 'shared' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "shared" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="c.hasAttachments">
|
||||
<i
|
||||
class="bwi bwi-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>
|
||||
<app-org-badge
|
||||
*ngIf="!organization"
|
||||
[disabled]="disabled"
|
||||
[organizationId]="c.organizationId"
|
||||
[organizationName]="c.organizationId | orgNameFromId: (organizations$ | async)"
|
||||
appStopProp
|
||||
title="{{ 'shared' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "shared" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="c.hasAttachments">
|
||||
<i
|
||||
class="bwi bwi-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>
|
||||
<app-org-badge
|
||||
*ngIf="!organization"
|
||||
[disabled]="disabled"
|
||||
[organizationId]="c.organizationId"
|
||||
[organizationName]="c.organizationId | orgNameFromId: (organizations$ | async)"
|
||||
appStopProp
|
||||
>
|
||||
</app-org-badge>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span bitBadge [variant]="passwordStrengthMap.get(c.id)[1]">
|
||||
{{ passwordStrengthMap.get(c.id)[0] | i18n }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</ng-container>
|
||||
</div>
|
||||
<ng-template #cipherAddEdit></ng-template>
|
||||
>
|
||||
</app-org-badge>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span bitBadge [variant]="passwordStrengthMap.get(c.id)[1]">
|
||||
{{ passwordStrengthMap.get(c.id)[0] | i18n }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</ng-container>
|
||||
</div>
|
||||
<ng-template #cipherAddEdit></ng-template>
|
||||
</bit-container>
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
<div class="container page-content">
|
||||
<router-outlet></router-outlet>
|
||||
<router-outlet></router-outlet>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col">
|
||||
<a bitButton routerLink="./" *ngIf="!homepage">
|
||||
<i class="bwi bwi-angle-left" aria-hidden="true"></i>
|
||||
{{ "backToReports" | i18n }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="row mt-4">
|
||||
<div class="col">
|
||||
<a bitButton routerLink="./" *ngIf="!homepage">
|
||||
<i class="bwi bwi-angle-left" aria-hidden="true"></i>
|
||||
{{ "backToReports" | i18n }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -20,7 +20,12 @@ const routes: Routes = [
|
||||
component: ReportsLayoutComponent,
|
||||
canActivate: [AuthGuard],
|
||||
children: [
|
||||
{ path: "", pathMatch: "full", component: ReportsHomeComponent, data: { homepage: true } },
|
||||
{
|
||||
path: "",
|
||||
pathMatch: "full",
|
||||
component: ReportsHomeComponent,
|
||||
data: { titleId: "reports", homepage: true },
|
||||
},
|
||||
{
|
||||
path: "breach-report",
|
||||
component: BreachReportComponent,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { NgModule } from "@angular/core";
|
||||
|
||||
import { HeaderModule } from "../../layouts/header/header.module";
|
||||
import { SharedModule } from "../../shared";
|
||||
import { OrganizationBadgeModule } from "../../vault/individual-vault/organization-badge/organization-badge.module";
|
||||
import { PipesModule } from "../../vault/individual-vault/pipes/pipes.module";
|
||||
@@ -24,6 +25,7 @@ import { ReportsSharedModule } from "./shared";
|
||||
ReportsRoutingModule,
|
||||
OrganizationBadgeModule,
|
||||
PipesModule,
|
||||
HeaderModule,
|
||||
],
|
||||
declarations: [
|
||||
BreachReportComponent,
|
||||
|
||||
@@ -1,221 +1,206 @@
|
||||
<div class="container page-content">
|
||||
<bit-callout type="warning" title="{{ 'sendDisabled' | i18n }}" *ngIf="disableSend">
|
||||
{{ "sendDisabledWarning" | i18n }}
|
||||
</bit-callout>
|
||||
<div class="tw-grid tw-grid-cols-12 tw-gap-4">
|
||||
<div class="groupings tw-col-span-3">
|
||||
<div class="card vault-filters">
|
||||
<div class="card-header d-flex">
|
||||
{{ "filters" | i18n }}
|
||||
<app-header>
|
||||
<ng-container slot="title-suffix">
|
||||
<small #actionSpinner [appApiAction]="actionPromise">
|
||||
<ng-container *ngIf="$any(actionSpinner).loading">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin text-muted"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "loading" | i18n }}</span>
|
||||
</ng-container>
|
||||
</small>
|
||||
</ng-container>
|
||||
|
||||
<button type="button" bitButton buttonType="primary" (click)="addSend()" [disabled]="disableSend">
|
||||
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
|
||||
{{ "createSend" | i18n }}
|
||||
</button>
|
||||
</app-header>
|
||||
|
||||
<bit-callout type="warning" title="{{ 'sendDisabled' | i18n }}" *ngIf="disableSend">
|
||||
{{ "sendDisabledWarning" | i18n }}
|
||||
</bit-callout>
|
||||
<div class="tw-grid tw-grid-cols-12 tw-gap-4">
|
||||
<div class="groupings tw-col-span-3">
|
||||
<div class="card vault-filters">
|
||||
<div class="card-header d-flex">
|
||||
{{ "filters" | i18n }}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="tw-mb-4">
|
||||
<bit-search
|
||||
[(ngModel)]="searchText"
|
||||
[placeholder]="'searchSends' | i18n"
|
||||
(input)="searchTextChanged()"
|
||||
appAutofocus
|
||||
/>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="tw-mb-4">
|
||||
<bit-search
|
||||
[(ngModel)]="searchText"
|
||||
[placeholder]="'searchSends' | i18n"
|
||||
(input)="searchTextChanged()"
|
||||
appAutofocus
|
||||
/>
|
||||
</div>
|
||||
<div class="filter">
|
||||
<ul class="filter-options">
|
||||
<li class="filter-option" [ngClass]="{ active: selectedAll }">
|
||||
<span class="filter-buttons">
|
||||
<button type="button" class="filter-button" appStopClick (click)="selectAll()">
|
||||
<i class="bwi bwi-fw bwi-filter"></i>{{ "allSends" | i18n }}
|
||||
</button>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="filter">
|
||||
<div class="filter-heading">
|
||||
<h3>{{ "types" | i18n }}</h3>
|
||||
</div>
|
||||
<ul class="filter-options">
|
||||
<li class="filter-option" [ngClass]="{ active: selectedType === sendType.Text }">
|
||||
<span class="filter-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="filter-button"
|
||||
appStopClick
|
||||
(click)="selectType(sendType.Text)"
|
||||
>
|
||||
<i class="bwi bwi-fw bwi-file-text"></i>{{ "sendTypeText" | i18n }}
|
||||
</button>
|
||||
</span>
|
||||
</li>
|
||||
<li class="filter-option" [ngClass]="{ active: selectedType === sendType.File }">
|
||||
<span class="filter-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="filter-button"
|
||||
appStopClick
|
||||
(click)="selectType(sendType.File)"
|
||||
>
|
||||
<i class="bwi bwi-fw bwi-file"></i>{{ "sendTypeFile" | i18n }}
|
||||
</button>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="filter">
|
||||
<ul class="filter-options">
|
||||
<li class="filter-option" [ngClass]="{ active: selectedAll }">
|
||||
<span class="filter-buttons">
|
||||
<button type="button" class="filter-button" appStopClick (click)="selectAll()">
|
||||
<i class="bwi bwi-fw bwi-filter"></i>{{ "allSends" | i18n }}
|
||||
</button>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="filter">
|
||||
<div class="filter-heading">
|
||||
<h3>{{ "types" | i18n }}</h3>
|
||||
</div>
|
||||
<ul class="filter-options">
|
||||
<li class="filter-option" [ngClass]="{ active: selectedType === sendType.Text }">
|
||||
<span class="filter-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="filter-button"
|
||||
appStopClick
|
||||
(click)="selectType(sendType.Text)"
|
||||
>
|
||||
<i class="bwi bwi-fw bwi-file-text"></i>{{ "sendTypeText" | i18n }}
|
||||
</button>
|
||||
</span>
|
||||
</li>
|
||||
<li class="filter-option" [ngClass]="{ active: selectedType === sendType.File }">
|
||||
<span class="filter-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="filter-button"
|
||||
appStopClick
|
||||
(click)="selectType(sendType.File)"
|
||||
>
|
||||
<i class="bwi bwi-fw bwi-file"></i>{{ "sendTypeFile" | i18n }}
|
||||
</button>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tw-col-span-9">
|
||||
<div class="tw-flex">
|
||||
<h1 bitTypography="h1">
|
||||
{{ "send" | i18n }}
|
||||
<small #actionSpinner [appApiAction]="actionPromise">
|
||||
<ng-container *ngIf="$any(actionSpinner).loading">
|
||||
</div>
|
||||
<div class="tw-col-span-9">
|
||||
<!--Listing Table-->
|
||||
<bit-table [dataSource]="dataSource" *ngIf="filteredSends && filteredSends.length">
|
||||
<ng-container header>
|
||||
<tr>
|
||||
<th bitCell bitSortable="name" default>{{ "name" | i18n }}</th>
|
||||
<th bitCell bitSortable="deletionDate">{{ "deletionDate" | i18n }}</th>
|
||||
<th bitCell>{{ "options" | i18n }}</th>
|
||||
</tr>
|
||||
</ng-container>
|
||||
<ng-template body let-rows$>
|
||||
<tr bitRow *ngFor="let s of rows$ | async">
|
||||
<td bitCell (click)="editSend(s)" class="tw-cursor-pointer">
|
||||
<span class="tw-mr-2" aria-hidden="true">
|
||||
<i class="bwi bwi-fw bwi-lg bwi-file" *ngIf="s.type == sendType.File"></i>
|
||||
<i class="bwi bwi-fw bwi-lg bwi-file-text" *ngIf="s.type == sendType.Text"></i>
|
||||
</span>
|
||||
<button type="button" bitLink>
|
||||
{{ s.name }}
|
||||
</button>
|
||||
<ng-container *ngIf="s.disabled">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin text-muted"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
class="bwi bwi-exclamation-triangle"
|
||||
appStopProp
|
||||
title="{{ 'disabled' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "loading" | i18n }}</span>
|
||||
<span class="sr-only">{{ "disabled" | i18n }}</span>
|
||||
</ng-container>
|
||||
</small>
|
||||
</h1>
|
||||
<div class="tw-ml-auto">
|
||||
<button
|
||||
type="button"
|
||||
bitButton
|
||||
buttonType="primary"
|
||||
(click)="addSend()"
|
||||
[disabled]="disableSend"
|
||||
>
|
||||
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
|
||||
{{ "createSend" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!--Listing Table-->
|
||||
<bit-table [dataSource]="dataSource" *ngIf="filteredSends && filteredSends.length">
|
||||
<ng-container header>
|
||||
<tr>
|
||||
<th bitCell bitSortable="name" default>{{ "name" | i18n }}</th>
|
||||
<th bitCell bitSortable="deletionDate">{{ "deletionDate" | i18n }}</th>
|
||||
<th bitCell>{{ "options" | i18n }}</th>
|
||||
</tr>
|
||||
</ng-container>
|
||||
<ng-template body let-rows$>
|
||||
<tr bitRow *ngFor="let s of rows$ | async">
|
||||
<td bitCell (click)="editSend(s)" class="tw-cursor-pointer">
|
||||
<span class="tw-mr-2" aria-hidden="true">
|
||||
<i class="bwi bwi-fw bwi-lg bwi-file" *ngIf="s.type == sendType.File"></i>
|
||||
<i class="bwi bwi-fw bwi-lg bwi-file-text" *ngIf="s.type == sendType.Text"></i>
|
||||
</span>
|
||||
<button type="button" bitLink>
|
||||
{{ s.name }}
|
||||
<ng-container *ngIf="s.password">
|
||||
<i
|
||||
class="bwi bwi-key"
|
||||
appStopProp
|
||||
title="{{ 'password' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "password" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="s.maxAccessCountReached">
|
||||
<i
|
||||
class="bwi bwi-ban"
|
||||
appStopProp
|
||||
title="{{ 'maxAccessCountReached' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "maxAccessCountReached" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="s.expired">
|
||||
<i
|
||||
class="bwi bwi-clock"
|
||||
appStopProp
|
||||
title="{{ 'expired' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "expired" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="s.pendingDelete">
|
||||
<i
|
||||
class="bwi bwi-trash"
|
||||
appStopProp
|
||||
title="{{ 'pendingDeletion' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "pendingDeletion" | i18n }}</span>
|
||||
</ng-container>
|
||||
</td>
|
||||
<td bitCell class="tw-text-muted" (click)="editSend(s)" class="tw-cursor-pointer">
|
||||
<small bitTypography="body2" appStopProp>{{ s.deletionDate | date: "medium" }}</small>
|
||||
</td>
|
||||
<td bitCell class="tw-w-0 tw-text-right">
|
||||
<button
|
||||
type="button"
|
||||
[bitMenuTriggerFor]="sendOptions"
|
||||
bitIconButton="bwi-ellipsis-v"
|
||||
appA11yTitle="{{ 'options' | i18n }}"
|
||||
></button>
|
||||
<bit-menu #sendOptions>
|
||||
<button type="button" bitMenuItem (click)="copy(s)">
|
||||
<i class="bwi bwi-fw bwi-clone" aria-hidden="true"></i>
|
||||
{{ "copySendLink" | i18n }}
|
||||
</button>
|
||||
<ng-container *ngIf="s.disabled">
|
||||
<i
|
||||
class="bwi bwi-exclamation-triangle"
|
||||
appStopProp
|
||||
title="{{ 'disabled' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "disabled" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="s.password">
|
||||
<i
|
||||
class="bwi bwi-key"
|
||||
appStopProp
|
||||
title="{{ 'password' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "password" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="s.maxAccessCountReached">
|
||||
<i
|
||||
class="bwi bwi-ban"
|
||||
appStopProp
|
||||
title="{{ 'maxAccessCountReached' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "maxAccessCountReached" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="s.expired">
|
||||
<i
|
||||
class="bwi bwi-clock"
|
||||
appStopProp
|
||||
title="{{ 'expired' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "expired" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="s.pendingDelete">
|
||||
<i
|
||||
class="bwi bwi-trash"
|
||||
appStopProp
|
||||
title="{{ 'pendingDeletion' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "pendingDeletion" | i18n }}</span>
|
||||
</ng-container>
|
||||
</td>
|
||||
<td bitCell class="tw-text-muted" (click)="editSend(s)" class="tw-cursor-pointer">
|
||||
<small bitTypography="body2" appStopProp>{{ s.deletionDate | date: "medium" }}</small>
|
||||
</td>
|
||||
<td bitCell class="tw-w-0 tw-text-right">
|
||||
<button
|
||||
type="button"
|
||||
[bitMenuTriggerFor]="sendOptions"
|
||||
bitIconButton="bwi-ellipsis-v"
|
||||
appA11yTitle="{{ 'options' | i18n }}"
|
||||
></button>
|
||||
<bit-menu #sendOptions>
|
||||
<button type="button" bitMenuItem (click)="copy(s)">
|
||||
<i class="bwi bwi-fw bwi-clone" aria-hidden="true"></i>
|
||||
{{ "copySendLink" | i18n }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
bitMenuItem
|
||||
(click)="removePassword(s)"
|
||||
*ngIf="s.password && !disableSend"
|
||||
>
|
||||
<i class="bwi bwi-fw bwi-close" aria-hidden="true"></i>
|
||||
{{ "removePassword" | i18n }}
|
||||
</button>
|
||||
<button type="button" bitMenuItem (click)="delete(s)">
|
||||
<span class="tw-text-danger">
|
||||
<i class="bwi bwi-fw bwi-trash" aria-hidden="true"></i>
|
||||
{{ "delete" | i18n }}
|
||||
</span>
|
||||
</button>
|
||||
</bit-menu>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</bit-table>
|
||||
<div class="no-items" *ngIf="filteredSends && !filteredSends.length">
|
||||
<ng-container *ngIf="!loaded">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin text-muted"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "loading" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="loaded">
|
||||
<bit-no-items [icon]="noItemIcon" class="tw-text-main">
|
||||
<ng-container slot="title">{{ "sendsNoItemsTitle" | i18n }}</ng-container>
|
||||
<ng-container slot="description">{{ "sendsNoItemsMessage" | i18n }}</ng-container>
|
||||
<button
|
||||
slot="button"
|
||||
type="button"
|
||||
bitButton
|
||||
buttonType="secondary"
|
||||
(click)="addSend()"
|
||||
>
|
||||
<i class="bwi bwi-plus" aria-hidden="true"></i>
|
||||
{{ "createSend" | i18n }}
|
||||
</button>
|
||||
</bit-no-items>
|
||||
</ng-container>
|
||||
</div>
|
||||
bitMenuItem
|
||||
(click)="removePassword(s)"
|
||||
*ngIf="s.password && !disableSend"
|
||||
>
|
||||
<i class="bwi bwi-fw bwi-close" aria-hidden="true"></i>
|
||||
{{ "removePassword" | i18n }}
|
||||
</button>
|
||||
<button type="button" bitMenuItem (click)="delete(s)">
|
||||
<span class="tw-text-danger">
|
||||
<i class="bwi bwi-fw bwi-trash" aria-hidden="true"></i>
|
||||
{{ "delete" | i18n }}
|
||||
</span>
|
||||
</button>
|
||||
</bit-menu>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</bit-table>
|
||||
<div class="no-items" *ngIf="filteredSends && !filteredSends.length">
|
||||
<ng-container *ngIf="!loaded">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin text-muted"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "loading" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="loaded">
|
||||
<bit-no-items [icon]="noItemIcon" class="tw-text-main">
|
||||
<ng-container slot="title">{{ "sendsNoItemsTitle" | i18n }}</ng-container>
|
||||
<ng-container slot="description">{{ "sendsNoItemsMessage" | i18n }}</ng-container>
|
||||
<button slot="button" type="button" bitButton buttonType="secondary" (click)="addSend()">
|
||||
<i class="bwi bwi-plus" aria-hidden="true"></i>
|
||||
{{ "createSend" | i18n }}
|
||||
</button>
|
||||
</bit-no-items>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,6 +14,7 @@ import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.s
|
||||
import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
|
||||
import { DialogService, NoItemsModule, SearchModule, TableDataSource } from "@bitwarden/components";
|
||||
|
||||
import { HeaderModule } from "../../layouts/header/header.module";
|
||||
import { SharedModule } from "../../shared";
|
||||
|
||||
import { AddEditComponent } from "./add-edit.component";
|
||||
@@ -24,7 +25,7 @@ const BroadcasterSubscriptionId = "SendComponent";
|
||||
@Component({
|
||||
selector: "app-send",
|
||||
standalone: true,
|
||||
imports: [SharedModule, SearchModule, NoItemsModule],
|
||||
imports: [SharedModule, SearchModule, NoItemsModule, HeaderModule],
|
||||
templateUrl: "send.component.html",
|
||||
})
|
||||
export class SendComponent extends BaseSendComponent {
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
<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">
|
||||
{{ "generator" | 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>
|
||||
<div class="col-9">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,79 +1,101 @@
|
||||
<form
|
||||
#form
|
||||
(ngSubmit)="submit()"
|
||||
[appApiAction]="formPromise"
|
||||
[formGroup]="exportForm"
|
||||
*ngIf="exportForm"
|
||||
>
|
||||
<h1 bitTypography="h1">{{ "exportVault" | i18n }}</h1>
|
||||
<app-header></app-header>
|
||||
|
||||
<bit-callout type="danger" title="{{ 'vaultExportDisabled' | i18n }}" *ngIf="disabledByPolicy">
|
||||
{{ "personalVaultExportPolicyInEffect" | i18n }}
|
||||
</bit-callout>
|
||||
<app-export-scope-callout
|
||||
[organizationId]="organizationId"
|
||||
*ngIf="!disabledByPolicy"
|
||||
></app-export-scope-callout>
|
||||
<bit-container>
|
||||
<form
|
||||
#form
|
||||
(ngSubmit)="submit()"
|
||||
[appApiAction]="formPromise"
|
||||
[formGroup]="exportForm"
|
||||
*ngIf="exportForm"
|
||||
>
|
||||
<bit-callout type="danger" title="{{ 'vaultExportDisabled' | i18n }}" *ngIf="disabledByPolicy">
|
||||
{{ "personalVaultExportPolicyInEffect" | i18n }}
|
||||
</bit-callout>
|
||||
<app-export-scope-callout
|
||||
[organizationId]="organizationId"
|
||||
*ngIf="!disabledByPolicy"
|
||||
></app-export-scope-callout>
|
||||
|
||||
<ng-container *ngIf="organizations$ | async as organizations">
|
||||
<bit-form-field *ngIf="organizations.length > 0">
|
||||
<bit-label>{{ "exportFrom" | i18n }}</bit-label>
|
||||
<bit-select formControlName="vaultSelector">
|
||||
<bit-option [label]="'myVault' | i18n" value="myVault" icon="bwi-user" />
|
||||
<bit-option
|
||||
*ngFor="let o of organizations$ | async"
|
||||
[value]="o.id"
|
||||
[label]="o.name"
|
||||
icon="bwi-business"
|
||||
/>
|
||||
<ng-container *ngIf="organizations$ | async as organizations">
|
||||
<bit-form-field *ngIf="organizations.length > 0">
|
||||
<bit-label>{{ "exportFrom" | i18n }}</bit-label>
|
||||
<bit-select formControlName="vaultSelector">
|
||||
<bit-option [label]="'myVault' | i18n" value="myVault" icon="bwi-user" />
|
||||
<bit-option
|
||||
*ngFor="let o of organizations$ | async"
|
||||
[value]="o.id"
|
||||
[label]="o.name"
|
||||
icon="bwi-business"
|
||||
/>
|
||||
</bit-select>
|
||||
</bit-form-field>
|
||||
</ng-container>
|
||||
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "fileFormat" | i18n }}</bit-label>
|
||||
<bit-select formControlName="format">
|
||||
<bit-option *ngFor="let f of formatOptions" [value]="f.value" [label]="f.name" />
|
||||
</bit-select>
|
||||
</bit-form-field>
|
||||
</ng-container>
|
||||
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "fileFormat" | i18n }}</bit-label>
|
||||
<bit-select formControlName="format">
|
||||
<bit-option *ngFor="let f of formatOptions" [value]="f.value" [label]="f.name" />
|
||||
</bit-select>
|
||||
</bit-form-field>
|
||||
<ng-container *ngIf="format === 'encrypted_json'">
|
||||
<bit-radio-group formControlName="fileEncryptionType" aria-label="exportTypeHeading">
|
||||
<bit-label>{{ "exportTypeHeading" | i18n }}</bit-label>
|
||||
|
||||
<ng-container *ngIf="format === 'encrypted_json'">
|
||||
<bit-radio-group formControlName="fileEncryptionType" aria-label="exportTypeHeading">
|
||||
<bit-label>{{ "exportTypeHeading" | i18n }}</bit-label>
|
||||
<bit-radio-button
|
||||
id="AccountEncrypted"
|
||||
name="fileEncryptionType"
|
||||
class="tw-block"
|
||||
[value]="encryptedExportType.AccountEncrypted"
|
||||
checked="fileEncryptionType === encryptedExportType.AccountEncrypted"
|
||||
>
|
||||
<bit-label>{{ "accountRestricted" | i18n }}</bit-label>
|
||||
<bit-hint>{{ "accountRestrictedOptionDescription" | i18n }}</bit-hint>
|
||||
</bit-radio-button>
|
||||
|
||||
<bit-radio-button
|
||||
id="AccountEncrypted"
|
||||
name="fileEncryptionType"
|
||||
class="tw-block"
|
||||
[value]="encryptedExportType.AccountEncrypted"
|
||||
checked="fileEncryptionType === encryptedExportType.AccountEncrypted"
|
||||
>
|
||||
<bit-label>{{ "accountRestricted" | i18n }}</bit-label>
|
||||
<bit-hint>{{ "accountRestrictedOptionDescription" | i18n }}</bit-hint>
|
||||
</bit-radio-button>
|
||||
<bit-radio-button
|
||||
id="FileEncrypted"
|
||||
name="fileEncryptionType"
|
||||
class="tw-block"
|
||||
[value]="encryptedExportType.FileEncrypted"
|
||||
checked="fileEncryptionType === encryptedExportType.FileEncrypted"
|
||||
>
|
||||
<bit-label>{{ "passwordProtected" | i18n }}</bit-label>
|
||||
<bit-hint>{{ "passwordProtectedOptionDescription" | i18n }}</bit-hint>
|
||||
</bit-radio-button>
|
||||
</bit-radio-group>
|
||||
|
||||
<bit-radio-button
|
||||
id="FileEncrypted"
|
||||
name="fileEncryptionType"
|
||||
class="tw-block"
|
||||
[value]="encryptedExportType.FileEncrypted"
|
||||
checked="fileEncryptionType === encryptedExportType.FileEncrypted"
|
||||
>
|
||||
<bit-label>{{ "passwordProtected" | i18n }}</bit-label>
|
||||
<bit-hint>{{ "passwordProtectedOptionDescription" | i18n }}</bit-hint>
|
||||
</bit-radio-button>
|
||||
</bit-radio-group>
|
||||
|
||||
<ng-container *ngIf="fileEncryptionType == encryptedExportType.FileEncrypted">
|
||||
<div class="tw-mb-3">
|
||||
<ng-container *ngIf="fileEncryptionType == encryptedExportType.FileEncrypted">
|
||||
<div class="tw-mb-3">
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "filePassword" | i18n }}</bit-label>
|
||||
<input
|
||||
bitInput
|
||||
type="password"
|
||||
id="filePassword"
|
||||
formControlName="filePassword"
|
||||
name="password"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
bitSuffix
|
||||
bitIconButton
|
||||
bitPasswordInputToggle
|
||||
[(toggled)]="showFilePassword"
|
||||
></button>
|
||||
<bit-hint>{{ "exportPasswordDescription" | i18n }}</bit-hint>
|
||||
</bit-form-field>
|
||||
<app-password-strength [password]="filePassword" [showText]="true">
|
||||
</app-password-strength>
|
||||
</div>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "filePassword" | i18n }}</bit-label>
|
||||
<bit-label>{{ "confirmFilePassword" | i18n }}</bit-label>
|
||||
<input
|
||||
bitInput
|
||||
type="password"
|
||||
id="filePassword"
|
||||
formControlName="filePassword"
|
||||
name="password"
|
||||
id="confirmFilePassword"
|
||||
formControlName="confirmFilePassword"
|
||||
name="confirmFilePassword"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
@@ -82,37 +104,18 @@
|
||||
bitPasswordInputToggle
|
||||
[(toggled)]="showFilePassword"
|
||||
></button>
|
||||
<bit-hint>{{ "exportPasswordDescription" | i18n }}</bit-hint>
|
||||
</bit-form-field>
|
||||
<app-password-strength [password]="filePassword" [showText]="true"> </app-password-strength>
|
||||
</div>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "confirmFilePassword" | i18n }}</bit-label>
|
||||
<input
|
||||
bitInput
|
||||
type="password"
|
||||
id="confirmFilePassword"
|
||||
formControlName="confirmFilePassword"
|
||||
name="confirmFilePassword"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
bitSuffix
|
||||
bitIconButton
|
||||
bitPasswordInputToggle
|
||||
[(toggled)]="showFilePassword"
|
||||
></button>
|
||||
</bit-form-field>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<button
|
||||
bitButton
|
||||
type="submit"
|
||||
buttonType="primary"
|
||||
[loading]="form.loading"
|
||||
[disabled]="disabledByPolicy"
|
||||
>
|
||||
{{ "confirmFormat" | i18n }}
|
||||
</button>
|
||||
</form>
|
||||
<button
|
||||
bitButton
|
||||
type="submit"
|
||||
buttonType="primary"
|
||||
[loading]="form.loading"
|
||||
[disabled]="disabledByPolicy"
|
||||
>
|
||||
{{ "confirmFormat" | i18n }}
|
||||
</button>
|
||||
</form>
|
||||
</bit-container>
|
||||
|
||||
Reference in New Issue
Block a user