mirror of
https://github.com/bitwarden/web
synced 2025-12-06 00:03:28 +00:00
Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1429cb3f76 | ||
|
|
13cbba3e99 | ||
|
|
73d24162a0 | ||
|
|
4964ebd31e | ||
|
|
5a8198a878 | ||
|
|
03aa806af6 | ||
|
|
023bf0474c | ||
|
|
2e22ca9216 | ||
|
|
5a540bba9e | ||
|
|
2047a6378b | ||
|
|
dc87510a7a | ||
|
|
c3f4c6c03b | ||
|
|
e8b72477c9 | ||
|
|
862874c2ae | ||
|
|
4d2d686078 | ||
|
|
6d458646fa | ||
|
|
a1345488d0 | ||
|
|
c43012a5f2 | ||
|
|
577cab24c4 | ||
|
|
5c0a77aec8 | ||
|
|
a0904b14ed | ||
|
|
6774ae0ef3 | ||
|
|
5a76ca4676 | ||
|
|
3c5a972bc9 | ||
|
|
54b68ac543 | ||
|
|
ff378f05fe | ||
|
|
f207aa3a9d | ||
|
|
7b43dcb6a1 | ||
|
|
c487cf3284 | ||
|
|
c2e1d325f2 | ||
|
|
f090e8febf | ||
|
|
087c84bcfb | ||
|
|
a457c83242 | ||
|
|
bcd8963e8b | ||
|
|
1464e0fbe8 | ||
|
|
ec2b048289 | ||
|
|
04811c934f | ||
|
|
f84ee30b9d | ||
|
|
218caa28b0 | ||
|
|
a8af807650 | ||
|
|
826170507e | ||
|
|
c37979e48d | ||
|
|
7ebb046cd8 | ||
|
|
7c4d0a15dd | ||
|
|
512b9e0a92 |
@@ -13,3 +13,6 @@ insert_final_newline = true
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.{ts}]
|
||||
quote_type = single
|
||||
|
||||
168
.github/workflows/build.yml
vendored
Normal file
168
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'l10n_master'
|
||||
- 'gh-pages'
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
|
||||
jobs:
|
||||
cloc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up cloc
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt -y install cloc
|
||||
|
||||
- name: Print lines of code
|
||||
run: cloc --include-lang TypeScript,JavaScript,HTML,Sass,CSS --vcs git
|
||||
|
||||
ubuntu:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '10.x'
|
||||
|
||||
- name: Print environment
|
||||
run: |
|
||||
whoami
|
||||
node --version
|
||||
npm --version
|
||||
gulp --version
|
||||
docker --version
|
||||
echo "GitHub ref: $GITHUB_REF"
|
||||
echo "GitHub event: $GITHUB_EVENT"
|
||||
env:
|
||||
GITHUB_REF: ${{ github.ref }}
|
||||
GITHUB_EVENT: ${{ github.event_name }}
|
||||
|
||||
- name: Log into docker
|
||||
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
||||
run: echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Setup Docker Trust
|
||||
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
||||
run: |
|
||||
mkdir -p ~/.docker/trust/private
|
||||
|
||||
echo "${{ secrets.DOCKER_DELEGATION_KEY }}" > ~/.docker/trust/private/$DOCKER_DELEGATION_KEY_ID.key
|
||||
echo "${{ secrets.DOCKER_REPO_WEB_KEY }}" > ~/.docker/trust/private/$DOCKER_WEB_KEY_ID.key
|
||||
env:
|
||||
DOCKER_DELEGATION_KEY_ID: "5702b22123e058cbd96a7a43000cb981ae98ef3f2f4aa34138ab3dc1d011e446"
|
||||
DOCKER_WEB_KEY_ID: "0f88641697187f42a31b584897cd4edfe80360a5209122d9e7f71af17a6422e4"
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Restore
|
||||
run: dotnet tool restore
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
chmod +x ./build.sh
|
||||
./build.sh
|
||||
|
||||
- name: Tag dev
|
||||
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
||||
run: ./build.sh tag dev
|
||||
|
||||
- name: Tag beta
|
||||
if: github.event_name == 'release'
|
||||
run: ./build.sh tag beta
|
||||
|
||||
- name: Tag version
|
||||
if: github.event_name == 'release'
|
||||
run: ./build.sh tag $($env:RELEASE_TAG_NAME.trimStart('v'))
|
||||
shell: pwsh
|
||||
env:
|
||||
RELEASE_TAG_NAME: ${{ github.event.release.tag_name }}
|
||||
|
||||
- name: List docker images
|
||||
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
||||
run: docker images
|
||||
|
||||
- name: Push dev images
|
||||
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
||||
run: ./build.sh push dev
|
||||
env:
|
||||
DOCKER_CONTENT_TRUST: 1
|
||||
DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ secrets.DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE }}
|
||||
|
||||
- name: Push beta images
|
||||
if: github.event_name == 'release'
|
||||
run: ./build.sh push beta
|
||||
env:
|
||||
DOCKER_CONTENT_TRUST: 1
|
||||
DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ secrets.DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE }}
|
||||
|
||||
- name: Push latest images
|
||||
if: github.event_name == 'release'
|
||||
run: ./build.sh push latest
|
||||
env:
|
||||
DOCKER_CONTENT_TRUST: 1
|
||||
DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ secrets.DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE }}
|
||||
|
||||
- name: Push version images
|
||||
if: github.event_name == 'release'
|
||||
run: ./build.sh push $($env:RELEASE_TAG_NAME.trimStart('v'))
|
||||
shell: pwsh
|
||||
env:
|
||||
RELEASE_TAG_NAME: ${{ github.event.release.tag_name }}
|
||||
DOCKER_CONTENT_TRUST: 1
|
||||
DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ secrets.DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE }}
|
||||
|
||||
- name: Log out of docker
|
||||
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
||||
run: docker logout
|
||||
|
||||
windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Set up NuGet
|
||||
uses: nuget/setup-nuget@v1
|
||||
with:
|
||||
nuget-version: 'latest'
|
||||
|
||||
- name: Set up MSBuild
|
||||
uses: microsoft/setup-msbuild@v1
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '10.x'
|
||||
|
||||
- name: Print environment
|
||||
run: |
|
||||
nuget help
|
||||
msbuild -version
|
||||
dotnet --info
|
||||
node --version
|
||||
npm --version
|
||||
Write-Output "GitHub ref: $env:GITHUB_REF"
|
||||
Write-Output "GitHub event: $env:GITHUB_EVENT"
|
||||
shell: pwsh
|
||||
env:
|
||||
GITHUB_REF: ${{ github.ref }}
|
||||
GITHUB_EVENT: ${{ github.event_name }}
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: npm install
|
||||
run: npm install
|
||||
|
||||
- name: npm build
|
||||
run: npm run build:prod
|
||||
|
||||
25
README.md
25
README.md
@@ -27,6 +27,8 @@
|
||||
|
||||
### Run the app
|
||||
|
||||
For local development, run the app with:
|
||||
|
||||
```
|
||||
npm install
|
||||
npm run build:watch
|
||||
@@ -39,6 +41,7 @@ await apiService.setUrls({
|
||||
base: isDev ? null : window.location.origin,
|
||||
api: isDev ? 'http://mylocalapi' : null,
|
||||
identity: isDev ? 'http://mylocalidentity' : null,
|
||||
events: isDev ? 'http://mylocalevents' : null,
|
||||
});
|
||||
```
|
||||
|
||||
@@ -49,9 +52,31 @@ await apiService.setUrls({
|
||||
base: null,
|
||||
api: 'https://api.bitwarden.com',
|
||||
identity: 'https://identity.bitwarden.com',
|
||||
events: 'https://events.bitwarden.com',
|
||||
});
|
||||
```
|
||||
|
||||
And note to run the app with:
|
||||
|
||||
```
|
||||
npm install
|
||||
npm run build:prod:watch
|
||||
```
|
||||
|
||||
## Common Issues:
|
||||
|
||||
### CORS
|
||||
|
||||
If you run the frontend and receive a notification after attempting to login that says:
|
||||
```
|
||||
An error has occurred.
|
||||
NetworkError when attempting to fetch resource.
|
||||
```
|
||||
And in the console:
|
||||
```
|
||||
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://api.bitwarden.com/accounts/prelogin. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).
|
||||
```
|
||||
This means that you are having a CORS header issue. This can be mitigated by using a CORS header changing extension in your browser such as [this one.](https://mybrowseraddon.com/access-control-allow-origin.html)
|
||||
|
||||
## Contribute
|
||||
|
||||
|
||||
2
jslib
2
jslib
Submodule jslib updated: abb54f0073...6ac6df75d7
5
package-lock.json
generated
5
package-lock.json
generated
@@ -1552,6 +1552,11 @@
|
||||
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
|
||||
"dev": true
|
||||
},
|
||||
"browser-process-hrtime": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz",
|
||||
"integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow=="
|
||||
},
|
||||
"browserify-aes": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "bitwarden-web",
|
||||
"version": "2.17.1",
|
||||
"version": "2.18.0",
|
||||
"license": "GPL-3.0",
|
||||
"repository": "https://github.com/bitwarden/web",
|
||||
"scripts": {
|
||||
@@ -80,6 +80,7 @@
|
||||
"big-integer": "1.6.36",
|
||||
"bootstrap": "4.3.1",
|
||||
"braintree-web-drop-in": "1.13.0",
|
||||
"browser-process-hrtime": "1.0.0",
|
||||
"core-js": "2.6.2",
|
||||
"duo_web_sdk": "git+https://github.com/duosecurity/duo_web_sdk.git#410a9186cc34663c4913b17d6528067cd3331f1d",
|
||||
"font-awesome": "4.7.0",
|
||||
|
||||
34
src/app/accounts/accept-emergency.component.html
Normal file
34
src/app/accounts/accept-emergency.component.html
Normal file
@@ -0,0 +1,34 @@
|
||||
<div class="mt-5 d-flex justify-content-center" *ngIf="loading">
|
||||
<div>
|
||||
<img src="../../images/logo-dark@2x.png" class="mb-4 logo" alt="Bitwarden">
|
||||
<p class="text-center">
|
||||
<i class="fa fa-spinner fa-spin fa-2x text-muted" title="{{'loading' | i18n}}" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{'loading' | i18n}}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container" *ngIf="!loading && !authed">
|
||||
<div class="row justify-content-md-center mt-5">
|
||||
<div class="col-5">
|
||||
<p class="lead text-center mb-4">{{'emergencyAccess' | i18n}}</p>
|
||||
<div class="card d-block">
|
||||
<div class="card-body">
|
||||
<p class="text-center">
|
||||
{{name}}
|
||||
</p>
|
||||
<p>{{'acceptEmergencyAccess' | i18n}}</p>
|
||||
<hr>
|
||||
<div class="d-flex">
|
||||
<a routerLink="/" [queryParams]="{email: email}" class="btn btn-primary btn-block">
|
||||
{{'logIn' | i18n}}
|
||||
</a>
|
||||
<a routerLink="/register" [queryParams]="{email: email}"
|
||||
class="btn btn-primary btn-block ml-2 mt-0">
|
||||
{{'createAccount' | i18n}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
93
src/app/accounts/accept-emergency.component.ts
Normal file
93
src/app/accounts/accept-emergency.component.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import {
|
||||
ActivatedRoute,
|
||||
Router,
|
||||
} from '@angular/router';
|
||||
|
||||
import {
|
||||
Toast,
|
||||
ToasterService,
|
||||
} from 'angular2-toaster';
|
||||
|
||||
import { ApiService } from 'jslib/abstractions/api.service';
|
||||
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||
import { StateService } from 'jslib/abstractions/state.service';
|
||||
import { UserService } from 'jslib/abstractions/user.service';
|
||||
import { EmergencyAccessAcceptRequest } from 'jslib/models/request/emergencyAccessAcceptRequest';
|
||||
|
||||
@Component({
|
||||
selector: 'app-accept-emergency',
|
||||
templateUrl: 'accept-emergency.component.html',
|
||||
})
|
||||
export class AcceptEmergencyComponent implements OnInit {
|
||||
loading = true;
|
||||
authed = false;
|
||||
name: string;
|
||||
email: string;
|
||||
actionPromise: Promise<any>;
|
||||
|
||||
constructor(private router: Router, private toasterService: ToasterService,
|
||||
private i18nService: I18nService, private route: ActivatedRoute,
|
||||
private apiService: ApiService, private userService: UserService,
|
||||
private stateService: StateService) { }
|
||||
|
||||
ngOnInit() {
|
||||
let fired = false;
|
||||
this.route.queryParams.subscribe(async (qParams) => {
|
||||
if (fired) {
|
||||
return;
|
||||
}
|
||||
fired = true;
|
||||
await this.stateService.remove('emergencyInvitation');
|
||||
let error = qParams.id == null || qParams.name == null || qParams.email == null || qParams.token == null;
|
||||
let errorMessage: string = null;
|
||||
if (!error) {
|
||||
this.authed = await this.userService.isAuthenticated();
|
||||
if (this.authed) {
|
||||
const request = new EmergencyAccessAcceptRequest();
|
||||
request.token = qParams.token;
|
||||
try {
|
||||
this.actionPromise = this.apiService.postEmergencyAccessAccept(qParams.id, request);
|
||||
await this.actionPromise;
|
||||
const toast: Toast = {
|
||||
type: 'success',
|
||||
title: this.i18nService.t('inviteAccepted'),
|
||||
body: this.i18nService.t('emergencyInviteAcceptedDesc'),
|
||||
timeout: 10000,
|
||||
};
|
||||
this.toasterService.popAsync(toast);
|
||||
this.router.navigate(['/vault']);
|
||||
} catch (e) {
|
||||
error = true;
|
||||
errorMessage = e.message;
|
||||
}
|
||||
} else {
|
||||
await this.stateService.save('emergencyInvitation', qParams);
|
||||
this.email = qParams.email;
|
||||
this.name = qParams.name;
|
||||
if (this.name != null) {
|
||||
// Fix URL encoding of space issue with Angular
|
||||
this.name = this.name.replace(/\+/g, ' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (error) {
|
||||
const toast: Toast = {
|
||||
type: 'error',
|
||||
title: null,
|
||||
body: errorMessage != null ? this.i18nService.t('emergencyInviteAcceptFailedShort', errorMessage) :
|
||||
this.i18nService.t('emergencyInviteAcceptFailed'),
|
||||
timeout: 10000,
|
||||
};
|
||||
this.toasterService.popAsync(toast);
|
||||
this.router.navigate(['/']);
|
||||
}
|
||||
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -52,9 +52,12 @@ export class LoginComponent extends BaseLoginComponent {
|
||||
}
|
||||
|
||||
async goAfterLogIn() {
|
||||
const invite = await this.stateService.get<any>('orgInvitation');
|
||||
if (invite != null) {
|
||||
this.router.navigate(['accept-organization'], { queryParams: invite });
|
||||
const orgInvite = await this.stateService.get<any>('orgInvitation');
|
||||
const emergencyInvite = await this.stateService.get<any>('emergencyInvitation');
|
||||
if (orgInvite != null) {
|
||||
this.router.navigate(['accept-organization'], { queryParams: orgInvite });
|
||||
} else if (emergencyInvite != null) {
|
||||
this.router.navigate(['accept-emergency'], { queryParams: emergencyInvite });
|
||||
} else {
|
||||
const loginRedirect = await this.stateService.get<any>('loginRedirect');
|
||||
if (loginRedirect != null) {
|
||||
|
||||
@@ -54,7 +54,7 @@ export class SsoComponent extends BaseSsoComponent {
|
||||
async submit() {
|
||||
await this.storageService.save(IdentifierStorageKey, this.identifier);
|
||||
if (this.clientId === 'browser') {
|
||||
document.cookie = `ssoHandOffMessage=${this.i18nService.t('ssoHandOff')};SameSite=strict`
|
||||
document.cookie = `ssoHandOffMessage=${this.i18nService.t('ssoHandOff')};SameSite=strict`;
|
||||
}
|
||||
super.submit();
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { FrontendLayoutComponent } from './layouts/frontend-layout.component';
|
||||
import { OrganizationLayoutComponent } from './layouts/organization-layout.component';
|
||||
import { UserLayoutComponent } from './layouts/user-layout.component';
|
||||
|
||||
import { AcceptEmergencyComponent } from './accounts/accept-emergency.component';
|
||||
import { AcceptOrganizationComponent } from './accounts/accept-organization.component';
|
||||
import { HintComponent } from './accounts/hint.component';
|
||||
import { LockComponent } from './accounts/lock.component';
|
||||
@@ -90,7 +91,10 @@ import { UnauthGuardService } from './services/unauth-guard.service';
|
||||
|
||||
import { AuthGuardService } from 'jslib/angular/services/auth-guard.service';
|
||||
|
||||
import { OrganizationUserType } from 'jslib/enums/organizationUserType';
|
||||
import { Permissions } from 'jslib/enums/permissions';
|
||||
|
||||
import { EmergencyAccessViewComponent } from './settings/emergency-access-view.component';
|
||||
import { EmergencyAccessComponent } from './settings/emergency-access.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@@ -125,6 +129,11 @@ const routes: Routes = [
|
||||
component: AcceptOrganizationComponent,
|
||||
data: { titleId: 'joinOrganization' },
|
||||
},
|
||||
{
|
||||
path: 'accept-emergency',
|
||||
component: AcceptEmergencyComponent,
|
||||
data: { titleId: 'acceptEmergency' },
|
||||
},
|
||||
{ path: 'recover', pathMatch: 'full', redirectTo: 'recover-2fa' },
|
||||
{
|
||||
path: 'recover-2fa',
|
||||
@@ -144,11 +153,11 @@ const routes: Routes = [
|
||||
canActivate: [UnauthGuardService],
|
||||
data: { titleId: 'deleteAccount' },
|
||||
},
|
||||
{
|
||||
/*{
|
||||
path: 'send/:sendId/:key',
|
||||
component: AccessComponent,
|
||||
data: { title: 'Bitwarden Send' },
|
||||
},
|
||||
},*/
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -157,7 +166,7 @@ const routes: Routes = [
|
||||
canActivate: [AuthGuardService],
|
||||
children: [
|
||||
{ path: 'vault', component: VaultComponent, data: { titleId: 'myVault' } },
|
||||
// { path: 'sends', component: SendComponent, data: { title: 'Send' } },
|
||||
{ path: 'sends', component: SendComponent, data: { title: 'Send' } },
|
||||
{
|
||||
path: 'settings',
|
||||
component: SettingsComponent,
|
||||
@@ -180,6 +189,21 @@ const routes: Routes = [
|
||||
component: CreateOrganizationComponent,
|
||||
data: { titleId: 'newOrganization' },
|
||||
},
|
||||
{
|
||||
path: 'emergency-access',
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: EmergencyAccessComponent,
|
||||
data: { titleId: 'emergencyAccess'},
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
component: EmergencyAccessViewComponent,
|
||||
data: { titleId: 'emergencyAccess'},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -236,35 +260,75 @@ const routes: Routes = [
|
||||
path: 'tools',
|
||||
component: OrgToolsComponent,
|
||||
canActivate: [OrganizationTypeGuardService],
|
||||
data: { allowedTypes: [OrganizationUserType.Owner, OrganizationUserType.Admin] },
|
||||
data: { permissions: [Permissions.AccessImportExport, Permissions.AccessReports] },
|
||||
children: [
|
||||
{ path: '', pathMatch: 'full', redirectTo: 'import' },
|
||||
{ path: 'import', component: OrgImportComponent, data: { titleId: 'importData' } },
|
||||
{ path: 'export', component: OrgExportComponent, data: { titleId: 'exportVault' } },
|
||||
{
|
||||
path: '',
|
||||
pathMatch: 'full',
|
||||
redirectTo: 'import',
|
||||
},
|
||||
{
|
||||
path: 'import',
|
||||
component: OrgImportComponent,
|
||||
canActivate: [OrganizationTypeGuardService],
|
||||
data: {
|
||||
titleId: 'importData',
|
||||
permissions: [Permissions.AccessImportExport],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'export',
|
||||
component: OrgExportComponent,
|
||||
canActivate: [OrganizationTypeGuardService],
|
||||
data: {
|
||||
titleId: 'exportVault',
|
||||
permissions: [Permissions.AccessImportExport],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'exposed-passwords-report',
|
||||
component: OrgExposedPasswordsReportComponent,
|
||||
data: { titleId: 'exposedPasswordsReport' },
|
||||
canActivate: [OrganizationTypeGuardService],
|
||||
data: {
|
||||
titleId: 'exposedPasswordsReport',
|
||||
permissions: [Permissions.AccessReports],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'inactive-two-factor-report',
|
||||
component: OrgInactiveTwoFactorReportComponent,
|
||||
data: { titleId: 'inactive2faReport' },
|
||||
canActivate: [OrganizationTypeGuardService],
|
||||
data: {
|
||||
titleId: 'inactive2faReport',
|
||||
permissions: [Permissions.AccessReports],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'reused-passwords-report',
|
||||
component: OrgReusedPasswordsReportComponent,
|
||||
data: { titleId: 'reusedPasswordsReport' },
|
||||
canActivate: [OrganizationTypeGuardService],
|
||||
data: {
|
||||
titleId: 'reusedPasswordsReport',
|
||||
permissions: [Permissions.AccessReports],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'unsecured-websites-report',
|
||||
component: OrgUnsecuredWebsitesReportComponent,
|
||||
data: { titleId: 'unsecuredWebsitesReport' },
|
||||
canActivate: [OrganizationTypeGuardService],
|
||||
data: {
|
||||
titleId: 'unsecuredWebsitesReport',
|
||||
permissions: [Permissions.AccessReports],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'weak-passwords-report',
|
||||
component: OrgWeakPasswordsReportComponent,
|
||||
data: { titleId: 'weakPasswordsReport' },
|
||||
canActivate: [OrganizationTypeGuardService],
|
||||
data: {
|
||||
titleId: 'weakPasswordsReport',
|
||||
permissions: [Permissions.AccessReports],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -273,26 +337,73 @@ const routes: Routes = [
|
||||
component: OrgManageComponent,
|
||||
canActivate: [OrganizationTypeGuardService],
|
||||
data: {
|
||||
allowedTypes: [
|
||||
OrganizationUserType.Owner,
|
||||
OrganizationUserType.Admin,
|
||||
OrganizationUserType.Manager,
|
||||
permissions: [
|
||||
Permissions.ManageAssignedCollections,
|
||||
Permissions.ManageAllCollections,
|
||||
Permissions.AccessEventLogs,
|
||||
Permissions.ManageGroups,
|
||||
Permissions.ManageUsers,
|
||||
Permissions.ManagePolicies,
|
||||
],
|
||||
},
|
||||
children: [
|
||||
{ path: '', pathMatch: 'full', redirectTo: 'people' },
|
||||
{ path: 'collections', component: OrgManageCollectionsComponent, data: { titleId: 'collections' } },
|
||||
{ path: 'events', component: OrgEventsComponent, data: { titleId: 'eventLogs' } },
|
||||
{ path: 'groups', component: OrgGroupsComponent, data: { titleId: 'groups' } },
|
||||
{ path: 'people', component: OrgPeopleComponent, data: { titleId: 'people' } },
|
||||
{ path: 'policies', component: OrgPoliciesComponent, data: { titleId: 'policies' } },
|
||||
{
|
||||
path: '',
|
||||
pathMatch: 'full',
|
||||
redirectTo: 'people',
|
||||
},
|
||||
{
|
||||
path: 'collections',
|
||||
component: OrgManageCollectionsComponent,
|
||||
canActivate: [OrganizationTypeGuardService],
|
||||
data: {
|
||||
titleId: 'collections',
|
||||
permissions: [Permissions.ManageAssignedCollections, Permissions.ManageAllCollections],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'events',
|
||||
component: OrgEventsComponent,
|
||||
canActivate: [OrganizationTypeGuardService],
|
||||
data: {
|
||||
titleId: 'eventLogs',
|
||||
permissions: [Permissions.AccessEventLogs],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'groups',
|
||||
component: OrgGroupsComponent,
|
||||
canActivate: [OrganizationTypeGuardService],
|
||||
data: {
|
||||
titleId: 'groups',
|
||||
permissions: [Permissions.ManageGroups],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'people',
|
||||
component: OrgPeopleComponent,
|
||||
canActivate: [OrganizationTypeGuardService],
|
||||
data: {
|
||||
titleId: 'people',
|
||||
permissions: [Permissions.ManageUsers],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'policies',
|
||||
component: OrgPoliciesComponent,
|
||||
canActivate: [OrganizationTypeGuardService],
|
||||
data: {
|
||||
titleId: 'policies',
|
||||
permissions: [Permissions.ManagePolicies],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'settings',
|
||||
component: OrgSettingsComponent,
|
||||
canActivate: [OrganizationTypeGuardService],
|
||||
data: { allowedTypes: [OrganizationUserType.Owner] },
|
||||
data: { permissions: [Permissions.ManageOrganization] },
|
||||
children: [
|
||||
{ path: '', pathMatch: 'full', redirectTo: 'account' },
|
||||
{ path: 'account', component: OrgAccountComponent, data: { titleId: 'myOrganization' } },
|
||||
@@ -317,6 +428,7 @@ const routes: Routes = [
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes, {
|
||||
useHash: true,
|
||||
paramsInheritanceStrategy: 'always',
|
||||
/*enableTracing: true,*/
|
||||
})],
|
||||
exports: [RouterModule],
|
||||
|
||||
@@ -27,6 +27,7 @@ import { NavbarComponent } from './layouts/navbar.component';
|
||||
import { OrganizationLayoutComponent } from './layouts/organization-layout.component';
|
||||
import { UserLayoutComponent } from './layouts/user-layout.component';
|
||||
|
||||
import { AcceptEmergencyComponent } from './accounts/accept-emergency.component';
|
||||
import { AcceptOrganizationComponent } from './accounts/accept-organization.component';
|
||||
import { HintComponent } from './accounts/hint.component';
|
||||
import { LockComponent } from './accounts/lock.component';
|
||||
@@ -112,6 +113,12 @@ import { CreateOrganizationComponent } from './settings/create-organization.comp
|
||||
import { DeauthorizeSessionsComponent } from './settings/deauthorize-sessions.component';
|
||||
import { DeleteAccountComponent } from './settings/delete-account.component';
|
||||
import { DomainRulesComponent } from './settings/domain-rules.component';
|
||||
import { EmergencyAccessAddEditComponent } from './settings/emergency-access-add-edit.component';
|
||||
import { EmergencyAccessComponent } from './settings/emergency-access.component';
|
||||
import { EmergencyAccessConfirmComponent } from './settings/emergency-access-confirm.component';
|
||||
import { EmergencyAccessTakeoverComponent } from './settings/emergency-access-takeover.component';
|
||||
import { EmergencyAccessViewComponent } from './settings/emergency-access-view.component';
|
||||
import { EmergencyAddEditComponent } from './settings/emergency-add-edit.component';
|
||||
import { LinkSsoComponent } from './settings/link-sso.component';
|
||||
import { OptionsComponent } from './settings/options.component';
|
||||
import { OrganizationPlansComponent } from './settings/organization-plans.component';
|
||||
@@ -258,6 +265,7 @@ registerLocaleData(localeZhTw, 'zh-TW');
|
||||
],
|
||||
declarations: [
|
||||
A11yTitleDirective,
|
||||
AcceptEmergencyComponent,
|
||||
AccessComponent,
|
||||
AcceptOrganizationComponent,
|
||||
AccountComponent,
|
||||
@@ -295,6 +303,12 @@ registerLocaleData(localeZhTw, 'zh-TW');
|
||||
DeleteOrganizationComponent,
|
||||
DomainRulesComponent,
|
||||
DownloadLicenseComponent,
|
||||
EmergencyAccessAddEditComponent,
|
||||
EmergencyAccessComponent,
|
||||
EmergencyAccessConfirmComponent,
|
||||
EmergencyAccessTakeoverComponent,
|
||||
EmergencyAccessViewComponent,
|
||||
EmergencyAddEditComponent,
|
||||
ExportComponent,
|
||||
ExposedPasswordsReportComponent,
|
||||
FallbackSrcDirective,
|
||||
@@ -409,6 +423,10 @@ registerLocaleData(localeZhTw, 'zh-TW');
|
||||
DeauthorizeSessionsComponent,
|
||||
DeleteAccountComponent,
|
||||
DeleteOrganizationComponent,
|
||||
EmergencyAccessAddEditComponent,
|
||||
EmergencyAccessConfirmComponent,
|
||||
EmergencyAccessTakeoverComponent,
|
||||
EmergencyAddEditComponent,
|
||||
FolderAddEditComponent,
|
||||
ModalComponent,
|
||||
OrgAddEditComponent,
|
||||
|
||||
@@ -8,11 +8,9 @@
|
||||
<li class="nav-item" routerLinkActive="active">
|
||||
<a class="nav-link" routerLink="/vault">{{'myVault' | i18n}}</a>
|
||||
</li>
|
||||
<!--
|
||||
<li class="nav-item" routerLinkActive="active">
|
||||
<!--<li class="nav-item" routerLinkActive="active">
|
||||
<a class="nav-link" routerLink="/sends">Send</a>
|
||||
</li>
|
||||
-->
|
||||
</li>-->
|
||||
<li class="nav-item" routerLinkActive="active">
|
||||
<a class="nav-link" routerLink="/tools">{{'tools' | i18n}}</a>
|
||||
</li>
|
||||
|
||||
@@ -15,21 +15,21 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="nav nav-tabs" *ngIf="organization.isManager">
|
||||
<ul class="nav nav-tabs" *ngIf="showMenuBar">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" routerLink="vault" routerLinkActive="active">
|
||||
<i class="fa fa-lock" aria-hidden="true"></i>
|
||||
{{'vault' | i18n}}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" routerLink="manage" routerLinkActive="active">
|
||||
<li class="nav-item" *ngIf="showManageTab">
|
||||
<a class="nav-link" [routerLink]="manageRoute" routerLinkActive="active">
|
||||
<i class="fa fa-sliders" aria-hidden="true"></i>
|
||||
{{'manage' | i18n}}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item" *ngIf="organization.isAdmin">
|
||||
<a class="nav-link" routerLink="tools" routerLinkActive="active">
|
||||
<li class="nav-item" *ngIf="showToolsTab">
|
||||
<a class="nav-link" [routerLink]="toolsRoute" routerLinkActive="active">
|
||||
<i class="fa fa-wrench" aria-hidden="true"></i>
|
||||
{{'tools' | i18n}}
|
||||
</a>
|
||||
@@ -43,10 +43,10 @@
|
||||
</ul>
|
||||
</div>
|
||||
<div class="ml-auto d-flex align-items-center">
|
||||
<button class="btn btn-primary" (click)="goToEnterprisePortal()" #enterpriseBtn
|
||||
[appApiAction]="enterpriseTokenPromise" *ngIf="organization.useBusinessPortal">
|
||||
<i class="fa fa-bank fa-fw" [hidden]="enterpriseBtn.loading" aria-hidden="true"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-fw" [hidden]="!enterpriseBtn.loading" title="{{'loading' | i18n}}"
|
||||
<button class="btn btn-primary" (click)="goToBusinessPortal()" #businessBtn
|
||||
[appApiAction]="businessTokenPromise" *ngIf="showBusinessPortalButton">
|
||||
<i class="fa fa-bank fa-fw" [hidden]="businessBtn.loading" aria-hidden="true"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-fw" [hidden]="!businessBtn.loading" title="{{'loading' | i18n}}"
|
||||
aria-hidden="true"></i>
|
||||
{{'businessPortal' | i18n}} →
|
||||
</button>
|
||||
|
||||
@@ -24,9 +24,9 @@ const BroadcasterSubscriptionId = 'OrganizationLayoutComponent';
|
||||
})
|
||||
export class OrganizationLayoutComponent implements OnInit, OnDestroy {
|
||||
organization: Organization;
|
||||
enterpriseTokenPromise: Promise<any>;
|
||||
businessTokenPromise: Promise<any>;
|
||||
private organizationId: string;
|
||||
private enterpriseUrl: string;
|
||||
private businessUrl: string;
|
||||
|
||||
constructor(private route: ActivatedRoute, private userService: UserService,
|
||||
private broadcasterService: BroadcasterService, private ngZone: NgZone,
|
||||
@@ -34,11 +34,11 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy {
|
||||
private environmentService: EnvironmentService) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.enterpriseUrl = 'https://portal.bitwarden.com';
|
||||
this.businessUrl = 'https://portal.bitwarden.com';
|
||||
if (this.environmentService.enterpriseUrl != null) {
|
||||
this.enterpriseUrl = this.environmentService.enterpriseUrl;
|
||||
this.businessUrl = this.environmentService.enterpriseUrl;
|
||||
} else if (this.environmentService.baseUrl != null) {
|
||||
this.enterpriseUrl = this.environmentService.baseUrl + '/portal';
|
||||
this.businessUrl = this.environmentService.baseUrl + '/portal';
|
||||
}
|
||||
|
||||
document.body.classList.remove('layout_frontend');
|
||||
@@ -65,19 +65,68 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy {
|
||||
this.organization = await this.userService.getOrganization(this.organizationId);
|
||||
}
|
||||
|
||||
async goToEnterprisePortal() {
|
||||
if (this.enterpriseTokenPromise != null) {
|
||||
async goToBusinessPortal() {
|
||||
if (this.businessTokenPromise != null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.enterpriseTokenPromise = this.apiService.getEnterprisePortalSignInToken();
|
||||
const token = await this.enterpriseTokenPromise;
|
||||
this.businessTokenPromise = this.apiService.getEnterprisePortalSignInToken();
|
||||
const token = await this.businessTokenPromise;
|
||||
if (token != null) {
|
||||
const userId = await this.userService.getUserId();
|
||||
this.platformUtilsService.launchUri(this.enterpriseUrl + '/login?userId=' + userId +
|
||||
this.platformUtilsService.launchUri(this.businessUrl + '/login?userId=' + userId +
|
||||
'&token=' + (window as any).encodeURIComponent(token) + '&organizationId=' + this.organization.id);
|
||||
}
|
||||
} catch { }
|
||||
this.enterpriseTokenPromise = null;
|
||||
this.businessTokenPromise = null;
|
||||
}
|
||||
|
||||
get showMenuBar() {
|
||||
return this.showManageTab || this.showToolsTab || this.organization.isOwner;
|
||||
}
|
||||
|
||||
get showManageTab(): boolean {
|
||||
return this.organization.canManageUsers ||
|
||||
this.organization.canManageAssignedCollections ||
|
||||
this.organization.canManageAllCollections ||
|
||||
this.organization.canManageGroups ||
|
||||
this.organization.canManagePolicies ||
|
||||
this.organization.canAccessEventLogs;
|
||||
}
|
||||
|
||||
get showToolsTab(): boolean {
|
||||
return this.organization.canAccessImportExport || this.organization.canAccessReports;
|
||||
}
|
||||
|
||||
get showBusinessPortalButton(): boolean {
|
||||
return this.organization.useBusinessPortal && this.organization.canAccessBusinessPortal;
|
||||
}
|
||||
|
||||
get toolsRoute(): string {
|
||||
return this.organization.canAccessImportExport ?
|
||||
'tools/import' :
|
||||
'tools/exposed-passwords-report';
|
||||
}
|
||||
|
||||
get manageRoute(): string {
|
||||
let route: string;
|
||||
switch (true) {
|
||||
case this.organization.canManageUsers:
|
||||
route = 'manage/people';
|
||||
break;
|
||||
case this.organization.canManageAssignedCollections || this.organization.canManageAllCollections:
|
||||
route = 'manage/collections';
|
||||
break;
|
||||
case this.organization.canManageGroups:
|
||||
route = 'manage/groups';
|
||||
break;
|
||||
case this.organization.canManagePolicies:
|
||||
route = 'manage/policies';
|
||||
break;
|
||||
case this.organization.canAccessEventLogs:
|
||||
route = 'manage/events';
|
||||
break;
|
||||
}
|
||||
return route;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ export class CollectionsComponent implements OnInit {
|
||||
async load() {
|
||||
const organization = await this.userService.getOrganization(this.organizationId);
|
||||
let response: ListResponse<CollectionResponse>;
|
||||
if (organization.isAdmin) {
|
||||
if (organization.canManageAllCollections) {
|
||||
response = await this.apiService.getCollections(this.organizationId);
|
||||
} else {
|
||||
response = await this.apiService.getUserCollections();
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
<span *ngIf="u.type === organizationUserType.Admin">{{'admin' | i18n}}</span>
|
||||
<span *ngIf="u.type === organizationUserType.Manager">{{'manager' | i18n}}</span>
|
||||
<span *ngIf="u.type === organizationUserType.User">{{'user' | i18n}}</span>
|
||||
<span *ngIf="u.type === organizationUserType.Custom">{{'custom' | i18n}}</span>
|
||||
</td>
|
||||
<td class="text-center" *ngIf="entity === 'collection'">
|
||||
<input type="checkbox" [(ngModel)]="u.hidePasswords"
|
||||
|
||||
@@ -5,22 +5,23 @@
|
||||
<div class="card-header">{{'manage' | i18n}}</div>
|
||||
<div class="list-group list-group-flush">
|
||||
<a routerLink="people" class="list-group-item" routerLinkActive="active"
|
||||
*ngIf="organization.isAdmin">
|
||||
*ngIf="organization.canManageUsers">
|
||||
{{'people' | i18n}}
|
||||
</a>
|
||||
<a routerLink="collections" class="list-group-item" routerLinkActive="active">
|
||||
<a routerLink="collections" class="list-group-item" routerLinkActive="active"
|
||||
*ngIf="organization.canManageAssignedCollections || organization.canManageAllCollections">
|
||||
{{'collections' | i18n}}
|
||||
</a>
|
||||
<a routerLink="groups" class="list-group-item" routerLinkActive="active"
|
||||
*ngIf="organization.isAdmin && accessGroups">
|
||||
*ngIf="organization.canManageGroups && accessGroups">
|
||||
{{'groups' | i18n}}
|
||||
</a>
|
||||
<a routerLink="policies" class="list-group-item" routerLinkActive="active"
|
||||
*ngIf="organization.isAdmin && accessPolicies">
|
||||
*ngIf="organization.canManagePolicies && accessPolicies">
|
||||
{{'policies' | i18n}}
|
||||
</a>
|
||||
<a routerLink="events" class="list-group-item" routerLinkActive="active"
|
||||
*ngIf="organization.isAdmin && accessEvents">
|
||||
*ngIf="organization.canAccessEventLogs && accessEvents">
|
||||
{{'eventLogs' | i18n}}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
<span *ngIf="u.type === organizationUserType.Admin">{{'admin' | i18n}}</span>
|
||||
<span *ngIf="u.type === organizationUserType.Manager">{{'manager' | i18n}}</span>
|
||||
<span *ngIf="u.type === organizationUserType.User">{{'user' | i18n}}</span>
|
||||
<span *ngIf="u.type === organizationUserType.Custom">{{'custom' | i18n}}</span>
|
||||
</td>
|
||||
<td class="table-list-options">
|
||||
<div class="dropdown" appListDropdown>
|
||||
|
||||
@@ -79,7 +79,7 @@ export class PeopleComponent implements OnInit {
|
||||
this.route.parent.parent.params.subscribe(async (params) => {
|
||||
this.organizationId = params.organizationId;
|
||||
const organization = await this.userService.getOrganization(this.organizationId);
|
||||
if (!organization.isAdmin) {
|
||||
if (!organization.canManageUsers) {
|
||||
this.router.navigate(['../collections'], { relativeTo: this.route });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<app-callout [type]="'warning'">
|
||||
<app-callout *ngIf="userCanAccessBusinessPortal" [type]="'warning'">
|
||||
<p>{{'webPoliciesDeprecationWarning' | i18n}}</p>
|
||||
<button type="button" class="btn btn-outline-secondary"
|
||||
(click)="goToEnterprisePortal()">{{'businessPortal' | i18n}}</button>
|
||||
|
||||
@@ -37,13 +37,14 @@ export class PoliciesComponent implements OnInit {
|
||||
|
||||
// Remove when removing deprecation warning
|
||||
enterpriseTokenPromise: Promise<any>;
|
||||
userCanAccessBusinessPortal = false;
|
||||
|
||||
private enterpriseUrl: string;
|
||||
|
||||
private modal: ModalComponent = null;
|
||||
private orgPolicies: PolicyResponse[];
|
||||
private policiesEnabledMap: Map<PolicyType, boolean> = new Map<PolicyType, boolean>();
|
||||
|
||||
|
||||
constructor(private apiService: ApiService, private route: ActivatedRoute,
|
||||
private i18nService: I18nService, private componentFactoryResolver: ComponentFactoryResolver,
|
||||
private platformUtilsService: PlatformUtilsService, private userService: UserService,
|
||||
@@ -57,6 +58,7 @@ export class PoliciesComponent implements OnInit {
|
||||
this.router.navigate(['/organizations', this.organizationId]);
|
||||
return;
|
||||
}
|
||||
this.userCanAccessBusinessPortal = organization.canAccessBusinessPortal;
|
||||
this.policies = [
|
||||
{
|
||||
name: this.i18nService.t('twoStepLogin'),
|
||||
@@ -93,8 +95,37 @@ export class PoliciesComponent implements OnInit {
|
||||
enabled: false,
|
||||
display: organization.useSso,
|
||||
},
|
||||
{
|
||||
name: this.i18nService.t('personalOwnership'),
|
||||
description: this.i18nService.t('personalOwnershipPolicyDesc'),
|
||||
type: PolicyType.PersonalOwnership,
|
||||
enabled: false,
|
||||
display: true,
|
||||
},
|
||||
];
|
||||
await this.load();
|
||||
|
||||
// Handle policies component launch from Event message
|
||||
const queryParamsSub = this.route.queryParams.subscribe(async (qParams) => {
|
||||
if (qParams.policyId != null) {
|
||||
const policyIdFromEvents: string = qParams.policyId;
|
||||
for (const orgPolicy of this.orgPolicies) {
|
||||
if (orgPolicy.id === policyIdFromEvents) {
|
||||
for (let i = 0; i < this.policies.length; i++) {
|
||||
if (this.policies[i].type === orgPolicy.type) {
|
||||
this.edit(this.policies[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (queryParamsSub != null) {
|
||||
queryParamsSub.unsubscribe();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Remove when removing deprecation warning
|
||||
|
||||
@@ -29,11 +29,14 @@
|
||||
{{'requireSsoExemption' | i18n}}
|
||||
</app-callout>
|
||||
</ng-container>
|
||||
<app-callout type="warning" *ngIf="type === policyType.PersonalOwnership">
|
||||
{{'personalOwnershipExemption' | i18n}}
|
||||
</app-callout>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="enabled" [(ngModel)]="enabled"
|
||||
name="Enabled">
|
||||
<label class="form-check-label" for="enabled">{{'enabled' | i18n}}</label>
|
||||
<label class="form-check-label" for="enabled">{{checkboxDesc}}</label>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="type === policyType.MasterPassword">
|
||||
|
||||
@@ -172,10 +172,16 @@ export class PolicyEditComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
get checkboxDesc(): string {
|
||||
return this.type === PolicyType.PersonalOwnership ? this.i18nService.t('personalOwnershipCheckboxDesc') :
|
||||
this.i18nService.t('enabled');
|
||||
}
|
||||
|
||||
private preValidate(): boolean {
|
||||
switch (this.type) {
|
||||
case PolicyType.RequireSso:
|
||||
if (!this.enabled) { // Don't need prevalidation checks if submitting to disable
|
||||
// Don't need prevalidation checks if submitting to disable
|
||||
if (!this.enabled) {
|
||||
return true;
|
||||
}
|
||||
// Have SingleOrg policy enabled?
|
||||
@@ -186,6 +192,19 @@ export class PolicyEditComponent implements OnInit {
|
||||
}
|
||||
return true;
|
||||
|
||||
case PolicyType.SingleOrg:
|
||||
// Don't need prevalidation checks if submitting to enable
|
||||
if (this.enabled) {
|
||||
return true;
|
||||
}
|
||||
// If RequireSso Policy is enabled prevent submittal
|
||||
if (this.policiesEnabledMap.has(PolicyType.RequireSso)
|
||||
&& this.policiesEnabledMap.get(PolicyType.RequireSso)) {
|
||||
this.toasterService.popAsync('error', null, this.i18nService.t('disableRequireSsoError'));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -63,8 +63,127 @@
|
||||
<small>{{'ownerDesc' | i18n}}</small>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check mt-2 form-check-block">
|
||||
<input class="form-check-input" type="radio" name="userType" id="userTypeCustom"
|
||||
[value]="organizationUserType.Custom" [(ngModel)]="type">
|
||||
<label class="form-check-label" for="userTypeCustom">
|
||||
{{'custom' | i18n}}
|
||||
<small>{{'customDesc' | i18n}}</small>
|
||||
</label>
|
||||
</div>
|
||||
<ng-container *ngIf="customUserTypeSelected">
|
||||
<h3 class="mt-4 d-flex">
|
||||
{{'permissions' | i18n}}
|
||||
</h3>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="mb-3">
|
||||
<label class="font-weight-bold mb-0">Manager Permissions</label>
|
||||
<hr class="my-0 mr-2" />
|
||||
<div class="form-group mb-0">
|
||||
<div class="form-check mt-1 form-check-block">
|
||||
<input class="form-check-input" type="checkbox" name="manageAssignedCollections"
|
||||
id="manageAssignedCollections"
|
||||
[(ngModel)]="permissions.manageAssignedCollections">
|
||||
<label class="form-check-label font-weight-normal"
|
||||
for="manageAssignedCollections">
|
||||
{{'manageAssignedCollections' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="mb-3">
|
||||
<label class="font-weight-bold mb-0">Admin Permissions</label>
|
||||
<hr class="my-0 mr-2" />
|
||||
<div class="form-group mb-0">
|
||||
<div class="form-check mt-1 form-check-block">
|
||||
<input class="form-check-input" type="checkbox" name="accessBusinessPortal"
|
||||
id="accessBusinessPortal" [(ngModel)]="permissions.accessBusinessPortal">
|
||||
<label class="form-check-label font-weight-normal" for="accessBusinessPortal">
|
||||
{{'accessBusinessPortal' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mb-0">
|
||||
<div class="form-check mt-1 form-check-block">
|
||||
<input class="form-check-input" type="checkbox" name="accessEventLogs"
|
||||
id="accessEventLogs" [(ngModel)]="permissions.accessEventLogs">
|
||||
<label class="form-check-label font-weight-normal" for="accessEventLogs">
|
||||
{{'accessEventLogs' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mb-0">
|
||||
<div class="form-check mt-1 form-check-block">
|
||||
<input class="form-check-input" type="checkbox" name="accessImportExport"
|
||||
id="accessImportExport" [(ngModel)]="permissions.accessImportExport">
|
||||
<label class="form-check-label font-weight-normal" for="accessImportExport">
|
||||
{{'accessImportExport' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mb-0">
|
||||
<div class="form-check mt-1 form-check-block">
|
||||
<input class="form-check-input" type="checkbox" name="accessReports"
|
||||
id="accessReports" [(ngModel)]="permissions.accessReports">
|
||||
<label class="form-check-label font-weight-normal" for="accessReports">
|
||||
{{'accessReports' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mb-0">
|
||||
<div class="form-check mt-1 form-check-block">
|
||||
<input class="form-check-input" type="checkbox" name="manageAllCollections"
|
||||
id="manageAllCollections" [(ngModel)]="permissions.manageAllCollections">
|
||||
<label class="form-check-label font-weight-normal" for="manageAllCollections">
|
||||
{{'manageAllCollections' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mb-0">
|
||||
<div class="form-check mt-1 form-check-block">
|
||||
<input class="form-check-input" type="checkbox" name="manageGroups"
|
||||
id="manageGroups" [(ngModel)]="permissions.manageGroups">
|
||||
<label class="form-check-label font-weight-normal" for="manageGroups">
|
||||
{{'manageGroups' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mb-0">
|
||||
<div class="form-check mt-1 form-check-block">
|
||||
<input class="form-check-input" type="checkbox" name="manageSso"
|
||||
id="managePolicies" [(ngModel)]="permissions.manageSso">
|
||||
<label class="form-check-label font-weight-normal" for="manageSso">
|
||||
{{'manageSso' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mb-0">
|
||||
<div class="form-check mt-1 form-check-block">
|
||||
<input class="form-check-input" type="checkbox" name="managePolicies"
|
||||
id="managePolicies" [(ngModel)]="permissions.managePolicies">
|
||||
<label class="form-check-label font-weight-normal" for="managePolicies">
|
||||
{{'managePolicies' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mb-0">
|
||||
<div class="form-check mt-1 form-check-block">
|
||||
<input class="form-check-input" type="checkbox" name="manageUsers"
|
||||
id="manageUsers" [(ngModel)]="permissions.manageUsers">
|
||||
<label class="form-check-label font-weight-normal" for="manageUsers">
|
||||
{{'manageUsers' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<h3 class="mt-4 d-flex">
|
||||
<div class="mb-2">
|
||||
<div class="mb-3">
|
||||
{{'accessControl' | i18n}}
|
||||
<a target="_blank" rel="noopener" appA11yTitle="{{'learnMore' | i18n}}"
|
||||
href="https://bitwarden.com/help/article/user-types-access-control/#access-control">
|
||||
|
||||
@@ -23,6 +23,7 @@ import { CollectionDetailsResponse } from 'jslib/models/response/collectionRespo
|
||||
import { CollectionView } from 'jslib/models/view/collectionView';
|
||||
|
||||
import { OrganizationUserType } from 'jslib/enums/organizationUserType';
|
||||
import { PermissionsApi } from 'jslib/models/api/permissionsApi';
|
||||
|
||||
@Component({
|
||||
selector: 'app-user-add-edit',
|
||||
@@ -40,12 +41,18 @@ export class UserAddEditComponent implements OnInit {
|
||||
title: string;
|
||||
emails: string;
|
||||
type: OrganizationUserType = OrganizationUserType.User;
|
||||
permissions = new PermissionsApi();
|
||||
showCustom = false;
|
||||
access: 'all' | 'selected' = 'selected';
|
||||
collections: CollectionView[] = [];
|
||||
formPromise: Promise<any>;
|
||||
deletePromise: Promise<any>;
|
||||
organizationUserType = OrganizationUserType;
|
||||
|
||||
get customUserTypeSelected(): boolean {
|
||||
return this.type === OrganizationUserType.Custom;
|
||||
}
|
||||
|
||||
constructor(private apiService: ApiService, private i18nService: I18nService,
|
||||
private analytics: Angulartics2, private toasterService: ToasterService,
|
||||
private collectionService: CollectionService, private platformUtilsService: PlatformUtilsService) { }
|
||||
@@ -61,6 +68,9 @@ export class UserAddEditComponent implements OnInit {
|
||||
const user = await this.apiService.getOrganizationUser(this.organizationId, this.organizationUserId);
|
||||
this.access = user.accessAll ? 'all' : 'selected';
|
||||
this.type = user.type;
|
||||
if (user.type === OrganizationUserType.Custom) {
|
||||
this.permissions = user.permissions;
|
||||
}
|
||||
if (user.collections != null && this.collections != null) {
|
||||
user.collections.forEach((s) => {
|
||||
const collection = this.collections.filter((c) => c.id === s.id);
|
||||
@@ -97,6 +107,40 @@ export class UserAddEditComponent implements OnInit {
|
||||
this.collections.forEach((c) => this.check(c, select));
|
||||
}
|
||||
|
||||
setRequestPermissions(p: PermissionsApi, clearPermissions: boolean) {
|
||||
p.accessBusinessPortal = clearPermissions ?
|
||||
false :
|
||||
this.permissions.accessBusinessPortal;
|
||||
p.accessEventLogs = this.permissions.accessEventLogs = clearPermissions ?
|
||||
false :
|
||||
this.permissions.accessEventLogs;
|
||||
p.accessImportExport = clearPermissions ?
|
||||
false :
|
||||
this.permissions.accessImportExport;
|
||||
p.accessReports = clearPermissions ?
|
||||
false :
|
||||
this.permissions.accessReports;
|
||||
p.manageAllCollections = clearPermissions ?
|
||||
false :
|
||||
this.permissions.manageAllCollections;
|
||||
p.manageAssignedCollections = clearPermissions ?
|
||||
false :
|
||||
this.permissions.manageAssignedCollections;
|
||||
p.manageGroups = clearPermissions ?
|
||||
false :
|
||||
this.permissions.manageGroups;
|
||||
p.manageSso = clearPermissions ?
|
||||
false :
|
||||
this.permissions.manageSso;
|
||||
p.managePolicies = clearPermissions ?
|
||||
false :
|
||||
this.permissions.managePolicies;
|
||||
p.manageUsers = clearPermissions ?
|
||||
false :
|
||||
this.permissions.manageUsers;
|
||||
return p;
|
||||
}
|
||||
|
||||
async submit() {
|
||||
let collections: SelectionReadOnlyRequest[] = null;
|
||||
if (this.access !== 'all') {
|
||||
@@ -110,6 +154,7 @@ export class UserAddEditComponent implements OnInit {
|
||||
request.accessAll = this.access === 'all';
|
||||
request.type = this.type;
|
||||
request.collections = collections;
|
||||
request.permissions = this.setRequestPermissions(request.permissions ?? new PermissionsApi(), request.type !== OrganizationUserType.Custom);
|
||||
this.formPromise = this.apiService.putOrganizationUser(this.organizationId, this.organizationUserId,
|
||||
request);
|
||||
} else {
|
||||
@@ -117,6 +162,7 @@ export class UserAddEditComponent implements OnInit {
|
||||
request.emails = this.emails.trim().split(/\s*,\s*/);
|
||||
request.accessAll = this.access === 'all';
|
||||
request.type = this.type;
|
||||
request.permissions = this.setRequestPermissions(request.permissions ?? new PermissionsApi(), request.type !== OrganizationUserType.Custom);
|
||||
request.collections = collections;
|
||||
this.formPromise = this.apiService.postOrganizationUserInvite(this.organizationId, request);
|
||||
}
|
||||
@@ -148,4 +194,5 @@ export class UserAddEditComponent implements OnInit {
|
||||
this.onDeletedUser.emit();
|
||||
} catch { }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@ import { EventType } from 'jslib/enums/eventType';
|
||||
templateUrl: '../../tools/export.component.html',
|
||||
})
|
||||
export class ExportComponent extends BaseExportComponent {
|
||||
organizationId: string;
|
||||
|
||||
constructor(cryptoService: CryptoService, i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService, exportService: ExportService,
|
||||
eventService: EventService, private route: ActivatedRoute) {
|
||||
|
||||
@@ -14,12 +14,15 @@ import {
|
||||
} from '../../tools/exposed-passwords-report.component';
|
||||
|
||||
import { CipherView } from 'jslib/models/view/cipherView';
|
||||
import { Cipher } from 'jslib/models/domain/cipher';
|
||||
|
||||
@Component({
|
||||
selector: 'app-exposed-passwords-report',
|
||||
templateUrl: '../../tools/exposed-passwords-report.component.html',
|
||||
})
|
||||
export class ExposedPasswordsReportComponent extends BaseExposedPasswordsReportComponent {
|
||||
manageableCiphers: Cipher[];
|
||||
|
||||
constructor(cipherService: CipherService, auditService: AuditService,
|
||||
componentFactoryResolver: ComponentFactoryResolver, messagingService: MessagingService,
|
||||
userService: UserService, private route: ActivatedRoute) {
|
||||
@@ -29,6 +32,7 @@ export class ExposedPasswordsReportComponent extends BaseExposedPasswordsReportC
|
||||
ngOnInit() {
|
||||
this.route.parent.parent.params.subscribe(async (params) => {
|
||||
this.organization = await this.userService.getOrganization(params.organizationId);
|
||||
this.manageableCiphers = await this.cipherService.getAll();
|
||||
super.ngOnInit();
|
||||
});
|
||||
}
|
||||
@@ -36,4 +40,8 @@ export class ExposedPasswordsReportComponent extends BaseExposedPasswordsReportC
|
||||
getAllCiphers(): Promise<CipherView[]> {
|
||||
return this.cipherService.getAllFromApiForOrganization(this.organization.id);
|
||||
}
|
||||
|
||||
canManageCipher(c: CipherView): boolean {
|
||||
return this.manageableCiphers.some(x => x.id === c.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import { CipherService } from 'jslib/abstractions/cipher.service';
|
||||
import { MessagingService } from 'jslib/abstractions/messaging.service';
|
||||
import { UserService } from 'jslib/abstractions/user.service';
|
||||
|
||||
import { Cipher } from 'jslib/models/domain/cipher';
|
||||
|
||||
import { CipherView } from 'jslib/models/view/cipherView';
|
||||
|
||||
import {
|
||||
@@ -19,6 +21,8 @@ import {
|
||||
templateUrl: '../../tools/reused-passwords-report.component.html',
|
||||
})
|
||||
export class ReusedPasswordsReportComponent extends BaseReusedPasswordsReportComponent {
|
||||
manageableCiphers: Cipher[];
|
||||
|
||||
constructor(cipherService: CipherService, componentFactoryResolver: ComponentFactoryResolver,
|
||||
messagingService: MessagingService, userService: UserService,
|
||||
private route: ActivatedRoute) {
|
||||
@@ -28,6 +32,7 @@ export class ReusedPasswordsReportComponent extends BaseReusedPasswordsReportCom
|
||||
async ngOnInit() {
|
||||
this.route.parent.parent.params.subscribe(async (params) => {
|
||||
this.organization = await this.userService.getOrganization(params.organizationId);
|
||||
this.manageableCiphers = await this.cipherService.getAll();
|
||||
await super.ngOnInit();
|
||||
});
|
||||
}
|
||||
@@ -35,4 +40,8 @@ export class ReusedPasswordsReportComponent extends BaseReusedPasswordsReportCom
|
||||
getAllCiphers(): Promise<CipherView[]> {
|
||||
return this.cipherService.getAllFromApiForOrganization(this.organization.id);
|
||||
}
|
||||
|
||||
canManageCipher(c: CipherView): boolean {
|
||||
return this.manageableCiphers.some(x => x.id === c.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +1,54 @@
|
||||
<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="import" class="list-group-item" routerLinkActive="active">
|
||||
{{'importData' | i18n}}
|
||||
</a>
|
||||
<a routerLink="export" class="list-group-item" routerLinkActive="active">
|
||||
{{'exportVault' | i18n}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-header d-flex">
|
||||
{{'reports' | i18n}}
|
||||
<div class="ml-auto">
|
||||
<a href="#" appStopClick class="badge badge-primary" *ngIf="!accessReports"
|
||||
(click)="upgradeOrganization()">
|
||||
{{'upgrade' | i18n}}
|
||||
<ng-container *ngIf="loading">
|
||||
<i class="fa fa-spinner fa-spin text-muted" title="{{'loading' | i18n}}" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{'loading' | i18n}}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!loading">
|
||||
<div class="row">
|
||||
<div class="col-3">
|
||||
<div class="card mb-4" *ngIf="organization.canAccessImportExport">
|
||||
<div class="card-header">{{'tools' | i18n}}</div>
|
||||
<div class="list-group list-group-flush">
|
||||
<a routerLink="import" class="list-group-item" routerLinkActive="active">
|
||||
{{'importData' | i18n}}
|
||||
</a>
|
||||
<a routerLink="export" class="list-group-item" routerLinkActive="active">
|
||||
{{'exportVault' | i18n}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group list-group-flush">
|
||||
<a routerLink="exposed-passwords-report" class="list-group-item" routerLinkActive="active">
|
||||
{{'exposedPasswordsReport' | i18n}}
|
||||
</a>
|
||||
<a routerLink="reused-passwords-report" class="list-group-item" routerLinkActive="active">
|
||||
{{'reusedPasswordsReport' | i18n}}
|
||||
</a>
|
||||
<a routerLink="weak-passwords-report" class="list-group-item" routerLinkActive="active">
|
||||
{{'weakPasswordsReport' | i18n}}
|
||||
</a>
|
||||
<a routerLink="unsecured-websites-report" class="list-group-item" routerLinkActive="active">
|
||||
{{'unsecuredWebsitesReport' | i18n}}
|
||||
</a>
|
||||
<a routerLink="inactive-two-factor-report" class="list-group-item" routerLinkActive="active">
|
||||
{{'inactive2faReport' | i18n}}
|
||||
</a>
|
||||
<div class="card" *ngIf="organization.canAccessReports">
|
||||
<div class="card-header d-flex">
|
||||
{{'reports' | i18n}}
|
||||
<div class="ml-auto">
|
||||
<a href="#" appStopClick class="badge badge-primary" *ngIf="!accessReports"
|
||||
(click)="upgradeOrganization()">
|
||||
{{'upgrade' | i18n}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group list-group-flush">
|
||||
<a routerLink="exposed-passwords-report" class="list-group-item" routerLinkActive="active">
|
||||
{{'exposedPasswordsReport' | i18n}}
|
||||
</a>
|
||||
<a routerLink="reused-passwords-report" class="list-group-item" routerLinkActive="active">
|
||||
{{'reusedPasswordsReport' | i18n}}
|
||||
</a>
|
||||
<a routerLink="weak-passwords-report" class="list-group-item" routerLinkActive="active">
|
||||
{{'weakPasswordsReport' | i18n}}
|
||||
</a>
|
||||
<a routerLink="unsecured-websites-report" class="list-group-item" routerLinkActive="active">
|
||||
{{'unsecuredWebsitesReport' | i18n}}
|
||||
</a>
|
||||
<a routerLink="inactive-two-factor-report" class="list-group-item" routerLinkActive="active">
|
||||
{{'inactive2faReport' | i18n}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-9">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-9">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
@@ -13,6 +13,7 @@ import { UserService } from 'jslib/abstractions/user.service';
|
||||
export class ToolsComponent {
|
||||
organization: Organization;
|
||||
accessReports = false;
|
||||
loading = true;
|
||||
|
||||
constructor(private route: ActivatedRoute, private userService: UserService,
|
||||
private messagingService: MessagingService) { }
|
||||
@@ -23,6 +24,7 @@ export class ToolsComponent {
|
||||
// TODO: Maybe we want to just make sure they are not on a free plan? Just compare useTotp for now
|
||||
// since all paid plans include useTotp
|
||||
this.accessReports = this.organization.useTotp;
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ import { MessagingService } from 'jslib/abstractions/messaging.service';
|
||||
import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service';
|
||||
import { UserService } from 'jslib/abstractions/user.service';
|
||||
|
||||
import { Cipher } from 'jslib/models/domain/cipher';
|
||||
|
||||
import { CipherView } from 'jslib/models/view/cipherView';
|
||||
|
||||
import {
|
||||
@@ -20,6 +22,8 @@ import {
|
||||
templateUrl: '../../tools/weak-passwords-report.component.html',
|
||||
})
|
||||
export class WeakPasswordsReportComponent extends BaseWeakPasswordsReportComponent {
|
||||
manageableCiphers: Cipher[];
|
||||
|
||||
constructor(cipherService: CipherService, passwordGenerationService: PasswordGenerationService,
|
||||
componentFactoryResolver: ComponentFactoryResolver, messagingService: MessagingService,
|
||||
userService: UserService, private route: ActivatedRoute) {
|
||||
@@ -29,6 +33,7 @@ export class WeakPasswordsReportComponent extends BaseWeakPasswordsReportCompone
|
||||
async ngOnInit() {
|
||||
this.route.parent.parent.params.subscribe(async (params) => {
|
||||
this.organization = await this.userService.getOrganization(params.organizationId);
|
||||
this.manageableCiphers = await this.cipherService.getAll();
|
||||
await super.ngOnInit();
|
||||
});
|
||||
}
|
||||
@@ -36,4 +41,8 @@ export class WeakPasswordsReportComponent extends BaseWeakPasswordsReportCompone
|
||||
getAllCiphers(): Promise<CipherView[]> {
|
||||
return this.cipherService.getAllFromApiForOrganization(this.organization.id);
|
||||
}
|
||||
|
||||
canManageCipher(c: CipherView): boolean {
|
||||
return this.manageableCiphers.some(x => x.id === c.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||
import { MessagingService } from 'jslib/abstractions/messaging.service';
|
||||
import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service';
|
||||
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||
import { PolicyService } from 'jslib/abstractions/policy.service';
|
||||
import { StateService } from 'jslib/abstractions/state.service';
|
||||
import { TotpService } from 'jslib/abstractions/totp.service';
|
||||
import { UserService } from 'jslib/abstractions/user.service';
|
||||
@@ -36,16 +37,16 @@ export class AddEditComponent extends BaseAddEditComponent {
|
||||
userService: UserService, collectionService: CollectionService,
|
||||
totpService: TotpService, passwordGenerationService: PasswordGenerationService,
|
||||
private apiService: ApiService, messagingService: MessagingService,
|
||||
eventService: EventService) {
|
||||
eventService: EventService, policyService: PolicyService) {
|
||||
super(cipherService, folderService, i18nService, platformUtilsService, auditService, stateService,
|
||||
userService, collectionService, totpService, passwordGenerationService, messagingService,
|
||||
eventService);
|
||||
eventService, policyService);
|
||||
}
|
||||
|
||||
protected allowOwnershipAssignment() {
|
||||
if (this.ownershipOptions != null && this.ownershipOptions.length > 1) {
|
||||
if (this.ownershipOptions != null && (this.ownershipOptions.length > 1 || !this.allowPersonal)) {
|
||||
if (this.organization != null) {
|
||||
return this.cloneMode && this.organization.isAdmin;
|
||||
return this.cloneMode && this.organization.canManageAllCollections;
|
||||
} else {
|
||||
return !this.editMode || this.cloneMode;
|
||||
}
|
||||
@@ -54,14 +55,14 @@ export class AddEditComponent extends BaseAddEditComponent {
|
||||
}
|
||||
|
||||
protected loadCollections() {
|
||||
if (!this.organization.isAdmin) {
|
||||
if (!this.organization.canManageAllCollections) {
|
||||
return super.loadCollections();
|
||||
}
|
||||
return Promise.resolve(this.collections);
|
||||
}
|
||||
|
||||
protected async loadCipher() {
|
||||
if (!this.organization.isAdmin) {
|
||||
if (!this.organization.canManageAllCollections) {
|
||||
return await super.loadCipher();
|
||||
}
|
||||
const response = await this.apiService.getCipherAdmin(this.cipherId);
|
||||
@@ -71,14 +72,14 @@ export class AddEditComponent extends BaseAddEditComponent {
|
||||
}
|
||||
|
||||
protected encryptCipher() {
|
||||
if (!this.organization.isAdmin) {
|
||||
if (!this.organization.canManageAllCollections) {
|
||||
return super.encryptCipher();
|
||||
}
|
||||
return this.cipherService.encrypt(this.cipher, null, this.originalCipher);
|
||||
}
|
||||
|
||||
protected async saveCipher(cipher: Cipher) {
|
||||
if (!this.organization.isAdmin || cipher.organizationId == null) {
|
||||
if (!this.organization.canManageAllCollections || cipher.organizationId == null) {
|
||||
return super.saveCipher(cipher);
|
||||
}
|
||||
if (this.editMode && !this.cloneMode) {
|
||||
@@ -91,7 +92,7 @@ export class AddEditComponent extends BaseAddEditComponent {
|
||||
}
|
||||
|
||||
protected async deleteCipher() {
|
||||
if (!this.organization.isAdmin) {
|
||||
if (!this.organization.canManageAllCollections) {
|
||||
return super.deleteCipher();
|
||||
}
|
||||
return this.cipher.isDeleted ? this.apiService.deleteCipherAdmin(this.cipherId)
|
||||
|
||||
@@ -29,13 +29,13 @@ export class AttachmentsComponent extends BaseAttachmentsComponent {
|
||||
}
|
||||
|
||||
protected async reupload(attachment: AttachmentView) {
|
||||
if (this.organization.isAdmin && this.showFixOldAttachments(attachment)) {
|
||||
if (this.organization.canManageAllCollections && this.showFixOldAttachments(attachment)) {
|
||||
await super.reuploadCipherAttachment(attachment, true);
|
||||
}
|
||||
}
|
||||
|
||||
protected async loadCipher() {
|
||||
if (!this.organization.isAdmin) {
|
||||
if (!this.organization.canManageAllCollections) {
|
||||
return await super.loadCipher();
|
||||
}
|
||||
const response = await this.apiService.getCipherAdmin(this.cipherId);
|
||||
@@ -43,17 +43,17 @@ export class AttachmentsComponent extends BaseAttachmentsComponent {
|
||||
}
|
||||
|
||||
protected saveCipherAttachment(file: File) {
|
||||
return this.cipherService.saveAttachmentWithServer(this.cipherDomain, file, this.organization.isAdmin);
|
||||
return this.cipherService.saveAttachmentWithServer(this.cipherDomain, file, this.organization.canManageAllCollections);
|
||||
}
|
||||
|
||||
protected deleteCipherAttachment(attachmentId: string) {
|
||||
if (!this.organization.isAdmin) {
|
||||
if (!this.organization.canManageAllCollections) {
|
||||
return super.deleteCipherAttachment(attachmentId);
|
||||
}
|
||||
return this.apiService.deleteCipherAttachmentAdmin(this.cipherId, attachmentId);
|
||||
}
|
||||
|
||||
protected showFixOldAttachments(attachment: AttachmentView) {
|
||||
return attachment.key == null && this.organization.isAdmin;
|
||||
return attachment.key == null && this.organization.canManageAllCollections;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ import { EventService } from 'jslib/abstractions/event.service';
|
||||
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||
import { SearchService } from 'jslib/abstractions/search.service';
|
||||
import { TotpService } from 'jslib/abstractions/totp.service';
|
||||
import { UserService } from 'jslib/abstractions/user.service';
|
||||
|
||||
import { Organization } from 'jslib/models/domain/organization';
|
||||
import { CipherView } from 'jslib/models/view/cipherView';
|
||||
@@ -34,13 +36,13 @@ export class CiphersComponent extends BaseCiphersComponent {
|
||||
constructor(searchService: SearchService, analytics: Angulartics2,
|
||||
toasterService: ToasterService, i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService, cipherService: CipherService,
|
||||
private apiService: ApiService, eventService: EventService) {
|
||||
private apiService: ApiService, eventService: EventService, totpService: TotpService, userService: UserService) {
|
||||
super(searchService, analytics, toasterService, i18nService, platformUtilsService,
|
||||
cipherService, eventService);
|
||||
cipherService, eventService, totpService, userService);
|
||||
}
|
||||
|
||||
async load(filter: (cipher: CipherView) => boolean = null) {
|
||||
if (!this.organization.isAdmin) {
|
||||
if (!this.organization.canManageAllCollections) {
|
||||
await super.load(filter, this.deleted);
|
||||
return;
|
||||
}
|
||||
@@ -51,7 +53,7 @@ export class CiphersComponent extends BaseCiphersComponent {
|
||||
}
|
||||
|
||||
async applyFilter(filter: (cipher: CipherView) => boolean = null) {
|
||||
if (this.organization.isAdmin) {
|
||||
if (this.organization.canManageAllCollections) {
|
||||
await super.applyFilter(filter);
|
||||
} else {
|
||||
const f = (c: CipherView) => c.organizationId === this.organization.id && (filter == null || filter(c));
|
||||
@@ -60,7 +62,7 @@ export class CiphersComponent extends BaseCiphersComponent {
|
||||
}
|
||||
|
||||
async search(timeout: number = null) {
|
||||
if (!this.organization.isAdmin) {
|
||||
if (!this.organization.canManageAllCollections) {
|
||||
return super.search(timeout);
|
||||
}
|
||||
this.searchPending = false;
|
||||
@@ -87,13 +89,13 @@ export class CiphersComponent extends BaseCiphersComponent {
|
||||
}
|
||||
|
||||
protected deleteCipher(id: string) {
|
||||
if (!this.organization.isAdmin) {
|
||||
if (!this.organization.canManageAllCollections) {
|
||||
return super.deleteCipher(id, this.deleted);
|
||||
}
|
||||
return this.deleted ? this.apiService.deleteCipherAdmin(id) : this.apiService.putDeleteCipherAdmin(id);
|
||||
}
|
||||
|
||||
protected showFixOldAttachments(c: CipherView) {
|
||||
return this.organization.isAdmin && c.hasOldAttachments;
|
||||
return this.organization.canManageAllCollections && c.hasOldAttachments;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export class CollectionsComponent extends BaseCollectionsComponent {
|
||||
}
|
||||
|
||||
protected async loadCipher() {
|
||||
if (!this.organization.isAdmin) {
|
||||
if (!this.organization.canManageAllCollections) {
|
||||
return await super.loadCipher();
|
||||
}
|
||||
const response = await this.apiService.getCipherAdmin(this.cipherId);
|
||||
@@ -36,21 +36,21 @@ export class CollectionsComponent extends BaseCollectionsComponent {
|
||||
}
|
||||
|
||||
protected loadCipherCollections() {
|
||||
if (!this.organization.isAdmin) {
|
||||
if (!this.organization.canManageAllCollections) {
|
||||
return super.loadCipherCollections();
|
||||
}
|
||||
return this.collectionIds;
|
||||
}
|
||||
|
||||
protected loadCollections() {
|
||||
if (!this.organization.isAdmin) {
|
||||
if (!this.organization.canManageAllCollections) {
|
||||
return super.loadCollections();
|
||||
}
|
||||
return Promise.resolve(this.collections);
|
||||
}
|
||||
|
||||
protected saveCollections() {
|
||||
if (this.organization.isAdmin) {
|
||||
if (this.organization.canManageAllCollections) {
|
||||
const request = new CipherCollectionsRequest(this.cipherDomain.collectionIds);
|
||||
return this.apiService.putCipherCollectionsAdmin(this.cipherId, request);
|
||||
} else {
|
||||
|
||||
@@ -29,7 +29,7 @@ export class GroupingsComponent extends BaseGroupingsComponent {
|
||||
}
|
||||
|
||||
async loadCollections() {
|
||||
if (!this.organization.isAdmin) {
|
||||
if (!this.organization.canManageAllCollections) {
|
||||
await super.loadCollections(this.organization.id);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
|
||||
const queryParamsSub = this.route.queryParams.subscribe(async (qParams) => {
|
||||
this.ciphersComponent.searchText = this.groupingsComponent.searchText = qParams.search;
|
||||
if (!this.organization.isAdmin) {
|
||||
if (!this.organization.canManageAllCollections) {
|
||||
await this.syncService.fullSync(false);
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
|
||||
this.ngZone.run(async () => {
|
||||
@@ -233,7 +233,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
this.modal = this.collectionsModalRef.createComponent(factory).instance;
|
||||
const childComponent = this.modal.show<CollectionsComponent>(CollectionsComponent, this.collectionsModalRef);
|
||||
|
||||
if (this.organization.isAdmin) {
|
||||
if (this.organization.canManageAllCollections) {
|
||||
childComponent.collectionIds = cipher.collectionIds;
|
||||
childComponent.collections = this.groupingsComponent.collections.filter((c) => !c.readOnly);
|
||||
}
|
||||
@@ -253,7 +253,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
const component = this.editCipher(null);
|
||||
component.organizationId = this.organization.id;
|
||||
component.type = this.type;
|
||||
if (this.organization.isAdmin) {
|
||||
if (this.organization.canManageAllCollections) {
|
||||
component.collections = this.groupingsComponent.collections.filter((c) => !c.readOnly);
|
||||
}
|
||||
if (this.collectionId != null) {
|
||||
@@ -296,7 +296,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
const component = this.editCipher(cipher);
|
||||
component.cloneMode = true;
|
||||
component.organizationId = this.organization.id;
|
||||
if (this.organization.isAdmin) {
|
||||
if (this.organization.canManageAllCollections) {
|
||||
component.collections = this.groupingsComponent.collections.filter((c) => !c.readOnly);
|
||||
}
|
||||
// Regardless of Admin state, the collection Ids need to passed manually as they are not assigned value
|
||||
|
||||
@@ -25,6 +25,12 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body" *ngIf="!loading && unavailable">
|
||||
{{'sendAccessUnavailable' | i18n}}
|
||||
</div>
|
||||
<div class="card-body" *ngIf="!loading && error">
|
||||
{{'unexpectedError' | i18n}}
|
||||
</div>
|
||||
<div class="card-body" *ngIf="!loading && !passwordRequired && send">
|
||||
<p class="text-center"><b>{{send.name}}</b></p>
|
||||
<hr>
|
||||
@@ -33,7 +39,7 @@
|
||||
<app-callout *ngIf="send.text.hidden" type="tip">{{'sendHiddenByDefault' | i18n}}</app-callout>
|
||||
<div class="form-group">
|
||||
<textarea id="text" rows="8" name="Text" [(ngModel)]="sendText" class="form-control"
|
||||
readonly (click)="selectText()"></textarea>
|
||||
readonly></textarea>
|
||||
</div>
|
||||
<button class="btn btn-block btn-link" type="button" (click)="toggleText()"
|
||||
*ngIf="send.text.hidden">
|
||||
|
||||
@@ -37,6 +37,8 @@ export class AccessComponent implements OnInit {
|
||||
formPromise: Promise<SendAccessResponse>;
|
||||
password: string;
|
||||
showText = false;
|
||||
unavailable = false;
|
||||
error = false;
|
||||
|
||||
private id: string;
|
||||
private key: string;
|
||||
@@ -93,10 +95,6 @@ export class AccessComponent implements OnInit {
|
||||
this.downloading = false;
|
||||
}
|
||||
|
||||
selectText() {
|
||||
(document.getElementById('text') as HTMLInputElement).select();
|
||||
}
|
||||
|
||||
copyText() {
|
||||
this.platformUtilsService.copyToClipboard(this.send.text.text);
|
||||
this.platformUtilsService.showToast('success', null,
|
||||
@@ -108,6 +106,8 @@ export class AccessComponent implements OnInit {
|
||||
}
|
||||
|
||||
async load() {
|
||||
this.unavailable = false;
|
||||
this.error = false;
|
||||
const keyArray = Utils.fromUrlB64ToArray(this.key);
|
||||
const accessRequest = new SendAccessRequest();
|
||||
if (this.password != null) {
|
||||
@@ -131,6 +131,10 @@ export class AccessComponent implements OnInit {
|
||||
if (e instanceof ErrorResponse) {
|
||||
if (e.statusCode === 401) {
|
||||
this.passwordRequired = true;
|
||||
} else if (e.statusCode === 404) {
|
||||
this.unavailable = true;
|
||||
} else {
|
||||
this.error = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
<div class="row" *ngIf="!editMode">
|
||||
<div class="col-6 form-group">
|
||||
<label for="type">{{'whatTypeOfSend' | i18n}}</label>
|
||||
<select id="type" name="Type" [(ngModel)]="send.type" class="form-control" appAutofocus>
|
||||
<select id="type" name="Type" [(ngModel)]="send.type" class="form-control" appAutofocus
|
||||
(change)='typeChanged()'>
|
||||
<option *ngFor="let o of typeOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
@@ -34,7 +35,7 @@
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" [(ngModel)]="send.text.hidden"
|
||||
id="text-hidden" name="Text.Hidden">
|
||||
<label class="form-check-label" for="text-hidden">{{'cfTypeHidden' | i18n}}</label>
|
||||
<label class="form-check-label" for="text-hidden">{{'textHiddenByDefault' | i18n}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
@@ -52,29 +53,55 @@
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<h3 class="mt-4">{{'options' | i18n}}</h3>
|
||||
<h3 class="mt-5">{{'options' | i18n}}</h3>
|
||||
<div class="row">
|
||||
<div class="col-6 form-group">
|
||||
<label for="deletionDate">{{'deletionDate' | i18n}}</label>
|
||||
<input id="deletionDate" class="form-control" type="datetime-local" name="DeletionDate"
|
||||
[(ngModel)]="deletionDate" required>
|
||||
<div *ngIf="!editMode">
|
||||
<select id="deletionDate" name="DeletionDateSelect" [(ngModel)]="deletionDateSelect"
|
||||
class="form-control" required>
|
||||
<option *ngFor="let o of deletionDateOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
<input id="deletionDateCustom" class="form-control mt-1" type="datetime-local"
|
||||
name="DeletionDate" [(ngModel)]="deletionDate" required *ngIf="deletionDateSelect === 0"
|
||||
placeholder="MM/DD/YYYY HH:MM AM/PM">
|
||||
</div>
|
||||
<div *ngIf="editMode">
|
||||
<input id="deletionDate" class="form-control" type="datetime-local" name="DeletionDate"
|
||||
[(ngModel)]="deletionDate" required placeholder="MM/DD/YYYY HH:MM AM/PM">
|
||||
</div>
|
||||
<div class="form-text text-muted small">{{'deletionDateDesc' | i18n}}</div>
|
||||
</div>
|
||||
<div class="col-6 form-group">
|
||||
<div class="d-flex">
|
||||
<label for="expirationDate">{{'expirationDate' | i18n}}</label>
|
||||
<a href="#" appStopClick (click)="clearExpiration()" class="ml-auto">
|
||||
<a href="#" appStopClick (click)="clearExpiration()" class="ml-auto" *ngIf="editMode">
|
||||
{{'clear' | i18n}}
|
||||
</a>
|
||||
</div>
|
||||
<input id="expirationDate" class="form-control" type="datetime-local" name="ExpirationDate"
|
||||
[(ngModel)]="expirationDate">
|
||||
<div *ngIf="!editMode">
|
||||
<select id="expirationDate" name="ExpirationDateSelect" [(ngModel)]="expirationDateSelect"
|
||||
class="form-control" required>
|
||||
<option *ngFor="let o of expirationDateOptions" [ngValue]="o.value">{{o.name}}
|
||||
</option>
|
||||
</select>
|
||||
<input id="expirationDateCustom" class="form-control mt-1" type="datetime-local"
|
||||
name="ExpirationDate" [(ngModel)]="expirationDate" required
|
||||
*ngIf="expirationDateSelect === 0" placeholder="MM/DD/YYYY HH:MM AM/PM">
|
||||
</div>
|
||||
<div *ngIf="editMode">
|
||||
<input id="expirationDate" class="form-control" type="datetime-local" name="ExpirationDate"
|
||||
[(ngModel)]="expirationDate" placeholder="MM/DD/YYYY HH:MM AM/PM">
|
||||
</div>
|
||||
<div class="form-text text-muted small">{{'expirationDateDesc' | i18n}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 form-group">
|
||||
<label for="maxAccessCount">{{'maxAccessCount' | i18n}}</label>
|
||||
<input id="maxAccessCount" class="form-control" type="number" name="MaxAccessCount"
|
||||
[(ngModel)]="send.maxAccessCount">
|
||||
[(ngModel)]="send.maxAccessCount" min="1">
|
||||
<div class="form-text text-muted small">{{'maxAccessCountDesc' | i18n}}</div>
|
||||
</div>
|
||||
<div class="col-6 form-group" *ngIf="editMode">
|
||||
<label for="accessCount">{{'currentAccessCount' | i18n}}</label>
|
||||
@@ -88,19 +115,22 @@
|
||||
<label for="password" *ngIf="hasPassword">{{'newPassword' | i18n}}</label>
|
||||
<input id="password" class="form-control" type="password" name="Password"
|
||||
[(ngModel)]="password">
|
||||
<div class="form-text text-muted small">{{'sendPasswordDesc' | i18n}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="notes">{{'notes' | i18n}}</label>
|
||||
<textarea id="notes" name="Notes" rows="6" [(ngModel)]="send.notes" class="form-control"></textarea>
|
||||
<div class="form-text text-muted small">{{'sendNotesDesc' | i18n}}</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" [(ngModel)]="send.disabled" id="disabled"
|
||||
name="Disabled">
|
||||
<label class="form-check-label" for="disabled">{{'disabled' | i18n}}</label>
|
||||
<label class="form-check-label" for="disabled">{{'disableThisSend' | i18n}}</label>
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="mt-5" *ngIf="link">{{'share' | i18n}}</h3>
|
||||
<div class="form-group" *ngIf="link">
|
||||
<label for="link">{{'sendLink' | i18n}}</label>
|
||||
<input type="text" readonly id="link" name="Link" [(ngModel)]="link" class="form-control">
|
||||
|
||||
@@ -10,20 +10,19 @@ import { Component } from '@angular/core';
|
||||
|
||||
import { SendType } from 'jslib/enums/sendType';
|
||||
|
||||
import { ApiService } from 'jslib/abstractions/api.service';
|
||||
import { EnvironmentService } from 'jslib/abstractions/environment.service';
|
||||
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||
import { MessagingService } from 'jslib/abstractions/messaging.service';
|
||||
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||
import { SendService } from 'jslib/abstractions/send.service';
|
||||
import { UserService } from 'jslib/abstractions/user.service';
|
||||
|
||||
import { SendView } from 'jslib/models/view/sendView';
|
||||
import { SendFileView } from 'jslib/models/view/sendFileView';
|
||||
import { SendTextView } from 'jslib/models/view/sendTextView';
|
||||
import { SendView } from 'jslib/models/view/sendView';
|
||||
|
||||
import { Send } from 'jslib/models/domain/send';
|
||||
|
||||
import { SendData } from 'jslib/models/data/sendData';
|
||||
|
||||
@Component({
|
||||
selector: 'app-send-add-edit',
|
||||
templateUrl: 'add-edit.component.html',
|
||||
@@ -48,14 +47,33 @@ export class AddEditComponent {
|
||||
deletePromise: Promise<any>;
|
||||
sendType = SendType;
|
||||
typeOptions: any[];
|
||||
deletionDateOptions: any[];
|
||||
expirationDateOptions: any[];
|
||||
deletionDateSelect = 168;
|
||||
expirationDateSelect: number = null;
|
||||
canAccessPremium = true;
|
||||
premiumRequiredAlertShown = false;
|
||||
|
||||
constructor(private i18nService: I18nService, private platformUtilsService: PlatformUtilsService,
|
||||
private apiService: ApiService, private environmentService: EnvironmentService,
|
||||
private datePipe: DatePipe, private sendService: SendService) {
|
||||
private environmentService: EnvironmentService, private datePipe: DatePipe,
|
||||
private sendService: SendService, private userService: UserService,
|
||||
private messagingService: MessagingService) {
|
||||
this.typeOptions = [
|
||||
{ name: i18nService.t('sendTypeFile'), value: SendType.File },
|
||||
{ name: i18nService.t('sendTypeText'), value: SendType.Text },
|
||||
];
|
||||
this.deletionDateOptions = this.expirationDateOptions = [
|
||||
{ name: i18nService.t('oneHour'), value: 1 },
|
||||
{ name: i18nService.t('oneDay'), value: 24 },
|
||||
{ name: i18nService.t('days', '2'), value: 48 },
|
||||
{ name: i18nService.t('days', '3'), value: 72 },
|
||||
{ name: i18nService.t('days', '7'), value: 168 },
|
||||
{ name: i18nService.t('days', '30'), value: 720 },
|
||||
{ name: i18nService.t('custom'), value: 0 },
|
||||
];
|
||||
this.expirationDateOptions = [
|
||||
{ name: i18nService.t('never'), value: null },
|
||||
].concat([...this.deletionDateOptions]);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
@@ -71,6 +89,11 @@ export class AddEditComponent {
|
||||
this.title = this.i18nService.t('createSend');
|
||||
}
|
||||
|
||||
this.canAccessPremium = await this.userService.canAccessPremium();
|
||||
if (!this.canAccessPremium) {
|
||||
this.type = SendType.Text;
|
||||
}
|
||||
|
||||
if (this.send == null) {
|
||||
if (this.editMode) {
|
||||
const send = await this.loadSend();
|
||||
@@ -88,10 +111,8 @@ export class AddEditComponent {
|
||||
this.hasPassword = this.send.password != null && this.send.password.trim() !== '';
|
||||
|
||||
// Parse dates
|
||||
this.deletionDate = this.send.deletionDate == null ? null :
|
||||
this.datePipe.transform(this.send.deletionDate, 'yyyy-MM-ddTHH:mm');
|
||||
this.expirationDate = this.send.expirationDate == null ? null :
|
||||
this.datePipe.transform(this.send.expirationDate, 'yyyy-MM-ddTHH:mm');
|
||||
this.deletionDate = this.dateToString(this.send.deletionDate);
|
||||
this.expirationDate = this.dateToString(this.send.expirationDate);
|
||||
|
||||
if (this.editMode) {
|
||||
let webVaultUrl = this.environmentService.getWebVaultUrl();
|
||||
@@ -127,6 +148,20 @@ export class AddEditComponent {
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.editMode) {
|
||||
const now = new Date();
|
||||
if (this.deletionDateSelect > 0) {
|
||||
const d = new Date();
|
||||
d.setHours(now.getHours() + this.deletionDateSelect);
|
||||
this.deletionDate = this.dateToString(d);
|
||||
}
|
||||
if (this.expirationDateSelect != null && this.expirationDateSelect > 0) {
|
||||
const d = new Date();
|
||||
d.setHours(now.getHours() + this.expirationDateSelect);
|
||||
this.expirationDate = this.dateToString(d);
|
||||
}
|
||||
}
|
||||
|
||||
const encSend = await this.encryptSend(file);
|
||||
try {
|
||||
this.formPromise = this.sendService.saveWithServer(encSend);
|
||||
@@ -158,7 +193,7 @@ export class AddEditComponent {
|
||||
}
|
||||
|
||||
try {
|
||||
this.deletePromise = this.apiService.deleteSend(this.send.id);
|
||||
this.deletePromise = this.sendService.deleteWithServer(this.send.id);
|
||||
await this.deletePromise;
|
||||
this.platformUtilsService.showToast('success', null, this.i18nService.t('deletedSend'));
|
||||
await this.load();
|
||||
@@ -166,10 +201,15 @@ export class AddEditComponent {
|
||||
} catch { }
|
||||
}
|
||||
|
||||
typeChanged() {
|
||||
if (!this.canAccessPremium && this.send.type === SendType.File && !this.premiumRequiredAlertShown) {
|
||||
this.premiumRequiredAlertShown = true;
|
||||
this.messagingService.send('premiumRequired');
|
||||
}
|
||||
}
|
||||
|
||||
protected async loadSend(): Promise<Send> {
|
||||
const response = await this.apiService.getSend(this.sendId);
|
||||
const data = new SendData(response);
|
||||
return new Send(data);
|
||||
return this.sendService.get(this.sendId);
|
||||
}
|
||||
|
||||
protected async encryptSend(file: File): Promise<[Send, ArrayBuffer]> {
|
||||
@@ -189,4 +229,8 @@ export class AddEditComponent {
|
||||
|
||||
return sendData;
|
||||
}
|
||||
|
||||
protected dateToString(d: Date) {
|
||||
return d == null ? null : this.datePipe.transform(d, 'yyyy-MM-ddTHH:mm');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,10 +62,27 @@
|
||||
</td>
|
||||
<td class="reduced-lh wrap">
|
||||
<a href="#" appStopClick appStopProp (click)="editSend(s)">{{s.name}}</a>
|
||||
<span appStopClick class="badge badge-secondary" *ngIf="s.disabled">
|
||||
{{'disabled' | i18n}}
|
||||
</span>
|
||||
<ng-container *ngIf="s.password">
|
||||
<i class="fa fa-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="fa fa-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="fa fa-clock-o" appStopProp title="{{'expired' | i18n}}" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{'expired' | i18n}}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="s.pendingDelete">
|
||||
<i class="fa fa-trash" appStopProp title="{{'pendingDeletion' | i18n}}"
|
||||
aria-hidden="true"></i>
|
||||
<span class="sr-only">{{'pendingDeletion' | i18n}}</span>
|
||||
</ng-container>
|
||||
<br>
|
||||
<small appStopProp>{{s.deletionDate | date:'medium'}}</small>
|
||||
</td>
|
||||
|
||||
@@ -143,7 +143,7 @@ export class SendComponent implements OnInit {
|
||||
}
|
||||
|
||||
try {
|
||||
this.actionPromise = this.apiService.putSendRemovePassword(s.id);
|
||||
this.actionPromise = this.sendService.removePasswordWithServer(s.id);
|
||||
await this.actionPromise;
|
||||
this.platformUtilsService.showToast('success', null, this.i18nService.t('removedPassword'));
|
||||
await this.load();
|
||||
@@ -164,7 +164,7 @@ export class SendComponent implements OnInit {
|
||||
}
|
||||
|
||||
try {
|
||||
this.actionPromise = this.apiService.deleteSend(s.id);
|
||||
this.actionPromise = this.sendService.deleteWithServer(s.id);
|
||||
await this.actionPromise;
|
||||
this.platformUtilsService.showToast('success', null, this.i18nService.t('deletedSend'));
|
||||
await this.load();
|
||||
|
||||
@@ -153,6 +153,8 @@ export class EventService {
|
||||
case EventType.OrganizationUser_UpdatedGroups:
|
||||
msg = this.i18nService.t('editedGroupsForUser', this.formatOrgUserId(ev));
|
||||
break;
|
||||
case EventType.OrganizationUser_UnlinkedSso:
|
||||
msg = this.i18nService.t('unlinkedSsoUser', this.formatOrgUserId(ev));
|
||||
// Org
|
||||
case EventType.Organization_Updated:
|
||||
msg = this.i18nService.t('editedOrgSettings');
|
||||
@@ -165,6 +167,11 @@ export class EventService {
|
||||
msg = this.i18nService.t('exportedOrganizationVault');
|
||||
break;
|
||||
*/
|
||||
// Policies
|
||||
case EventType.Policy_Updated:
|
||||
msg = this.i18nService.t('modifiedPolicy', this.formatPolicyId(ev));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -251,6 +258,13 @@ export class EventService {
|
||||
return a.outerHTML;
|
||||
}
|
||||
|
||||
private formatPolicyId(ev: EventResponse) {
|
||||
const shortId = this.getShortId(ev.policyId);
|
||||
const a = this.makeAnchor(shortId);
|
||||
a.setAttribute('href', '#/organizations/' + ev.organizationId + '/manage/policies?policyId=' + ev.policyId);
|
||||
return a.outerHTML;
|
||||
}
|
||||
|
||||
private makeAnchor(shortId: string) {
|
||||
const a = document.createElement('a');
|
||||
a.title = this.i18nService.t('view');
|
||||
|
||||
@@ -7,20 +7,32 @@ import {
|
||||
|
||||
import { UserService } from 'jslib/abstractions/user.service';
|
||||
|
||||
import { OrganizationUserType } from 'jslib/enums/organizationUserType';
|
||||
import { Permissions } from 'jslib/enums/permissions';
|
||||
|
||||
@Injectable()
|
||||
export class OrganizationTypeGuardService implements CanActivate {
|
||||
constructor(private userService: UserService, private router: Router) { }
|
||||
|
||||
async canActivate(route: ActivatedRouteSnapshot) {
|
||||
const org = await this.userService.getOrganization(route.parent.params.organizationId);
|
||||
const allowedTypes = route.data == null ? null : route.data.allowedTypes as OrganizationUserType[];
|
||||
if (allowedTypes == null || allowedTypes.indexOf(org.type) === -1) {
|
||||
this.router.navigate(['/organizations', org.id]);
|
||||
return false;
|
||||
const org = await this.userService.getOrganization(route.params.organizationId);
|
||||
const permissions = route.data == null ? null : route.data.permissions as Permissions[];
|
||||
|
||||
if (
|
||||
(permissions.indexOf(Permissions.AccessBusinessPortal) !== -1 && org.canAccessBusinessPortal) ||
|
||||
(permissions.indexOf(Permissions.AccessEventLogs) !== -1 && org.canAccessEventLogs) ||
|
||||
(permissions.indexOf(Permissions.AccessImportExport) !== -1 && org.canAccessImportExport) ||
|
||||
(permissions.indexOf(Permissions.AccessReports) !== -1 && org.canAccessReports) ||
|
||||
(permissions.indexOf(Permissions.ManageAllCollections) !== -1 && org.canManageAllCollections) ||
|
||||
(permissions.indexOf(Permissions.ManageAssignedCollections) !== -1 && org.canManageAssignedCollections) ||
|
||||
(permissions.indexOf(Permissions.ManageGroups) !== -1 && org.canManageGroups) ||
|
||||
(permissions.indexOf(Permissions.ManageOrganization) !== -1 && org.isOwner) ||
|
||||
(permissions.indexOf(Permissions.ManagePolicies) !== -1 && org.canManagePolicies) ||
|
||||
(permissions.indexOf(Permissions.ManageUsers) !== -1 && org.canManageUsers)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
this.router.navigate(['/organizations', org.id]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import { AuditService } from 'jslib/services/audit.service';
|
||||
import { AuthService } from 'jslib/services/auth.service';
|
||||
import { CipherService } from 'jslib/services/cipher.service';
|
||||
import { CollectionService } from 'jslib/services/collection.service';
|
||||
import { ConsoleLogService } from 'jslib/services/consoleLog.service';
|
||||
import { ConstantsService } from 'jslib/services/constants.service';
|
||||
import { ContainerService } from 'jslib/services/container.service';
|
||||
import { CryptoService } from 'jslib/services/crypto.service';
|
||||
@@ -94,8 +95,10 @@ const storageService: StorageServiceAbstraction = new HtmlStorageService(platfor
|
||||
const secureStorageService: StorageServiceAbstraction = new MemoryStorageService();
|
||||
const cryptoFunctionService: CryptoFunctionServiceAbstraction = new WebCryptoFunctionService(window,
|
||||
platformUtilsService);
|
||||
const consoleLogService = new ConsoleLogService(false);
|
||||
const cryptoService = new CryptoService(storageService,
|
||||
platformUtilsService.isDev() ? storageService : secureStorageService, cryptoFunctionService, platformUtilsService);
|
||||
platformUtilsService.isDev() ? storageService : secureStorageService, cryptoFunctionService, platformUtilsService,
|
||||
consoleLogService);
|
||||
const tokenService = new TokenService(storageService);
|
||||
const appIdService = new AppIdService(storageService);
|
||||
const apiService = new ApiService(tokenService, platformUtilsService,
|
||||
@@ -108,7 +111,7 @@ const cipherService = new CipherService(cryptoService, userService, settingsServ
|
||||
const folderService = new FolderService(cryptoService, userService, apiService, storageService,
|
||||
i18nService, cipherService);
|
||||
const collectionService = new CollectionService(cryptoService, userService, storageService, i18nService);
|
||||
searchService = new SearchService(cipherService);
|
||||
searchService = new SearchService(cipherService, consoleLogService);
|
||||
const policyService = new PolicyService(userService, storageService);
|
||||
const sendService = new SendService(cryptoService, userService, apiService, storageService,
|
||||
i18nService, cryptoFunctionService);
|
||||
@@ -122,11 +125,12 @@ const passwordGenerationService = new PasswordGenerationService(cryptoService, s
|
||||
const totpService = new TotpService(storageService, cryptoFunctionService);
|
||||
const containerService = new ContainerService(cryptoService);
|
||||
const authService = new AuthService(cryptoService, apiService,
|
||||
userService, tokenService, appIdService, i18nService, platformUtilsService, messagingService, vaultTimeoutService);
|
||||
userService, tokenService, appIdService, i18nService, platformUtilsService, messagingService, vaultTimeoutService,
|
||||
consoleLogService);
|
||||
const exportService = new ExportService(folderService, cipherService, apiService);
|
||||
const importService = new ImportService(cipherService, folderService, apiService, i18nService, collectionService);
|
||||
const notificationsService = new NotificationsService(userService, syncService, appIdService,
|
||||
apiService, vaultTimeoutService, async () => messagingService.send('logout', { expired: true }));
|
||||
apiService, vaultTimeoutService, async () => messagingService.send('logout', { expired: true }), consoleLogService);
|
||||
const environmentService = new EnvironmentService(apiService, storageService, notificationsService);
|
||||
const auditService = new AuditService(cryptoFunctionService, apiService);
|
||||
const eventLoggingService = new EventLoggingService(storageService, apiService, userService, cipherService);
|
||||
|
||||
@@ -16,10 +16,14 @@ import {
|
||||
ChangePasswordComponent as BaseChangePasswordComponent,
|
||||
} from 'jslib/angular/components/change-password.component';
|
||||
|
||||
import { EmergencyAccessStatusType } from 'jslib/enums/emergencyAccessStatusType';
|
||||
import { Utils } from 'jslib/misc/utils';
|
||||
|
||||
import { CipherString } from 'jslib/models/domain/cipherString';
|
||||
import { SymmetricCryptoKey } from 'jslib/models/domain/symmetricCryptoKey';
|
||||
|
||||
import { CipherWithIdRequest } from 'jslib/models/request/cipherWithIdRequest';
|
||||
import { EmergencyAccessUpdateRequest } from 'jslib/models/request/emergencyAccessUpdateRequest';
|
||||
import { FolderWithIdRequest } from 'jslib/models/request/folderWithIdRequest';
|
||||
import { PasswordRequest } from 'jslib/models/request/passwordRequest';
|
||||
import { UpdateKeyRequest } from 'jslib/models/request/updateKeyRequest';
|
||||
@@ -37,7 +41,7 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent {
|
||||
userService: UserService, passwordGenerationService: PasswordGenerationService,
|
||||
platformUtilsService: PlatformUtilsService, policyService: PolicyService,
|
||||
private folderService: FolderService, private cipherService: CipherService,
|
||||
private syncService: SyncService, private apiService: ApiService, ) {
|
||||
private syncService: SyncService, private apiService: ApiService ) {
|
||||
super(i18nService, cryptoService, messagingService, userService, passwordGenerationService,
|
||||
platformUtilsService, policyService);
|
||||
}
|
||||
@@ -69,6 +73,7 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent {
|
||||
|
||||
const result = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t('updateEncryptionKeyWarning') + ' ' +
|
||||
this.i18nService.t('updateEncryptionKeyExportWarning') + ' ' +
|
||||
this.i18nService.t('rotateEncKeyConfirmation'), this.i18nService.t('rotateEncKeyTitle'),
|
||||
this.i18nService.t('yes'), this.i18nService.t('no'), 'warning');
|
||||
if (!result) {
|
||||
@@ -159,5 +164,32 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent {
|
||||
}
|
||||
|
||||
await this.apiService.postAccountKey(request);
|
||||
|
||||
await this.updateEmergencyAccesses(encKey[0]);
|
||||
}
|
||||
|
||||
private async updateEmergencyAccesses(encKey: SymmetricCryptoKey) {
|
||||
const emergencyAccess = await this.apiService.getEmergencyAccessTrusted();
|
||||
const allowedStatuses = [
|
||||
EmergencyAccessStatusType.Confirmed,
|
||||
EmergencyAccessStatusType.RecoveryInitiated,
|
||||
EmergencyAccessStatusType.RecoveryApproved,
|
||||
];
|
||||
|
||||
const filteredAccesses = emergencyAccess.data.filter(d => allowedStatuses.includes(d.status));
|
||||
|
||||
for (const details of filteredAccesses) {
|
||||
const publicKeyResponse = await this.apiService.getUserPublicKey(details.granteeId);
|
||||
const publicKey = Utils.fromB64ToArray(publicKeyResponse.publicKey);
|
||||
|
||||
const encryptedKey = await this.cryptoService.rsaEncrypt(encKey.key, publicKey.buffer);
|
||||
|
||||
const updateRequest = new EmergencyAccessUpdateRequest();
|
||||
updateRequest.type = details.type;
|
||||
updateRequest.waitTimeDays = details.waitTimeDays;
|
||||
updateRequest.keyEncrypted = encryptedKey.encryptedString;
|
||||
|
||||
await this.apiService.putEmergencyAccess(details.id, updateRequest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@ import {
|
||||
} from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
import { PlanType } from 'jslib/enums/planType';
|
||||
import { ProductType } from 'jslib/enums/productType';
|
||||
|
||||
import { OrganizationPlansComponent } from './organization-plans.component';
|
||||
|
||||
@Component({
|
||||
@@ -18,8 +21,15 @@ export class CreateOrganizationComponent implements OnInit {
|
||||
|
||||
ngOnInit() {
|
||||
const queryParamsSub = this.route.queryParams.subscribe(async (qParams) => {
|
||||
if (qParams.plan === 'families' || qParams.plan === 'teams' || qParams.plan === 'enterprise') {
|
||||
this.orgPlansComponent.plan = qParams.plan;
|
||||
if (qParams.plan === 'families') {
|
||||
this.orgPlansComponent.plan = PlanType.FamiliesAnnually;
|
||||
this.orgPlansComponent.product = ProductType.Families;
|
||||
} else if (qParams.plan === 'teams') {
|
||||
this.orgPlansComponent.plan = PlanType.TeamsAnnually;
|
||||
this.orgPlansComponent.product = ProductType.Teams;
|
||||
} else if (qParams.plan === 'enterprise') {
|
||||
this.orgPlansComponent.plan = PlanType.EnterpriseAnnually;
|
||||
this.orgPlansComponent.product = ProductType.Enterprise;
|
||||
}
|
||||
if (queryParamsSub != null) {
|
||||
queryParamsSub.unsubscribe();
|
||||
|
||||
76
src/app/settings/emergency-access-add-edit.component.html
Normal file
76
src/app/settings/emergency-access-add-edit.component.html
Normal file
@@ -0,0 +1,76 @@
|
||||
<div class="modal fade" tabindex="-1" role="dialog" aria-modal="true" aria-labelledby="userAddEditTitle">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate>
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title" id="userAddEditTitle">
|
||||
<span class="badge badge-primary" *ngIf="readOnly">{{'premium' | i18n}}</span>
|
||||
{{title}}
|
||||
<small class="text-muted" *ngIf="name">{{name}}</small>
|
||||
</h2>
|
||||
<button type="button" class="close" data-dismiss="modal" appA11yTitle="{{'close' | i18n}}">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body" *ngIf="loading">
|
||||
<i class="fa fa-spinner fa-spin text-muted" title="{{'loading' | i18n}}" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{'loading' | i18n}}</span>
|
||||
</div>
|
||||
<div class="modal-body" *ngIf="!loading">
|
||||
<ng-container *ngIf="!editMode">
|
||||
<p>{{'inviteEmergencyContactDesc' | i18n}}</p>
|
||||
<div class="form-group mb-4">
|
||||
<label for="email">{{'email' | i18n}}</label>
|
||||
<input id="email" class="form-control" type="text" name="Email" [(ngModel)]="email" required>
|
||||
</div>
|
||||
</ng-container>
|
||||
<h3>
|
||||
{{'userAccess' | i18n}}
|
||||
<a target="_blank" rel="noopener" appA11yTitle="{{'learnMore' | i18n}}"
|
||||
href="https://bitwarden.com/help/article/user-types-access-control/#user-types">
|
||||
<i class="fa fa-question-circle-o" aria-hidden="true"></i>
|
||||
</a>
|
||||
</h3>
|
||||
<div class="form-check mt-2 form-check-block">
|
||||
<input class="form-check-input" type="radio" name="userType" id="emergencyTypeView"
|
||||
[value]="emergencyAccessType.View" [(ngModel)]="type">
|
||||
<label class="form-check-label" for="emergencyTypeView">
|
||||
{{'view' | i18n}}
|
||||
<small>{{'viewDesc' | i18n}}</small>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check mt-2 form-check-block">
|
||||
<input class="form-check-input" type="radio" name="userType" id="emergencyTypeTakeover"
|
||||
[value]="emergencyAccessType.Takeover" [(ngModel)]="type" [disabled]="readOnly">
|
||||
<label class="form-check-label" for="emergencyTypeTakeover">
|
||||
{{'takeover' | i18n}}
|
||||
<small>{{'takeoverDesc' | i18n}}</small>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group col-6 mt-4">
|
||||
<label for="waitTime">{{'waitTime' | i18n}}</label>
|
||||
<select id="waitTime" name="waitTime" [(ngModel)]="waitTime" class="form-control" [disabled]="readOnly">
|
||||
<option *ngFor="let o of waitTimes" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
<small class="text-muted">{{'waitTimeDesc' | i18n}}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary" [disabled]="loading || readOnly">
|
||||
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}" aria-hidden="true" *ngIf="loading"></i>
|
||||
<span *ngIf="!loading">{{'save' | i18n}}</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary"
|
||||
data-dismiss="modal">{{'cancel' | i18n}}</button>
|
||||
<div class="ml-auto">
|
||||
<button #deleteBtn type="button" (click)="delete()" class="btn btn-outline-danger"
|
||||
appA11yTitle="{{'delete' | i18n}}" *ngIf="editMode" [disabled]="deleteBtn.loading"
|
||||
[appApiAction]="deletePromise">
|
||||
<i class="fa fa-trash-o fa-lg fa-fw" [hidden]="deleteBtn.loading" aria-hidden="true"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!deleteBtn.loading"
|
||||
title="{{'loading' | i18n}}" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
99
src/app/settings/emergency-access-add-edit.component.ts
Normal file
99
src/app/settings/emergency-access-add-edit.component.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnInit,
|
||||
Output,
|
||||
} from '@angular/core';
|
||||
|
||||
import { ToasterService } from 'angular2-toaster';
|
||||
|
||||
import { ApiService } from 'jslib/abstractions/api.service';
|
||||
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||
|
||||
import { EmergencyAccessType } from 'jslib/enums/emergencyAccessType';
|
||||
import { EmergencyAccessInviteRequest } from 'jslib/models/request/emergencyAccessInviteRequest';
|
||||
import { EmergencyAccessUpdateRequest } from 'jslib/models/request/emergencyAccessUpdateRequest';
|
||||
|
||||
@Component({
|
||||
selector: 'emergency-access-add-edit',
|
||||
templateUrl: 'emergency-access-add-edit.component.html',
|
||||
})
|
||||
export class EmergencyAccessAddEditComponent implements OnInit {
|
||||
@Input() name: string;
|
||||
@Input() emergencyAccessId: string;
|
||||
@Output() onSaved = new EventEmitter();
|
||||
@Output() onDeleted = new EventEmitter();
|
||||
|
||||
loading = true;
|
||||
readOnly: boolean = false;
|
||||
editMode: boolean = false;
|
||||
title: string;
|
||||
email: string;
|
||||
type: EmergencyAccessType = EmergencyAccessType.View;
|
||||
|
||||
formPromise: Promise<any>;
|
||||
|
||||
emergencyAccessType = EmergencyAccessType;
|
||||
waitTimes: { name: string; value: number; }[];
|
||||
waitTime: number;
|
||||
|
||||
constructor(private apiService: ApiService, private i18nService: I18nService,
|
||||
private toasterService: ToasterService) { }
|
||||
|
||||
async ngOnInit() {
|
||||
this.editMode = this.loading = this.emergencyAccessId != null;
|
||||
|
||||
this.waitTimes = [
|
||||
{ name: this.i18nService.t('oneDay'), value: 1 },
|
||||
{ name: this.i18nService.t('days', '2'), value: 2 },
|
||||
{ name: this.i18nService.t('days', '7'), value: 7 },
|
||||
{ name: this.i18nService.t('days', '14'), value: 14 },
|
||||
{ name: this.i18nService.t('days', '30'), value: 30 },
|
||||
{ name: this.i18nService.t('days', '90'), value: 90 },
|
||||
];
|
||||
|
||||
if (this.editMode) {
|
||||
this.editMode = true;
|
||||
this.title = this.i18nService.t('editEmergencyContact');
|
||||
try {
|
||||
const emergencyAccess = await this.apiService.getEmergencyAccess(this.emergencyAccessId);
|
||||
this.type = emergencyAccess.type;
|
||||
this.waitTime = emergencyAccess.waitTimeDays;
|
||||
} catch { }
|
||||
} else {
|
||||
this.title = this.i18nService.t('inviteEmergencyContact');
|
||||
this.waitTime = this.waitTimes[2].value;
|
||||
}
|
||||
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
async submit() {
|
||||
try {
|
||||
if (this.editMode) {
|
||||
const request = new EmergencyAccessUpdateRequest();
|
||||
request.type = this.type;
|
||||
request.waitTimeDays = this.waitTime;
|
||||
|
||||
this.formPromise = this.apiService.putEmergencyAccess(this.emergencyAccessId, request);
|
||||
} else {
|
||||
const request = new EmergencyAccessInviteRequest();
|
||||
request.email = this.email.trim();
|
||||
request.type = this.type;
|
||||
request.waitTimeDays = this.waitTime;
|
||||
|
||||
this.formPromise = this.apiService.postEmergencyAccessInvite(request);
|
||||
}
|
||||
|
||||
await this.formPromise;
|
||||
this.toasterService.popAsync('success', null,
|
||||
this.i18nService.t(this.editMode ? 'editedUserId' : 'invitedUsers', this.name));
|
||||
this.onSaved.emit();
|
||||
} catch { }
|
||||
}
|
||||
|
||||
async delete() {
|
||||
this.onDeleted.emit();
|
||||
}
|
||||
}
|
||||
38
src/app/settings/emergency-access-confirm.component.html
Normal file
38
src/app/settings/emergency-access-confirm.component.html
Normal file
@@ -0,0 +1,38 @@
|
||||
<div class="modal fade" tabindex="-1" role="dialog" aria-modal="true" aria-labelledby="confirmUserTitle">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title" id="confirmUserTitle">
|
||||
{{'confirmUser' | i18n}}
|
||||
<small class="text-muted" *ngIf="name">{{name}}</small>
|
||||
</h2>
|
||||
<button type="button" class="close" data-dismiss="modal" appA11yTitle="{{'close' | i18n}}">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>
|
||||
{{'fingerprintEnsureIntegrityVerify' | i18n}}
|
||||
<a href="https://help.bitwarden.com/article/fingerprint-phrase/" target="_blank" rel="noopener">
|
||||
{{'learnMore' | i18n}}</a>
|
||||
</p>
|
||||
<p><code>{{fingerprint}}</code></p>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="dontAskAgain" name="DontAskAgain"
|
||||
[(ngModel)]="dontAskAgain">
|
||||
<label class="form-check-label" for="dontAskAgain">
|
||||
{{'dontAskFingerprintAgain' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading">
|
||||
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}" aria-hidden="true"></i>
|
||||
<span>{{'confirm' | i18n}}</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary"
|
||||
data-dismiss="modal">{{'cancel' | i18n}}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
62
src/app/settings/emergency-access-confirm.component.ts
Normal file
62
src/app/settings/emergency-access-confirm.component.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnInit,
|
||||
Output,
|
||||
} from '@angular/core';
|
||||
|
||||
import { ConstantsService } from 'jslib/services/constants.service';
|
||||
|
||||
import { ApiService } from 'jslib/abstractions/api.service';
|
||||
import { CryptoService } from 'jslib/abstractions/crypto.service';
|
||||
import { StorageService } from 'jslib/abstractions/storage.service';
|
||||
|
||||
import { Utils } from 'jslib/misc/utils';
|
||||
|
||||
@Component({
|
||||
selector: 'emergency-access-confirm',
|
||||
templateUrl: 'emergency-access-confirm.component.html',
|
||||
})
|
||||
export class EmergencyAccessConfirmComponent implements OnInit {
|
||||
@Input() name: string;
|
||||
@Input() userId: string;
|
||||
@Input() emergencyAccessId: string;
|
||||
@Input() formPromise: Promise<any>;
|
||||
@Output() onConfirmed = new EventEmitter();
|
||||
|
||||
dontAskAgain = false;
|
||||
loading = true;
|
||||
fingerprint: string;
|
||||
|
||||
constructor(private apiService: ApiService, private cryptoService: CryptoService,
|
||||
private storageService: StorageService) { }
|
||||
|
||||
async ngOnInit() {
|
||||
try {
|
||||
const publicKeyResponse = await this.apiService.getUserPublicKey(this.userId);
|
||||
if (publicKeyResponse != null) {
|
||||
const publicKey = Utils.fromB64ToArray(publicKeyResponse.publicKey);
|
||||
const fingerprint = await this.cryptoService.getFingerprint(this.userId, publicKey.buffer);
|
||||
if (fingerprint != null) {
|
||||
this.fingerprint = fingerprint.join('-');
|
||||
}
|
||||
}
|
||||
} catch { }
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
async submit() {
|
||||
if (this.loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.dontAskAgain) {
|
||||
await this.storageService.save(ConstantsService.autoConfirmFingerprints, true);
|
||||
}
|
||||
|
||||
try {
|
||||
this.onConfirmed.emit();
|
||||
} catch { }
|
||||
}
|
||||
}
|
||||
44
src/app/settings/emergency-access-takeover.component.html
Normal file
44
src/app/settings/emergency-access-takeover.component.html
Normal file
@@ -0,0 +1,44 @@
|
||||
<div class="modal fade" tabindex="-1" role="dialog" aria-modal="true" aria-labelledby="userAddEditTitle">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate>
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title" id="userAddEditTitle">
|
||||
{{'takeover' | i18n}}
|
||||
<small class="text-muted" *ngIf="name">{{name}}</small>
|
||||
</h2>
|
||||
<button type="button" class="close" data-dismiss="modal" appA11yTitle="{{'close' | i18n}}">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<app-callout type="warning">{{'loggedOutWarning' | i18n}}</app-callout>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="form-group">
|
||||
<label for="masterPassword">{{'newMasterPass' | i18n}}</label>
|
||||
<input id="masterPassword" type="password" name="NewMasterPasswordHash" class="form-control mb-1"
|
||||
[(ngModel)]="masterPassword" (input)="updatePasswordStrength()" required appInputVerbatim
|
||||
autocomplete="new-password">
|
||||
<app-password-strength [score]="masterPasswordScore" [showText]="true"></app-password-strength>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="form-group">
|
||||
<label for="masterPasswordRetype">{{'confirmNewMasterPass' | i18n}}</label>
|
||||
<input id="masterPasswordRetype" type="password" name="MasterPasswordRetype"
|
||||
class="form-control" [(ngModel)]="masterPasswordRetype" required appInputVerbatim
|
||||
autocomplete="new-password">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading">
|
||||
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}" aria-hidden="true"></i>
|
||||
<span>{{'save' | i18n}}</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">{{'cancel' | i18n}}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
81
src/app/settings/emergency-access-takeover.component.ts
Normal file
81
src/app/settings/emergency-access-takeover.component.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnInit,
|
||||
Output,
|
||||
} from '@angular/core';
|
||||
|
||||
import { ToasterService } from 'angular2-toaster';
|
||||
|
||||
import { ApiService } from 'jslib/abstractions/api.service';
|
||||
import { CryptoService } from 'jslib/abstractions/crypto.service';
|
||||
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||
import { MessagingService } from 'jslib/abstractions/messaging.service';
|
||||
import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service';
|
||||
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||
import { PolicyService } from 'jslib/abstractions/policy.service';
|
||||
import { UserService } from 'jslib/abstractions/user.service';
|
||||
import { ChangePasswordComponent } from 'jslib/angular/components/change-password.component';
|
||||
|
||||
import { KdfType } from 'jslib/enums/kdfType';
|
||||
import { SymmetricCryptoKey } from 'jslib/models/domain/symmetricCryptoKey';
|
||||
import { EmergencyAccessPasswordRequest } from 'jslib/models/request/emergencyAccessPasswordRequest';
|
||||
|
||||
@Component({
|
||||
selector: 'emergency-access-takeover',
|
||||
templateUrl: 'emergency-access-takeover.component.html',
|
||||
})
|
||||
export class EmergencyAccessTakeoverComponent extends ChangePasswordComponent implements OnInit {
|
||||
@Output() onDone = new EventEmitter();
|
||||
@Input() emergencyAccessId: string;
|
||||
@Input() name: string;
|
||||
@Input() email: string;
|
||||
@Input() kdf: KdfType;
|
||||
@Input() kdfIterations: number;
|
||||
|
||||
formPromise: Promise<any>;
|
||||
|
||||
constructor(i18nService: I18nService, cryptoService: CryptoService,
|
||||
messagingService: MessagingService, userService: UserService,
|
||||
passwordGenerationService: PasswordGenerationService,
|
||||
platformUtilsService: PlatformUtilsService, policyService: PolicyService,
|
||||
private apiService: ApiService, private toasterService: ToasterService) {
|
||||
super(i18nService, cryptoService, messagingService, userService, passwordGenerationService,
|
||||
platformUtilsService, policyService);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line
|
||||
async ngOnInit() { }
|
||||
|
||||
async submit() {
|
||||
if (!await this.strongPassword()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const takeoverResponse = await this.apiService.postEmergencyAccessTakeover(this.emergencyAccessId);
|
||||
|
||||
const oldKeyBuffer = await this.cryptoService.rsaDecrypt(takeoverResponse.keyEncrypted);
|
||||
const oldEncKey = new SymmetricCryptoKey(oldKeyBuffer);
|
||||
|
||||
if (oldEncKey == null) {
|
||||
this.toasterService.popAsync('error', this.i18nService.t('errorOccurred'), this.i18nService.t('unexpectedError'));
|
||||
return;
|
||||
}
|
||||
|
||||
const key = await this.cryptoService.makeKey(this.masterPassword, this.email, takeoverResponse.kdf, takeoverResponse.kdfIterations);
|
||||
const masterPasswordHash = await this.cryptoService.hashPassword(this.masterPassword, key);
|
||||
|
||||
const encKey = await this.cryptoService.remakeEncKey(key, oldEncKey);
|
||||
|
||||
const request = new EmergencyAccessPasswordRequest();
|
||||
request.newMasterPasswordHash = masterPasswordHash;
|
||||
request.key = encKey[1].encryptedString;
|
||||
|
||||
this.apiService.postEmergencyAccessPassword(this.emergencyAccessId, request);
|
||||
|
||||
try {
|
||||
this.onDone.emit();
|
||||
} catch { }
|
||||
}
|
||||
}
|
||||
31
src/app/settings/emergency-access-view.component.html
Normal file
31
src/app/settings/emergency-access-view.component.html
Normal file
@@ -0,0 +1,31 @@
|
||||
<div class="page-header">
|
||||
<h1>{{'vault' | i18n}}</h1>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<ng-container *ngIf="ciphers.length">
|
||||
<table class="table table-hover table-list table-ciphers">
|
||||
<tbody>
|
||||
<tr *ngFor="let c of ciphers">
|
||||
<td class="table-list-icon">
|
||||
<app-vault-icon [cipher]="c"></app-vault-icon>
|
||||
</td>
|
||||
<td class="reduced-lh wrap">
|
||||
<a href="#" appStopClick (click)="selectCipher(c)" title="{{'editItem' | i18n}}">{{c.name}}</a>
|
||||
<ng-container *ngIf="!organization && c.organizationId">
|
||||
<i class="fa fa-share-alt" appStopProp title="{{'shared' | i18n}}" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{'shared' | i18n}}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="c.hasAttachments">
|
||||
<i class="fa fa-paperclip" appStopProp title="{{'attachments' | i18n}}"
|
||||
aria-hidden="true"></i>
|
||||
<span class="sr-only">{{'attachments' | i18n}}</span>
|
||||
</ng-container>
|
||||
<br>
|
||||
<small>{{c.subTitle}}</small>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</ng-container>
|
||||
</div>
|
||||
<ng-template #cipherAddEdit></ng-template>
|
||||
94
src/app/settings/emergency-access-view.component.ts
Normal file
94
src/app/settings/emergency-access-view.component.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import {
|
||||
Component,
|
||||
ComponentFactoryResolver,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
ViewContainerRef,
|
||||
} from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
|
||||
import { ApiService } from 'jslib/abstractions/api.service';
|
||||
import { CipherService } from 'jslib/abstractions/cipher.service';
|
||||
import { CryptoService } from 'jslib/abstractions/crypto.service';
|
||||
|
||||
import { CipherData } from 'jslib/models/data';
|
||||
import { Cipher, SymmetricCryptoKey } from 'jslib/models/domain';
|
||||
import { EmergencyAccessViewResponse } from 'jslib/models/response/emergencyAccessResponse';
|
||||
import { CipherView } from 'jslib/models/view/cipherView';
|
||||
|
||||
import { ModalComponent } from '../modal.component';
|
||||
|
||||
import { EmergencyAddEditComponent } from './emergency-add-edit.component';
|
||||
|
||||
@Component({
|
||||
selector: 'emergency-access-view',
|
||||
templateUrl: 'emergency-access-view.component.html',
|
||||
})
|
||||
export class EmergencyAccessViewComponent implements OnInit {
|
||||
@ViewChild('cipherAddEdit', { read: ViewContainerRef, static: true }) cipherAddEditModalRef: ViewContainerRef;
|
||||
|
||||
id: string;
|
||||
ciphers: CipherView[] = [];
|
||||
|
||||
private modal: ModalComponent = null;
|
||||
|
||||
constructor(private cipherService: CipherService, private cryptoService: CryptoService,
|
||||
private componentFactoryResolver: ComponentFactoryResolver, private router: Router,
|
||||
private route: ActivatedRoute, private apiService: ApiService) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.route.params.subscribe((qParams) => {
|
||||
if (qParams.id == null) {
|
||||
return this.router.navigate(['settings/emergency-access']);
|
||||
}
|
||||
|
||||
this.id = qParams.id;
|
||||
|
||||
this.load();
|
||||
});
|
||||
}
|
||||
|
||||
selectCipher(cipher: CipherView) {
|
||||
if (this.modal != null) {
|
||||
this.modal.close();
|
||||
}
|
||||
|
||||
const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent);
|
||||
this.modal = this.cipherAddEditModalRef.createComponent(factory).instance;
|
||||
const childComponent = this.modal.show<EmergencyAddEditComponent>(EmergencyAddEditComponent, this.cipherAddEditModalRef);
|
||||
|
||||
childComponent.cipherId = cipher == null ? null : cipher.id;
|
||||
childComponent.cipher = cipher;
|
||||
|
||||
this.modal.onClosed.subscribe(() => {
|
||||
this.modal = null;
|
||||
});
|
||||
|
||||
return childComponent;
|
||||
}
|
||||
|
||||
async load() {
|
||||
const response = await this.apiService.postEmergencyAccessView(this.id);
|
||||
this.ciphers = await this.getAllCiphers(response);
|
||||
}
|
||||
|
||||
protected async getAllCiphers(response: EmergencyAccessViewResponse): Promise<CipherView[]> {
|
||||
const ciphers = response.ciphers;
|
||||
|
||||
const decCiphers: CipherView[] = [];
|
||||
const oldKeyBuffer = await this.cryptoService.rsaDecrypt(response.keyEncrypted);
|
||||
const oldEncKey = new SymmetricCryptoKey(oldKeyBuffer);
|
||||
|
||||
const promises: any[] = [];
|
||||
ciphers.forEach((cipherResponse) => {
|
||||
const cipherData = new CipherData(cipherResponse);
|
||||
const cipher = new Cipher(cipherData);
|
||||
promises.push(cipher.decrypt(oldEncKey).then((c) => decCiphers.push(c)));
|
||||
});
|
||||
|
||||
await Promise.all(promises);
|
||||
decCiphers.sort(this.cipherService.getLocaleSortingFunction());
|
||||
|
||||
return decCiphers;
|
||||
}
|
||||
}
|
||||
159
src/app/settings/emergency-access.component.html
Normal file
159
src/app/settings/emergency-access.component.html
Normal file
@@ -0,0 +1,159 @@
|
||||
<div class="page-header">
|
||||
<h1>{{'emergencyAccess' | i18n}}</h1>
|
||||
</div>
|
||||
<p>
|
||||
{{'emergencyAccessDesc' | i18n}}
|
||||
<a href="https://help.bitwarden.com/article/fingerprint-phrase/" target="_blank" rel="noopener">
|
||||
{{'learnMore' | i18n}}.
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<div class="page-header d-flex">
|
||||
<h2>
|
||||
{{'trustedEmergencyContacts' | i18n}}
|
||||
<a href="#" appStopClick class="badge badge-primary" *ngIf="!canAccessPremium" (click)="premiumRequired()">
|
||||
{{'premium' | i18n}}
|
||||
</a>
|
||||
</h2>
|
||||
<div class="ml-auto d-flex">
|
||||
<button class="btn btn-sm btn-outline-primary ml-3" type="button" (click)="invite()" [disabled]="!canAccessPremium">
|
||||
<i aria-hidden="true" class="fa fa-plus fa-fw"></i>
|
||||
{{'addEmergencyContact' |i18n}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="table table-hover table-list mb-0" *ngIf="trustedContacts && trustedContacts.length">
|
||||
<tbody>
|
||||
<tr *ngFor="let c of trustedContacts; let i = index">
|
||||
<td width="30">
|
||||
<app-avatar [data]="c.name || c.email" [email]="c.email" size="25" [circle]="true"
|
||||
[fontSize]="14"></app-avatar>
|
||||
</td>
|
||||
<td>
|
||||
<a href="#" appStopClick (click)="edit(c)">{{c.email}}</a>
|
||||
<span class="badge badge-secondary"
|
||||
*ngIf="c.status === emergencyAccessStatusType.Invited">{{'invited' | i18n}}</span>
|
||||
<span class="badge badge-warning"
|
||||
*ngIf="c.status === emergencyAccessStatusType.Accepted">{{'accepted' | i18n}}</span>
|
||||
<span class="badge badge-warning"
|
||||
*ngIf="c.status === emergencyAccessStatusType.RecoveryInitiated">{{'emergencyAccessRecoveryInitiated' | i18n}}</span>
|
||||
<span class="badge badge-success"
|
||||
*ngIf="c.status === emergencyAccessStatusType.RecoveryApproved">{{'emergencyAccessRecoveryApproved' | i18n}}</span>
|
||||
|
||||
<span class="badge badge-primary"
|
||||
*ngIf="c.type === emergencyAccessType.View">{{'view' | i18n}}</span>
|
||||
<span class="badge badge-primary"
|
||||
*ngIf="c.type === emergencyAccessType.Takeover">{{'takeover' | i18n}}</span>
|
||||
|
||||
<small class="text-muted d-block" *ngIf="c.name">{{c.name}}</small>
|
||||
</td>
|
||||
<td class="table-list-options">
|
||||
<div class="dropdown" appListDropdown>
|
||||
<button class="btn btn-outline-secondary dropdown-toggle" type="button"
|
||||
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
|
||||
appA11yTitle="{{'options' | i18n}}">
|
||||
<i class="fa fa-cog fa-lg" aria-hidden="true"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item" href="#" appStopClick (click)="reinvite(c)"
|
||||
*ngIf="c.status === emergencyAccessStatusType.Invited">
|
||||
<i class="fa fa-fw fa-envelope-o" aria-hidden="true"></i>
|
||||
{{'resendInvitation' | i18n}}
|
||||
</a>
|
||||
<a class="dropdown-item text-success" href="#" appStopClick (click)="confirm(c)"
|
||||
*ngIf="c.status === emergencyAccessStatusType.Accepted">
|
||||
<i class="fa fa-fw fa-check" aria-hidden="true"></i>
|
||||
{{'confirm' | i18n}}
|
||||
</a>
|
||||
<a class="dropdown-item text-success" href="#" appStopClick (click)="approve(c)"
|
||||
*ngIf="c.status === emergencyAccessStatusType.RecoveryInitiated">
|
||||
<i class="fa fa-fw fa-check" aria-hidden="true"></i>
|
||||
{{'approve' | i18n}}
|
||||
</a>
|
||||
<a class="dropdown-item text-warning" href="#" appStopClick (click)="reject(c)"
|
||||
*ngIf="c.status === emergencyAccessStatusType.RecoveryInitiated || c.status === emergencyAccessStatusType.RecoveryApproved">
|
||||
<i class="fa fa-fw fa-remove" aria-hidden="true"></i>
|
||||
{{'reject' | i18n}}
|
||||
</a>
|
||||
<a class="dropdown-item text-danger" href="#" appStopClick (click)="remove(c)">
|
||||
<i class="fa fa-fw fa-remove" aria-hidden="true"></i>
|
||||
{{'remove' | i18n}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p *ngIf="!trustedContacts || !trustedContacts.length">{{'noTrustedContacts' | i18n}}</p>
|
||||
|
||||
<div class="page-header spaced-header">
|
||||
<h2>{{'designatedEmergencyContacts' | i18n}}</h2>
|
||||
</div>
|
||||
|
||||
<table class="table table-hover table-list mb-0" *ngIf="grantedContacts && grantedContacts.length">
|
||||
<tbody>
|
||||
<tr *ngFor="let c of grantedContacts; let i = index">
|
||||
<td width="30">
|
||||
<app-avatar [data]="c.name || c.email" [email]="c.email" size="25" [circle]="true"
|
||||
[fontSize]="14"></app-avatar>
|
||||
</td>
|
||||
<td>
|
||||
<span>{{c.email}}</span>
|
||||
<span class="badge badge-secondary"
|
||||
*ngIf="c.status === emergencyAccessStatusType.Invited">{{'invited' | i18n}}</span>
|
||||
<span class="badge badge-warning"
|
||||
*ngIf="c.status === emergencyAccessStatusType.Accepted">{{'accepted' | i18n}}</span>
|
||||
<span class="badge badge-warning"
|
||||
*ngIf="c.status === emergencyAccessStatusType.RecoveryInitiated">{{'emergencyAccessRecoveryInitiated' | i18n}}</span>
|
||||
<span class="badge badge-success"
|
||||
*ngIf="c.status === emergencyAccessStatusType.RecoveryApproved">{{'emergencyAccessRecoveryApproved' | i18n}}</span>
|
||||
|
||||
<span class="badge badge-primary"
|
||||
*ngIf="c.type === emergencyAccessType.View">{{'view' | i18n}}</span>
|
||||
<span class="badge badge-primary"
|
||||
*ngIf="c.type === emergencyAccessType.Takeover">{{'takeover' | i18n}}</span>
|
||||
|
||||
<small class="text-muted d-block" *ngIf="c.name">{{c.name}}</small>
|
||||
</td>
|
||||
<td class="table-list-options">
|
||||
<div class="dropdown" appListDropdown>
|
||||
<button class="btn btn-outline-secondary dropdown-toggle" type="button"
|
||||
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
|
||||
appA11yTitle="{{'options' | i18n}}">
|
||||
<i class="fa fa-cog fa-lg" aria-hidden="true"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item" href="#" appStopClick (click)="requestAccess(c)"
|
||||
*ngIf="c.status === emergencyAccessStatusType.Confirmed">
|
||||
<i class="fa fa-fw fa-envelope-o" aria-hidden="true"></i>
|
||||
{{'requestAccess' | i18n}}
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" appStopClick (click)="takeover(c)"
|
||||
*ngIf="c.status === emergencyAccessStatusType.RecoveryApproved && c.type === emergencyAccessType.Takeover">
|
||||
<i class="fa fa-fw fa-key" aria-hidden="true"></i>
|
||||
{{'takeover' | i18n}}
|
||||
</a>
|
||||
<a class="dropdown-item" [routerLink]="c.id"
|
||||
*ngIf="c.status === emergencyAccessStatusType.RecoveryApproved && c.type === emergencyAccessType.View">
|
||||
<i class="fa fa-fw fa-eye" aria-hidden="true"></i>
|
||||
{{'view' | i18n}}
|
||||
</a>
|
||||
<a class="dropdown-item text-danger" href="#" appStopClick (click)="remove(c)">
|
||||
<i class="fa fa-fw fa-remove" aria-hidden="true"></i>
|
||||
{{'remove' | i18n}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p *ngIf="!grantedContacts || !grantedContacts.length">{{'noGrantedAccess' | i18n}}</p>
|
||||
|
||||
<ng-template #addEdit></ng-template>
|
||||
<ng-template #takeoverTemplate></ng-template>
|
||||
<ng-template #confirmTemplate></ng-template>
|
||||
276
src/app/settings/emergency-access.component.ts
Normal file
276
src/app/settings/emergency-access.component.ts
Normal file
@@ -0,0 +1,276 @@
|
||||
import { Component, ComponentFactoryResolver, OnInit, ViewChild, ViewContainerRef } from '@angular/core';
|
||||
import { ToasterService } from 'angular2-toaster';
|
||||
|
||||
import { ApiService } from 'jslib/abstractions/api.service';
|
||||
import { CryptoService } from 'jslib/abstractions/crypto.service';
|
||||
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||
import { MessagingService } from 'jslib/abstractions/messaging.service';
|
||||
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||
import { StorageService } from 'jslib/abstractions/storage.service';
|
||||
import { UserService } from 'jslib/abstractions/user.service';
|
||||
|
||||
import { EmergencyAccessStatusType } from 'jslib/enums/emergencyAccessStatusType';
|
||||
import { EmergencyAccessType } from 'jslib/enums/emergencyAccessType';
|
||||
import { Utils } from 'jslib/misc/utils';
|
||||
import { EmergencyAccessConfirmRequest } from 'jslib/models/request/emergencyAccessConfirmRequest';
|
||||
import { EmergencyAccessGranteeDetailsResponse, EmergencyAccessGrantorDetailsResponse } from 'jslib/models/response/emergencyAccessResponse';
|
||||
import { ConstantsService } from 'jslib/services/constants.service';
|
||||
|
||||
import { ModalComponent } from '../modal.component';
|
||||
import { EmergencyAccessAddEditComponent } from './emergency-access-add-edit.component';
|
||||
import { EmergencyAccessConfirmComponent } from './emergency-access-confirm.component';
|
||||
import { EmergencyAccessTakeoverComponent } from './emergency-access-takeover.component';
|
||||
|
||||
@Component({
|
||||
selector: 'emergency-access',
|
||||
templateUrl: 'emergency-access.component.html',
|
||||
})
|
||||
export class EmergencyAccessComponent implements OnInit {
|
||||
@ViewChild('addEdit', { read: ViewContainerRef, static: true }) addEditModalRef: ViewContainerRef;
|
||||
@ViewChild('takeoverTemplate', { read: ViewContainerRef, static: true}) takeoverModalRef: ViewContainerRef;
|
||||
@ViewChild('confirmTemplate', { read: ViewContainerRef, static: true }) confirmModalRef: ViewContainerRef;
|
||||
|
||||
canAccessPremium: boolean;
|
||||
trustedContacts: EmergencyAccessGranteeDetailsResponse[];
|
||||
grantedContacts: EmergencyAccessGrantorDetailsResponse[];
|
||||
emergencyAccessType = EmergencyAccessType;
|
||||
emergencyAccessStatusType = EmergencyAccessStatusType;
|
||||
actionPromise: Promise<any>;
|
||||
|
||||
private modal: ModalComponent = null;
|
||||
|
||||
constructor(private apiService: ApiService, private i18nService: I18nService,
|
||||
private componentFactoryResolver: ComponentFactoryResolver,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private toasterService: ToasterService, private cryptoService: CryptoService,
|
||||
private storageService: StorageService, private userService: UserService,
|
||||
private messagingService: MessagingService) { }
|
||||
|
||||
async ngOnInit() {
|
||||
this.canAccessPremium = await this.userService.canAccessPremium();
|
||||
this.load();
|
||||
}
|
||||
|
||||
async load() {
|
||||
this.trustedContacts = (await this.apiService.getEmergencyAccessTrusted()).data;
|
||||
this.grantedContacts = (await this.apiService.getEmergencyAccessGranted()).data;
|
||||
}
|
||||
|
||||
async premiumRequired() {
|
||||
if (!this.canAccessPremium) {
|
||||
this.messagingService.send('premiumRequired');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
edit(details: EmergencyAccessGranteeDetailsResponse) {
|
||||
if (this.modal != null) {
|
||||
this.modal.close();
|
||||
}
|
||||
|
||||
const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent);
|
||||
this.modal = this.addEditModalRef.createComponent(factory).instance;
|
||||
const childComponent = this.modal.show<EmergencyAccessAddEditComponent>(
|
||||
EmergencyAccessAddEditComponent, this.addEditModalRef);
|
||||
|
||||
childComponent.name = details?.name ?? details?.email;
|
||||
childComponent.emergencyAccessId = details?.id;
|
||||
childComponent.readOnly = !this.canAccessPremium;
|
||||
childComponent.onSaved.subscribe(() => {
|
||||
this.modal.close();
|
||||
this.load();
|
||||
});
|
||||
childComponent.onDeleted.subscribe(() => {
|
||||
this.modal.close();
|
||||
this.remove(details);
|
||||
});
|
||||
|
||||
this.modal.onClosed.subscribe(() => {
|
||||
this.modal = null;
|
||||
});
|
||||
}
|
||||
|
||||
invite() {
|
||||
this.edit(null);
|
||||
}
|
||||
|
||||
async reinvite(contact: EmergencyAccessGranteeDetailsResponse) {
|
||||
if (this.actionPromise != null) {
|
||||
return;
|
||||
}
|
||||
this.actionPromise = this.apiService.postEmergencyAccessReinvite(contact.id);
|
||||
await this.actionPromise;
|
||||
this.toasterService.popAsync('success', null, this.i18nService.t('hasBeenReinvited', contact.email));
|
||||
this.actionPromise = null;
|
||||
}
|
||||
|
||||
async confirm(contact: EmergencyAccessGranteeDetailsResponse) {
|
||||
function updateUser() {
|
||||
contact.status = EmergencyAccessStatusType.Confirmed;
|
||||
}
|
||||
|
||||
if (this.actionPromise != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const autoConfirm = await this.storageService.get<boolean>(ConstantsService.autoConfirmFingerprints);
|
||||
if (autoConfirm == null || !autoConfirm) {
|
||||
if (this.modal != null) {
|
||||
this.modal.close();
|
||||
}
|
||||
|
||||
const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent);
|
||||
this.modal = this.confirmModalRef.createComponent(factory).instance;
|
||||
const childComponent = this.modal.show<EmergencyAccessConfirmComponent>(
|
||||
EmergencyAccessConfirmComponent, this.confirmModalRef);
|
||||
|
||||
childComponent.name = contact?.name ?? contact?.email;
|
||||
childComponent.emergencyAccessId = contact.id;
|
||||
childComponent.userId = contact?.granteeId;
|
||||
childComponent.onConfirmed.subscribe(async () => {
|
||||
this.modal.close();
|
||||
|
||||
childComponent.formPromise = this.doConfirmation(contact);
|
||||
await childComponent.formPromise;
|
||||
|
||||
updateUser();
|
||||
this.toasterService.popAsync('success', null, this.i18nService.t('hasBeenConfirmed', contact.name || contact.email));
|
||||
});
|
||||
|
||||
this.modal.onClosed.subscribe(() => {
|
||||
this.modal = null;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.actionPromise = this.doConfirmation(contact);
|
||||
await this.actionPromise;
|
||||
updateUser();
|
||||
|
||||
this.toasterService.popAsync('success', null, this.i18nService.t('hasBeenConfirmed', contact.name || contact.email));
|
||||
this.actionPromise = null;
|
||||
}
|
||||
|
||||
async remove(details: EmergencyAccessGranteeDetailsResponse | EmergencyAccessGrantorDetailsResponse) {
|
||||
const confirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t('removeUserConfirmation'), details.name || details.email,
|
||||
this.i18nService.t('yes'), this.i18nService.t('no'), 'warning');
|
||||
if (!confirmed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.apiService.deleteEmergencyAccess(details.id);
|
||||
this.toasterService.popAsync('success', null, this.i18nService.t('removedUserId', details.name || details.email));
|
||||
|
||||
if (details instanceof EmergencyAccessGranteeDetailsResponse) {
|
||||
this.removeGrantee(details);
|
||||
} else {
|
||||
this.removeGrantor(details);
|
||||
}
|
||||
} catch { }
|
||||
}
|
||||
|
||||
async requestAccess(details: EmergencyAccessGrantorDetailsResponse) {
|
||||
const confirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t('requestAccessConfirmation', details.waitTimeDays.toString()),
|
||||
details.name || details.email,
|
||||
this.i18nService.t('requestAccess'),
|
||||
this.i18nService.t('no'),
|
||||
'warning',
|
||||
);
|
||||
|
||||
if (!confirmed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await this.apiService.postEmergencyAccessInitiate(details.id);
|
||||
|
||||
details.status = EmergencyAccessStatusType.RecoveryInitiated;
|
||||
this.toasterService.popAsync('success', null, this.i18nService.t('requestSent', details.name || details.email));
|
||||
}
|
||||
|
||||
async approve(details: EmergencyAccessGranteeDetailsResponse) {
|
||||
const type = this.i18nService.t(details.type === EmergencyAccessType.View ? 'view' : 'takeover');
|
||||
|
||||
const confirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t('approveAccessConfirmation', details.name, type),
|
||||
details.name || details.email,
|
||||
this.i18nService.t('approve'),
|
||||
this.i18nService.t('no'),
|
||||
'warning',
|
||||
);
|
||||
|
||||
if (!confirmed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await this.apiService.postEmergencyAccessApprove(details.id);
|
||||
details.status = EmergencyAccessStatusType.RecoveryApproved;
|
||||
|
||||
this.toasterService.popAsync('success', null, this.i18nService.t('emergencyApproved', details.name || details.email));
|
||||
}
|
||||
|
||||
async reject(details: EmergencyAccessGranteeDetailsResponse) {
|
||||
await this.apiService.postEmergencyAccessReject(details.id);
|
||||
details.status = EmergencyAccessStatusType.Confirmed;
|
||||
|
||||
this.toasterService.popAsync('success', null, this.i18nService.t('emergencyRejected', details.name || details.email));
|
||||
}
|
||||
|
||||
async takeover(details: EmergencyAccessGrantorDetailsResponse) {
|
||||
if (this.modal != null) {
|
||||
this.modal.close();
|
||||
}
|
||||
|
||||
const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent);
|
||||
this.modal = this.addEditModalRef.createComponent(factory).instance;
|
||||
const childComponent = this.modal.show<EmergencyAccessTakeoverComponent>(
|
||||
EmergencyAccessTakeoverComponent, this.takeoverModalRef);
|
||||
|
||||
childComponent.name = details != null ? details.name || details.email : null;
|
||||
childComponent.email = details.email;
|
||||
childComponent.emergencyAccessId = details != null ? details.id : null;
|
||||
|
||||
childComponent.onDone.subscribe(() => {
|
||||
this.modal.close();
|
||||
this.toasterService.popAsync('success', null, this.i18nService.t('passwordResetFor', details.name || details.email));
|
||||
});
|
||||
|
||||
this.modal.onClosed.subscribe(() => {
|
||||
this.modal = null;
|
||||
});
|
||||
}
|
||||
|
||||
private removeGrantee(details: EmergencyAccessGranteeDetailsResponse) {
|
||||
const index = this.trustedContacts.indexOf(details);
|
||||
if (index > -1) {
|
||||
this.trustedContacts.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private removeGrantor(details: EmergencyAccessGrantorDetailsResponse) {
|
||||
const index = this.grantedContacts.indexOf(details);
|
||||
if (index > -1) {
|
||||
this.grantedContacts.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Encrypt the master password hash using the grantees public key, and send it to bitwarden for escrow.
|
||||
private async doConfirmation(details: EmergencyAccessGranteeDetailsResponse) {
|
||||
const encKey = await this.cryptoService.getEncKey();
|
||||
const publicKeyResponse = await this.apiService.getUserPublicKey(details.granteeId);
|
||||
const publicKey = Utils.fromB64ToArray(publicKeyResponse.publicKey);
|
||||
|
||||
try {
|
||||
// tslint:disable-next-line
|
||||
console.log('User\'s fingerprint: ' +
|
||||
(await this.cryptoService.getFingerprint(details.granteeId, publicKey.buffer)).join('-'));
|
||||
} catch { }
|
||||
|
||||
const encryptedKey = await this.cryptoService.rsaEncrypt(encKey.key, publicKey.buffer);
|
||||
const request = new EmergencyAccessConfirmRequest();
|
||||
request.key = encryptedKey.encryptedString;
|
||||
await this.apiService.postEmergencyAccessConfirm(details.id, request);
|
||||
}
|
||||
}
|
||||
47
src/app/settings/emergency-add-edit.component.ts
Normal file
47
src/app/settings/emergency-add-edit.component.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import { AuditService } from 'jslib/abstractions/audit.service';
|
||||
import { CipherService } from 'jslib/abstractions/cipher.service';
|
||||
import { CollectionService } from 'jslib/abstractions/collection.service';
|
||||
import { EventService } from 'jslib/abstractions/event.service';
|
||||
import { FolderService } from 'jslib/abstractions/folder.service';
|
||||
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||
import { MessagingService } from 'jslib/abstractions/messaging.service';
|
||||
import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service';
|
||||
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||
import { PolicyService } from 'jslib/abstractions/policy.service';
|
||||
import { StateService } from 'jslib/abstractions/state.service';
|
||||
import { TotpService } from 'jslib/abstractions/totp.service';
|
||||
import { UserService } from 'jslib/abstractions/user.service';
|
||||
|
||||
import { Cipher } from 'jslib/models/domain/cipher';
|
||||
|
||||
import { AddEditComponent as BaseAddEditComponent } from '../vault/add-edit.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-org-vault-add-edit',
|
||||
templateUrl: '../vault/add-edit.component.html',
|
||||
})
|
||||
export class EmergencyAddEditComponent extends BaseAddEditComponent {
|
||||
originalCipher: Cipher = null;
|
||||
viewOnly = true;
|
||||
|
||||
constructor(cipherService: CipherService, folderService: FolderService,
|
||||
i18nService: I18nService, platformUtilsService: PlatformUtilsService,
|
||||
auditService: AuditService, stateService: StateService,
|
||||
userService: UserService, collectionService: CollectionService,
|
||||
totpService: TotpService, passwordGenerationService: PasswordGenerationService,
|
||||
messagingService: MessagingService, eventService: EventService, policyService: PolicyService) {
|
||||
super(cipherService, folderService, i18nService, platformUtilsService, auditService, stateService,
|
||||
userService, collectionService, totpService, passwordGenerationService, messagingService,
|
||||
eventService, policyService);
|
||||
}
|
||||
|
||||
async load() {
|
||||
this.title = this.i18nService.t('viewItem');
|
||||
}
|
||||
|
||||
protected async loadCipher() {
|
||||
return Promise.resolve(this.originalCipher);
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import { Organization } from 'jslib/models/domain/organization';
|
||||
})
|
||||
export class LinkSsoComponent extends SsoComponent implements AfterContentInit {
|
||||
@Input() organization: Organization;
|
||||
returnUri: string = '/settings/organizations'
|
||||
returnUri: string = '/settings/organizations';
|
||||
|
||||
constructor(platformUtilsService: PlatformUtilsService, i18nService: I18nService,
|
||||
apiService: ApiService, authService: AuthService,
|
||||
|
||||
@@ -54,6 +54,9 @@
|
||||
<small *ngIf="selectableProduct.hasSelfHost">• {{'onPremHostingOptional' | i18n}}</small>
|
||||
<small *ngIf="selectableProduct.hasSso">• {{'includeSsoAuthentication' | i18n}}</small>
|
||||
<small *ngIf="selectableProduct.hasPolicies">• {{'includeEnterprisePolicies' | i18n}}</small>
|
||||
<small *ngIf="selectableProduct.trialPeriodDays">•
|
||||
{{'xDayFreeTrial' | i18n : selectableProduct.trialPeriodDays }}
|
||||
</small>
|
||||
</ng-container>
|
||||
<ng-template #fullFeatureList>
|
||||
<small *ngIf="selectableProduct.product == productTypes.Free">•
|
||||
@@ -206,21 +209,27 @@
|
||||
</label>
|
||||
</div>
|
||||
<hr class="my-3">
|
||||
<div class="text-lg">
|
||||
<strong>{{'total' | i18n}}:</strong> {{subtotal | currency:'USD $'}} /{{selectedPlanInterval | i18n}}
|
||||
<h2 class="spaced-header mb-4">{{ (createOrganization ? 'paymentInformation' : 'billingInformation') | i18n}}</h2>
|
||||
<app-payment *ngIf="createOrganization" [hideCredit]="true"></app-payment>
|
||||
<app-tax-info (onCountryChanged)="changedCountry()"></app-tax-info>
|
||||
<div id="price" class="my-4">
|
||||
<div class="text-muted text-sm">
|
||||
{{ 'planPrice' | i18n }}: {{ subtotal | currency: 'USD $' }}
|
||||
<br />
|
||||
<ng-container>
|
||||
{{ 'estimatedTax' | i18n }}: {{ taxCharges | currency: 'USD $' }}
|
||||
</ng-container>
|
||||
</div>
|
||||
<hr class="my-1 col-3 ml-0">
|
||||
<p class="text-lg"><strong>{{'total' | i18n}}:</strong>
|
||||
{{total | currency:'USD $'}}/{{selectedPlanInterval | i18n}}</p>
|
||||
</div>
|
||||
<ng-container *ngIf="createOrganization">
|
||||
<small
|
||||
class="text-muted font-italic">{{'paymentChargedWithTrial' | i18n : (selectedPlanInterval | i18n) }}</small>
|
||||
<h2 class="spaced-header mb-4">{{'paymentInformation' | i18n}}</h2>
|
||||
<app-payment [hideCredit]="true"></app-payment>
|
||||
<app-tax-info (onCountryChanged)="changedCountry()"></app-tax-info>
|
||||
</ng-container>
|
||||
<small class="text-muted font-italic">{{'paymentChargedWithTrial' | i18n : (selectedPlanInterval | i18n) }}</small>
|
||||
<ng-container *ngIf="!createOrganization">
|
||||
<app-payment [showMethods]="false"></app-payment>
|
||||
</ng-container>
|
||||
<small class="text-muted font-italic mt-2 d-block" *ngIf="!createOrganization">
|
||||
{{'paymentCharged' | i18n : (interval | i18n) }}</small>
|
||||
{{'paymentCharged' | i18n : (selectedPlanInterval | i18n) }}</small>
|
||||
</div>
|
||||
<div *ngIf="singleOrgPolicyBlock" class="mt-4">
|
||||
<app-callout [type]="'error'">{{'singleOrgBlockCreateMessage' | i18n}}</app-callout>
|
||||
|
||||
@@ -74,6 +74,9 @@ export class OrganizationPlansComponent implements OnInit {
|
||||
if (!this.selfHosted) {
|
||||
const plans = await this.apiService.getPlans();
|
||||
this.plans = plans.data;
|
||||
if (this.product === ProductType.Enterprise || this.product === ProductType.Teams) {
|
||||
this.ownedBusiness = true;
|
||||
}
|
||||
}
|
||||
this.loading = false;
|
||||
}
|
||||
@@ -159,6 +162,16 @@ export class OrganizationPlansComponent implements OnInit {
|
||||
return subTotal;
|
||||
}
|
||||
|
||||
get taxCharges() {
|
||||
return this.taxComponent != null && this.taxComponent.taxRate != null ?
|
||||
(this.taxComponent.taxRate / 100) * this.subtotal :
|
||||
0;
|
||||
}
|
||||
|
||||
get total() {
|
||||
return (this.subtotal + this.taxCharges) || 0;
|
||||
}
|
||||
|
||||
changedProduct() {
|
||||
this.plan = this.selectablePlans[0].type;
|
||||
if (!this.selectedPlan.hasPremiumAccessOption) {
|
||||
@@ -179,7 +192,8 @@ export class OrganizationPlansComponent implements OnInit {
|
||||
if (!this.ownedBusiness || this.selectedPlan.canBeUsedByBusiness) {
|
||||
return;
|
||||
}
|
||||
this.plan = PlanType.TeamsMonthly;
|
||||
this.product = ProductType.Teams;
|
||||
this.plan = PlanType.TeamsAnnually;
|
||||
}
|
||||
|
||||
changedCountry() {
|
||||
@@ -278,6 +292,9 @@ export class OrganizationPlansComponent implements OnInit {
|
||||
request.premiumAccessAddon = this.selectedPlan.hasPremiumAccessOption &&
|
||||
this.premiumAccessAddon;
|
||||
request.planType = this.selectedPlan.type;
|
||||
request.billingAddressCountry = this.taxComponent.taxInfo.country;
|
||||
request.billingAddressPostalCode = this.taxComponent.taxInfo.postalCode;
|
||||
|
||||
const result = await this.apiService.postOrganizationUpgrade(this.organizationId, request);
|
||||
if (!result.success && result.paymentIntentClientSecret != null) {
|
||||
await this.paymentComponent.handleStripeCardPayment(result.paymentIntentClientSecret, null);
|
||||
|
||||
@@ -69,13 +69,22 @@
|
||||
<br> {{'additionalStorageGb' | i18n}}: {{additionalStorage || 0}} GB × {{storageGbPrice | currency:'$'}} = {{additionalStorageTotal
|
||||
| currency:'$'}}
|
||||
<hr class="my-3">
|
||||
<div class="text-lg">
|
||||
<strong>{{'total' | i18n}}:</strong> {{total | currency:'USD $'}} /{{'year' | i18n}}
|
||||
</div>
|
||||
<small class="text-muted font-italic">{{'paymentChargedAnnually' | i18n}}</small>
|
||||
<h2 class="spaced-header mb-4">{{'paymentInformation' | i18n}}</h2>
|
||||
<app-payment [hideBank]="true"></app-payment>
|
||||
<app-tax-info></app-tax-info>
|
||||
<div id="price" class="my-4">
|
||||
<div class="text-muted text-sm">
|
||||
{{ 'planPrice' | i18n }}: {{ subtotal | currency: 'USD $' }}
|
||||
<br />
|
||||
<ng-container>
|
||||
{{ 'estimatedTax' | i18n }}: {{ taxCharges | currency: 'USD $' }}
|
||||
</ng-container>
|
||||
</div>
|
||||
<hr class="my-1 col-3 ml-0">
|
||||
<p class="text-lg"><strong>{{'total' | i18n}}:</strong>
|
||||
{{total | currency:'USD $'}}/{{'year' | i18n}}</p>
|
||||
</div>
|
||||
<small class="text-muted font-italic">{{'paymentChargedAnnually' | i18n}}</small>
|
||||
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading">
|
||||
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}" aria-hidden="true"></i>
|
||||
<span>{{'submit' | i18n}}</span>
|
||||
|
||||
@@ -114,7 +114,17 @@ export class PremiumComponent implements OnInit {
|
||||
return this.storageGbPrice * Math.abs(this.additionalStorage || 0);
|
||||
}
|
||||
|
||||
get subtotal(): number {
|
||||
return this.premiumPrice + this.additionalStorageTotal;
|
||||
}
|
||||
|
||||
get taxCharges(): number {
|
||||
return this.taxInfoComponent != null && this.taxInfoComponent.taxRate != null ?
|
||||
(this.taxInfoComponent.taxRate / 100) * this.subtotal :
|
||||
0;
|
||||
}
|
||||
|
||||
get total(): number {
|
||||
return this.additionalStorageTotal + this.premiumPrice;
|
||||
return (this.subtotal + this.taxCharges) || 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
<a routerLink="domain-rules" class="list-group-item" routerLinkActive="active">
|
||||
{{'domainRules' | i18n}}
|
||||
</a>
|
||||
<a routerLink="emergency-access" class="list-group-item" routerLinkActive="active">
|
||||
{{'emergencyAccess' | i18n}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,7 @@ import { ActivatedRoute } from '@angular/router';
|
||||
import { ApiService } from 'jslib/abstractions/api.service';
|
||||
import { OrganizationTaxInfoUpdateRequest } from 'jslib/models/request/organizationTaxInfoUpdateRequest';
|
||||
import { TaxInfoUpdateRequest } from 'jslib/models/request/taxInfoUpdateRequest';
|
||||
import { TaxRateResponse } from 'jslib/models/response/taxRateResponse';
|
||||
|
||||
@Component({
|
||||
selector: 'app-tax-info',
|
||||
@@ -28,6 +29,8 @@ export class TaxInfoComponent {
|
||||
includeTaxId: false,
|
||||
};
|
||||
|
||||
taxRates: TaxRateResponse[];
|
||||
|
||||
private pristine: any = {
|
||||
taxId: null,
|
||||
line1: null,
|
||||
@@ -77,9 +80,22 @@ export class TaxInfoComponent {
|
||||
this.onCountryChanged.emit();
|
||||
}
|
||||
});
|
||||
|
||||
const taxRates = await this.apiService.getTaxRates();
|
||||
this.taxRates = taxRates.data;
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
get taxRate() {
|
||||
if (this.taxRates != null) {
|
||||
const localTaxRate = this.taxRates.find(x =>
|
||||
x.country === this.taxInfo.country &&
|
||||
x.postalCode === this.taxInfo.postalCode
|
||||
);
|
||||
return localTaxRate?.rate ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
getTaxInfoRequest(): TaxInfoUpdateRequest {
|
||||
if (this.organizationId) {
|
||||
const request = new OrganizationTaxInfoUpdateRequest();
|
||||
|
||||
@@ -30,8 +30,8 @@ export class TwoFactorRecoveryComponent {
|
||||
'<code style="font-family: Menlo, Monaco, Consolas, \'Courier New\', monospace;">' +
|
||||
this.code + '</code></div>' +
|
||||
'<p style="text-align: center;">' + new Date() + '</p>');
|
||||
w.onafterprint = () => w.close();
|
||||
w.print();
|
||||
w.close();
|
||||
}
|
||||
|
||||
private formatString(s: string) {
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
<h1>{{'exportVault' | i18n}}</h1>
|
||||
</div>
|
||||
<p>{{'exportMasterPassword' | i18n}}</p>
|
||||
<app-callout type="warning">{{'exportWarningDesc' | i18n}}</app-callout>
|
||||
<div class="row">
|
||||
<div class="form-group col-6">
|
||||
<label for="format">{{'fileFormat' | i18n}}</label>
|
||||
<select class="form-control" id="format" name="Format" [(ngModel)]="format">
|
||||
<option value="json">.json</option>
|
||||
<option value="csv">.csv</option>
|
||||
<option value="encrypted_json">.json (Encrypted)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -13,6 +13,8 @@ import { ExportComponent as BaseExportComponent } from 'jslib/angular/components
|
||||
templateUrl: 'export.component.html',
|
||||
})
|
||||
export class ExportComponent extends BaseExportComponent {
|
||||
organizationId: string;
|
||||
|
||||
constructor(cryptoService: CryptoService, i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService, exportService: ExportService,
|
||||
eventService: EventService) {
|
||||
|
||||
@@ -21,7 +21,12 @@
|
||||
<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 || canManageCipher(c) ; else cantManage">
|
||||
<a href="#" appStopClick (click)="selectCipher(c)" title="{{'editItem' | i18n}}">{{c.name}}</a>
|
||||
</ng-container>
|
||||
<ng-template #cantManage>
|
||||
<span>{{c.name}}</span>
|
||||
</ng-template>
|
||||
<ng-container *ngIf="!organization && c.organizationId">
|
||||
<i class="fa fa-share-alt" appStopProp title="{{'shared' | i18n}}" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{'shared' | i18n}}</span>
|
||||
|
||||
@@ -61,4 +61,9 @@ export class ExposedPasswordsReportComponent extends CipherReportComponent imple
|
||||
protected getAllCiphers(): Promise<CipherView[]> {
|
||||
return this.cipherService.getAllDecrypted();
|
||||
}
|
||||
|
||||
protected canManageCipher(c: CipherView): boolean {
|
||||
// this will only ever be false from the org view;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,12 +77,11 @@
|
||||
https://help.bitwarden.com/article/import-from-chrome/</a>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="format === 'firefoxcsv'">
|
||||
Use the
|
||||
<a target="_blank" rel="noopener"
|
||||
href="https://github.com/kspearrin/ff-password-exporter/blob/master/README.md#ff-password-exporter">
|
||||
FF Password Exporter</a> application to export your passwords to a CSV file.
|
||||
See detailed instructions on our help site at
|
||||
<a target="_blank" rel="noopener" href="https://bitwarden.com/help/article/import-from-firefox/">
|
||||
https://bitwarden.com/help/article/import-from-firefox/</a>.
|
||||
</ng-container>
|
||||
<ng-container *ngIf="format === '1password1pif' || format === '1passwordwincsv'">
|
||||
<ng-container *ngIf="format === '1password1pif' || format === '1passwordwincsv' || format === '1passwordmaccsv'">
|
||||
See detailed instructions on our help site at
|
||||
<a target="_blank" rel="noopener" href="https://help.bitwarden.com/article/import-from-1password/">
|
||||
https://help.bitwarden.com/article/import-from-1password/</a>.
|
||||
|
||||
@@ -47,7 +47,7 @@ export class ImportComponent implements OnInit {
|
||||
}
|
||||
|
||||
async submit() {
|
||||
const importer = this.importService.getImporter(this.format, this.organizationId != null);
|
||||
const importer = this.importService.getImporter(this.format, this.organizationId);
|
||||
if (importer === null) {
|
||||
this.toasterService.popAsync('error', this.i18nService.t('errorOccurred'),
|
||||
this.i18nService.t('selectFormat'));
|
||||
|
||||
@@ -27,7 +27,12 @@
|
||||
<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 || canManageCipher(c) ; else cantManage">
|
||||
<a href="#" appStopClick (click)="selectCipher(c)" title="{{'editItem' | i18n}}">{{c.name}}</a>
|
||||
</ng-container>
|
||||
<ng-template #cantManage>
|
||||
<span>{{c.name}}</span>
|
||||
</ng-template>
|
||||
<ng-container *ngIf="!organization && c.organizationId">
|
||||
<i class="fa fa-share-alt" appStopProp title="{{'shared' | i18n}}" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{'shared' | i18n}}</span>
|
||||
|
||||
@@ -55,4 +55,9 @@ export class ReusedPasswordsReportComponent extends CipherReportComponent implem
|
||||
protected getAllCiphers(): Promise<CipherView[]> {
|
||||
return this.cipherService.getAllDecrypted();
|
||||
}
|
||||
|
||||
protected canManageCipher(c: CipherView): boolean {
|
||||
// this will only ever be false from an organization view
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,12 @@
|
||||
<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 || canManageCipher(c) ; else cantManage">
|
||||
<a href="#" appStopClick (click)="selectCipher(c)" title="{{'editItem' | i18n}}">{{c.name}}</a>
|
||||
</ng-container>
|
||||
<ng-template #cantManage>
|
||||
<span>{{c.name}}</span>
|
||||
</ng-template>
|
||||
<ng-container *ngIf="!organization && c.organizationId">
|
||||
<i class="fa fa-share-alt" appStopProp title="{{'shared' | i18n}}" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{'shared' | i18n}}</span>
|
||||
|
||||
@@ -75,6 +75,11 @@ export class WeakPasswordsReportComponent extends CipherReportComponent implemen
|
||||
return this.cipherService.getAllDecrypted();
|
||||
}
|
||||
|
||||
protected canManageCipher(c: CipherView): boolean {
|
||||
// this will only ever be false from the org view;
|
||||
return true;
|
||||
}
|
||||
|
||||
private scoreKey(score: number): [string, string] {
|
||||
switch (score) {
|
||||
case 4:
|
||||
|
||||
@@ -9,7 +9,10 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body" *ngIf="cipher">
|
||||
<div class="row" *ngIf="!editMode">
|
||||
<app-callout type="info" *ngIf="allowOwnershipAssignment() && !allowPersonal">
|
||||
{{'personalOwnershipPolicyInEffect' | i18n}}
|
||||
</app-callout>
|
||||
<div class="row" *ngIf="!editMode && !viewOnly">
|
||||
<div class="col-6 form-group">
|
||||
<label for="type">{{'whatTypeOfItem' | i18n}}</label>
|
||||
<select id="type" name="Type" [(ngModel)]="cipher.type" class="form-control"
|
||||
@@ -21,13 +24,13 @@
|
||||
<div class="row">
|
||||
<div class="col-6 form-group">
|
||||
<label for="name">{{'name' | i18n}}</label>
|
||||
<input id="name" class="form-control" type="text" name="Name" [(ngModel)]="cipher.name"
|
||||
required [disabled]="cipher.isDeleted">
|
||||
<input id="name" class="form-control" type="text" name="Name" [(ngModel)]="cipher.name" required
|
||||
[disabled]="cipher.isDeleted || viewOnly">
|
||||
</div>
|
||||
<div class="col-6 form-group" *ngIf="!organization">
|
||||
<label for="folder">{{'folder' | i18n}}</label>
|
||||
<select id="folder" name="FolderId" [(ngModel)]="cipher.folderId" class="form-control"
|
||||
[disabled]="cipher.isDeleted">
|
||||
<select id="folder" name="FolderId" [(ngModel)]="cipher.folderId" class="form-control"
|
||||
[disabled]="cipher.isDeleted || viewOnly">
|
||||
<option *ngFor="let f of folders" [ngValue]="f.id">{{f.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
@@ -39,7 +42,8 @@
|
||||
<label for="loginUsername">{{'username' | i18n}}</label>
|
||||
<div class="input-group">
|
||||
<input id="loginUsername" class="form-control" type="text" name="Login.Username"
|
||||
[(ngModel)]="cipher.login.username" appInputVerbatim [disabled]="cipher.isDeleted">
|
||||
[(ngModel)]="cipher.login.username" appInputVerbatim
|
||||
[disabled]="cipher.isDeleted || viewOnly">
|
||||
<div class="input-group-append" *ngIf="!cipher.isDeleted">
|
||||
<button type="button" class="btn btn-outline-secondary"
|
||||
appA11yTitle="{{'copyUsername' | i18n}}"
|
||||
@@ -52,7 +56,7 @@
|
||||
<div class="col-6 form-group">
|
||||
<div class="d-flex">
|
||||
<label for="loginPassword">{{'password' | i18n}}</label>
|
||||
<div class="ml-auto d-flex" *ngIf="!cipher.isDeleted">
|
||||
<div class="ml-auto d-flex" *ngIf="!cipher.isDeleted && !viewOnly">
|
||||
<a href="#" class="d-block mr-2" appStopClick
|
||||
appA11yTitle="{{'generatePassword' | i18n}}" (click)="generatePassword()"
|
||||
*ngIf="cipher.viewPassword">
|
||||
@@ -72,7 +76,7 @@
|
||||
<input id="loginPassword" class="form-control text-monospace"
|
||||
type="{{showPassword ? 'text' : 'password'}}" name="Login.Password"
|
||||
[(ngModel)]="cipher.login.password" appInputVerbatim autocomplete="new-password"
|
||||
[disabled]="cipher.isDeleted || !cipher.viewPassword">
|
||||
[disabled]="cipher.isDeleted || !cipher.viewPassword || viewOnly">
|
||||
<div class="input-group-append">
|
||||
<button type="button" class="btn btn-outline-secondary"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword()"
|
||||
@@ -93,8 +97,9 @@
|
||||
<div class="row">
|
||||
<div class="col-6 form-group">
|
||||
<label for="loginTotp">{{'authenticatorKeyTotp' | i18n}}</label>
|
||||
<input id="loginTotp" type="{{cipher.viewPassword ? 'text' : 'password'}}" name="Login.Totp" class="form-control text-monospace"
|
||||
[(ngModel)]="cipher.login.totp" appInputVerbatim [disabled]="cipher.isDeleted || !cipher.viewPassword">
|
||||
<input id="loginTotp" type="{{cipher.viewPassword ? 'text' : 'password'}}" name="Login.Totp"
|
||||
class="form-control text-monospace" [(ngModel)]="cipher.login.totp" appInputVerbatim
|
||||
[disabled]="cipher.isDeleted || !cipher.viewPassword || viewOnly">
|
||||
</div>
|
||||
<div class="col-6 form-group totp d-flex align-items-end" [ngClass]="{'low': totpLow}">
|
||||
<div *ngIf="!cipher.login.totp || !totpCode">
|
||||
@@ -137,7 +142,8 @@
|
||||
<label for="loginUri{{i}}">{{'uriPosition' | i18n : (i + 1)}}</label>
|
||||
<div class="input-group">
|
||||
<input class="form-control" id="loginUri{{i}}" type="text"
|
||||
name="Login.Uris[{{i}}].Uri" [(ngModel)]="u.uri" [disabled]="cipher.isDeleted"
|
||||
name="Login.Uris[{{i}}].Uri" [(ngModel)]="u.uri"
|
||||
[disabled]="cipher.isDeleted || viewOnly"
|
||||
placeholder="{{'ex' | i18n}} https://google.com" appInputVerbatim>
|
||||
<div class="input-group-append">
|
||||
<button type="button" class="btn btn-outline-secondary"
|
||||
@@ -164,21 +170,22 @@
|
||||
</a>
|
||||
</div>
|
||||
<div class="d-flex">
|
||||
<select class="form-control" id="loginUriMatch{{i}}" name="Login.Uris[{{i}}].Match"
|
||||
[(ngModel)]="u.match" (change)="loginUriMatchChanged(u)"
|
||||
[disabled]="cipher.isDeleted">
|
||||
<select class="form-control overflow-hidden" id="loginUriMatch{{i}}"
|
||||
name="Login.Uris[{{i}}].Match" [(ngModel)]="u.match"
|
||||
(change)="loginUriMatchChanged(u)" [disabled]="cipher.isDeleted || viewOnly">
|
||||
<option *ngFor="let o of uriMatchOptions" [ngValue]="o.value">{{o.name}}
|
||||
</option>
|
||||
</select>
|
||||
<button type="button" class="btn btn-link text-danger ml-2" (click)="removeUri(u)"
|
||||
appA11yTitle="{{'remove' | i18n}}" *ngIf="!cipher.isDeleted">
|
||||
appA11yTitle="{{'remove' | i18n}}" *ngIf="!cipher.isDeleted && !viewOnly">
|
||||
<i class="fa fa-minus-circle fa-lg" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<a href="#" appStopClick (click)="addUri()" class="d-inline-block mb-3" *ngIf="!cipher.isDeleted">
|
||||
<a href="#" appStopClick (click)="addUri()" class="d-inline-block mb-3"
|
||||
*ngIf="!cipher.isDeleted && !viewOnly">
|
||||
<i class="fa fa-plus-circle fa-fw" aria-hidden="true"></i> {{'newUri' | i18n}}
|
||||
</a>
|
||||
</ng-container>
|
||||
@@ -189,12 +196,12 @@
|
||||
<label for="cardCardholderName">{{'cardholderName' | i18n}}</label>
|
||||
<input id="cardCardholderName" class="form-control" type="text"
|
||||
name="Card.CardCardholderName" [(ngModel)]="cipher.card.cardholderName"
|
||||
[disabled]="cipher.isDeleted">
|
||||
[disabled]="cipher.isDeleted || viewOnly">
|
||||
</div>
|
||||
<div class="col-6 form-group">
|
||||
<label for="cardBrand">{{'brand' | i18n}}</label>
|
||||
<select id="cardBrand" class="form-control" name="Card.Brand"
|
||||
[(ngModel)]="cipher.card.brand" [disabled]="cipher.isDeleted">
|
||||
[(ngModel)]="cipher.card.brand" [disabled]="cipher.isDeleted || viewOnly">
|
||||
<option *ngFor="let o of cardBrandOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
@@ -204,7 +211,8 @@
|
||||
<label for="cardNumber">{{'number' | i18n}}</label>
|
||||
<div class="input-group">
|
||||
<input id="cardNumber" class="form-control" type="text" name="Card.Number"
|
||||
[(ngModel)]="cipher.card.number" appInputVerbatim [disabled]="cipher.isDeleted">
|
||||
[(ngModel)]="cipher.card.number" appInputVerbatim
|
||||
[disabled]="cipher.isDeleted || viewOnly">
|
||||
<div class="input-group-append">
|
||||
<button type="button" class="btn btn-outline-secondary"
|
||||
appA11yTitle="{{'copyNumber' | i18n}}"
|
||||
@@ -217,15 +225,15 @@
|
||||
<div class="col form-group">
|
||||
<label for="cardExpMonth">{{'expirationMonth' | i18n}}</label>
|
||||
<select id="cardExpMonth" class="form-control" name="Card.ExpMonth"
|
||||
[(ngModel)]="cipher.card.expMonth" [disabled]="cipher.isDeleted">
|
||||
[(ngModel)]="cipher.card.expMonth" [disabled]="cipher.isDeleted || viewOnly">
|
||||
<option *ngFor="let o of cardExpMonthOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col form-group">
|
||||
<label for="cardExpYear">{{'expirationYear' | i18n}}</label>
|
||||
<input id="cardExpYear" class="form-control" type="text" name="Card.ExpYear"
|
||||
[(ngModel)]="cipher.card.expYear" placeholder="{{'ex' | i18n}} 2019"
|
||||
[disabled]="cipher.isDeleted">
|
||||
[(ngModel)]="cipher.card.expYear" placeholder="{{'ex' | i18n}} 2019"
|
||||
[disabled]="cipher.isDeleted || viewOnly">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
@@ -235,7 +243,7 @@
|
||||
<input id="cardCode" class="form-control text-monospace"
|
||||
type="{{showCardCode ? 'text' : 'password'}}" name="Card.Code"
|
||||
[(ngModel)]="cipher.card.code" appInputVerbatim autocomplete="new-password"
|
||||
[disabled]="cipher.isDeleted">
|
||||
[disabled]="cipher.isDeleted || viewOnly">
|
||||
<div class="input-group-append">
|
||||
<button type="button" class="btn btn-outline-secondary"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="toggleCardCode()"
|
||||
@@ -259,7 +267,7 @@
|
||||
<div class="col-4 form-group">
|
||||
<label for="idTitle">{{'title' | i18n}}</label>
|
||||
<select id="idTitle" class="form-control" name="Identity.Title"
|
||||
[(ngModel)]="cipher.identity.title" [disabled]="cipher.isDeleted">
|
||||
[(ngModel)]="cipher.identity.title" [disabled]="cipher.isDeleted || viewOnly">
|
||||
<option *ngFor="let o of identityTitleOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
@@ -268,108 +276,113 @@
|
||||
<div class="col-4 form-group">
|
||||
<label for="idFirstName">{{'firstName' | i18n}}</label>
|
||||
<input id="idFirstName" class="form-control" type="text" name="Identity.FirstName"
|
||||
[(ngModel)]="cipher.identity.firstName" [disabled]="cipher.isDeleted">
|
||||
[(ngModel)]="cipher.identity.firstName" [disabled]="cipher.isDeleted || viewOnly">
|
||||
</div>
|
||||
<div class="col-4 form-group">
|
||||
<label for="idMiddleName">{{'middleName' | i18n}}</label>
|
||||
<input id="idMiddleName" class="form-control" type="text" name="Identity.MiddleName"
|
||||
[(ngModel)]="cipher.identity.middleName" [disabled]="cipher.isDeleted">
|
||||
[(ngModel)]="cipher.identity.middleName" [disabled]="cipher.isDeleted || viewOnly">
|
||||
</div>
|
||||
<div class="col-4 form-group">
|
||||
<label for="idLastName">{{'lastName' | i18n}}</label>
|
||||
<input id="idLastName" class="form-control" type="text" name="Identity.LastName"
|
||||
[(ngModel)]="cipher.identity.lastName" [disabled]="cipher.isDeleted">
|
||||
[(ngModel)]="cipher.identity.lastName" [disabled]="cipher.isDeleted || viewOnly">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 form-group">
|
||||
<label for="idUsername">{{'username' | i18n}}</label>
|
||||
<input id="idUsername" class="form-control" type="text" name="Identity.Username"
|
||||
[(ngModel)]="cipher.identity.username" appInputVerbatim [disabled]="cipher.isDeleted">
|
||||
[(ngModel)]="cipher.identity.username" appInputVerbatim
|
||||
[disabled]="cipher.isDeleted || viewOnly">
|
||||
</div>
|
||||
<div class="col-4 form-group">
|
||||
<label for="idCompany">{{'company' | i18n}}</label>
|
||||
<input id="idCompany" class="form-control" type="text" name="Identity.Company"
|
||||
[(ngModel)]="cipher.identity.company" [disabled]="cipher.isDeleted">
|
||||
[(ngModel)]="cipher.identity.company" [disabled]="cipher.isDeleted || viewOnly">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 form-group">
|
||||
<label for="idSsn">{{'ssn' | i18n}}</label>
|
||||
<input id="idSsn" class="form-control" type="text" name="Identity.SSN"
|
||||
[(ngModel)]="cipher.identity.ssn" appInputVerbatim [disabled]="cipher.isDeleted">
|
||||
[(ngModel)]="cipher.identity.ssn" appInputVerbatim
|
||||
[disabled]="cipher.isDeleted || viewOnly">
|
||||
</div>
|
||||
<div class="col-4 form-group">
|
||||
<label for="idPassportNumber">{{'passportNumber' | i18n}}</label>
|
||||
<input id="idPassportNumber" class="form-control" type="text" name="Identity.PassportNumber"
|
||||
[(ngModel)]="cipher.identity.passportNumber" appInputVerbatim [disabled]="cipher.isDeleted">
|
||||
[(ngModel)]="cipher.identity.passportNumber" appInputVerbatim
|
||||
[disabled]="cipher.isDeleted || viewOnly">
|
||||
</div>
|
||||
<div class="col-4 form-group">
|
||||
<label for="idLicenseNumber">{{'licenseNumber' | i18n}}</label>
|
||||
<input id="idLicenseNumber" class="form-control" type="text" name="Identity.LicenseNumber"
|
||||
[(ngModel)]="cipher.identity.licenseNumber" appInputVerbatim [disabled]="cipher.isDeleted">
|
||||
[(ngModel)]="cipher.identity.licenseNumber" appInputVerbatim
|
||||
[disabled]="cipher.isDeleted || viewOnly">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 form-group">
|
||||
<label for="idEmail">{{'email' | i18n}}</label>
|
||||
<input id="idEmail" class="form-control" type="text" name="Identity.Email"
|
||||
[(ngModel)]="cipher.identity.email" appInputVerbatim [disabled]="cipher.isDeleted">
|
||||
[(ngModel)]="cipher.identity.email" appInputVerbatim
|
||||
[disabled]="cipher.isDeleted || viewOnly">
|
||||
</div>
|
||||
<div class="col-6 form-group">
|
||||
<label for="idPhone">{{'phone' | i18n}}</label>
|
||||
<input id="idPhone" class="form-control" type="text" name="Identity.Phone"
|
||||
[(ngModel)]="cipher.identity.phone" [disabled]="cipher.isDeleted">
|
||||
[(ngModel)]="cipher.identity.phone" [disabled]="cipher.isDeleted || viewOnly">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 form-group">
|
||||
<label for="idAddress1">{{'address1' | i18n}}</label>
|
||||
<input id="idAddress1" class="form-control" type="text" name="Identity.Address1"
|
||||
[(ngModel)]="cipher.identity.address1" [disabled]="cipher.isDeleted">
|
||||
[(ngModel)]="cipher.identity.address1" [disabled]="cipher.isDeleted || viewOnly">
|
||||
</div>
|
||||
<div class="col-6 form-group">
|
||||
<label for="idAddress2">{{'address2' | i18n}}</label>
|
||||
<input id="idAddress2" class="form-control" type="text" name="Identity.Address2"
|
||||
[(ngModel)]="cipher.identity.address2" [disabled]="cipher.isDeleted">
|
||||
[(ngModel)]="cipher.identity.address2" [disabled]="cipher.isDeleted || viewOnly">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 form-group">
|
||||
<label for="idAddress3">{{'address3' | i18n}}</label>
|
||||
<input id="idAddress3" class="form-control" type="text" name="Identity.Address3"
|
||||
[(ngModel)]="cipher.identity.address3" [disabled]="cipher.isDeleted">
|
||||
[(ngModel)]="cipher.identity.address3" [disabled]="cipher.isDeleted || viewOnly">
|
||||
</div>
|
||||
<div class="col-6 form-group">
|
||||
<label for="idCity">{{'cityTown' | i18n}}</label>
|
||||
<input id="idCity" class="form-control" type="text" name="Identity.City"
|
||||
[(ngModel)]="cipher.identity.city" [disabled]="cipher.isDeleted">
|
||||
[(ngModel)]="cipher.identity.city" [disabled]="cipher.isDeleted || viewOnly">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 form-group">
|
||||
<label for="idState">{{'stateProvince' | i18n}}</label>
|
||||
<input id="idState" class="form-control" type="text" name="Identity.State"
|
||||
[(ngModel)]="cipher.identity.state" [disabled]="cipher.isDeleted">
|
||||
[(ngModel)]="cipher.identity.state" [disabled]="cipher.isDeleted || viewOnly">
|
||||
</div>
|
||||
<div class="col-6 form-group">
|
||||
<label for="idPostalCode">{{'zipPostalCode' | i18n}}</label>
|
||||
<input id="idPostalCode" class="form-control" type="text" name="Identity.PostalCode"
|
||||
[(ngModel)]="cipher.identity.postalCode" [disabled]="cipher.isDeleted">
|
||||
[(ngModel)]="cipher.identity.postalCode" [disabled]="cipher.isDeleted || viewOnly">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 form-group">
|
||||
<label for="idCountry">{{'country' | i18n}}</label>
|
||||
<input id="idCountry" class="form-control" type="text" name="Identity.Country"
|
||||
[(ngModel)]="cipher.identity.country" [disabled]="cipher.isDeleted">
|
||||
[(ngModel)]="cipher.identity.country" [disabled]="cipher.isDeleted || viewOnly">
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="form-group">
|
||||
<label for="notes">{{'notes' | i18n}}</label>
|
||||
<textarea id="notes" name="Notes" rows="6" [(ngModel)]="cipher.notes" [disabled]="cipher.isDeleted"
|
||||
class="form-control"></textarea>
|
||||
<textarea id="notes" name="Notes" rows="6" [(ngModel)]="cipher.notes"
|
||||
[disabled]="cipher.isDeleted || viewOnly" class="form-control"></textarea>
|
||||
</div>
|
||||
<h3 class="mt-4">{{'customFields' | i18n}}</h3>
|
||||
<div cdkDropList (cdkDropListDropped)="drop($event)" *ngIf="cipher.hasFields">
|
||||
@@ -383,14 +396,15 @@
|
||||
</a>
|
||||
</div>
|
||||
<input id="fieldName{{i}}" type="text" name="Field.Name{{i}}" [(ngModel)]="f.name"
|
||||
class="form-control" appInputVerbatim [disabled]="cipher.isDeleted">
|
||||
class="form-control" appInputVerbatim [disabled]="cipher.isDeleted || viewOnly">
|
||||
</div>
|
||||
<div class="col-7 form-group">
|
||||
<label for="fieldValue{{i}}">{{'value' | i18n}}</label>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="input-group" *ngIf="f.type === fieldType.Text">
|
||||
<input id="fieldValue{{i}}" class="form-control" type="text" name="Field.Value{{i}}"
|
||||
[(ngModel)]="f.value" appInputVerbatim [disabled]="cipher.isDeleted">
|
||||
[(ngModel)]="f.value" appInputVerbatim
|
||||
[disabled]="cipher.isDeleted || viewOnly">
|
||||
<div class="input-group-append">
|
||||
<button type="button" class="btn btn-outline-secondary"
|
||||
appA11yTitle="{{'copyValue' | i18n}}"
|
||||
@@ -402,8 +416,8 @@
|
||||
<div class="input-group" *ngIf="f.type === fieldType.Hidden">
|
||||
<input id="fieldValue{{i}}" type="{{f.showValue ? 'text' : 'password'}}"
|
||||
name="Field.Value{{i}}" [(ngModel)]="f.value"
|
||||
class="form-control text-monospace" appInputVerbatim
|
||||
autocomplete="new-password" [disabled]="cipher.isDeleted || (!cipher.viewPassword && !f.newField)">
|
||||
class="form-control text-monospace" appInputVerbatim autocomplete="new-password"
|
||||
[disabled]="cipher.isDeleted || viewOnly || (!cipher.viewPassword && !f.newField)">
|
||||
<div class="input-group-append">
|
||||
<button type="button" class="btn btn-outline-secondary"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="toggleFieldValue(f)"
|
||||
@@ -423,24 +437,25 @@
|
||||
<div class="flex-fill">
|
||||
<input id="fieldValue{{i}}" name="Field.Value{{i}}" type="checkbox"
|
||||
[(ngModel)]="f.value" *ngIf="f.type === fieldType.Boolean" appTrueFalseValue
|
||||
trueValue="true" falseValue="false" [disabled]="cipher.isDeleted">
|
||||
trueValue="true" falseValue="false" [disabled]="cipher.isDeleted || viewOnly">
|
||||
</div>
|
||||
<button type="button" class="btn btn-link text-danger ml-2" (click)="removeField(f)"
|
||||
appA11yTitle="{{'remove' | i18n}}" *ngIf="!cipher.isDeleted">
|
||||
appA11yTitle="{{'remove' | i18n}}" *ngIf="!cipher.isDeleted && !viewOnly">
|
||||
<i class="fa fa-minus-circle fa-lg" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-link text-muted cursor-move"
|
||||
appA11yTitle="{{'dragToSort' | i18n}}" *ngIf="!cipher.isDeleted">
|
||||
appA11yTitle="{{'dragToSort' | i18n}}" *ngIf="!cipher.isDeleted && !viewOnly">
|
||||
<i class="fa fa-bars fa-lg" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="#" appStopClick (click)="addField()" class="d-inline-block mb-2" *ngIf="!cipher.isDeleted">
|
||||
<a href="#" appStopClick (click)="addField()" class="d-inline-block mb-2"
|
||||
*ngIf="!cipher.isDeleted && !viewOnly">
|
||||
<i class="fa fa-plus-circle fa-fw" aria-hidden="true"></i> {{'newCustomField' | i18n}}
|
||||
</a>
|
||||
<div class="row" *ngIf="!cipher.isDeleted">
|
||||
<div class="row" *ngIf="!cipher.isDeleted && !viewOnly">
|
||||
<div class="col-5">
|
||||
<label for="addFieldType" class="sr-only">{{'type' | i18n}}</label>
|
||||
<select id="addFieldType" class="form-control" name="AddFieldType" [(ngModel)]="addFieldType">
|
||||
@@ -455,7 +470,7 @@
|
||||
<label for="organizationId">{{'whoOwnsThisItem' | i18n}}</label>
|
||||
<select id="organizationId" class="form-control" name="OrganizationId"
|
||||
[(ngModel)]="cipher.organizationId" (change)="organizationChanged()"
|
||||
[disabled]="cipher.isDeleted">
|
||||
[disabled]="cipher.isDeleted || viewOnly">
|
||||
<option *ngFor="let o of ownershipOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
@@ -469,7 +484,8 @@
|
||||
<ng-container *ngIf="collections && collections.length">
|
||||
<div class="form-check" *ngFor="let c of collections; let i = index">
|
||||
<input class="form-check-input" type="checkbox" [(ngModel)]="c.checked"
|
||||
id="collection-{{i}}" name="Collection[{{i}}].Checked" [disabled]="cipher.isDeleted">
|
||||
id="collection-{{i}}" name="Collection[{{i}}].Checked"
|
||||
[disabled]="cipher.isDeleted || viewOnly">
|
||||
<label class="form-check-label" for="collection-{{i}}">{{c.name}}</label>
|
||||
</div>
|
||||
</ng-container>
|
||||
@@ -493,30 +509,29 @@
|
||||
<div class="ml-3" *ngIf="viewingPasswordHistory">
|
||||
<div *ngFor="let ph of cipher.passwordHistory">
|
||||
{{ph.lastUsedDate | date:'short'}} -
|
||||
<span class="text-monospace ml-2">{{ph.password}}</span>
|
||||
<span class="password-wrapper text-monospace ml-2">{{ph.password}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading">
|
||||
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading" *ngIf="!viewOnly">
|
||||
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}" aria-hidden="true"></i>
|
||||
<span>{{(cipher?.isDeleted ? 'restore' : 'save') | i18n}}</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
|
||||
{{'cancel' | i18n}}
|
||||
{{(viewOnly ? 'close' : 'cancel') | i18n}}
|
||||
</button>
|
||||
<div class="ml-auto" *ngIf="cipher">
|
||||
<button *ngIf="!organization && !cipher.isDeleted" type="button" (click)="toggleFavorite()" class="btn btn-link"
|
||||
appA11yTitle="{{(cipher.favorite ? 'unfavorite' : 'favorite') | i18n}}">
|
||||
<div class="ml-auto" *ngIf="cipher && !viewOnly">
|
||||
<button *ngIf="!organization && !cipher.isDeleted" type="button" (click)="toggleFavorite()"
|
||||
class="btn btn-link" appA11yTitle="{{(cipher.favorite ? 'unfavorite' : 'favorite') | i18n}}">
|
||||
<i class="fa fa-lg" [ngClass]="{'fa-star': cipher.favorite, 'fa-star-o': !cipher.favorite}"
|
||||
aria-hidden="true"></i>
|
||||
</button>
|
||||
<button #deleteBtn type="button" (click)="delete()" class="btn btn-outline-danger"
|
||||
appA11yTitle="{{(cipher.isDeleted ? 'permanentlyDelete' : 'delete') | i18n}}"
|
||||
*ngIf="editMode && !cloneMode" [disabled]="deleteBtn.loading"
|
||||
[appApiAction]="deletePromise">
|
||||
*ngIf="editMode && !cloneMode" [disabled]="deleteBtn.loading" [appApiAction]="deletePromise">
|
||||
<i class="fa fa-trash-o fa-lg fa-fw" [hidden]="deleteBtn.loading" aria-hidden="true"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!deleteBtn.loading"
|
||||
title="{{'loading' | i18n}}" aria-hidden="true"></i>
|
||||
|
||||
@@ -12,6 +12,7 @@ import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||
import { MessagingService } from 'jslib/abstractions/messaging.service';
|
||||
import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service';
|
||||
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||
import { PolicyService } from 'jslib/abstractions/policy.service';
|
||||
import { StateService } from 'jslib/abstractions/state.service';
|
||||
import { TotpService } from 'jslib/abstractions/totp.service';
|
||||
import { UserService } from 'jslib/abstractions/user.service';
|
||||
@@ -33,6 +34,7 @@ export class AddEditComponent extends BaseAddEditComponent {
|
||||
showRevisionDate = false;
|
||||
hasPasswordHistory = false;
|
||||
viewingPasswordHistory = false;
|
||||
viewOnly = false;
|
||||
|
||||
protected totpInterval: number;
|
||||
|
||||
@@ -41,9 +43,10 @@ export class AddEditComponent extends BaseAddEditComponent {
|
||||
auditService: AuditService, stateService: StateService,
|
||||
userService: UserService, collectionService: CollectionService,
|
||||
protected totpService: TotpService, protected passwordGenerationService: PasswordGenerationService,
|
||||
protected messagingService: MessagingService, eventService: EventService) {
|
||||
protected messagingService: MessagingService, eventService: EventService,
|
||||
protected policyService: PolicyService) {
|
||||
super(cipherService, folderService, i18nService, platformUtilsService, auditService, stateService,
|
||||
userService, collectionService, messagingService, eventService);
|
||||
userService, collectionService, messagingService, eventService, policyService);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
@@ -155,7 +158,8 @@ export class AddEditComponent extends BaseAddEditComponent {
|
||||
}
|
||||
|
||||
protected allowOwnershipAssignment() {
|
||||
return (!this.editMode || this.cloneMode) && this.ownershipOptions != null && this.ownershipOptions.length > 1;
|
||||
return (!this.editMode || this.cloneMode) && this.ownershipOptions != null
|
||||
&& (this.ownershipOptions.length > 1 || !this.allowPersonal);
|
||||
}
|
||||
|
||||
private async totpTick(intervalSeconds: number) {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<i class="fa fa-spinner fa-lg fa-fw fa-spin" *ngIf="a.downloading"
|
||||
aria-hidden="true"></i>
|
||||
</td>
|
||||
<td>
|
||||
<td class="wrap">
|
||||
<div class="d-flex">
|
||||
<a href="#" appStopClick (click)="download(a)">{{a.fileName}}</a>
|
||||
<div *ngIf="showFixOldAttachments(a)" class="ml-2">
|
||||
|
||||
@@ -31,7 +31,7 @@ export class BulkDeleteComponent {
|
||||
private apiService: ApiService) { }
|
||||
|
||||
async submit() {
|
||||
if (!this.organization || !this.organization.isAdmin) {
|
||||
if (!this.organization || !this.organization.canManageAllCollections) {
|
||||
await this.deleteCiphers();
|
||||
} else {
|
||||
await this.deleteCiphersAdmin();
|
||||
|
||||
@@ -47,6 +47,11 @@
|
||||
<i class="fa fa-fw fa-clone" aria-hidden="true"></i>
|
||||
{{'copyPassword' | i18n}}
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" appStopClick (click)="copy(c, c.login.totp, 'verificationCodeTotp', 'TOTP')"
|
||||
*ngIf="displayTotpCopyButton(c)">
|
||||
<i class="fa fa-fw fa-clone" aria-hidden="true"></i>
|
||||
{{'copyVerificationCode' | i18n}}
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" appStopClick *ngIf="c.login.canLaunch"
|
||||
(click)="launch(c.login.launchUri)">
|
||||
<i class="fa fa-fw fa-share-square-o" aria-hidden="true"></i>
|
||||
|
||||
@@ -14,6 +14,8 @@ import { EventService } from 'jslib/abstractions/event.service';
|
||||
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||
import { SearchService } from 'jslib/abstractions/search.service';
|
||||
import { TotpService } from 'jslib/abstractions/totp.service';
|
||||
import { UserService } from 'jslib/abstractions/user.service';
|
||||
|
||||
import { CiphersComponent as BaseCiphersComponent } from 'jslib/angular/components/ciphers.component';
|
||||
|
||||
@@ -37,15 +39,20 @@ export class CiphersComponent extends BaseCiphersComponent implements OnDestroy
|
||||
|
||||
cipherType = CipherType;
|
||||
actionPromise: Promise<any>;
|
||||
userHasPremiumAccess = false;
|
||||
|
||||
constructor(searchService: SearchService, protected analytics: Angulartics2,
|
||||
protected toasterService: ToasterService, protected i18nService: I18nService,
|
||||
protected platformUtilsService: PlatformUtilsService, protected cipherService: CipherService,
|
||||
protected eventService: EventService) {
|
||||
protected eventService: EventService, protected totpService: TotpService, protected userService: UserService) {
|
||||
super(searchService);
|
||||
this.pageSize = 200;
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this.userHasPremiumAccess = await this.userService.canAccessPremium();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.selectAll(false);
|
||||
}
|
||||
@@ -117,9 +124,11 @@ export class CiphersComponent extends BaseCiphersComponent implements OnDestroy
|
||||
this.actionPromise = null;
|
||||
}
|
||||
|
||||
copy(cipher: CipherView, value: string, typeI18nKey: string, aType: string) {
|
||||
if (value == null) {
|
||||
async copy(cipher: CipherView, value: string, typeI18nKey: string, aType: string) {
|
||||
if (value == null || !this.displayTotpCopyButton(cipher)) {
|
||||
return;
|
||||
} else if (value === cipher.login.totp) {
|
||||
value = await this.totpService.getCode(value);
|
||||
}
|
||||
|
||||
this.analytics.eventTrack.next({ action: 'Copied ' + aType.toLowerCase() + ' from listing.' });
|
||||
@@ -127,7 +136,7 @@ export class CiphersComponent extends BaseCiphersComponent implements OnDestroy
|
||||
this.toasterService.popAsync('info', null,
|
||||
this.i18nService.t('valueCopied', this.i18nService.t(typeI18nKey)));
|
||||
|
||||
if (typeI18nKey === 'password') {
|
||||
if (typeI18nKey === 'password' || typeI18nKey === 'verificationCodeTotp') {
|
||||
this.eventService.collect(EventType.Cipher_ClientToggledHiddenFieldVisible, cipher.id);
|
||||
} else if (typeI18nKey === 'securityCode') {
|
||||
this.eventService.collect(EventType.Cipher_ClientCopiedCardCode, cipher.id);
|
||||
@@ -161,6 +170,11 @@ export class CiphersComponent extends BaseCiphersComponent implements OnDestroy
|
||||
return this.getSelected().map((c) => c.id);
|
||||
}
|
||||
|
||||
displayTotpCopyButton(cipher: CipherView) {
|
||||
return (cipher?.login?.hasTotp ?? false) &&
|
||||
(cipher.organizationUseTotp || this.userHasPremiumAccess);
|
||||
}
|
||||
|
||||
protected deleteCipher(id: string, permanent: boolean) {
|
||||
return permanent ? this.cipherService.deleteWithServer(id) : this.cipherService.softDeleteWithServer(id);
|
||||
}
|
||||
|
||||
@@ -62,15 +62,17 @@
|
||||
<i class="fa fa-plus fa-fw" aria-hidden="true"></i>
|
||||
</a>
|
||||
</h3>
|
||||
<ul class="fa-ul card-ul carets">
|
||||
<ul class="fa-ul card-ul">
|
||||
<ng-template #recursiveFolders let-folders>
|
||||
<li *ngFor="let f of folders"
|
||||
[ngClass]="{active: selectedFolder && f.node.id === selectedFolderId}">
|
||||
<div class="d-flex">
|
||||
<i class="fa-li fa" title="{{'toggleCollapse' | i18n}}"
|
||||
<i *ngIf="f.children.length" class="fa-li fa" title="{{'toggleCollapse' | i18n}}"
|
||||
[ngClass]="{'fa-caret-right': isCollapsed(f.node), 'fa-caret-down': !isCollapsed(f.node)}"
|
||||
(click)="collapse(f.node)"></i>
|
||||
<a href="#" appStopClick (click)="selectFolder(f.node)">{{f.node.name}}</a>
|
||||
<a href="#" appStopClick (click)="selectFolder(f.node)">
|
||||
<i *ngIf="f.children.length === 0" class="fa-li fa fa-folder-o" aria-hidden="true"></i>{{f.node.name}}
|
||||
</a>
|
||||
<a href="#" class="text-muted ml-auto show-active" appStopClick
|
||||
(click)="editFolder(f.node)" appA11yTitle="{{'editFolder' | i18n}}"
|
||||
*ngIf="f.node.id">
|
||||
@@ -89,13 +91,15 @@
|
||||
</ng-container>
|
||||
<ng-container *ngIf="showCollections && collections && collections.length">
|
||||
<h3>{{'collections' | i18n}}</h3>
|
||||
<ul class="fa-ul card-ul carets">
|
||||
<ul class="fa-ul card-ul">
|
||||
<ng-template #recursiveCollections let-collections>
|
||||
<li *ngFor="let c of collections" [ngClass]="{active: c.node.id === selectedCollectionId}">
|
||||
<i class="fa-li fa" title="{{'toggleCollapse' | i18n}}"
|
||||
<i *ngIf="c.children.length" class="fa-li fa" title="{{'toggleCollapse' | i18n}}"
|
||||
[ngClass]="{'fa-caret-right': isCollapsed(c.node), 'fa-caret-down': !isCollapsed(c.node)}"
|
||||
(click)="collapse(c.node)"></i>
|
||||
<a href="#" appStopClick (click)="selectCollection(c.node)">{{c.node.name}}</a>
|
||||
<a href="#" appStopClick (click)="selectCollection(c.node)">
|
||||
<i *ngIf="c.children.length === 0" class="fa-li fa fa-cube" aria-hidden="true"></i>{{c.node.name}}
|
||||
</a>
|
||||
<ul class="fa-ul card-ul carets" *ngIf="c.children.length && !isCollapsed(c.node)">
|
||||
<ng-container
|
||||
*ngTemplateOutlet="recursiveCollections; context:{ $implicit: c.children }">
|
||||
|
||||
@@ -37,8 +37,8 @@ function getQsParam(name: string) {
|
||||
|
||||
function initiateBrowserSso(code: string, state: string) {
|
||||
window.postMessage({ command: 'authResult', code: code, state: state }, '*');
|
||||
let handOffMessage = ('; ' + document.cookie).split('; ssoHandOffMessage=').pop().split(';').shift();
|
||||
document.cookie = 'ssoHandOffMessage=;SameSite=strict;max-age=0'
|
||||
const handOffMessage = ('; ' + document.cookie).split('; ssoHandOffMessage=').pop().split(';').shift();
|
||||
document.cookie = 'ssoHandOffMessage=;SameSite=strict;max-age=0';
|
||||
document.getElementById('content').innerHTML =
|
||||
`<p>${handOffMessage}</p>`;
|
||||
}
|
||||
|
||||
@@ -347,6 +347,9 @@
|
||||
"editItem": {
|
||||
"message": "Edit Item"
|
||||
},
|
||||
"viewItem": {
|
||||
"message": "View Item"
|
||||
},
|
||||
"ex": {
|
||||
"message": "ex.",
|
||||
"description": "Short abbreviation for 'example'."
|
||||
@@ -790,9 +793,15 @@
|
||||
"warning": {
|
||||
"message": "Warning"
|
||||
},
|
||||
"confirmVaultExport": {
|
||||
"message": "Confirm Vault Export"
|
||||
},
|
||||
"exportWarningDesc": {
|
||||
"message": "This export contains your vault data in an unencrypted format. You should not store or send the exported file over unsecure channels (such as email). Delete it immediately after you are done using it."
|
||||
},
|
||||
"encExportWarningDesc": {
|
||||
"message": "This export encrypts your data using your account's encryption key. If you ever rotate your account's encryption key you should export again since you will not be able to decrypt this export file."
|
||||
},
|
||||
"exportMasterPassword": {
|
||||
"message": "Enter your master password to export your vault data."
|
||||
},
|
||||
@@ -1659,6 +1668,9 @@
|
||||
"paymentInformation": {
|
||||
"message": "Betaalinligting"
|
||||
},
|
||||
"billingInformation": {
|
||||
"message": "Billing Information"
|
||||
},
|
||||
"creditCard": {
|
||||
"message": "Kredietkaart"
|
||||
},
|
||||
@@ -2483,6 +2495,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"unlinkedSsoUser": {
|
||||
"message": "Unlinked SSO for user $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"device": {
|
||||
"message": "Toestel"
|
||||
},
|
||||
@@ -2786,6 +2807,9 @@
|
||||
"updateEncryptionKeyWarning": {
|
||||
"message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed."
|
||||
},
|
||||
"updateEncryptionKeyExportWarning": {
|
||||
"message": "Any encrypted exports that you have saved will also become invalid."
|
||||
},
|
||||
"subscription": {
|
||||
"message": "Intekening"
|
||||
},
|
||||
@@ -3295,15 +3319,35 @@
|
||||
"deletionDate": {
|
||||
"message": "Deletion Date"
|
||||
},
|
||||
"deletionDateDesc": {
|
||||
"message": "The Send will be permanently deleted on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"expirationDate": {
|
||||
"message": "Expiration Date"
|
||||
},
|
||||
"expirationDateDesc": {
|
||||
"message": "If set, access to this Send will expire on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"maxAccessCount": {
|
||||
"message": "Maximum Access Count"
|
||||
},
|
||||
"maxAccessCountDesc": {
|
||||
"message": "If set, users will no longer be able to access this send once the maximum access count is reached.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"currentAccessCount": {
|
||||
"message": "Current Access Count"
|
||||
},
|
||||
"sendPasswordDesc": {
|
||||
"message": "Optionally require a password for users to access this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendNotesDesc": {
|
||||
"message": "Private notes about this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"disabled": {
|
||||
"message": "Disabled"
|
||||
},
|
||||
@@ -3324,9 +3368,22 @@
|
||||
"removePasswordConfirmation": {
|
||||
"message": "Are you sure you want to remove the password?"
|
||||
},
|
||||
"disableThisSend": {
|
||||
"message": "Disable this Send so that no one can access it.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"allSends": {
|
||||
"message": "All Sends"
|
||||
},
|
||||
"maxAccessCountReached": {
|
||||
"message": "Max access count reached"
|
||||
},
|
||||
"pendingDeletion": {
|
||||
"message": "Pending deletion"
|
||||
},
|
||||
"expired": {
|
||||
"message": "Expired"
|
||||
},
|
||||
"searchSends": {
|
||||
"message": "Search Sends",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
@@ -3346,8 +3403,230 @@
|
||||
"downloadFile": {
|
||||
"message": "Download File"
|
||||
},
|
||||
"sendAccessUnavailable": {
|
||||
"message": "The Send you are trying to access does not exist or is no longer available.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"noSendsInList": {
|
||||
"message": "There are no Sends to list.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"emergencyAccess": {
|
||||
"message": "Emergency Access"
|
||||
},
|
||||
"emergencyAccessDesc": {
|
||||
"message": "Grant and manage emergency access for trusted contacts. Trusted contacts may request access to either View or Takeover your account in case of a emergency. Visit our help page for more information and details into how zero knowledge sharing works."
|
||||
},
|
||||
"trustedEmergencyContacts": {
|
||||
"message": "Trusted emergency contacts"
|
||||
},
|
||||
"noTrustedContacts": {
|
||||
"message": "You have not added any emergency contacts yet, invite a trusted contact to get started."
|
||||
},
|
||||
"addEmergencyContact": {
|
||||
"message": "Add emergency contact"
|
||||
},
|
||||
"designatedEmergencyContacts": {
|
||||
"message": "Designated as emergency contact"
|
||||
},
|
||||
"noGrantedAccess": {
|
||||
"message": "You have not been designated as an emergency contact for anyone yet."
|
||||
},
|
||||
"inviteEmergencyContact": {
|
||||
"message": "Invite emergency contact"
|
||||
},
|
||||
"editEmergencyContact": {
|
||||
"message": "Edit emergency contact"
|
||||
},
|
||||
"inviteEmergencyContactDesc": {
|
||||
"message": "Invite a new emergency contact by entering their Bitwarden account email address below. If they do not have a Bitwarden account already, they will be prompted to create a new account."
|
||||
},
|
||||
"emergencyAccessRecoveryInitiated": {
|
||||
"message": "Emergency Access Initiated"
|
||||
},
|
||||
"emergencyAccessRecoveryApproved": {
|
||||
"message": "Emergency Access Approved"
|
||||
},
|
||||
"viewDesc": {
|
||||
"message": "Can view all items in your own vault."
|
||||
},
|
||||
"takeover": {
|
||||
"message": "Takeover"
|
||||
},
|
||||
"takeoverDesc": {
|
||||
"message": "Can reset your account with a new master password."
|
||||
},
|
||||
"waitTime": {
|
||||
"message": "Wait Time"
|
||||
},
|
||||
"waitTimeDesc": {
|
||||
"message": "Time required before automatically granting access."
|
||||
},
|
||||
"oneDay": {
|
||||
"message": "1 day"
|
||||
},
|
||||
"days": {
|
||||
"message": "$DAYS$ days",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"invitedUser": {
|
||||
"message": "Invited user."
|
||||
},
|
||||
"acceptEmergencyAccess": {
|
||||
"message": "You've been invited to become an emergency contact for the user listed above. To accept the invitation, you need to log in or create a new Bitwarden account."
|
||||
},
|
||||
"emergencyInviteAcceptFailed": {
|
||||
"message": "Unable to accept invitation. Ask the user to send a new invitation."
|
||||
},
|
||||
"emergencyInviteAcceptFailedShort": {
|
||||
"message": "Unable to accept invitation. $DESCRIPTION$",
|
||||
"placeholders": {
|
||||
"description": {
|
||||
"content": "$1",
|
||||
"example": "You must enable 2FA on your user account before you can join this organization."
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyInviteAcceptedDesc": {
|
||||
"message": "You can access the emergency options for this user after your identity has been confirmed. We'll send you an email when that happens."
|
||||
},
|
||||
"requestAccess": {
|
||||
"message": "Request Access"
|
||||
},
|
||||
"requestAccessConfirmation": {
|
||||
"message": "Are you sure you want to request emergency access? You will be provided access after $WAITTIME$ day(s) or whenever the user manually approves the request.",
|
||||
"placeholders": {
|
||||
"waittime": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requestSent": {
|
||||
"message": "Emergency access requested for $USER$. We'll notify you by email when it's possible to continue.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"approve": {
|
||||
"message": "Approve"
|
||||
},
|
||||
"reject": {
|
||||
"message": "Reject"
|
||||
},
|
||||
"approveAccessConfirmation": {
|
||||
"message": "Are you sure you want to approve emergency access? This will allow $USER$ to $ACTION$ your account.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
},
|
||||
"action": {
|
||||
"content": "$2",
|
||||
"example": "View"
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyApproved": {
|
||||
"message": "Emergency access approved."
|
||||
},
|
||||
"emergencyRejected": {
|
||||
"message": "Emergency access rejected"
|
||||
},
|
||||
"passwordResetFor": {
|
||||
"message": "Password reset for $USER$. You can now login using the new password.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"personalOwnership": {
|
||||
"message": "Personal Ownership"
|
||||
},
|
||||
"personalOwnershipPolicyDesc": {
|
||||
"message": "Require users to save vault items to an organization by removing the personal ownership option."
|
||||
},
|
||||
"personalOwnershipExemption": {
|
||||
"message": "Organization Owners and Administrators are exempt from this policy's enforcement."
|
||||
},
|
||||
"personalOwnershipSubmitError": {
|
||||
"message": "Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organization and choose from available Collections."
|
||||
},
|
||||
"modifiedPolicyId": {
|
||||
"message": "Modified policy $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "Master Password"
|
||||
}
|
||||
}
|
||||
},
|
||||
"planPrice": {
|
||||
"message": "Plan price"
|
||||
},
|
||||
"estimatedTax": {
|
||||
"message": "Estimated tax"
|
||||
},
|
||||
"custom": {
|
||||
"message": "Custom"
|
||||
},
|
||||
"customDesc": {
|
||||
"message": "Allows more granular control of user permissions for advanced configurations."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "Permissions"
|
||||
},
|
||||
"accessBusinessPortal": {
|
||||
"message": "Access Business Portal"
|
||||
},
|
||||
"accessEventLogs": {
|
||||
"message": "Access Event Logs"
|
||||
},
|
||||
"accessImportExport": {
|
||||
"message": "Access Import/Export"
|
||||
},
|
||||
"accessReports": {
|
||||
"message": "Access Reports"
|
||||
},
|
||||
"manageAllCollections": {
|
||||
"message": "Manage All Collections"
|
||||
},
|
||||
"manageAssignedCollections": {
|
||||
"message": "Manage Assigned Collections"
|
||||
},
|
||||
"manageGroups": {
|
||||
"message": "Manage Groups"
|
||||
},
|
||||
"managePolicies": {
|
||||
"message": "Manage Policies"
|
||||
},
|
||||
"manageSso": {
|
||||
"message": "Manage SSO"
|
||||
},
|
||||
"manageUsers": {
|
||||
"message": "Manage Users"
|
||||
},
|
||||
"disableRequireSsoError": {
|
||||
"message": "You must manually disable the Single Sign-On Authentication policy before this policy can be disabled."
|
||||
},
|
||||
"personalOwnershipPolicyInEffect": {
|
||||
"message": "An organization policy is affecting your ownership options."
|
||||
},
|
||||
"personalOwnershipCheckboxDesc": {
|
||||
"message": "Disable personal ownership for organization users"
|
||||
},
|
||||
"textHiddenByDefault": {
|
||||
"message": "When accessing the Send, hide the text by default",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,6 +347,9 @@
|
||||
"editItem": {
|
||||
"message": "Рэдагаванне элемента"
|
||||
},
|
||||
"viewItem": {
|
||||
"message": "View Item"
|
||||
},
|
||||
"ex": {
|
||||
"message": "напр.",
|
||||
"description": "Short abbreviation for 'example'."
|
||||
@@ -790,9 +793,15 @@
|
||||
"warning": {
|
||||
"message": "Увага"
|
||||
},
|
||||
"confirmVaultExport": {
|
||||
"message": "Confirm Vault Export"
|
||||
},
|
||||
"exportWarningDesc": {
|
||||
"message": "Экспартуемы файл утрымлівае даныя вашага сховішча ў незашыфраваным фармаце. Яго не варта захоўваць ці адпраўляць па небяспечным каналам (напрыклад, па электроннай пошце). Выдаліце яго адразу пасля выкарыстання."
|
||||
},
|
||||
"encExportWarningDesc": {
|
||||
"message": "This export encrypts your data using your account's encryption key. If you ever rotate your account's encryption key you should export again since you will not be able to decrypt this export file."
|
||||
},
|
||||
"exportMasterPassword": {
|
||||
"message": "Увядзіце ваш асноўны пароль для экспарту даных са сховішча."
|
||||
},
|
||||
@@ -1659,6 +1668,9 @@
|
||||
"paymentInformation": {
|
||||
"message": "Payment Information"
|
||||
},
|
||||
"billingInformation": {
|
||||
"message": "Billing Information"
|
||||
},
|
||||
"creditCard": {
|
||||
"message": "Credit Card"
|
||||
},
|
||||
@@ -2483,6 +2495,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"unlinkedSsoUser": {
|
||||
"message": "Unlinked SSO for user $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"device": {
|
||||
"message": "Device"
|
||||
},
|
||||
@@ -2786,6 +2807,9 @@
|
||||
"updateEncryptionKeyWarning": {
|
||||
"message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed."
|
||||
},
|
||||
"updateEncryptionKeyExportWarning": {
|
||||
"message": "Any encrypted exports that you have saved will also become invalid."
|
||||
},
|
||||
"subscription": {
|
||||
"message": "Subscription"
|
||||
},
|
||||
@@ -3295,15 +3319,35 @@
|
||||
"deletionDate": {
|
||||
"message": "Deletion Date"
|
||||
},
|
||||
"deletionDateDesc": {
|
||||
"message": "The Send will be permanently deleted on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"expirationDate": {
|
||||
"message": "Expiration Date"
|
||||
},
|
||||
"expirationDateDesc": {
|
||||
"message": "If set, access to this Send will expire on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"maxAccessCount": {
|
||||
"message": "Maximum Access Count"
|
||||
},
|
||||
"maxAccessCountDesc": {
|
||||
"message": "If set, users will no longer be able to access this send once the maximum access count is reached.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"currentAccessCount": {
|
||||
"message": "Current Access Count"
|
||||
},
|
||||
"sendPasswordDesc": {
|
||||
"message": "Optionally require a password for users to access this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendNotesDesc": {
|
||||
"message": "Private notes about this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"disabled": {
|
||||
"message": "Disabled"
|
||||
},
|
||||
@@ -3324,9 +3368,22 @@
|
||||
"removePasswordConfirmation": {
|
||||
"message": "Are you sure you want to remove the password?"
|
||||
},
|
||||
"disableThisSend": {
|
||||
"message": "Disable this Send so that no one can access it.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"allSends": {
|
||||
"message": "All Sends"
|
||||
},
|
||||
"maxAccessCountReached": {
|
||||
"message": "Max access count reached"
|
||||
},
|
||||
"pendingDeletion": {
|
||||
"message": "Pending deletion"
|
||||
},
|
||||
"expired": {
|
||||
"message": "Expired"
|
||||
},
|
||||
"searchSends": {
|
||||
"message": "Search Sends",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
@@ -3346,8 +3403,230 @@
|
||||
"downloadFile": {
|
||||
"message": "Download File"
|
||||
},
|
||||
"sendAccessUnavailable": {
|
||||
"message": "The Send you are trying to access does not exist or is no longer available.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"noSendsInList": {
|
||||
"message": "There are no Sends to list.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"emergencyAccess": {
|
||||
"message": "Emergency Access"
|
||||
},
|
||||
"emergencyAccessDesc": {
|
||||
"message": "Grant and manage emergency access for trusted contacts. Trusted contacts may request access to either View or Takeover your account in case of a emergency. Visit our help page for more information and details into how zero knowledge sharing works."
|
||||
},
|
||||
"trustedEmergencyContacts": {
|
||||
"message": "Trusted emergency contacts"
|
||||
},
|
||||
"noTrustedContacts": {
|
||||
"message": "You have not added any emergency contacts yet, invite a trusted contact to get started."
|
||||
},
|
||||
"addEmergencyContact": {
|
||||
"message": "Add emergency contact"
|
||||
},
|
||||
"designatedEmergencyContacts": {
|
||||
"message": "Designated as emergency contact"
|
||||
},
|
||||
"noGrantedAccess": {
|
||||
"message": "You have not been designated as an emergency contact for anyone yet."
|
||||
},
|
||||
"inviteEmergencyContact": {
|
||||
"message": "Invite emergency contact"
|
||||
},
|
||||
"editEmergencyContact": {
|
||||
"message": "Edit emergency contact"
|
||||
},
|
||||
"inviteEmergencyContactDesc": {
|
||||
"message": "Invite a new emergency contact by entering their Bitwarden account email address below. If they do not have a Bitwarden account already, they will be prompted to create a new account."
|
||||
},
|
||||
"emergencyAccessRecoveryInitiated": {
|
||||
"message": "Emergency Access Initiated"
|
||||
},
|
||||
"emergencyAccessRecoveryApproved": {
|
||||
"message": "Emergency Access Approved"
|
||||
},
|
||||
"viewDesc": {
|
||||
"message": "Can view all items in your own vault."
|
||||
},
|
||||
"takeover": {
|
||||
"message": "Takeover"
|
||||
},
|
||||
"takeoverDesc": {
|
||||
"message": "Can reset your account with a new master password."
|
||||
},
|
||||
"waitTime": {
|
||||
"message": "Wait Time"
|
||||
},
|
||||
"waitTimeDesc": {
|
||||
"message": "Time required before automatically granting access."
|
||||
},
|
||||
"oneDay": {
|
||||
"message": "1 day"
|
||||
},
|
||||
"days": {
|
||||
"message": "$DAYS$ days",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"invitedUser": {
|
||||
"message": "Invited user."
|
||||
},
|
||||
"acceptEmergencyAccess": {
|
||||
"message": "You've been invited to become an emergency contact for the user listed above. To accept the invitation, you need to log in or create a new Bitwarden account."
|
||||
},
|
||||
"emergencyInviteAcceptFailed": {
|
||||
"message": "Unable to accept invitation. Ask the user to send a new invitation."
|
||||
},
|
||||
"emergencyInviteAcceptFailedShort": {
|
||||
"message": "Unable to accept invitation. $DESCRIPTION$",
|
||||
"placeholders": {
|
||||
"description": {
|
||||
"content": "$1",
|
||||
"example": "You must enable 2FA on your user account before you can join this organization."
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyInviteAcceptedDesc": {
|
||||
"message": "You can access the emergency options for this user after your identity has been confirmed. We'll send you an email when that happens."
|
||||
},
|
||||
"requestAccess": {
|
||||
"message": "Request Access"
|
||||
},
|
||||
"requestAccessConfirmation": {
|
||||
"message": "Are you sure you want to request emergency access? You will be provided access after $WAITTIME$ day(s) or whenever the user manually approves the request.",
|
||||
"placeholders": {
|
||||
"waittime": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requestSent": {
|
||||
"message": "Emergency access requested for $USER$. We'll notify you by email when it's possible to continue.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"approve": {
|
||||
"message": "Approve"
|
||||
},
|
||||
"reject": {
|
||||
"message": "Reject"
|
||||
},
|
||||
"approveAccessConfirmation": {
|
||||
"message": "Are you sure you want to approve emergency access? This will allow $USER$ to $ACTION$ your account.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
},
|
||||
"action": {
|
||||
"content": "$2",
|
||||
"example": "View"
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyApproved": {
|
||||
"message": "Emergency access approved."
|
||||
},
|
||||
"emergencyRejected": {
|
||||
"message": "Emergency access rejected"
|
||||
},
|
||||
"passwordResetFor": {
|
||||
"message": "Password reset for $USER$. You can now login using the new password.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"personalOwnership": {
|
||||
"message": "Personal Ownership"
|
||||
},
|
||||
"personalOwnershipPolicyDesc": {
|
||||
"message": "Require users to save vault items to an organization by removing the personal ownership option."
|
||||
},
|
||||
"personalOwnershipExemption": {
|
||||
"message": "Organization Owners and Administrators are exempt from this policy's enforcement."
|
||||
},
|
||||
"personalOwnershipSubmitError": {
|
||||
"message": "Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organization and choose from available Collections."
|
||||
},
|
||||
"modifiedPolicyId": {
|
||||
"message": "Modified policy $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "Master Password"
|
||||
}
|
||||
}
|
||||
},
|
||||
"planPrice": {
|
||||
"message": "Plan price"
|
||||
},
|
||||
"estimatedTax": {
|
||||
"message": "Estimated tax"
|
||||
},
|
||||
"custom": {
|
||||
"message": "Custom"
|
||||
},
|
||||
"customDesc": {
|
||||
"message": "Allows more granular control of user permissions for advanced configurations."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "Permissions"
|
||||
},
|
||||
"accessBusinessPortal": {
|
||||
"message": "Access Business Portal"
|
||||
},
|
||||
"accessEventLogs": {
|
||||
"message": "Access Event Logs"
|
||||
},
|
||||
"accessImportExport": {
|
||||
"message": "Access Import/Export"
|
||||
},
|
||||
"accessReports": {
|
||||
"message": "Access Reports"
|
||||
},
|
||||
"manageAllCollections": {
|
||||
"message": "Manage All Collections"
|
||||
},
|
||||
"manageAssignedCollections": {
|
||||
"message": "Manage Assigned Collections"
|
||||
},
|
||||
"manageGroups": {
|
||||
"message": "Manage Groups"
|
||||
},
|
||||
"managePolicies": {
|
||||
"message": "Manage Policies"
|
||||
},
|
||||
"manageSso": {
|
||||
"message": "Manage SSO"
|
||||
},
|
||||
"manageUsers": {
|
||||
"message": "Manage Users"
|
||||
},
|
||||
"disableRequireSsoError": {
|
||||
"message": "You must manually disable the Single Sign-On Authentication policy before this policy can be disabled."
|
||||
},
|
||||
"personalOwnershipPolicyInEffect": {
|
||||
"message": "An organization policy is affecting your ownership options."
|
||||
},
|
||||
"personalOwnershipCheckboxDesc": {
|
||||
"message": "Disable personal ownership for organization users"
|
||||
},
|
||||
"textHiddenByDefault": {
|
||||
"message": "When accessing the Send, hide the text by default",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,6 +347,9 @@
|
||||
"editItem": {
|
||||
"message": "Редактиране на елемента"
|
||||
},
|
||||
"viewItem": {
|
||||
"message": "View Item"
|
||||
},
|
||||
"ex": {
|
||||
"message": "напр.",
|
||||
"description": "Short abbreviation for 'example'."
|
||||
@@ -790,9 +793,15 @@
|
||||
"warning": {
|
||||
"message": "ВНИМАНИЕ"
|
||||
},
|
||||
"confirmVaultExport": {
|
||||
"message": "Confirm Vault Export"
|
||||
},
|
||||
"exportWarningDesc": {
|
||||
"message": "Данните от трезора ви ще се изнесат в незащитен формат. Не го пращайте по незащитени канали като електронна поща. Изтрийте файла незабавно след като свършите работата си с него."
|
||||
},
|
||||
"encExportWarningDesc": {
|
||||
"message": "This export encrypts your data using your account's encryption key. If you ever rotate your account's encryption key you should export again since you will not be able to decrypt this export file."
|
||||
},
|
||||
"exportMasterPassword": {
|
||||
"message": "Въведете главната парола, за да изнесете данните."
|
||||
},
|
||||
@@ -1659,6 +1668,9 @@
|
||||
"paymentInformation": {
|
||||
"message": "Информация за плащането"
|
||||
},
|
||||
"billingInformation": {
|
||||
"message": "Billing Information"
|
||||
},
|
||||
"creditCard": {
|
||||
"message": "Кредитна карта"
|
||||
},
|
||||
@@ -2483,6 +2495,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"unlinkedSsoUser": {
|
||||
"message": "Unlinked SSO for user $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"device": {
|
||||
"message": "Устройство"
|
||||
},
|
||||
@@ -2786,6 +2807,9 @@
|
||||
"updateEncryptionKeyWarning": {
|
||||
"message": "След смяната на ключа за шифриране ще трябва да се отпишете и след това да се впишете в регистрацията си във всички приложения на Битуорден, които ползвате (като мобилното приложение и разширенията за браузъри). Ако не се отпишете и впишете повторно (за да получите достъп до новия ключ), рискувате да повредите записите си. Сега ще се пробва да бъдете отписани автоматично, това обаче може да се забави."
|
||||
},
|
||||
"updateEncryptionKeyExportWarning": {
|
||||
"message": "Any encrypted exports that you have saved will also become invalid."
|
||||
},
|
||||
"subscription": {
|
||||
"message": "Абонамент"
|
||||
},
|
||||
@@ -3295,15 +3319,35 @@
|
||||
"deletionDate": {
|
||||
"message": "Deletion Date"
|
||||
},
|
||||
"deletionDateDesc": {
|
||||
"message": "The Send will be permanently deleted on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"expirationDate": {
|
||||
"message": "Срок на валидност"
|
||||
},
|
||||
"expirationDateDesc": {
|
||||
"message": "If set, access to this Send will expire on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"maxAccessCount": {
|
||||
"message": "Maximum Access Count"
|
||||
},
|
||||
"maxAccessCountDesc": {
|
||||
"message": "If set, users will no longer be able to access this send once the maximum access count is reached.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"currentAccessCount": {
|
||||
"message": "Current Access Count"
|
||||
},
|
||||
"sendPasswordDesc": {
|
||||
"message": "Optionally require a password for users to access this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendNotesDesc": {
|
||||
"message": "Private notes about this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"disabled": {
|
||||
"message": "Disabled"
|
||||
},
|
||||
@@ -3324,9 +3368,22 @@
|
||||
"removePasswordConfirmation": {
|
||||
"message": "Are you sure you want to remove the password?"
|
||||
},
|
||||
"disableThisSend": {
|
||||
"message": "Disable this Send so that no one can access it.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"allSends": {
|
||||
"message": "All Sends"
|
||||
},
|
||||
"maxAccessCountReached": {
|
||||
"message": "Max access count reached"
|
||||
},
|
||||
"pendingDeletion": {
|
||||
"message": "Pending deletion"
|
||||
},
|
||||
"expired": {
|
||||
"message": "Expired"
|
||||
},
|
||||
"searchSends": {
|
||||
"message": "Search Sends",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
@@ -3346,8 +3403,230 @@
|
||||
"downloadFile": {
|
||||
"message": "Изтеглете файл"
|
||||
},
|
||||
"sendAccessUnavailable": {
|
||||
"message": "The Send you are trying to access does not exist or is no longer available.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"noSendsInList": {
|
||||
"message": "There are no Sends to list.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"emergencyAccess": {
|
||||
"message": "Emergency Access"
|
||||
},
|
||||
"emergencyAccessDesc": {
|
||||
"message": "Grant and manage emergency access for trusted contacts. Trusted contacts may request access to either View or Takeover your account in case of a emergency. Visit our help page for more information and details into how zero knowledge sharing works."
|
||||
},
|
||||
"trustedEmergencyContacts": {
|
||||
"message": "Trusted emergency contacts"
|
||||
},
|
||||
"noTrustedContacts": {
|
||||
"message": "You have not added any emergency contacts yet, invite a trusted contact to get started."
|
||||
},
|
||||
"addEmergencyContact": {
|
||||
"message": "Add emergency contact"
|
||||
},
|
||||
"designatedEmergencyContacts": {
|
||||
"message": "Designated as emergency contact"
|
||||
},
|
||||
"noGrantedAccess": {
|
||||
"message": "You have not been designated as an emergency contact for anyone yet."
|
||||
},
|
||||
"inviteEmergencyContact": {
|
||||
"message": "Invite emergency contact"
|
||||
},
|
||||
"editEmergencyContact": {
|
||||
"message": "Edit emergency contact"
|
||||
},
|
||||
"inviteEmergencyContactDesc": {
|
||||
"message": "Invite a new emergency contact by entering their Bitwarden account email address below. If they do not have a Bitwarden account already, they will be prompted to create a new account."
|
||||
},
|
||||
"emergencyAccessRecoveryInitiated": {
|
||||
"message": "Emergency Access Initiated"
|
||||
},
|
||||
"emergencyAccessRecoveryApproved": {
|
||||
"message": "Emergency Access Approved"
|
||||
},
|
||||
"viewDesc": {
|
||||
"message": "Can view all items in your own vault."
|
||||
},
|
||||
"takeover": {
|
||||
"message": "Takeover"
|
||||
},
|
||||
"takeoverDesc": {
|
||||
"message": "Can reset your account with a new master password."
|
||||
},
|
||||
"waitTime": {
|
||||
"message": "Wait Time"
|
||||
},
|
||||
"waitTimeDesc": {
|
||||
"message": "Time required before automatically granting access."
|
||||
},
|
||||
"oneDay": {
|
||||
"message": "1 day"
|
||||
},
|
||||
"days": {
|
||||
"message": "$DAYS$ days",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"invitedUser": {
|
||||
"message": "Invited user."
|
||||
},
|
||||
"acceptEmergencyAccess": {
|
||||
"message": "You've been invited to become an emergency contact for the user listed above. To accept the invitation, you need to log in or create a new Bitwarden account."
|
||||
},
|
||||
"emergencyInviteAcceptFailed": {
|
||||
"message": "Unable to accept invitation. Ask the user to send a new invitation."
|
||||
},
|
||||
"emergencyInviteAcceptFailedShort": {
|
||||
"message": "Unable to accept invitation. $DESCRIPTION$",
|
||||
"placeholders": {
|
||||
"description": {
|
||||
"content": "$1",
|
||||
"example": "You must enable 2FA on your user account before you can join this organization."
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyInviteAcceptedDesc": {
|
||||
"message": "You can access the emergency options for this user after your identity has been confirmed. We'll send you an email when that happens."
|
||||
},
|
||||
"requestAccess": {
|
||||
"message": "Request Access"
|
||||
},
|
||||
"requestAccessConfirmation": {
|
||||
"message": "Are you sure you want to request emergency access? You will be provided access after $WAITTIME$ day(s) or whenever the user manually approves the request.",
|
||||
"placeholders": {
|
||||
"waittime": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requestSent": {
|
||||
"message": "Emergency access requested for $USER$. We'll notify you by email when it's possible to continue.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"approve": {
|
||||
"message": "Approve"
|
||||
},
|
||||
"reject": {
|
||||
"message": "Reject"
|
||||
},
|
||||
"approveAccessConfirmation": {
|
||||
"message": "Are you sure you want to approve emergency access? This will allow $USER$ to $ACTION$ your account.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
},
|
||||
"action": {
|
||||
"content": "$2",
|
||||
"example": "View"
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyApproved": {
|
||||
"message": "Emergency access approved."
|
||||
},
|
||||
"emergencyRejected": {
|
||||
"message": "Emergency access rejected"
|
||||
},
|
||||
"passwordResetFor": {
|
||||
"message": "Password reset for $USER$. You can now login using the new password.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"personalOwnership": {
|
||||
"message": "Personal Ownership"
|
||||
},
|
||||
"personalOwnershipPolicyDesc": {
|
||||
"message": "Require users to save vault items to an organization by removing the personal ownership option."
|
||||
},
|
||||
"personalOwnershipExemption": {
|
||||
"message": "Organization Owners and Administrators are exempt from this policy's enforcement."
|
||||
},
|
||||
"personalOwnershipSubmitError": {
|
||||
"message": "Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organization and choose from available Collections."
|
||||
},
|
||||
"modifiedPolicyId": {
|
||||
"message": "Modified policy $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "Master Password"
|
||||
}
|
||||
}
|
||||
},
|
||||
"planPrice": {
|
||||
"message": "Plan price"
|
||||
},
|
||||
"estimatedTax": {
|
||||
"message": "Estimated tax"
|
||||
},
|
||||
"custom": {
|
||||
"message": "Custom"
|
||||
},
|
||||
"customDesc": {
|
||||
"message": "Allows more granular control of user permissions for advanced configurations."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "Permissions"
|
||||
},
|
||||
"accessBusinessPortal": {
|
||||
"message": "Access Business Portal"
|
||||
},
|
||||
"accessEventLogs": {
|
||||
"message": "Access Event Logs"
|
||||
},
|
||||
"accessImportExport": {
|
||||
"message": "Access Import/Export"
|
||||
},
|
||||
"accessReports": {
|
||||
"message": "Access Reports"
|
||||
},
|
||||
"manageAllCollections": {
|
||||
"message": "Manage All Collections"
|
||||
},
|
||||
"manageAssignedCollections": {
|
||||
"message": "Manage Assigned Collections"
|
||||
},
|
||||
"manageGroups": {
|
||||
"message": "Manage Groups"
|
||||
},
|
||||
"managePolicies": {
|
||||
"message": "Manage Policies"
|
||||
},
|
||||
"manageSso": {
|
||||
"message": "Manage SSO"
|
||||
},
|
||||
"manageUsers": {
|
||||
"message": "Manage Users"
|
||||
},
|
||||
"disableRequireSsoError": {
|
||||
"message": "You must manually disable the Single Sign-On Authentication policy before this policy can be disabled."
|
||||
},
|
||||
"personalOwnershipPolicyInEffect": {
|
||||
"message": "An organization policy is affecting your ownership options."
|
||||
},
|
||||
"personalOwnershipCheckboxDesc": {
|
||||
"message": "Disable personal ownership for organization users"
|
||||
},
|
||||
"textHiddenByDefault": {
|
||||
"message": "When accessing the Send, hide the text by default",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,6 +347,9 @@
|
||||
"editItem": {
|
||||
"message": "Edita l'element"
|
||||
},
|
||||
"viewItem": {
|
||||
"message": "View Item"
|
||||
},
|
||||
"ex": {
|
||||
"message": "ex.",
|
||||
"description": "Short abbreviation for 'example'."
|
||||
@@ -790,9 +793,15 @@
|
||||
"warning": {
|
||||
"message": "Advertiment"
|
||||
},
|
||||
"confirmVaultExport": {
|
||||
"message": "Confirm Vault Export"
|
||||
},
|
||||
"exportWarningDesc": {
|
||||
"message": "Aquesta exportació conté les dades de la vostra caixa forta en un format no xifrat. No hauríeu d'emmagatzemar o enviar el fitxer exportat a través de canals no segurs (com ara el correu electrònic). Elimineu-lo immediatament després d'haver acabat d'usar-lo."
|
||||
},
|
||||
"encExportWarningDesc": {
|
||||
"message": "This export encrypts your data using your account's encryption key. If you ever rotate your account's encryption key you should export again since you will not be able to decrypt this export file."
|
||||
},
|
||||
"exportMasterPassword": {
|
||||
"message": "Introduïu la contrasenya mestra per exportar les dades de la caixa forta."
|
||||
},
|
||||
@@ -1659,6 +1668,9 @@
|
||||
"paymentInformation": {
|
||||
"message": "Informació de pagament"
|
||||
},
|
||||
"billingInformation": {
|
||||
"message": "Billing Information"
|
||||
},
|
||||
"creditCard": {
|
||||
"message": "Targeta de crèdit"
|
||||
},
|
||||
@@ -2483,6 +2495,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"unlinkedSsoUser": {
|
||||
"message": "Unlinked SSO for user $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"device": {
|
||||
"message": "Dispositiu"
|
||||
},
|
||||
@@ -2786,6 +2807,9 @@
|
||||
"updateEncryptionKeyWarning": {
|
||||
"message": "Després d'actualitzar la vostra clau de xifratge, heu de tancar la sessió i tornar a entrar a totes les aplicacions de Bitwarden que esteu utilitzant actualment (com ara l'aplicació mòbil o les extensions del navegador). Si no es tanca i torna a iniciar la sessió (la qual descarrega la vostra nova clau de xifratge) pot provocar corrupció en les dades. Intentarem registrar-vos automàticament, però, es pot retardar."
|
||||
},
|
||||
"updateEncryptionKeyExportWarning": {
|
||||
"message": "Any encrypted exports that you have saved will also become invalid."
|
||||
},
|
||||
"subscription": {
|
||||
"message": "Subscripció"
|
||||
},
|
||||
@@ -3194,7 +3218,7 @@
|
||||
"message": "Inici de sessió únic d'empresa"
|
||||
},
|
||||
"ssoHandOff": {
|
||||
"message": "You may now close this tab and continue in the extension."
|
||||
"message": "Ara podeu tancar aquesta pestanya i continuar a l'extensió."
|
||||
},
|
||||
"businessPortal": {
|
||||
"message": "Portal empresarial",
|
||||
@@ -3295,15 +3319,35 @@
|
||||
"deletionDate": {
|
||||
"message": "Data de supressió"
|
||||
},
|
||||
"deletionDateDesc": {
|
||||
"message": "The Send will be permanently deleted on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"expirationDate": {
|
||||
"message": "Data de venciment"
|
||||
},
|
||||
"expirationDateDesc": {
|
||||
"message": "If set, access to this Send will expire on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"maxAccessCount": {
|
||||
"message": "Recompte màxim d'accés"
|
||||
},
|
||||
"maxAccessCountDesc": {
|
||||
"message": "If set, users will no longer be able to access this send once the maximum access count is reached.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"currentAccessCount": {
|
||||
"message": "Recompte d’accés actual"
|
||||
},
|
||||
"sendPasswordDesc": {
|
||||
"message": "Optionally require a password for users to access this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendNotesDesc": {
|
||||
"message": "Private notes about this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"disabled": {
|
||||
"message": "Deshabilitat"
|
||||
},
|
||||
@@ -3324,9 +3368,22 @@
|
||||
"removePasswordConfirmation": {
|
||||
"message": "Esteu segur que voleu suprimir la contrasenya?"
|
||||
},
|
||||
"disableThisSend": {
|
||||
"message": "Disable this Send so that no one can access it.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"allSends": {
|
||||
"message": "Tots els Send"
|
||||
},
|
||||
"maxAccessCountReached": {
|
||||
"message": "Max access count reached"
|
||||
},
|
||||
"pendingDeletion": {
|
||||
"message": "Pending deletion"
|
||||
},
|
||||
"expired": {
|
||||
"message": "Expired"
|
||||
},
|
||||
"searchSends": {
|
||||
"message": "Cerca Sends",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
@@ -3346,8 +3403,230 @@
|
||||
"downloadFile": {
|
||||
"message": "Baixa el fitxer"
|
||||
},
|
||||
"sendAccessUnavailable": {
|
||||
"message": "The Send you are trying to access does not exist or is no longer available.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"noSendsInList": {
|
||||
"message": "No hi ha cap Send a llistar.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"emergencyAccess": {
|
||||
"message": "Emergency Access"
|
||||
},
|
||||
"emergencyAccessDesc": {
|
||||
"message": "Grant and manage emergency access for trusted contacts. Trusted contacts may request access to either View or Takeover your account in case of a emergency. Visit our help page for more information and details into how zero knowledge sharing works."
|
||||
},
|
||||
"trustedEmergencyContacts": {
|
||||
"message": "Trusted emergency contacts"
|
||||
},
|
||||
"noTrustedContacts": {
|
||||
"message": "You have not added any emergency contacts yet, invite a trusted contact to get started."
|
||||
},
|
||||
"addEmergencyContact": {
|
||||
"message": "Add emergency contact"
|
||||
},
|
||||
"designatedEmergencyContacts": {
|
||||
"message": "Designated as emergency contact"
|
||||
},
|
||||
"noGrantedAccess": {
|
||||
"message": "You have not been designated as an emergency contact for anyone yet."
|
||||
},
|
||||
"inviteEmergencyContact": {
|
||||
"message": "Invite emergency contact"
|
||||
},
|
||||
"editEmergencyContact": {
|
||||
"message": "Edit emergency contact"
|
||||
},
|
||||
"inviteEmergencyContactDesc": {
|
||||
"message": "Invite a new emergency contact by entering their Bitwarden account email address below. If they do not have a Bitwarden account already, they will be prompted to create a new account."
|
||||
},
|
||||
"emergencyAccessRecoveryInitiated": {
|
||||
"message": "Emergency Access Initiated"
|
||||
},
|
||||
"emergencyAccessRecoveryApproved": {
|
||||
"message": "Emergency Access Approved"
|
||||
},
|
||||
"viewDesc": {
|
||||
"message": "Can view all items in your own vault."
|
||||
},
|
||||
"takeover": {
|
||||
"message": "Takeover"
|
||||
},
|
||||
"takeoverDesc": {
|
||||
"message": "Can reset your account with a new master password."
|
||||
},
|
||||
"waitTime": {
|
||||
"message": "Wait Time"
|
||||
},
|
||||
"waitTimeDesc": {
|
||||
"message": "Time required before automatically granting access."
|
||||
},
|
||||
"oneDay": {
|
||||
"message": "1 day"
|
||||
},
|
||||
"days": {
|
||||
"message": "$DAYS$ days",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"invitedUser": {
|
||||
"message": "Invited user."
|
||||
},
|
||||
"acceptEmergencyAccess": {
|
||||
"message": "You've been invited to become an emergency contact for the user listed above. To accept the invitation, you need to log in or create a new Bitwarden account."
|
||||
},
|
||||
"emergencyInviteAcceptFailed": {
|
||||
"message": "Unable to accept invitation. Ask the user to send a new invitation."
|
||||
},
|
||||
"emergencyInviteAcceptFailedShort": {
|
||||
"message": "Unable to accept invitation. $DESCRIPTION$",
|
||||
"placeholders": {
|
||||
"description": {
|
||||
"content": "$1",
|
||||
"example": "You must enable 2FA on your user account before you can join this organization."
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyInviteAcceptedDesc": {
|
||||
"message": "You can access the emergency options for this user after your identity has been confirmed. We'll send you an email when that happens."
|
||||
},
|
||||
"requestAccess": {
|
||||
"message": "Request Access"
|
||||
},
|
||||
"requestAccessConfirmation": {
|
||||
"message": "Are you sure you want to request emergency access? You will be provided access after $WAITTIME$ day(s) or whenever the user manually approves the request.",
|
||||
"placeholders": {
|
||||
"waittime": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requestSent": {
|
||||
"message": "Emergency access requested for $USER$. We'll notify you by email when it's possible to continue.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"approve": {
|
||||
"message": "Approve"
|
||||
},
|
||||
"reject": {
|
||||
"message": "Reject"
|
||||
},
|
||||
"approveAccessConfirmation": {
|
||||
"message": "Are you sure you want to approve emergency access? This will allow $USER$ to $ACTION$ your account.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
},
|
||||
"action": {
|
||||
"content": "$2",
|
||||
"example": "View"
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyApproved": {
|
||||
"message": "Emergency access approved."
|
||||
},
|
||||
"emergencyRejected": {
|
||||
"message": "Emergency access rejected"
|
||||
},
|
||||
"passwordResetFor": {
|
||||
"message": "Password reset for $USER$. You can now login using the new password.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"personalOwnership": {
|
||||
"message": "Personal Ownership"
|
||||
},
|
||||
"personalOwnershipPolicyDesc": {
|
||||
"message": "Require users to save vault items to an organization by removing the personal ownership option."
|
||||
},
|
||||
"personalOwnershipExemption": {
|
||||
"message": "Organization Owners and Administrators are exempt from this policy's enforcement."
|
||||
},
|
||||
"personalOwnershipSubmitError": {
|
||||
"message": "Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organization and choose from available Collections."
|
||||
},
|
||||
"modifiedPolicyId": {
|
||||
"message": "Modified policy $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "Master Password"
|
||||
}
|
||||
}
|
||||
},
|
||||
"planPrice": {
|
||||
"message": "Plan price"
|
||||
},
|
||||
"estimatedTax": {
|
||||
"message": "Estimated tax"
|
||||
},
|
||||
"custom": {
|
||||
"message": "Custom"
|
||||
},
|
||||
"customDesc": {
|
||||
"message": "Allows more granular control of user permissions for advanced configurations."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "Permissions"
|
||||
},
|
||||
"accessBusinessPortal": {
|
||||
"message": "Access Business Portal"
|
||||
},
|
||||
"accessEventLogs": {
|
||||
"message": "Access Event Logs"
|
||||
},
|
||||
"accessImportExport": {
|
||||
"message": "Access Import/Export"
|
||||
},
|
||||
"accessReports": {
|
||||
"message": "Access Reports"
|
||||
},
|
||||
"manageAllCollections": {
|
||||
"message": "Manage All Collections"
|
||||
},
|
||||
"manageAssignedCollections": {
|
||||
"message": "Manage Assigned Collections"
|
||||
},
|
||||
"manageGroups": {
|
||||
"message": "Manage Groups"
|
||||
},
|
||||
"managePolicies": {
|
||||
"message": "Manage Policies"
|
||||
},
|
||||
"manageSso": {
|
||||
"message": "Manage SSO"
|
||||
},
|
||||
"manageUsers": {
|
||||
"message": "Manage Users"
|
||||
},
|
||||
"disableRequireSsoError": {
|
||||
"message": "You must manually disable the Single Sign-On Authentication policy before this policy can be disabled."
|
||||
},
|
||||
"personalOwnershipPolicyInEffect": {
|
||||
"message": "An organization policy is affecting your ownership options."
|
||||
},
|
||||
"personalOwnershipCheckboxDesc": {
|
||||
"message": "Disable personal ownership for organization users"
|
||||
},
|
||||
"textHiddenByDefault": {
|
||||
"message": "When accessing the Send, hide the text by default",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,6 +347,9 @@
|
||||
"editItem": {
|
||||
"message": "Upravit položku"
|
||||
},
|
||||
"viewItem": {
|
||||
"message": "View Item"
|
||||
},
|
||||
"ex": {
|
||||
"message": "např.",
|
||||
"description": "Short abbreviation for 'example'."
|
||||
@@ -790,9 +793,15 @@
|
||||
"warning": {
|
||||
"message": "Varování"
|
||||
},
|
||||
"confirmVaultExport": {
|
||||
"message": "Confirm Vault Export"
|
||||
},
|
||||
"exportWarningDesc": {
|
||||
"message": "Tento export obsahuje data vašeho trezoru v nezašifrovaném formátu. Soubor exportu byste neměli ukládat ani odesílat přes nezabezpečené kanály (např. e-mailem). Odstraňte jej okamžitě po jeho použití."
|
||||
},
|
||||
"encExportWarningDesc": {
|
||||
"message": "This export encrypts your data using your account's encryption key. If you ever rotate your account's encryption key you should export again since you will not be able to decrypt this export file."
|
||||
},
|
||||
"exportMasterPassword": {
|
||||
"message": "Zadejte své hlavní heslo pro export dat."
|
||||
},
|
||||
@@ -1659,6 +1668,9 @@
|
||||
"paymentInformation": {
|
||||
"message": "Informace o platbě"
|
||||
},
|
||||
"billingInformation": {
|
||||
"message": "Billing Information"
|
||||
},
|
||||
"creditCard": {
|
||||
"message": "Kreditní karta"
|
||||
},
|
||||
@@ -2483,6 +2495,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"unlinkedSsoUser": {
|
||||
"message": "Unlinked SSO for user $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"device": {
|
||||
"message": "Zařízení"
|
||||
},
|
||||
@@ -2786,6 +2807,9 @@
|
||||
"updateEncryptionKeyWarning": {
|
||||
"message": "Po aktualizace šifrovacího klíče dojde k odhlášení a budete se muset opětovně přihlásit do všech Bitwarden aplikací, které aktuálně používáte (např. mobilní aplikace či rozšíření pro prohlížeč). Nezdaří-li se odhlášení a opětovné přihlášení (během něhož bude stažen nový šifrovací klíč), může dojít k poškození údajů. Pokusíme se vás automaticky odhlásit, nicméně, může to chvíli trvat."
|
||||
},
|
||||
"updateEncryptionKeyExportWarning": {
|
||||
"message": "Any encrypted exports that you have saved will also become invalid."
|
||||
},
|
||||
"subscription": {
|
||||
"message": "Odběr"
|
||||
},
|
||||
@@ -3295,15 +3319,35 @@
|
||||
"deletionDate": {
|
||||
"message": "Datum odstranění"
|
||||
},
|
||||
"deletionDateDesc": {
|
||||
"message": "The Send will be permanently deleted on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"expirationDate": {
|
||||
"message": "Datum expirace"
|
||||
},
|
||||
"expirationDateDesc": {
|
||||
"message": "If set, access to this Send will expire on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"maxAccessCount": {
|
||||
"message": "Maximum Access Count"
|
||||
},
|
||||
"maxAccessCountDesc": {
|
||||
"message": "If set, users will no longer be able to access this send once the maximum access count is reached.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"currentAccessCount": {
|
||||
"message": "Current Access Count"
|
||||
},
|
||||
"sendPasswordDesc": {
|
||||
"message": "Optionally require a password for users to access this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendNotesDesc": {
|
||||
"message": "Private notes about this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"disabled": {
|
||||
"message": "Disabled"
|
||||
},
|
||||
@@ -3324,9 +3368,22 @@
|
||||
"removePasswordConfirmation": {
|
||||
"message": "Are you sure you want to remove the password?"
|
||||
},
|
||||
"disableThisSend": {
|
||||
"message": "Disable this Send so that no one can access it.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"allSends": {
|
||||
"message": "All Sends"
|
||||
},
|
||||
"maxAccessCountReached": {
|
||||
"message": "Max access count reached"
|
||||
},
|
||||
"pendingDeletion": {
|
||||
"message": "Pending deletion"
|
||||
},
|
||||
"expired": {
|
||||
"message": "Expired"
|
||||
},
|
||||
"searchSends": {
|
||||
"message": "Search Sends",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
@@ -3346,8 +3403,230 @@
|
||||
"downloadFile": {
|
||||
"message": "Stáhnout soubor"
|
||||
},
|
||||
"sendAccessUnavailable": {
|
||||
"message": "The Send you are trying to access does not exist or is no longer available.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"noSendsInList": {
|
||||
"message": "There are no Sends to list.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"emergencyAccess": {
|
||||
"message": "Emergency Access"
|
||||
},
|
||||
"emergencyAccessDesc": {
|
||||
"message": "Grant and manage emergency access for trusted contacts. Trusted contacts may request access to either View or Takeover your account in case of a emergency. Visit our help page for more information and details into how zero knowledge sharing works."
|
||||
},
|
||||
"trustedEmergencyContacts": {
|
||||
"message": "Trusted emergency contacts"
|
||||
},
|
||||
"noTrustedContacts": {
|
||||
"message": "You have not added any emergency contacts yet, invite a trusted contact to get started."
|
||||
},
|
||||
"addEmergencyContact": {
|
||||
"message": "Add emergency contact"
|
||||
},
|
||||
"designatedEmergencyContacts": {
|
||||
"message": "Designated as emergency contact"
|
||||
},
|
||||
"noGrantedAccess": {
|
||||
"message": "You have not been designated as an emergency contact for anyone yet."
|
||||
},
|
||||
"inviteEmergencyContact": {
|
||||
"message": "Invite emergency contact"
|
||||
},
|
||||
"editEmergencyContact": {
|
||||
"message": "Edit emergency contact"
|
||||
},
|
||||
"inviteEmergencyContactDesc": {
|
||||
"message": "Invite a new emergency contact by entering their Bitwarden account email address below. If they do not have a Bitwarden account already, they will be prompted to create a new account."
|
||||
},
|
||||
"emergencyAccessRecoveryInitiated": {
|
||||
"message": "Emergency Access Initiated"
|
||||
},
|
||||
"emergencyAccessRecoveryApproved": {
|
||||
"message": "Emergency Access Approved"
|
||||
},
|
||||
"viewDesc": {
|
||||
"message": "Can view all items in your own vault."
|
||||
},
|
||||
"takeover": {
|
||||
"message": "Takeover"
|
||||
},
|
||||
"takeoverDesc": {
|
||||
"message": "Can reset your account with a new master password."
|
||||
},
|
||||
"waitTime": {
|
||||
"message": "Wait Time"
|
||||
},
|
||||
"waitTimeDesc": {
|
||||
"message": "Time required before automatically granting access."
|
||||
},
|
||||
"oneDay": {
|
||||
"message": "1 day"
|
||||
},
|
||||
"days": {
|
||||
"message": "$DAYS$ days",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"invitedUser": {
|
||||
"message": "Invited user."
|
||||
},
|
||||
"acceptEmergencyAccess": {
|
||||
"message": "You've been invited to become an emergency contact for the user listed above. To accept the invitation, you need to log in or create a new Bitwarden account."
|
||||
},
|
||||
"emergencyInviteAcceptFailed": {
|
||||
"message": "Unable to accept invitation. Ask the user to send a new invitation."
|
||||
},
|
||||
"emergencyInviteAcceptFailedShort": {
|
||||
"message": "Unable to accept invitation. $DESCRIPTION$",
|
||||
"placeholders": {
|
||||
"description": {
|
||||
"content": "$1",
|
||||
"example": "You must enable 2FA on your user account before you can join this organization."
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyInviteAcceptedDesc": {
|
||||
"message": "You can access the emergency options for this user after your identity has been confirmed. We'll send you an email when that happens."
|
||||
},
|
||||
"requestAccess": {
|
||||
"message": "Request Access"
|
||||
},
|
||||
"requestAccessConfirmation": {
|
||||
"message": "Are you sure you want to request emergency access? You will be provided access after $WAITTIME$ day(s) or whenever the user manually approves the request.",
|
||||
"placeholders": {
|
||||
"waittime": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requestSent": {
|
||||
"message": "Emergency access requested for $USER$. We'll notify you by email when it's possible to continue.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"approve": {
|
||||
"message": "Approve"
|
||||
},
|
||||
"reject": {
|
||||
"message": "Reject"
|
||||
},
|
||||
"approveAccessConfirmation": {
|
||||
"message": "Are you sure you want to approve emergency access? This will allow $USER$ to $ACTION$ your account.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
},
|
||||
"action": {
|
||||
"content": "$2",
|
||||
"example": "View"
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyApproved": {
|
||||
"message": "Emergency access approved."
|
||||
},
|
||||
"emergencyRejected": {
|
||||
"message": "Emergency access rejected"
|
||||
},
|
||||
"passwordResetFor": {
|
||||
"message": "Password reset for $USER$. You can now login using the new password.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"personalOwnership": {
|
||||
"message": "Personal Ownership"
|
||||
},
|
||||
"personalOwnershipPolicyDesc": {
|
||||
"message": "Require users to save vault items to an organization by removing the personal ownership option."
|
||||
},
|
||||
"personalOwnershipExemption": {
|
||||
"message": "Organization Owners and Administrators are exempt from this policy's enforcement."
|
||||
},
|
||||
"personalOwnershipSubmitError": {
|
||||
"message": "Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organization and choose from available Collections."
|
||||
},
|
||||
"modifiedPolicyId": {
|
||||
"message": "Modified policy $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "Master Password"
|
||||
}
|
||||
}
|
||||
},
|
||||
"planPrice": {
|
||||
"message": "Plan price"
|
||||
},
|
||||
"estimatedTax": {
|
||||
"message": "Estimated tax"
|
||||
},
|
||||
"custom": {
|
||||
"message": "Custom"
|
||||
},
|
||||
"customDesc": {
|
||||
"message": "Allows more granular control of user permissions for advanced configurations."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "Permissions"
|
||||
},
|
||||
"accessBusinessPortal": {
|
||||
"message": "Access Business Portal"
|
||||
},
|
||||
"accessEventLogs": {
|
||||
"message": "Access Event Logs"
|
||||
},
|
||||
"accessImportExport": {
|
||||
"message": "Access Import/Export"
|
||||
},
|
||||
"accessReports": {
|
||||
"message": "Access Reports"
|
||||
},
|
||||
"manageAllCollections": {
|
||||
"message": "Manage All Collections"
|
||||
},
|
||||
"manageAssignedCollections": {
|
||||
"message": "Manage Assigned Collections"
|
||||
},
|
||||
"manageGroups": {
|
||||
"message": "Manage Groups"
|
||||
},
|
||||
"managePolicies": {
|
||||
"message": "Manage Policies"
|
||||
},
|
||||
"manageSso": {
|
||||
"message": "Manage SSO"
|
||||
},
|
||||
"manageUsers": {
|
||||
"message": "Manage Users"
|
||||
},
|
||||
"disableRequireSsoError": {
|
||||
"message": "You must manually disable the Single Sign-On Authentication policy before this policy can be disabled."
|
||||
},
|
||||
"personalOwnershipPolicyInEffect": {
|
||||
"message": "An organization policy is affecting your ownership options."
|
||||
},
|
||||
"personalOwnershipCheckboxDesc": {
|
||||
"message": "Disable personal ownership for organization users"
|
||||
},
|
||||
"textHiddenByDefault": {
|
||||
"message": "When accessing the Send, hide the text by default",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,6 +347,9 @@
|
||||
"editItem": {
|
||||
"message": "Redigér element"
|
||||
},
|
||||
"viewItem": {
|
||||
"message": "View Item"
|
||||
},
|
||||
"ex": {
|
||||
"message": "eks.",
|
||||
"description": "Short abbreviation for 'example'."
|
||||
@@ -461,7 +464,7 @@
|
||||
"message": "Slet vedhæftning"
|
||||
},
|
||||
"deleteItemConfirmation": {
|
||||
"message": "Er du sikker på, at du vil slette dette element?"
|
||||
"message": "Er du sikker på, at du sende til papirkurven?"
|
||||
},
|
||||
"deletedItem": {
|
||||
"message": "Element sendt til papirkurven"
|
||||
@@ -790,9 +793,15 @@
|
||||
"warning": {
|
||||
"message": "Advarsel"
|
||||
},
|
||||
"confirmVaultExport": {
|
||||
"message": "Confirm Vault Export"
|
||||
},
|
||||
"exportWarningDesc": {
|
||||
"message": "Denne eksport indeholder dine boksdata i ukrypteret form. Du bør ikke gemme eller sende den eksporterede fil over usikre kanaler (f.eks. e-mail). Slet den straks efter at du er færdig med at bruge den."
|
||||
},
|
||||
"encExportWarningDesc": {
|
||||
"message": "This export encrypts your data using your account's encryption key. If you ever rotate your account's encryption key you should export again since you will not be able to decrypt this export file."
|
||||
},
|
||||
"exportMasterPassword": {
|
||||
"message": "Indtast din hovedadgangskode for at eksportere dine data fra boksen."
|
||||
},
|
||||
@@ -1659,6 +1668,9 @@
|
||||
"paymentInformation": {
|
||||
"message": "Betalingsoplysninger"
|
||||
},
|
||||
"billingInformation": {
|
||||
"message": "Billing Information"
|
||||
},
|
||||
"creditCard": {
|
||||
"message": "Betalingskort"
|
||||
},
|
||||
@@ -2483,6 +2495,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"unlinkedSsoUser": {
|
||||
"message": "Unlinked SSO for user $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"device": {
|
||||
"message": "Enhed"
|
||||
},
|
||||
@@ -2786,6 +2807,9 @@
|
||||
"updateEncryptionKeyWarning": {
|
||||
"message": "Efter opdatering af din krypteringsnøgle skal du logge ud og ind igen i alle Bitwarden-programmer, du bruger i øjeblikket (f.eks. mobilapp eller browserudvidelser). Hvis du ikke logger ud og ind (som downloader din nye krypteringsnøgle), kan det resultere i data korruption. Vi vil forsøge at logge dig ud automatisk, men det kan blive forsinket."
|
||||
},
|
||||
"updateEncryptionKeyExportWarning": {
|
||||
"message": "Any encrypted exports that you have saved will also become invalid."
|
||||
},
|
||||
"subscription": {
|
||||
"message": "Abonnement"
|
||||
},
|
||||
@@ -3194,7 +3218,7 @@
|
||||
"message": "Virksomheds Single Sign On"
|
||||
},
|
||||
"ssoHandOff": {
|
||||
"message": "You may now close this tab and continue in the extension."
|
||||
"message": "Du kan nu lukke denne fane og fortsætte i udvidelsen."
|
||||
},
|
||||
"businessPortal": {
|
||||
"message": "Virksomhedssportal",
|
||||
@@ -3295,15 +3319,35 @@
|
||||
"deletionDate": {
|
||||
"message": "Sletningsdato"
|
||||
},
|
||||
"deletionDateDesc": {
|
||||
"message": "The Send will be permanently deleted on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"expirationDate": {
|
||||
"message": "Udløbsdato"
|
||||
},
|
||||
"expirationDateDesc": {
|
||||
"message": "If set, access to this Send will expire on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"maxAccessCount": {
|
||||
"message": "Maksimal antal tilgange"
|
||||
},
|
||||
"maxAccessCountDesc": {
|
||||
"message": "If set, users will no longer be able to access this send once the maximum access count is reached.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"currentAccessCount": {
|
||||
"message": "Aktuelt antal tilgange"
|
||||
},
|
||||
"sendPasswordDesc": {
|
||||
"message": "Optionally require a password for users to access this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendNotesDesc": {
|
||||
"message": "Private notes about this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"disabled": {
|
||||
"message": "Deaktiveret"
|
||||
},
|
||||
@@ -3324,9 +3368,22 @@
|
||||
"removePasswordConfirmation": {
|
||||
"message": "Er du sikker på, at du vil fjerne adgangskoden?"
|
||||
},
|
||||
"disableThisSend": {
|
||||
"message": "Disable this Send so that no one can access it.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"allSends": {
|
||||
"message": "Alle Send"
|
||||
},
|
||||
"maxAccessCountReached": {
|
||||
"message": "Max access count reached"
|
||||
},
|
||||
"pendingDeletion": {
|
||||
"message": "Pending deletion"
|
||||
},
|
||||
"expired": {
|
||||
"message": "Expired"
|
||||
},
|
||||
"searchSends": {
|
||||
"message": "Søg Send",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
@@ -3346,8 +3403,230 @@
|
||||
"downloadFile": {
|
||||
"message": "Download fil"
|
||||
},
|
||||
"sendAccessUnavailable": {
|
||||
"message": "The Send you are trying to access does not exist or is no longer available.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"noSendsInList": {
|
||||
"message": "Der er ingen Send at vise.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"emergencyAccess": {
|
||||
"message": "Emergency Access"
|
||||
},
|
||||
"emergencyAccessDesc": {
|
||||
"message": "Grant and manage emergency access for trusted contacts. Trusted contacts may request access to either View or Takeover your account in case of a emergency. Visit our help page for more information and details into how zero knowledge sharing works."
|
||||
},
|
||||
"trustedEmergencyContacts": {
|
||||
"message": "Trusted emergency contacts"
|
||||
},
|
||||
"noTrustedContacts": {
|
||||
"message": "You have not added any emergency contacts yet, invite a trusted contact to get started."
|
||||
},
|
||||
"addEmergencyContact": {
|
||||
"message": "Add emergency contact"
|
||||
},
|
||||
"designatedEmergencyContacts": {
|
||||
"message": "Designated as emergency contact"
|
||||
},
|
||||
"noGrantedAccess": {
|
||||
"message": "You have not been designated as an emergency contact for anyone yet."
|
||||
},
|
||||
"inviteEmergencyContact": {
|
||||
"message": "Invite emergency contact"
|
||||
},
|
||||
"editEmergencyContact": {
|
||||
"message": "Edit emergency contact"
|
||||
},
|
||||
"inviteEmergencyContactDesc": {
|
||||
"message": "Invite a new emergency contact by entering their Bitwarden account email address below. If they do not have a Bitwarden account already, they will be prompted to create a new account."
|
||||
},
|
||||
"emergencyAccessRecoveryInitiated": {
|
||||
"message": "Emergency Access Initiated"
|
||||
},
|
||||
"emergencyAccessRecoveryApproved": {
|
||||
"message": "Emergency Access Approved"
|
||||
},
|
||||
"viewDesc": {
|
||||
"message": "Can view all items in your own vault."
|
||||
},
|
||||
"takeover": {
|
||||
"message": "Takeover"
|
||||
},
|
||||
"takeoverDesc": {
|
||||
"message": "Can reset your account with a new master password."
|
||||
},
|
||||
"waitTime": {
|
||||
"message": "Wait Time"
|
||||
},
|
||||
"waitTimeDesc": {
|
||||
"message": "Time required before automatically granting access."
|
||||
},
|
||||
"oneDay": {
|
||||
"message": "1 day"
|
||||
},
|
||||
"days": {
|
||||
"message": "$DAYS$ days",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"invitedUser": {
|
||||
"message": "Invited user."
|
||||
},
|
||||
"acceptEmergencyAccess": {
|
||||
"message": "You've been invited to become an emergency contact for the user listed above. To accept the invitation, you need to log in or create a new Bitwarden account."
|
||||
},
|
||||
"emergencyInviteAcceptFailed": {
|
||||
"message": "Unable to accept invitation. Ask the user to send a new invitation."
|
||||
},
|
||||
"emergencyInviteAcceptFailedShort": {
|
||||
"message": "Unable to accept invitation. $DESCRIPTION$",
|
||||
"placeholders": {
|
||||
"description": {
|
||||
"content": "$1",
|
||||
"example": "You must enable 2FA on your user account before you can join this organization."
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyInviteAcceptedDesc": {
|
||||
"message": "You can access the emergency options for this user after your identity has been confirmed. We'll send you an email when that happens."
|
||||
},
|
||||
"requestAccess": {
|
||||
"message": "Request Access"
|
||||
},
|
||||
"requestAccessConfirmation": {
|
||||
"message": "Are you sure you want to request emergency access? You will be provided access after $WAITTIME$ day(s) or whenever the user manually approves the request.",
|
||||
"placeholders": {
|
||||
"waittime": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requestSent": {
|
||||
"message": "Emergency access requested for $USER$. We'll notify you by email when it's possible to continue.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"approve": {
|
||||
"message": "Approve"
|
||||
},
|
||||
"reject": {
|
||||
"message": "Reject"
|
||||
},
|
||||
"approveAccessConfirmation": {
|
||||
"message": "Are you sure you want to approve emergency access? This will allow $USER$ to $ACTION$ your account.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
},
|
||||
"action": {
|
||||
"content": "$2",
|
||||
"example": "View"
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyApproved": {
|
||||
"message": "Emergency access approved."
|
||||
},
|
||||
"emergencyRejected": {
|
||||
"message": "Emergency access rejected"
|
||||
},
|
||||
"passwordResetFor": {
|
||||
"message": "Password reset for $USER$. You can now login using the new password.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"personalOwnership": {
|
||||
"message": "Personal Ownership"
|
||||
},
|
||||
"personalOwnershipPolicyDesc": {
|
||||
"message": "Require users to save vault items to an organization by removing the personal ownership option."
|
||||
},
|
||||
"personalOwnershipExemption": {
|
||||
"message": "Organization Owners and Administrators are exempt from this policy's enforcement."
|
||||
},
|
||||
"personalOwnershipSubmitError": {
|
||||
"message": "Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organization and choose from available Collections."
|
||||
},
|
||||
"modifiedPolicyId": {
|
||||
"message": "Modified policy $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "Master Password"
|
||||
}
|
||||
}
|
||||
},
|
||||
"planPrice": {
|
||||
"message": "Plan price"
|
||||
},
|
||||
"estimatedTax": {
|
||||
"message": "Estimated tax"
|
||||
},
|
||||
"custom": {
|
||||
"message": "Custom"
|
||||
},
|
||||
"customDesc": {
|
||||
"message": "Allows more granular control of user permissions for advanced configurations."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "Permissions"
|
||||
},
|
||||
"accessBusinessPortal": {
|
||||
"message": "Access Business Portal"
|
||||
},
|
||||
"accessEventLogs": {
|
||||
"message": "Access Event Logs"
|
||||
},
|
||||
"accessImportExport": {
|
||||
"message": "Access Import/Export"
|
||||
},
|
||||
"accessReports": {
|
||||
"message": "Access Reports"
|
||||
},
|
||||
"manageAllCollections": {
|
||||
"message": "Manage All Collections"
|
||||
},
|
||||
"manageAssignedCollections": {
|
||||
"message": "Manage Assigned Collections"
|
||||
},
|
||||
"manageGroups": {
|
||||
"message": "Manage Groups"
|
||||
},
|
||||
"managePolicies": {
|
||||
"message": "Manage Policies"
|
||||
},
|
||||
"manageSso": {
|
||||
"message": "Manage SSO"
|
||||
},
|
||||
"manageUsers": {
|
||||
"message": "Manage Users"
|
||||
},
|
||||
"disableRequireSsoError": {
|
||||
"message": "You must manually disable the Single Sign-On Authentication policy before this policy can be disabled."
|
||||
},
|
||||
"personalOwnershipPolicyInEffect": {
|
||||
"message": "An organization policy is affecting your ownership options."
|
||||
},
|
||||
"personalOwnershipCheckboxDesc": {
|
||||
"message": "Disable personal ownership for organization users"
|
||||
},
|
||||
"textHiddenByDefault": {
|
||||
"message": "When accessing the Send, hide the text by default",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,6 +347,9 @@
|
||||
"editItem": {
|
||||
"message": "Eintrag bearbeiten"
|
||||
},
|
||||
"viewItem": {
|
||||
"message": "View Item"
|
||||
},
|
||||
"ex": {
|
||||
"message": "Bsp.",
|
||||
"description": "Short abbreviation for 'example'."
|
||||
@@ -790,9 +793,15 @@
|
||||
"warning": {
|
||||
"message": "Warnung"
|
||||
},
|
||||
"confirmVaultExport": {
|
||||
"message": "Confirm Vault Export"
|
||||
},
|
||||
"exportWarningDesc": {
|
||||
"message": "Dieser Export enthält Ihre Tresor-Daten in einem unverschlüsseltem Format. Sie sollten die Datei daher nicht über unsichere Kanäle (z.B. E-Mail) versenden oder speichern. Löschen Sie die Datei sofort nach ihrer Verwendung."
|
||||
},
|
||||
"encExportWarningDesc": {
|
||||
"message": "This export encrypts your data using your account's encryption key. If you ever rotate your account's encryption key you should export again since you will not be able to decrypt this export file."
|
||||
},
|
||||
"exportMasterPassword": {
|
||||
"message": "Geben Sie das Master-Passwort ein, um Ihre Tresordaten zu exportieren."
|
||||
},
|
||||
@@ -1659,6 +1668,9 @@
|
||||
"paymentInformation": {
|
||||
"message": "Zahlungsinformationen"
|
||||
},
|
||||
"billingInformation": {
|
||||
"message": "Billing Information"
|
||||
},
|
||||
"creditCard": {
|
||||
"message": "Kreditkarte"
|
||||
},
|
||||
@@ -2483,6 +2495,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"unlinkedSsoUser": {
|
||||
"message": "Unlinked SSO for user $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"device": {
|
||||
"message": "Gerät"
|
||||
},
|
||||
@@ -2786,6 +2807,9 @@
|
||||
"updateEncryptionKeyWarning": {
|
||||
"message": "Nach der Aktualisierung Ihres Verschlüsselungscodes, müssen Sie sich bei allen Bitwarden-Anwendungen, welche Sie momentan benutzen (wie z. B. Smartphone-App, Browser-Erweiterungen), erneut anmelden. Ein Versäumnis der Ab- und Anmeldung (welche Ihren neuen Verschlüsselungscode bezieht) könnte zu einer Beschädigung der Daten führen. Wir werden versuchen Sie automatisch auszuloggen, was jedoch verzögert geschehen kann."
|
||||
},
|
||||
"updateEncryptionKeyExportWarning": {
|
||||
"message": "Any encrypted exports that you have saved will also become invalid."
|
||||
},
|
||||
"subscription": {
|
||||
"message": "Abo"
|
||||
},
|
||||
@@ -3194,7 +3218,7 @@
|
||||
"message": "Enterprise Single Sign-On"
|
||||
},
|
||||
"ssoHandOff": {
|
||||
"message": "You may now close this tab and continue in the extension."
|
||||
"message": "Sie können diesen Tab nun schließen und in der Erweiterung fortfahren."
|
||||
},
|
||||
"businessPortal": {
|
||||
"message": "Unternehmensportal",
|
||||
@@ -3295,15 +3319,35 @@
|
||||
"deletionDate": {
|
||||
"message": "Löschdatum"
|
||||
},
|
||||
"deletionDateDesc": {
|
||||
"message": "The Send will be permanently deleted on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"expirationDate": {
|
||||
"message": "Ablaufdatum"
|
||||
},
|
||||
"expirationDateDesc": {
|
||||
"message": "If set, access to this Send will expire on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"maxAccessCount": {
|
||||
"message": "Maximale Zugriffsanzahl"
|
||||
},
|
||||
"maxAccessCountDesc": {
|
||||
"message": "If set, users will no longer be able to access this send once the maximum access count is reached.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"currentAccessCount": {
|
||||
"message": "Aktuelle Zugriffsanzahl"
|
||||
},
|
||||
"sendPasswordDesc": {
|
||||
"message": "Optionally require a password for users to access this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendNotesDesc": {
|
||||
"message": "Private notes about this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"disabled": {
|
||||
"message": "Deaktiviert"
|
||||
},
|
||||
@@ -3324,9 +3368,22 @@
|
||||
"removePasswordConfirmation": {
|
||||
"message": "Sind Sie sicher, dass Sie das Passwort entfernen möchten?"
|
||||
},
|
||||
"disableThisSend": {
|
||||
"message": "Disable this Send so that no one can access it.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"allSends": {
|
||||
"message": "Alle Sends"
|
||||
},
|
||||
"maxAccessCountReached": {
|
||||
"message": "Max access count reached"
|
||||
},
|
||||
"pendingDeletion": {
|
||||
"message": "Pending deletion"
|
||||
},
|
||||
"expired": {
|
||||
"message": "Expired"
|
||||
},
|
||||
"searchSends": {
|
||||
"message": "Sends suchen",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
@@ -3346,8 +3403,230 @@
|
||||
"downloadFile": {
|
||||
"message": "Datei herunterladen"
|
||||
},
|
||||
"sendAccessUnavailable": {
|
||||
"message": "The Send you are trying to access does not exist or is no longer available.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"noSendsInList": {
|
||||
"message": "Es gibt keine Sends aufzulisten.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"emergencyAccess": {
|
||||
"message": "Emergency Access"
|
||||
},
|
||||
"emergencyAccessDesc": {
|
||||
"message": "Grant and manage emergency access for trusted contacts. Trusted contacts may request access to either View or Takeover your account in case of a emergency. Visit our help page for more information and details into how zero knowledge sharing works."
|
||||
},
|
||||
"trustedEmergencyContacts": {
|
||||
"message": "Trusted emergency contacts"
|
||||
},
|
||||
"noTrustedContacts": {
|
||||
"message": "You have not added any emergency contacts yet, invite a trusted contact to get started."
|
||||
},
|
||||
"addEmergencyContact": {
|
||||
"message": "Add emergency contact"
|
||||
},
|
||||
"designatedEmergencyContacts": {
|
||||
"message": "Designated as emergency contact"
|
||||
},
|
||||
"noGrantedAccess": {
|
||||
"message": "You have not been designated as an emergency contact for anyone yet."
|
||||
},
|
||||
"inviteEmergencyContact": {
|
||||
"message": "Invite emergency contact"
|
||||
},
|
||||
"editEmergencyContact": {
|
||||
"message": "Edit emergency contact"
|
||||
},
|
||||
"inviteEmergencyContactDesc": {
|
||||
"message": "Invite a new emergency contact by entering their Bitwarden account email address below. If they do not have a Bitwarden account already, they will be prompted to create a new account."
|
||||
},
|
||||
"emergencyAccessRecoveryInitiated": {
|
||||
"message": "Emergency Access Initiated"
|
||||
},
|
||||
"emergencyAccessRecoveryApproved": {
|
||||
"message": "Emergency Access Approved"
|
||||
},
|
||||
"viewDesc": {
|
||||
"message": "Can view all items in your own vault."
|
||||
},
|
||||
"takeover": {
|
||||
"message": "Takeover"
|
||||
},
|
||||
"takeoverDesc": {
|
||||
"message": "Can reset your account with a new master password."
|
||||
},
|
||||
"waitTime": {
|
||||
"message": "Wait Time"
|
||||
},
|
||||
"waitTimeDesc": {
|
||||
"message": "Time required before automatically granting access."
|
||||
},
|
||||
"oneDay": {
|
||||
"message": "1 day"
|
||||
},
|
||||
"days": {
|
||||
"message": "$DAYS$ days",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"invitedUser": {
|
||||
"message": "Invited user."
|
||||
},
|
||||
"acceptEmergencyAccess": {
|
||||
"message": "You've been invited to become an emergency contact for the user listed above. To accept the invitation, you need to log in or create a new Bitwarden account."
|
||||
},
|
||||
"emergencyInviteAcceptFailed": {
|
||||
"message": "Unable to accept invitation. Ask the user to send a new invitation."
|
||||
},
|
||||
"emergencyInviteAcceptFailedShort": {
|
||||
"message": "Unable to accept invitation. $DESCRIPTION$",
|
||||
"placeholders": {
|
||||
"description": {
|
||||
"content": "$1",
|
||||
"example": "You must enable 2FA on your user account before you can join this organization."
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyInviteAcceptedDesc": {
|
||||
"message": "You can access the emergency options for this user after your identity has been confirmed. We'll send you an email when that happens."
|
||||
},
|
||||
"requestAccess": {
|
||||
"message": "Request Access"
|
||||
},
|
||||
"requestAccessConfirmation": {
|
||||
"message": "Are you sure you want to request emergency access? You will be provided access after $WAITTIME$ day(s) or whenever the user manually approves the request.",
|
||||
"placeholders": {
|
||||
"waittime": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requestSent": {
|
||||
"message": "Emergency access requested for $USER$. We'll notify you by email when it's possible to continue.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"approve": {
|
||||
"message": "Approve"
|
||||
},
|
||||
"reject": {
|
||||
"message": "Reject"
|
||||
},
|
||||
"approveAccessConfirmation": {
|
||||
"message": "Are you sure you want to approve emergency access? This will allow $USER$ to $ACTION$ your account.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
},
|
||||
"action": {
|
||||
"content": "$2",
|
||||
"example": "View"
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyApproved": {
|
||||
"message": "Emergency access approved."
|
||||
},
|
||||
"emergencyRejected": {
|
||||
"message": "Emergency access rejected"
|
||||
},
|
||||
"passwordResetFor": {
|
||||
"message": "Password reset for $USER$. You can now login using the new password.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"personalOwnership": {
|
||||
"message": "Personal Ownership"
|
||||
},
|
||||
"personalOwnershipPolicyDesc": {
|
||||
"message": "Require users to save vault items to an organization by removing the personal ownership option."
|
||||
},
|
||||
"personalOwnershipExemption": {
|
||||
"message": "Organization Owners and Administrators are exempt from this policy's enforcement."
|
||||
},
|
||||
"personalOwnershipSubmitError": {
|
||||
"message": "Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organization and choose from available Collections."
|
||||
},
|
||||
"modifiedPolicyId": {
|
||||
"message": "Modified policy $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "Master Password"
|
||||
}
|
||||
}
|
||||
},
|
||||
"planPrice": {
|
||||
"message": "Plan price"
|
||||
},
|
||||
"estimatedTax": {
|
||||
"message": "Estimated tax"
|
||||
},
|
||||
"custom": {
|
||||
"message": "Custom"
|
||||
},
|
||||
"customDesc": {
|
||||
"message": "Allows more granular control of user permissions for advanced configurations."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "Permissions"
|
||||
},
|
||||
"accessBusinessPortal": {
|
||||
"message": "Access Business Portal"
|
||||
},
|
||||
"accessEventLogs": {
|
||||
"message": "Access Event Logs"
|
||||
},
|
||||
"accessImportExport": {
|
||||
"message": "Access Import/Export"
|
||||
},
|
||||
"accessReports": {
|
||||
"message": "Access Reports"
|
||||
},
|
||||
"manageAllCollections": {
|
||||
"message": "Manage All Collections"
|
||||
},
|
||||
"manageAssignedCollections": {
|
||||
"message": "Manage Assigned Collections"
|
||||
},
|
||||
"manageGroups": {
|
||||
"message": "Manage Groups"
|
||||
},
|
||||
"managePolicies": {
|
||||
"message": "Manage Policies"
|
||||
},
|
||||
"manageSso": {
|
||||
"message": "Manage SSO"
|
||||
},
|
||||
"manageUsers": {
|
||||
"message": "Manage Users"
|
||||
},
|
||||
"disableRequireSsoError": {
|
||||
"message": "You must manually disable the Single Sign-On Authentication policy before this policy can be disabled."
|
||||
},
|
||||
"personalOwnershipPolicyInEffect": {
|
||||
"message": "An organization policy is affecting your ownership options."
|
||||
},
|
||||
"personalOwnershipCheckboxDesc": {
|
||||
"message": "Disable personal ownership for organization users"
|
||||
},
|
||||
"textHiddenByDefault": {
|
||||
"message": "When accessing the Send, hide the text by default",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,6 +347,9 @@
|
||||
"editItem": {
|
||||
"message": "Επεξεργασία Στοιχείου"
|
||||
},
|
||||
"viewItem": {
|
||||
"message": "View Item"
|
||||
},
|
||||
"ex": {
|
||||
"message": "πχ.",
|
||||
"description": "Short abbreviation for 'example'."
|
||||
@@ -461,7 +464,7 @@
|
||||
"message": "Διαγραφή συνημμένου"
|
||||
},
|
||||
"deleteItemConfirmation": {
|
||||
"message": "Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτό το στοιχείο;"
|
||||
"message": "Θέλετε πραγματικά να στείλετε στον κάδο απορριμμάτων;"
|
||||
},
|
||||
"deletedItem": {
|
||||
"message": "Διαγραμμένο στοιχείο"
|
||||
@@ -790,9 +793,15 @@
|
||||
"warning": {
|
||||
"message": "Προειδοποίηση"
|
||||
},
|
||||
"confirmVaultExport": {
|
||||
"message": "Confirm Vault Export"
|
||||
},
|
||||
"exportWarningDesc": {
|
||||
"message": "Αυτή η εξαγωγή περιέχει τα δεδομένα σε μη κρυπτογραφημένη μορφή. Δεν πρέπει να αποθηκεύετε ή να στείλετε το εξαγόμενο αρχείο μέσω μη ασφαλών τρόπων (όπως μέσω email). Διαγράψτε το αμέσως μόλις τελειώσετε με τη χρήση του."
|
||||
},
|
||||
"encExportWarningDesc": {
|
||||
"message": "This export encrypts your data using your account's encryption key. If you ever rotate your account's encryption key you should export again since you will not be able to decrypt this export file."
|
||||
},
|
||||
"exportMasterPassword": {
|
||||
"message": "Πληκτρολογήστε τον κύριο κωδικό για εξαγωγή των δεδομένων vault."
|
||||
},
|
||||
@@ -1659,6 +1668,9 @@
|
||||
"paymentInformation": {
|
||||
"message": "Πληροφορίες Πληρωμής"
|
||||
},
|
||||
"billingInformation": {
|
||||
"message": "Billing Information"
|
||||
},
|
||||
"creditCard": {
|
||||
"message": "Πιστωτική Κάρτα"
|
||||
},
|
||||
@@ -2483,6 +2495,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"unlinkedSsoUser": {
|
||||
"message": "Unlinked SSO for user $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"device": {
|
||||
"message": "Συσκευή"
|
||||
},
|
||||
@@ -2786,6 +2807,9 @@
|
||||
"updateEncryptionKeyWarning": {
|
||||
"message": "Μετά την ενημέρωση του κλειδιού κρυπτογράφησης, πρέπει να αποσυνδεθείτε και να επιστρέψετε σε όλες τις εφαρμογές Bitwarden που χρησιμοποιείτε αυτήν τη στιγμή (όπως η εφαρμογή για κινητά ή οι επεκτάσεις του προγράμματος περιήγησης). Η αποτυχία αποσύνδεσης και επαναφοράς (στην οποία γίνεται λήψη του νέου κλειδιού κρυπτογράφησης) ενδέχεται να προκαλέσει καταστροφή δεδομένων. Θα προσπαθήσουμε να αποσυνδεθείτε αυτόματα, ωστόσο αυτό μπορεί να καθυστερήσει."
|
||||
},
|
||||
"updateEncryptionKeyExportWarning": {
|
||||
"message": "Any encrypted exports that you have saved will also become invalid."
|
||||
},
|
||||
"subscription": {
|
||||
"message": "Συνδρομή"
|
||||
},
|
||||
@@ -3194,7 +3218,7 @@
|
||||
"message": "Ενιαία είσοδος για επιχειρήσεις"
|
||||
},
|
||||
"ssoHandOff": {
|
||||
"message": "You may now close this tab and continue in the extension."
|
||||
"message": "Μπορείτε να κλείσετε αυτήν την καρτέλα τώρα και να συνεχίσετε στην επέκταση."
|
||||
},
|
||||
"businessPortal": {
|
||||
"message": "Επιχειρηματική πύλη",
|
||||
@@ -3295,15 +3319,35 @@
|
||||
"deletionDate": {
|
||||
"message": "Ημερομηνία διαγραφής"
|
||||
},
|
||||
"deletionDateDesc": {
|
||||
"message": "The Send will be permanently deleted on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"expirationDate": {
|
||||
"message": "Ημερομηνία Λήξης"
|
||||
},
|
||||
"expirationDateDesc": {
|
||||
"message": "If set, access to this Send will expire on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"maxAccessCount": {
|
||||
"message": "Μέγιστος Αριθμός Πρόσβασης"
|
||||
},
|
||||
"maxAccessCountDesc": {
|
||||
"message": "If set, users will no longer be able to access this send once the maximum access count is reached.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"currentAccessCount": {
|
||||
"message": "Τρέχων Αριθμός Πρόσβασης"
|
||||
},
|
||||
"sendPasswordDesc": {
|
||||
"message": "Optionally require a password for users to access this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendNotesDesc": {
|
||||
"message": "Private notes about this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"disabled": {
|
||||
"message": "Απενεργοποιημένο"
|
||||
},
|
||||
@@ -3324,9 +3368,22 @@
|
||||
"removePasswordConfirmation": {
|
||||
"message": "Είστε βέβαιοι ότι θέλετε να καταργήσετε τον κωδικό πρόσβασης;"
|
||||
},
|
||||
"disableThisSend": {
|
||||
"message": "Disable this Send so that no one can access it.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"allSends": {
|
||||
"message": "Όλα τα Sends"
|
||||
},
|
||||
"maxAccessCountReached": {
|
||||
"message": "Max access count reached"
|
||||
},
|
||||
"pendingDeletion": {
|
||||
"message": "Pending deletion"
|
||||
},
|
||||
"expired": {
|
||||
"message": "Expired"
|
||||
},
|
||||
"searchSends": {
|
||||
"message": "Αναζήτηση Sends",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
@@ -3346,8 +3403,230 @@
|
||||
"downloadFile": {
|
||||
"message": "Λήψη Αρχείου"
|
||||
},
|
||||
"sendAccessUnavailable": {
|
||||
"message": "The Send you are trying to access does not exist or is no longer available.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"noSendsInList": {
|
||||
"message": "Δεν υπάρχουν Sends στη λίστα.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"emergencyAccess": {
|
||||
"message": "Emergency Access"
|
||||
},
|
||||
"emergencyAccessDesc": {
|
||||
"message": "Grant and manage emergency access for trusted contacts. Trusted contacts may request access to either View or Takeover your account in case of a emergency. Visit our help page for more information and details into how zero knowledge sharing works."
|
||||
},
|
||||
"trustedEmergencyContacts": {
|
||||
"message": "Trusted emergency contacts"
|
||||
},
|
||||
"noTrustedContacts": {
|
||||
"message": "You have not added any emergency contacts yet, invite a trusted contact to get started."
|
||||
},
|
||||
"addEmergencyContact": {
|
||||
"message": "Add emergency contact"
|
||||
},
|
||||
"designatedEmergencyContacts": {
|
||||
"message": "Designated as emergency contact"
|
||||
},
|
||||
"noGrantedAccess": {
|
||||
"message": "You have not been designated as an emergency contact for anyone yet."
|
||||
},
|
||||
"inviteEmergencyContact": {
|
||||
"message": "Invite emergency contact"
|
||||
},
|
||||
"editEmergencyContact": {
|
||||
"message": "Edit emergency contact"
|
||||
},
|
||||
"inviteEmergencyContactDesc": {
|
||||
"message": "Invite a new emergency contact by entering their Bitwarden account email address below. If they do not have a Bitwarden account already, they will be prompted to create a new account."
|
||||
},
|
||||
"emergencyAccessRecoveryInitiated": {
|
||||
"message": "Emergency Access Initiated"
|
||||
},
|
||||
"emergencyAccessRecoveryApproved": {
|
||||
"message": "Emergency Access Approved"
|
||||
},
|
||||
"viewDesc": {
|
||||
"message": "Can view all items in your own vault."
|
||||
},
|
||||
"takeover": {
|
||||
"message": "Takeover"
|
||||
},
|
||||
"takeoverDesc": {
|
||||
"message": "Can reset your account with a new master password."
|
||||
},
|
||||
"waitTime": {
|
||||
"message": "Wait Time"
|
||||
},
|
||||
"waitTimeDesc": {
|
||||
"message": "Time required before automatically granting access."
|
||||
},
|
||||
"oneDay": {
|
||||
"message": "1 day"
|
||||
},
|
||||
"days": {
|
||||
"message": "$DAYS$ days",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"invitedUser": {
|
||||
"message": "Invited user."
|
||||
},
|
||||
"acceptEmergencyAccess": {
|
||||
"message": "You've been invited to become an emergency contact for the user listed above. To accept the invitation, you need to log in or create a new Bitwarden account."
|
||||
},
|
||||
"emergencyInviteAcceptFailed": {
|
||||
"message": "Unable to accept invitation. Ask the user to send a new invitation."
|
||||
},
|
||||
"emergencyInviteAcceptFailedShort": {
|
||||
"message": "Unable to accept invitation. $DESCRIPTION$",
|
||||
"placeholders": {
|
||||
"description": {
|
||||
"content": "$1",
|
||||
"example": "You must enable 2FA on your user account before you can join this organization."
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyInviteAcceptedDesc": {
|
||||
"message": "You can access the emergency options for this user after your identity has been confirmed. We'll send you an email when that happens."
|
||||
},
|
||||
"requestAccess": {
|
||||
"message": "Request Access"
|
||||
},
|
||||
"requestAccessConfirmation": {
|
||||
"message": "Are you sure you want to request emergency access? You will be provided access after $WAITTIME$ day(s) or whenever the user manually approves the request.",
|
||||
"placeholders": {
|
||||
"waittime": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requestSent": {
|
||||
"message": "Emergency access requested for $USER$. We'll notify you by email when it's possible to continue.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"approve": {
|
||||
"message": "Approve"
|
||||
},
|
||||
"reject": {
|
||||
"message": "Reject"
|
||||
},
|
||||
"approveAccessConfirmation": {
|
||||
"message": "Are you sure you want to approve emergency access? This will allow $USER$ to $ACTION$ your account.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
},
|
||||
"action": {
|
||||
"content": "$2",
|
||||
"example": "View"
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyApproved": {
|
||||
"message": "Emergency access approved."
|
||||
},
|
||||
"emergencyRejected": {
|
||||
"message": "Emergency access rejected"
|
||||
},
|
||||
"passwordResetFor": {
|
||||
"message": "Password reset for $USER$. You can now login using the new password.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"personalOwnership": {
|
||||
"message": "Personal Ownership"
|
||||
},
|
||||
"personalOwnershipPolicyDesc": {
|
||||
"message": "Require users to save vault items to an organization by removing the personal ownership option."
|
||||
},
|
||||
"personalOwnershipExemption": {
|
||||
"message": "Organization Owners and Administrators are exempt from this policy's enforcement."
|
||||
},
|
||||
"personalOwnershipSubmitError": {
|
||||
"message": "Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organization and choose from available Collections."
|
||||
},
|
||||
"modifiedPolicyId": {
|
||||
"message": "Modified policy $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "Master Password"
|
||||
}
|
||||
}
|
||||
},
|
||||
"planPrice": {
|
||||
"message": "Plan price"
|
||||
},
|
||||
"estimatedTax": {
|
||||
"message": "Estimated tax"
|
||||
},
|
||||
"custom": {
|
||||
"message": "Custom"
|
||||
},
|
||||
"customDesc": {
|
||||
"message": "Allows more granular control of user permissions for advanced configurations."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "Permissions"
|
||||
},
|
||||
"accessBusinessPortal": {
|
||||
"message": "Access Business Portal"
|
||||
},
|
||||
"accessEventLogs": {
|
||||
"message": "Access Event Logs"
|
||||
},
|
||||
"accessImportExport": {
|
||||
"message": "Access Import/Export"
|
||||
},
|
||||
"accessReports": {
|
||||
"message": "Access Reports"
|
||||
},
|
||||
"manageAllCollections": {
|
||||
"message": "Manage All Collections"
|
||||
},
|
||||
"manageAssignedCollections": {
|
||||
"message": "Manage Assigned Collections"
|
||||
},
|
||||
"manageGroups": {
|
||||
"message": "Manage Groups"
|
||||
},
|
||||
"managePolicies": {
|
||||
"message": "Manage Policies"
|
||||
},
|
||||
"manageSso": {
|
||||
"message": "Manage SSO"
|
||||
},
|
||||
"manageUsers": {
|
||||
"message": "Manage Users"
|
||||
},
|
||||
"disableRequireSsoError": {
|
||||
"message": "You must manually disable the Single Sign-On Authentication policy before this policy can be disabled."
|
||||
},
|
||||
"personalOwnershipPolicyInEffect": {
|
||||
"message": "An organization policy is affecting your ownership options."
|
||||
},
|
||||
"personalOwnershipCheckboxDesc": {
|
||||
"message": "Disable personal ownership for organization users"
|
||||
},
|
||||
"textHiddenByDefault": {
|
||||
"message": "When accessing the Send, hide the text by default",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,6 +347,9 @@
|
||||
"editItem": {
|
||||
"message": "Edit Item"
|
||||
},
|
||||
"viewItem": {
|
||||
"message": "View Item"
|
||||
},
|
||||
"ex": {
|
||||
"message": "ex.",
|
||||
"description": "Short abbreviation for 'example'."
|
||||
@@ -790,9 +793,15 @@
|
||||
"warning": {
|
||||
"message": "Warning"
|
||||
},
|
||||
"confirmVaultExport": {
|
||||
"message": "Confirm Vault Export"
|
||||
},
|
||||
"exportWarningDesc": {
|
||||
"message": "This export contains your vault data in an unencrypted format. You should not store or send the exported file over unsecure channels (such as email). Delete it immediately after you are done using it."
|
||||
},
|
||||
"encExportWarningDesc": {
|
||||
"message": "This export encrypts your data using your account's encryption key. If you ever rotate your account's encryption key you should export again since you will not be able to decrypt this export file."
|
||||
},
|
||||
"exportMasterPassword": {
|
||||
"message": "Enter your master password to export your vault data."
|
||||
},
|
||||
@@ -1659,6 +1668,9 @@
|
||||
"paymentInformation": {
|
||||
"message": "Payment Information"
|
||||
},
|
||||
"billingInformation": {
|
||||
"message": "Billing Information"
|
||||
},
|
||||
"creditCard": {
|
||||
"message": "Credit Card"
|
||||
},
|
||||
@@ -2483,6 +2495,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"unlinkedSsoUser": {
|
||||
"message": "Unlinked SSO for user $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"device": {
|
||||
"message": "Device"
|
||||
},
|
||||
@@ -2786,6 +2807,9 @@
|
||||
"updateEncryptionKeyWarning": {
|
||||
"message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed."
|
||||
},
|
||||
"updateEncryptionKeyExportWarning": {
|
||||
"message": "Any encrypted exports that you have saved will also become invalid."
|
||||
},
|
||||
"subscription": {
|
||||
"message": "Subscription"
|
||||
},
|
||||
@@ -3295,15 +3319,35 @@
|
||||
"deletionDate": {
|
||||
"message": "Deletion Date"
|
||||
},
|
||||
"deletionDateDesc": {
|
||||
"message": "The Send will be permanently deleted on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"expirationDate": {
|
||||
"message": "Expiration Date"
|
||||
},
|
||||
"expirationDateDesc": {
|
||||
"message": "If set, access to this Send will expire on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"maxAccessCount": {
|
||||
"message": "Maximum Access Count"
|
||||
},
|
||||
"maxAccessCountDesc": {
|
||||
"message": "If set, users will no longer be able to access this send once the maximum access count is reached.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"currentAccessCount": {
|
||||
"message": "Current Access Count"
|
||||
},
|
||||
"sendPasswordDesc": {
|
||||
"message": "Optionally require a password for users to access this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendNotesDesc": {
|
||||
"message": "Private notes about this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"disabled": {
|
||||
"message": "Disabled"
|
||||
},
|
||||
@@ -3324,9 +3368,22 @@
|
||||
"removePasswordConfirmation": {
|
||||
"message": "Are you sure you want to remove the password?"
|
||||
},
|
||||
"disableThisSend": {
|
||||
"message": "Disable this Send so that no one can access it.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"allSends": {
|
||||
"message": "All Sends"
|
||||
},
|
||||
"maxAccessCountReached": {
|
||||
"message": "Max access count reached"
|
||||
},
|
||||
"pendingDeletion": {
|
||||
"message": "Pending deletion"
|
||||
},
|
||||
"expired": {
|
||||
"message": "Expired"
|
||||
},
|
||||
"searchSends": {
|
||||
"message": "Search Sends",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
@@ -3346,8 +3403,233 @@
|
||||
"downloadFile": {
|
||||
"message": "Download File"
|
||||
},
|
||||
"sendAccessUnavailable": {
|
||||
"message": "The Send you are trying to access does not exist or is no longer available.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"noSendsInList": {
|
||||
"message": "There are no Sends to list.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"emergencyAccess": {
|
||||
"message": "Emergency Access"
|
||||
},
|
||||
"emergencyAccessDesc": {
|
||||
"message": "Grant and manage emergency access for trusted contacts. Trusted contacts may request access to either View or Takeover your account in case of a emergency. Visit our help page for more information and details into how zero knowledge sharing works."
|
||||
},
|
||||
"trustedEmergencyContacts": {
|
||||
"message": "Trusted emergency contacts"
|
||||
},
|
||||
"noTrustedContacts": {
|
||||
"message": "You have not added any emergency contacts yet, invite a trusted contact to get started."
|
||||
},
|
||||
"addEmergencyContact": {
|
||||
"message": "Add emergency contact"
|
||||
},
|
||||
"designatedEmergencyContacts": {
|
||||
"message": "Designated as emergency contact"
|
||||
},
|
||||
"noGrantedAccess": {
|
||||
"message": "You have not been designated as an emergency contact for anyone yet."
|
||||
},
|
||||
"inviteEmergencyContact": {
|
||||
"message": "Invite emergency contact"
|
||||
},
|
||||
"editEmergencyContact": {
|
||||
"message": "Edit emergency contact"
|
||||
},
|
||||
"inviteEmergencyContactDesc": {
|
||||
"message": "Invite a new emergency contact by entering their Bitwarden account email address below. If they do not have a Bitwarden account already, they will be prompted to create a new account."
|
||||
},
|
||||
"emergencyAccessRecoveryInitiated": {
|
||||
"message": "Emergency Access Initiated"
|
||||
},
|
||||
"emergencyAccessRecoveryApproved": {
|
||||
"message": "Emergency Access Approved"
|
||||
},
|
||||
"viewDesc": {
|
||||
"message": "Can view all items in your own vault."
|
||||
},
|
||||
"takeover": {
|
||||
"message": "Takeover"
|
||||
},
|
||||
"takeoverDesc": {
|
||||
"message": "Can reset your account with a new master password."
|
||||
},
|
||||
"waitTime": {
|
||||
"message": "Wait Time"
|
||||
},
|
||||
"waitTimeDesc": {
|
||||
"message": "Time required before automatically granting access."
|
||||
},
|
||||
"oneDay": {
|
||||
"message": "1 day"
|
||||
},
|
||||
"days": {
|
||||
"message": "$DAYS$ days",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"invitedUser": {
|
||||
"message": "Invited user."
|
||||
},
|
||||
"acceptEmergencyAccess": {
|
||||
"message": "You've been invited to become an emergency contact for the user listed above. To accept the invitation, you need to log in or create a new Bitwarden account."
|
||||
},
|
||||
"emergencyInviteAcceptFailed": {
|
||||
"message": "Unable to accept invitation. Ask the user to send a new invitation."
|
||||
},
|
||||
"emergencyInviteAcceptFailedShort": {
|
||||
"message": "Unable to accept invitation. $DESCRIPTION$",
|
||||
"placeholders": {
|
||||
"description": {
|
||||
"content": "$1",
|
||||
"example": "You must enable 2FA on your user account before you can join this organization."
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyInviteAcceptedDesc": {
|
||||
"message": "You can access the emergency options for this user after your identity has been confirmed. We'll send you an email when that happens."
|
||||
},
|
||||
"requestAccess": {
|
||||
"message": "Request Access"
|
||||
},
|
||||
"requestAccessConfirmation": {
|
||||
"message": "Are you sure you want to request emergency access? You will be provided access after $WAITTIME$ day(s) or whenever the user manually approves the request.",
|
||||
"placeholders": {
|
||||
"waittime": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requestSent": {
|
||||
"message": "Emergency access requested for $USER$. We'll notify you by email when it's possible to continue.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"approve": {
|
||||
"message": "Approve"
|
||||
},
|
||||
"reject": {
|
||||
"message": "Reject"
|
||||
},
|
||||
"approveAccessConfirmation": {
|
||||
"message": "Are you sure you want to approve emergency access? This will allow $USER$ to $ACTION$ your account.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
},
|
||||
"action": {
|
||||
"content": "$2",
|
||||
"example": "View"
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyApproved": {
|
||||
"message": "Emergency access approved."
|
||||
},
|
||||
"emergencyRejected": {
|
||||
"message": "Emergency access rejected"
|
||||
},
|
||||
"passwordResetFor": {
|
||||
"message": "Password reset for $USER$. You can now login using the new password.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"personalOwnership": {
|
||||
"message": "Personal Ownership"
|
||||
},
|
||||
"personalOwnershipPolicyDesc": {
|
||||
"message": "Require users to save vault items to an organization by removing the personal ownership option."
|
||||
},
|
||||
"personalOwnershipExemption": {
|
||||
"message": "Organization Owners and Administrators are exempt from this policy's enforcement."
|
||||
},
|
||||
"personalOwnershipSubmitError": {
|
||||
"message": "Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organization and choose from available Collections."
|
||||
},
|
||||
"modifiedPolicyId": {
|
||||
"message": "Modified policy $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "Master Password"
|
||||
}
|
||||
}
|
||||
},
|
||||
"planPrice": {
|
||||
"message": "Plan price"
|
||||
},
|
||||
"estimatedTax": {
|
||||
"message": "Estimated tax"
|
||||
},
|
||||
"custom": {
|
||||
"message": "Custom"
|
||||
},
|
||||
"customDesc": {
|
||||
"message": "Allows more granular control of user permissions for advanced configurations."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "Permissions"
|
||||
},
|
||||
"accessBusinessPortal": {
|
||||
"message": "Access Business Portal"
|
||||
},
|
||||
"accessEventLogs": {
|
||||
"message": "Access Event Logs"
|
||||
},
|
||||
"accessImportExport": {
|
||||
"message": "Access Import/Export"
|
||||
},
|
||||
"accessReports": {
|
||||
"message": "Access Reports"
|
||||
},
|
||||
"manageAllCollections": {
|
||||
"message": "Manage All Collections"
|
||||
},
|
||||
"manageAssignedCollections": {
|
||||
"message": "Manage Assigned Collections"
|
||||
},
|
||||
"manageGroups": {
|
||||
"message": "Manage Groups"
|
||||
},
|
||||
"managePolicies": {
|
||||
"message": "Manage Policies"
|
||||
},
|
||||
"manageSso": {
|
||||
"message": "Manage SSO"
|
||||
},
|
||||
"manageUsers": {
|
||||
"message": "Manage Users"
|
||||
},
|
||||
"disableRequireSsoError": {
|
||||
"message": "You must manually disable the Single Sign-On Authentication policy before this policy can be disabled."
|
||||
},
|
||||
"personalOwnershipPolicyInEffect": {
|
||||
"message": "An organization policy is affecting your ownership options."
|
||||
},
|
||||
"custom": {
|
||||
"message": "Custom"
|
||||
},
|
||||
"personalOwnershipCheckboxDesc": {
|
||||
"message": "Disable personal ownership for organization users"
|
||||
},
|
||||
"textHiddenByDefault": {
|
||||
"message": "When accessing the Send, hide the text by default",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,6 +347,9 @@
|
||||
"editItem": {
|
||||
"message": "Edit item"
|
||||
},
|
||||
"viewItem": {
|
||||
"message": "View Item"
|
||||
},
|
||||
"ex": {
|
||||
"message": "e.g.",
|
||||
"description": "Short abbreviation for 'example'."
|
||||
@@ -461,7 +464,7 @@
|
||||
"message": "Delete attachment"
|
||||
},
|
||||
"deleteItemConfirmation": {
|
||||
"message": "Are you sure you want to delete this item?"
|
||||
"message": "Do you really want to send to the bin?"
|
||||
},
|
||||
"deletedItem": {
|
||||
"message": "Item sent to bin"
|
||||
@@ -790,9 +793,15 @@
|
||||
"warning": {
|
||||
"message": "Warning"
|
||||
},
|
||||
"confirmVaultExport": {
|
||||
"message": "Confirm Vault Export"
|
||||
},
|
||||
"exportWarningDesc": {
|
||||
"message": "This export contains your vault data in an unencrypted format. You should not store or send the exported file over insecure channels (such as email). Delete it immediately after you are done using it."
|
||||
},
|
||||
"encExportWarningDesc": {
|
||||
"message": "This export encrypts your data using your account's encryption key. If you ever rotate your account's encryption key you should export again since you will not be able to decrypt this export file."
|
||||
},
|
||||
"exportMasterPassword": {
|
||||
"message": "Enter your master password to export your vault data."
|
||||
},
|
||||
@@ -1659,6 +1668,9 @@
|
||||
"paymentInformation": {
|
||||
"message": "Payment information"
|
||||
},
|
||||
"billingInformation": {
|
||||
"message": "Billing Information"
|
||||
},
|
||||
"creditCard": {
|
||||
"message": "Credit card"
|
||||
},
|
||||
@@ -2483,6 +2495,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"unlinkedSsoUser": {
|
||||
"message": "Unlinked SSO for user $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"device": {
|
||||
"message": "Device"
|
||||
},
|
||||
@@ -2786,6 +2807,9 @@
|
||||
"updateEncryptionKeyWarning": {
|
||||
"message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, although this may be delayed."
|
||||
},
|
||||
"updateEncryptionKeyExportWarning": {
|
||||
"message": "Any encrypted exports that you have saved will also become invalid."
|
||||
},
|
||||
"subscription": {
|
||||
"message": "Subscription"
|
||||
},
|
||||
@@ -3295,15 +3319,35 @@
|
||||
"deletionDate": {
|
||||
"message": "Deletion date"
|
||||
},
|
||||
"deletionDateDesc": {
|
||||
"message": "The Send will be permanently deleted on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"expirationDate": {
|
||||
"message": "Expiration date"
|
||||
},
|
||||
"expirationDateDesc": {
|
||||
"message": "If set, access to this Send will expire on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"maxAccessCount": {
|
||||
"message": "Maximum access count"
|
||||
},
|
||||
"maxAccessCountDesc": {
|
||||
"message": "If set, users will no longer be able to access this send once the maximum access count is reached.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"currentAccessCount": {
|
||||
"message": "Current access count"
|
||||
},
|
||||
"sendPasswordDesc": {
|
||||
"message": "Optionally require a password for users to access this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendNotesDesc": {
|
||||
"message": "Private notes about this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"disabled": {
|
||||
"message": "Disabled"
|
||||
},
|
||||
@@ -3324,9 +3368,22 @@
|
||||
"removePasswordConfirmation": {
|
||||
"message": "Are you sure you want to remove the password?"
|
||||
},
|
||||
"disableThisSend": {
|
||||
"message": "Disable this Send so that no one can access it.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"allSends": {
|
||||
"message": "All sends"
|
||||
},
|
||||
"maxAccessCountReached": {
|
||||
"message": "Max access count reached"
|
||||
},
|
||||
"pendingDeletion": {
|
||||
"message": "Pending deletion"
|
||||
},
|
||||
"expired": {
|
||||
"message": "Expired"
|
||||
},
|
||||
"searchSends": {
|
||||
"message": "Search sends",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
@@ -3346,8 +3403,230 @@
|
||||
"downloadFile": {
|
||||
"message": "Download file"
|
||||
},
|
||||
"sendAccessUnavailable": {
|
||||
"message": "The Send you are trying to access does not exist or is no longer available.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"noSendsInList": {
|
||||
"message": "There are no sends to list.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"emergencyAccess": {
|
||||
"message": "Emergency Access"
|
||||
},
|
||||
"emergencyAccessDesc": {
|
||||
"message": "Grant and manage emergency access for trusted contacts. Trusted contacts may request access to either View or Takeover your account in case of a emergency. Visit our help page for more information and details into how zero knowledge sharing works."
|
||||
},
|
||||
"trustedEmergencyContacts": {
|
||||
"message": "Trusted emergency contacts"
|
||||
},
|
||||
"noTrustedContacts": {
|
||||
"message": "You have not added any emergency contacts yet, invite a trusted contact to get started."
|
||||
},
|
||||
"addEmergencyContact": {
|
||||
"message": "Add emergency contact"
|
||||
},
|
||||
"designatedEmergencyContacts": {
|
||||
"message": "Designated as emergency contact"
|
||||
},
|
||||
"noGrantedAccess": {
|
||||
"message": "You have not been designated as an emergency contact for anyone yet."
|
||||
},
|
||||
"inviteEmergencyContact": {
|
||||
"message": "Invite emergency contact"
|
||||
},
|
||||
"editEmergencyContact": {
|
||||
"message": "Edit emergency contact"
|
||||
},
|
||||
"inviteEmergencyContactDesc": {
|
||||
"message": "Invite a new emergency contact by entering their Bitwarden account email address below. If they do not have a Bitwarden account already, they will be prompted to create a new account."
|
||||
},
|
||||
"emergencyAccessRecoveryInitiated": {
|
||||
"message": "Emergency Access Initiated"
|
||||
},
|
||||
"emergencyAccessRecoveryApproved": {
|
||||
"message": "Emergency Access Approved"
|
||||
},
|
||||
"viewDesc": {
|
||||
"message": "Can view all items in your own vault."
|
||||
},
|
||||
"takeover": {
|
||||
"message": "Takeover"
|
||||
},
|
||||
"takeoverDesc": {
|
||||
"message": "Can reset your account with a new master password."
|
||||
},
|
||||
"waitTime": {
|
||||
"message": "Wait Time"
|
||||
},
|
||||
"waitTimeDesc": {
|
||||
"message": "Time required before automatically granting access."
|
||||
},
|
||||
"oneDay": {
|
||||
"message": "1 day"
|
||||
},
|
||||
"days": {
|
||||
"message": "$DAYS$ days",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"invitedUser": {
|
||||
"message": "Invited user."
|
||||
},
|
||||
"acceptEmergencyAccess": {
|
||||
"message": "You've been invited to become an emergency contact for the user listed above. To accept the invitation, you need to log in or create a new Bitwarden account."
|
||||
},
|
||||
"emergencyInviteAcceptFailed": {
|
||||
"message": "Unable to accept invitation. Ask the user to send a new invitation."
|
||||
},
|
||||
"emergencyInviteAcceptFailedShort": {
|
||||
"message": "Unable to accept invitation. $DESCRIPTION$",
|
||||
"placeholders": {
|
||||
"description": {
|
||||
"content": "$1",
|
||||
"example": "You must enable 2FA on your user account before you can join this organization."
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyInviteAcceptedDesc": {
|
||||
"message": "You can access the emergency options for this user after your identity has been confirmed. We'll send you an email when that happens."
|
||||
},
|
||||
"requestAccess": {
|
||||
"message": "Request Access"
|
||||
},
|
||||
"requestAccessConfirmation": {
|
||||
"message": "Are you sure you want to request emergency access? You will be provided access after $WAITTIME$ day(s) or whenever the user manually approves the request.",
|
||||
"placeholders": {
|
||||
"waittime": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requestSent": {
|
||||
"message": "Emergency access requested for $USER$. We'll notify you by email when it's possible to continue.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"approve": {
|
||||
"message": "Approve"
|
||||
},
|
||||
"reject": {
|
||||
"message": "Reject"
|
||||
},
|
||||
"approveAccessConfirmation": {
|
||||
"message": "Are you sure you want to approve emergency access? This will allow $USER$ to $ACTION$ your account.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
},
|
||||
"action": {
|
||||
"content": "$2",
|
||||
"example": "View"
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyApproved": {
|
||||
"message": "Emergency access approved."
|
||||
},
|
||||
"emergencyRejected": {
|
||||
"message": "Emergency access rejected"
|
||||
},
|
||||
"passwordResetFor": {
|
||||
"message": "Password reset for $USER$. You can now login using the new password.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"personalOwnership": {
|
||||
"message": "Personal Ownership"
|
||||
},
|
||||
"personalOwnershipPolicyDesc": {
|
||||
"message": "Require users to save vault items to an organization by removing the personal ownership option."
|
||||
},
|
||||
"personalOwnershipExemption": {
|
||||
"message": "Organization Owners and Administrators are exempt from this policy's enforcement."
|
||||
},
|
||||
"personalOwnershipSubmitError": {
|
||||
"message": "Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organization and choose from available Collections."
|
||||
},
|
||||
"modifiedPolicyId": {
|
||||
"message": "Modified policy $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "Master Password"
|
||||
}
|
||||
}
|
||||
},
|
||||
"planPrice": {
|
||||
"message": "Plan price"
|
||||
},
|
||||
"estimatedTax": {
|
||||
"message": "Estimated tax"
|
||||
},
|
||||
"custom": {
|
||||
"message": "Custom"
|
||||
},
|
||||
"customDesc": {
|
||||
"message": "Allows more granular control of user permissions for advanced configurations."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "Permissions"
|
||||
},
|
||||
"accessBusinessPortal": {
|
||||
"message": "Access Business Portal"
|
||||
},
|
||||
"accessEventLogs": {
|
||||
"message": "Access Event Logs"
|
||||
},
|
||||
"accessImportExport": {
|
||||
"message": "Access Import/Export"
|
||||
},
|
||||
"accessReports": {
|
||||
"message": "Access Reports"
|
||||
},
|
||||
"manageAllCollections": {
|
||||
"message": "Manage All Collections"
|
||||
},
|
||||
"manageAssignedCollections": {
|
||||
"message": "Manage Assigned Collections"
|
||||
},
|
||||
"manageGroups": {
|
||||
"message": "Manage Groups"
|
||||
},
|
||||
"managePolicies": {
|
||||
"message": "Manage Policies"
|
||||
},
|
||||
"manageSso": {
|
||||
"message": "Manage SSO"
|
||||
},
|
||||
"manageUsers": {
|
||||
"message": "Manage Users"
|
||||
},
|
||||
"disableRequireSsoError": {
|
||||
"message": "You must manually disable the Single Sign-On Authentication policy before this policy can be disabled."
|
||||
},
|
||||
"personalOwnershipPolicyInEffect": {
|
||||
"message": "An organization policy is affecting your ownership options."
|
||||
},
|
||||
"personalOwnershipCheckboxDesc": {
|
||||
"message": "Disable personal ownership for organization users"
|
||||
},
|
||||
"textHiddenByDefault": {
|
||||
"message": "When accessing the Send, hide the text by default",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,6 +347,9 @@
|
||||
"editItem": {
|
||||
"message": "Edit item"
|
||||
},
|
||||
"viewItem": {
|
||||
"message": "View Item"
|
||||
},
|
||||
"ex": {
|
||||
"message": "e.g.",
|
||||
"description": "Short abbreviation for 'example'."
|
||||
@@ -790,9 +793,15 @@
|
||||
"warning": {
|
||||
"message": "Warning"
|
||||
},
|
||||
"confirmVaultExport": {
|
||||
"message": "Confirm Vault Export"
|
||||
},
|
||||
"exportWarningDesc": {
|
||||
"message": "This export contains your vault data in an unencrypted format. You should not store or send the exported file over insecure channels (such as email). Delete it immediately after you are done using it."
|
||||
},
|
||||
"encExportWarningDesc": {
|
||||
"message": "This export encrypts your data using your account's encryption key. If you ever rotate your account's encryption key you should export again since you will not be able to decrypt this export file."
|
||||
},
|
||||
"exportMasterPassword": {
|
||||
"message": "Enter your master password to export your vault data."
|
||||
},
|
||||
@@ -1659,6 +1668,9 @@
|
||||
"paymentInformation": {
|
||||
"message": "Payment information"
|
||||
},
|
||||
"billingInformation": {
|
||||
"message": "Billing Information"
|
||||
},
|
||||
"creditCard": {
|
||||
"message": "Credit card"
|
||||
},
|
||||
@@ -2483,6 +2495,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"unlinkedSsoUser": {
|
||||
"message": "Unlinked SSO for user $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"device": {
|
||||
"message": "Device"
|
||||
},
|
||||
@@ -2786,6 +2807,9 @@
|
||||
"updateEncryptionKeyWarning": {
|
||||
"message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, although this may be delayed."
|
||||
},
|
||||
"updateEncryptionKeyExportWarning": {
|
||||
"message": "Any encrypted exports that you have saved will also become invalid."
|
||||
},
|
||||
"subscription": {
|
||||
"message": "Subscription"
|
||||
},
|
||||
@@ -3295,15 +3319,35 @@
|
||||
"deletionDate": {
|
||||
"message": "Deletion Date"
|
||||
},
|
||||
"deletionDateDesc": {
|
||||
"message": "The Send will be permanently deleted on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"expirationDate": {
|
||||
"message": "Expiration Date"
|
||||
},
|
||||
"expirationDateDesc": {
|
||||
"message": "If set, access to this Send will expire on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"maxAccessCount": {
|
||||
"message": "Maximum Access Count"
|
||||
},
|
||||
"maxAccessCountDesc": {
|
||||
"message": "If set, users will no longer be able to access this send once the maximum access count is reached.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"currentAccessCount": {
|
||||
"message": "Current Access Count"
|
||||
},
|
||||
"sendPasswordDesc": {
|
||||
"message": "Optionally require a password for users to access this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendNotesDesc": {
|
||||
"message": "Private notes about this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"disabled": {
|
||||
"message": "Disabled"
|
||||
},
|
||||
@@ -3324,9 +3368,22 @@
|
||||
"removePasswordConfirmation": {
|
||||
"message": "Are you sure you want to remove the password?"
|
||||
},
|
||||
"disableThisSend": {
|
||||
"message": "Disable this Send so that no one can access it.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"allSends": {
|
||||
"message": "All Sends"
|
||||
},
|
||||
"maxAccessCountReached": {
|
||||
"message": "Max access count reached"
|
||||
},
|
||||
"pendingDeletion": {
|
||||
"message": "Pending deletion"
|
||||
},
|
||||
"expired": {
|
||||
"message": "Expired"
|
||||
},
|
||||
"searchSends": {
|
||||
"message": "Search Sends",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
@@ -3346,8 +3403,230 @@
|
||||
"downloadFile": {
|
||||
"message": "Download File"
|
||||
},
|
||||
"sendAccessUnavailable": {
|
||||
"message": "The Send you are trying to access does not exist or is no longer available.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"noSendsInList": {
|
||||
"message": "There are no Sends to list.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"emergencyAccess": {
|
||||
"message": "Emergency Access"
|
||||
},
|
||||
"emergencyAccessDesc": {
|
||||
"message": "Grant and manage emergency access for trusted contacts. Trusted contacts may request access to either View or Takeover your account in case of a emergency. Visit our help page for more information and details into how zero knowledge sharing works."
|
||||
},
|
||||
"trustedEmergencyContacts": {
|
||||
"message": "Trusted emergency contacts"
|
||||
},
|
||||
"noTrustedContacts": {
|
||||
"message": "You have not added any emergency contacts yet, invite a trusted contact to get started."
|
||||
},
|
||||
"addEmergencyContact": {
|
||||
"message": "Add emergency contact"
|
||||
},
|
||||
"designatedEmergencyContacts": {
|
||||
"message": "Designated as emergency contact"
|
||||
},
|
||||
"noGrantedAccess": {
|
||||
"message": "You have not been designated as an emergency contact for anyone yet."
|
||||
},
|
||||
"inviteEmergencyContact": {
|
||||
"message": "Invite emergency contact"
|
||||
},
|
||||
"editEmergencyContact": {
|
||||
"message": "Edit emergency contact"
|
||||
},
|
||||
"inviteEmergencyContactDesc": {
|
||||
"message": "Invite a new emergency contact by entering their Bitwarden account email address below. If they do not have a Bitwarden account already, they will be prompted to create a new account."
|
||||
},
|
||||
"emergencyAccessRecoveryInitiated": {
|
||||
"message": "Emergency Access Initiated"
|
||||
},
|
||||
"emergencyAccessRecoveryApproved": {
|
||||
"message": "Emergency Access Approved"
|
||||
},
|
||||
"viewDesc": {
|
||||
"message": "Can view all items in your own vault."
|
||||
},
|
||||
"takeover": {
|
||||
"message": "Takeover"
|
||||
},
|
||||
"takeoverDesc": {
|
||||
"message": "Can reset your account with a new master password."
|
||||
},
|
||||
"waitTime": {
|
||||
"message": "Wait Time"
|
||||
},
|
||||
"waitTimeDesc": {
|
||||
"message": "Time required before automatically granting access."
|
||||
},
|
||||
"oneDay": {
|
||||
"message": "1 day"
|
||||
},
|
||||
"days": {
|
||||
"message": "$DAYS$ days",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"invitedUser": {
|
||||
"message": "Invited user."
|
||||
},
|
||||
"acceptEmergencyAccess": {
|
||||
"message": "You've been invited to become an emergency contact for the user listed above. To accept the invitation, you need to log in or create a new Bitwarden account."
|
||||
},
|
||||
"emergencyInviteAcceptFailed": {
|
||||
"message": "Unable to accept invitation. Ask the user to send a new invitation."
|
||||
},
|
||||
"emergencyInviteAcceptFailedShort": {
|
||||
"message": "Unable to accept invitation. $DESCRIPTION$",
|
||||
"placeholders": {
|
||||
"description": {
|
||||
"content": "$1",
|
||||
"example": "You must enable 2FA on your user account before you can join this organization."
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyInviteAcceptedDesc": {
|
||||
"message": "You can access the emergency options for this user after your identity has been confirmed. We'll send you an email when that happens."
|
||||
},
|
||||
"requestAccess": {
|
||||
"message": "Request Access"
|
||||
},
|
||||
"requestAccessConfirmation": {
|
||||
"message": "Are you sure you want to request emergency access? You will be provided access after $WAITTIME$ day(s) or whenever the user manually approves the request.",
|
||||
"placeholders": {
|
||||
"waittime": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requestSent": {
|
||||
"message": "Emergency access requested for $USER$. We'll notify you by email when it's possible to continue.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"approve": {
|
||||
"message": "Approve"
|
||||
},
|
||||
"reject": {
|
||||
"message": "Reject"
|
||||
},
|
||||
"approveAccessConfirmation": {
|
||||
"message": "Are you sure you want to approve emergency access? This will allow $USER$ to $ACTION$ your account.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
},
|
||||
"action": {
|
||||
"content": "$2",
|
||||
"example": "View"
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyApproved": {
|
||||
"message": "Emergency access approved."
|
||||
},
|
||||
"emergencyRejected": {
|
||||
"message": "Emergency access rejected"
|
||||
},
|
||||
"passwordResetFor": {
|
||||
"message": "Password reset for $USER$. You can now login using the new password.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"personalOwnership": {
|
||||
"message": "Personal Ownership"
|
||||
},
|
||||
"personalOwnershipPolicyDesc": {
|
||||
"message": "Require users to save vault items to an organization by removing the personal ownership option."
|
||||
},
|
||||
"personalOwnershipExemption": {
|
||||
"message": "Organization Owners and Administrators are exempt from this policy's enforcement."
|
||||
},
|
||||
"personalOwnershipSubmitError": {
|
||||
"message": "Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organization and choose from available Collections."
|
||||
},
|
||||
"modifiedPolicyId": {
|
||||
"message": "Modified policy $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "Master Password"
|
||||
}
|
||||
}
|
||||
},
|
||||
"planPrice": {
|
||||
"message": "Plan price"
|
||||
},
|
||||
"estimatedTax": {
|
||||
"message": "Estimated tax"
|
||||
},
|
||||
"custom": {
|
||||
"message": "Custom"
|
||||
},
|
||||
"customDesc": {
|
||||
"message": "Allows more granular control of user permissions for advanced configurations."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "Permissions"
|
||||
},
|
||||
"accessBusinessPortal": {
|
||||
"message": "Access Business Portal"
|
||||
},
|
||||
"accessEventLogs": {
|
||||
"message": "Access Event Logs"
|
||||
},
|
||||
"accessImportExport": {
|
||||
"message": "Access Import/Export"
|
||||
},
|
||||
"accessReports": {
|
||||
"message": "Access Reports"
|
||||
},
|
||||
"manageAllCollections": {
|
||||
"message": "Manage All Collections"
|
||||
},
|
||||
"manageAssignedCollections": {
|
||||
"message": "Manage Assigned Collections"
|
||||
},
|
||||
"manageGroups": {
|
||||
"message": "Manage Groups"
|
||||
},
|
||||
"managePolicies": {
|
||||
"message": "Manage Policies"
|
||||
},
|
||||
"manageSso": {
|
||||
"message": "Manage SSO"
|
||||
},
|
||||
"manageUsers": {
|
||||
"message": "Manage Users"
|
||||
},
|
||||
"disableRequireSsoError": {
|
||||
"message": "You must manually disable the Single Sign-On Authentication policy before this policy can be disabled."
|
||||
},
|
||||
"personalOwnershipPolicyInEffect": {
|
||||
"message": "An organization policy is affecting your ownership options."
|
||||
},
|
||||
"personalOwnershipCheckboxDesc": {
|
||||
"message": "Disable personal ownership for organization users"
|
||||
},
|
||||
"textHiddenByDefault": {
|
||||
"message": "When accessing the Send, hide the text by default",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,6 +347,9 @@
|
||||
"editItem": {
|
||||
"message": "Edit Item"
|
||||
},
|
||||
"viewItem": {
|
||||
"message": "View Item"
|
||||
},
|
||||
"ex": {
|
||||
"message": "ekx.",
|
||||
"description": "Short abbreviation for 'example'."
|
||||
@@ -790,9 +793,15 @@
|
||||
"warning": {
|
||||
"message": "Warning"
|
||||
},
|
||||
"confirmVaultExport": {
|
||||
"message": "Confirm Vault Export"
|
||||
},
|
||||
"exportWarningDesc": {
|
||||
"message": "This export contains your vault data in an unencrypted format. You should not store or send the exported file over unsecure channels (such as email). Delete it immediately after you are done using it."
|
||||
},
|
||||
"encExportWarningDesc": {
|
||||
"message": "This export encrypts your data using your account's encryption key. If you ever rotate your account's encryption key you should export again since you will not be able to decrypt this export file."
|
||||
},
|
||||
"exportMasterPassword": {
|
||||
"message": "Enter your master password to export your vault data."
|
||||
},
|
||||
@@ -1659,6 +1668,9 @@
|
||||
"paymentInformation": {
|
||||
"message": "Informoj pri Pago"
|
||||
},
|
||||
"billingInformation": {
|
||||
"message": "Billing Information"
|
||||
},
|
||||
"creditCard": {
|
||||
"message": "Credit Card"
|
||||
},
|
||||
@@ -2483,6 +2495,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"unlinkedSsoUser": {
|
||||
"message": "Unlinked SSO for user $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"device": {
|
||||
"message": "Aparato"
|
||||
},
|
||||
@@ -2786,6 +2807,9 @@
|
||||
"updateEncryptionKeyWarning": {
|
||||
"message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed."
|
||||
},
|
||||
"updateEncryptionKeyExportWarning": {
|
||||
"message": "Any encrypted exports that you have saved will also become invalid."
|
||||
},
|
||||
"subscription": {
|
||||
"message": "Abono"
|
||||
},
|
||||
@@ -3295,15 +3319,35 @@
|
||||
"deletionDate": {
|
||||
"message": "Deletion Date"
|
||||
},
|
||||
"deletionDateDesc": {
|
||||
"message": "The Send will be permanently deleted on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"expirationDate": {
|
||||
"message": "Expiration Date"
|
||||
},
|
||||
"expirationDateDesc": {
|
||||
"message": "If set, access to this Send will expire on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"maxAccessCount": {
|
||||
"message": "Maximum Access Count"
|
||||
},
|
||||
"maxAccessCountDesc": {
|
||||
"message": "If set, users will no longer be able to access this send once the maximum access count is reached.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"currentAccessCount": {
|
||||
"message": "Current Access Count"
|
||||
},
|
||||
"sendPasswordDesc": {
|
||||
"message": "Optionally require a password for users to access this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendNotesDesc": {
|
||||
"message": "Private notes about this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"disabled": {
|
||||
"message": "Disabled"
|
||||
},
|
||||
@@ -3324,9 +3368,22 @@
|
||||
"removePasswordConfirmation": {
|
||||
"message": "Are you sure you want to remove the password?"
|
||||
},
|
||||
"disableThisSend": {
|
||||
"message": "Disable this Send so that no one can access it.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"allSends": {
|
||||
"message": "All Sends"
|
||||
},
|
||||
"maxAccessCountReached": {
|
||||
"message": "Max access count reached"
|
||||
},
|
||||
"pendingDeletion": {
|
||||
"message": "Pending deletion"
|
||||
},
|
||||
"expired": {
|
||||
"message": "Expired"
|
||||
},
|
||||
"searchSends": {
|
||||
"message": "Search Sends",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
@@ -3346,8 +3403,230 @@
|
||||
"downloadFile": {
|
||||
"message": "Download File"
|
||||
},
|
||||
"sendAccessUnavailable": {
|
||||
"message": "The Send you are trying to access does not exist or is no longer available.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"noSendsInList": {
|
||||
"message": "There are no Sends to list.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"emergencyAccess": {
|
||||
"message": "Emergency Access"
|
||||
},
|
||||
"emergencyAccessDesc": {
|
||||
"message": "Grant and manage emergency access for trusted contacts. Trusted contacts may request access to either View or Takeover your account in case of a emergency. Visit our help page for more information and details into how zero knowledge sharing works."
|
||||
},
|
||||
"trustedEmergencyContacts": {
|
||||
"message": "Trusted emergency contacts"
|
||||
},
|
||||
"noTrustedContacts": {
|
||||
"message": "You have not added any emergency contacts yet, invite a trusted contact to get started."
|
||||
},
|
||||
"addEmergencyContact": {
|
||||
"message": "Add emergency contact"
|
||||
},
|
||||
"designatedEmergencyContacts": {
|
||||
"message": "Designated as emergency contact"
|
||||
},
|
||||
"noGrantedAccess": {
|
||||
"message": "You have not been designated as an emergency contact for anyone yet."
|
||||
},
|
||||
"inviteEmergencyContact": {
|
||||
"message": "Invite emergency contact"
|
||||
},
|
||||
"editEmergencyContact": {
|
||||
"message": "Edit emergency contact"
|
||||
},
|
||||
"inviteEmergencyContactDesc": {
|
||||
"message": "Invite a new emergency contact by entering their Bitwarden account email address below. If they do not have a Bitwarden account already, they will be prompted to create a new account."
|
||||
},
|
||||
"emergencyAccessRecoveryInitiated": {
|
||||
"message": "Emergency Access Initiated"
|
||||
},
|
||||
"emergencyAccessRecoveryApproved": {
|
||||
"message": "Emergency Access Approved"
|
||||
},
|
||||
"viewDesc": {
|
||||
"message": "Can view all items in your own vault."
|
||||
},
|
||||
"takeover": {
|
||||
"message": "Takeover"
|
||||
},
|
||||
"takeoverDesc": {
|
||||
"message": "Can reset your account with a new master password."
|
||||
},
|
||||
"waitTime": {
|
||||
"message": "Wait Time"
|
||||
},
|
||||
"waitTimeDesc": {
|
||||
"message": "Time required before automatically granting access."
|
||||
},
|
||||
"oneDay": {
|
||||
"message": "1 day"
|
||||
},
|
||||
"days": {
|
||||
"message": "$DAYS$ days",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"invitedUser": {
|
||||
"message": "Invited user."
|
||||
},
|
||||
"acceptEmergencyAccess": {
|
||||
"message": "You've been invited to become an emergency contact for the user listed above. To accept the invitation, you need to log in or create a new Bitwarden account."
|
||||
},
|
||||
"emergencyInviteAcceptFailed": {
|
||||
"message": "Unable to accept invitation. Ask the user to send a new invitation."
|
||||
},
|
||||
"emergencyInviteAcceptFailedShort": {
|
||||
"message": "Unable to accept invitation. $DESCRIPTION$",
|
||||
"placeholders": {
|
||||
"description": {
|
||||
"content": "$1",
|
||||
"example": "You must enable 2FA on your user account before you can join this organization."
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyInviteAcceptedDesc": {
|
||||
"message": "You can access the emergency options for this user after your identity has been confirmed. We'll send you an email when that happens."
|
||||
},
|
||||
"requestAccess": {
|
||||
"message": "Request Access"
|
||||
},
|
||||
"requestAccessConfirmation": {
|
||||
"message": "Are you sure you want to request emergency access? You will be provided access after $WAITTIME$ day(s) or whenever the user manually approves the request.",
|
||||
"placeholders": {
|
||||
"waittime": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requestSent": {
|
||||
"message": "Emergency access requested for $USER$. We'll notify you by email when it's possible to continue.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"approve": {
|
||||
"message": "Approve"
|
||||
},
|
||||
"reject": {
|
||||
"message": "Reject"
|
||||
},
|
||||
"approveAccessConfirmation": {
|
||||
"message": "Are you sure you want to approve emergency access? This will allow $USER$ to $ACTION$ your account.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
},
|
||||
"action": {
|
||||
"content": "$2",
|
||||
"example": "View"
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyApproved": {
|
||||
"message": "Emergency access approved."
|
||||
},
|
||||
"emergencyRejected": {
|
||||
"message": "Emergency access rejected"
|
||||
},
|
||||
"passwordResetFor": {
|
||||
"message": "Password reset for $USER$. You can now login using the new password.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"personalOwnership": {
|
||||
"message": "Personal Ownership"
|
||||
},
|
||||
"personalOwnershipPolicyDesc": {
|
||||
"message": "Require users to save vault items to an organization by removing the personal ownership option."
|
||||
},
|
||||
"personalOwnershipExemption": {
|
||||
"message": "Organization Owners and Administrators are exempt from this policy's enforcement."
|
||||
},
|
||||
"personalOwnershipSubmitError": {
|
||||
"message": "Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organization and choose from available Collections."
|
||||
},
|
||||
"modifiedPolicyId": {
|
||||
"message": "Modified policy $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "Master Password"
|
||||
}
|
||||
}
|
||||
},
|
||||
"planPrice": {
|
||||
"message": "Plan price"
|
||||
},
|
||||
"estimatedTax": {
|
||||
"message": "Estimated tax"
|
||||
},
|
||||
"custom": {
|
||||
"message": "Custom"
|
||||
},
|
||||
"customDesc": {
|
||||
"message": "Allows more granular control of user permissions for advanced configurations."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "Permissions"
|
||||
},
|
||||
"accessBusinessPortal": {
|
||||
"message": "Access Business Portal"
|
||||
},
|
||||
"accessEventLogs": {
|
||||
"message": "Access Event Logs"
|
||||
},
|
||||
"accessImportExport": {
|
||||
"message": "Access Import/Export"
|
||||
},
|
||||
"accessReports": {
|
||||
"message": "Access Reports"
|
||||
},
|
||||
"manageAllCollections": {
|
||||
"message": "Manage All Collections"
|
||||
},
|
||||
"manageAssignedCollections": {
|
||||
"message": "Manage Assigned Collections"
|
||||
},
|
||||
"manageGroups": {
|
||||
"message": "Manage Groups"
|
||||
},
|
||||
"managePolicies": {
|
||||
"message": "Manage Policies"
|
||||
},
|
||||
"manageSso": {
|
||||
"message": "Manage SSO"
|
||||
},
|
||||
"manageUsers": {
|
||||
"message": "Manage Users"
|
||||
},
|
||||
"disableRequireSsoError": {
|
||||
"message": "You must manually disable the Single Sign-On Authentication policy before this policy can be disabled."
|
||||
},
|
||||
"personalOwnershipPolicyInEffect": {
|
||||
"message": "An organization policy is affecting your ownership options."
|
||||
},
|
||||
"personalOwnershipCheckboxDesc": {
|
||||
"message": "Disable personal ownership for organization users"
|
||||
},
|
||||
"textHiddenByDefault": {
|
||||
"message": "When accessing the Send, hide the text by default",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,6 +347,9 @@
|
||||
"editItem": {
|
||||
"message": "Editar elemento"
|
||||
},
|
||||
"viewItem": {
|
||||
"message": "View Item"
|
||||
},
|
||||
"ex": {
|
||||
"message": "ej.",
|
||||
"description": "Short abbreviation for 'example'."
|
||||
@@ -790,9 +793,15 @@
|
||||
"warning": {
|
||||
"message": "Advertencia"
|
||||
},
|
||||
"confirmVaultExport": {
|
||||
"message": "Confirm Vault Export"
|
||||
},
|
||||
"exportWarningDesc": {
|
||||
"message": "Esta exportación contiene los datos de tu caja fuerte en un formato no cifrado. No deberías almacenar o enviar el archivo exportado por canales no seguros (como el correo electrónico). Elimínalo inmediatamente cuando termines de utilizarlo."
|
||||
},
|
||||
"encExportWarningDesc": {
|
||||
"message": "This export encrypts your data using your account's encryption key. If you ever rotate your account's encryption key you should export again since you will not be able to decrypt this export file."
|
||||
},
|
||||
"exportMasterPassword": {
|
||||
"message": "Introduce tu contraseña maestra para exportar la información de tu caja fuerte."
|
||||
},
|
||||
@@ -1659,6 +1668,9 @@
|
||||
"paymentInformation": {
|
||||
"message": "Información de pago"
|
||||
},
|
||||
"billingInformation": {
|
||||
"message": "Billing Information"
|
||||
},
|
||||
"creditCard": {
|
||||
"message": "Tarjeta de crédito"
|
||||
},
|
||||
@@ -2483,6 +2495,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"unlinkedSsoUser": {
|
||||
"message": "Unlinked SSO for user $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"device": {
|
||||
"message": "Dispositivo"
|
||||
},
|
||||
@@ -2786,6 +2807,9 @@
|
||||
"updateEncryptionKeyWarning": {
|
||||
"message": "Una vez actualices tu clave de cifrado, será necesario que cierres sesión y vuelvas a identificarte en todas las aplicaciones de Bitwarden que estés utilizando (como la aplicación móvil o la extensión de navegador). Si la reautenticación falla (la cual descargaría la nueva clave de cifrad) puede producirse corrupción de datos. Intentaremos cerrar tu sesión automáticamente, pero puede tardar un tiempo."
|
||||
},
|
||||
"updateEncryptionKeyExportWarning": {
|
||||
"message": "Any encrypted exports that you have saved will also become invalid."
|
||||
},
|
||||
"subscription": {
|
||||
"message": "Suscripción"
|
||||
},
|
||||
@@ -3194,7 +3218,7 @@
|
||||
"message": "Inicio de sesión único empresarial"
|
||||
},
|
||||
"ssoHandOff": {
|
||||
"message": "You may now close this tab and continue in the extension."
|
||||
"message": "Ya puedes cerrar esta pestaña y continuar en la extensión."
|
||||
},
|
||||
"businessPortal": {
|
||||
"message": "Portal de negocios",
|
||||
@@ -3295,15 +3319,35 @@
|
||||
"deletionDate": {
|
||||
"message": "Fecha de eliminación"
|
||||
},
|
||||
"deletionDateDesc": {
|
||||
"message": "The Send will be permanently deleted on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"expirationDate": {
|
||||
"message": "Fecha de Expiración"
|
||||
},
|
||||
"expirationDateDesc": {
|
||||
"message": "If set, access to this Send will expire on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"maxAccessCount": {
|
||||
"message": "Número máximo de accesos"
|
||||
},
|
||||
"maxAccessCountDesc": {
|
||||
"message": "If set, users will no longer be able to access this send once the maximum access count is reached.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"currentAccessCount": {
|
||||
"message": "Número de accesos actuales"
|
||||
},
|
||||
"sendPasswordDesc": {
|
||||
"message": "Optionally require a password for users to access this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendNotesDesc": {
|
||||
"message": "Private notes about this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"disabled": {
|
||||
"message": "Deshabilitado"
|
||||
},
|
||||
@@ -3324,9 +3368,22 @@
|
||||
"removePasswordConfirmation": {
|
||||
"message": "¿Está seguro que desea eliminar la contraseña?"
|
||||
},
|
||||
"disableThisSend": {
|
||||
"message": "Disable this Send so that no one can access it.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"allSends": {
|
||||
"message": "Todos los Sends"
|
||||
},
|
||||
"maxAccessCountReached": {
|
||||
"message": "Max access count reached"
|
||||
},
|
||||
"pendingDeletion": {
|
||||
"message": "Pending deletion"
|
||||
},
|
||||
"expired": {
|
||||
"message": "Expired"
|
||||
},
|
||||
"searchSends": {
|
||||
"message": "Buscar Sends",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
@@ -3346,8 +3403,230 @@
|
||||
"downloadFile": {
|
||||
"message": "Descargar archivo"
|
||||
},
|
||||
"sendAccessUnavailable": {
|
||||
"message": "The Send you are trying to access does not exist or is no longer available.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"noSendsInList": {
|
||||
"message": "No hay Sends que listar.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"emergencyAccess": {
|
||||
"message": "Emergency Access"
|
||||
},
|
||||
"emergencyAccessDesc": {
|
||||
"message": "Grant and manage emergency access for trusted contacts. Trusted contacts may request access to either View or Takeover your account in case of a emergency. Visit our help page for more information and details into how zero knowledge sharing works."
|
||||
},
|
||||
"trustedEmergencyContacts": {
|
||||
"message": "Trusted emergency contacts"
|
||||
},
|
||||
"noTrustedContacts": {
|
||||
"message": "You have not added any emergency contacts yet, invite a trusted contact to get started."
|
||||
},
|
||||
"addEmergencyContact": {
|
||||
"message": "Add emergency contact"
|
||||
},
|
||||
"designatedEmergencyContacts": {
|
||||
"message": "Designated as emergency contact"
|
||||
},
|
||||
"noGrantedAccess": {
|
||||
"message": "You have not been designated as an emergency contact for anyone yet."
|
||||
},
|
||||
"inviteEmergencyContact": {
|
||||
"message": "Invite emergency contact"
|
||||
},
|
||||
"editEmergencyContact": {
|
||||
"message": "Edit emergency contact"
|
||||
},
|
||||
"inviteEmergencyContactDesc": {
|
||||
"message": "Invite a new emergency contact by entering their Bitwarden account email address below. If they do not have a Bitwarden account already, they will be prompted to create a new account."
|
||||
},
|
||||
"emergencyAccessRecoveryInitiated": {
|
||||
"message": "Emergency Access Initiated"
|
||||
},
|
||||
"emergencyAccessRecoveryApproved": {
|
||||
"message": "Emergency Access Approved"
|
||||
},
|
||||
"viewDesc": {
|
||||
"message": "Can view all items in your own vault."
|
||||
},
|
||||
"takeover": {
|
||||
"message": "Takeover"
|
||||
},
|
||||
"takeoverDesc": {
|
||||
"message": "Can reset your account with a new master password."
|
||||
},
|
||||
"waitTime": {
|
||||
"message": "Wait Time"
|
||||
},
|
||||
"waitTimeDesc": {
|
||||
"message": "Time required before automatically granting access."
|
||||
},
|
||||
"oneDay": {
|
||||
"message": "1 day"
|
||||
},
|
||||
"days": {
|
||||
"message": "$DAYS$ days",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"invitedUser": {
|
||||
"message": "Invited user."
|
||||
},
|
||||
"acceptEmergencyAccess": {
|
||||
"message": "You've been invited to become an emergency contact for the user listed above. To accept the invitation, you need to log in or create a new Bitwarden account."
|
||||
},
|
||||
"emergencyInviteAcceptFailed": {
|
||||
"message": "Unable to accept invitation. Ask the user to send a new invitation."
|
||||
},
|
||||
"emergencyInviteAcceptFailedShort": {
|
||||
"message": "Unable to accept invitation. $DESCRIPTION$",
|
||||
"placeholders": {
|
||||
"description": {
|
||||
"content": "$1",
|
||||
"example": "You must enable 2FA on your user account before you can join this organization."
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyInviteAcceptedDesc": {
|
||||
"message": "You can access the emergency options for this user after your identity has been confirmed. We'll send you an email when that happens."
|
||||
},
|
||||
"requestAccess": {
|
||||
"message": "Request Access"
|
||||
},
|
||||
"requestAccessConfirmation": {
|
||||
"message": "Are you sure you want to request emergency access? You will be provided access after $WAITTIME$ day(s) or whenever the user manually approves the request.",
|
||||
"placeholders": {
|
||||
"waittime": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requestSent": {
|
||||
"message": "Emergency access requested for $USER$. We'll notify you by email when it's possible to continue.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"approve": {
|
||||
"message": "Approve"
|
||||
},
|
||||
"reject": {
|
||||
"message": "Reject"
|
||||
},
|
||||
"approveAccessConfirmation": {
|
||||
"message": "Are you sure you want to approve emergency access? This will allow $USER$ to $ACTION$ your account.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
},
|
||||
"action": {
|
||||
"content": "$2",
|
||||
"example": "View"
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyApproved": {
|
||||
"message": "Emergency access approved."
|
||||
},
|
||||
"emergencyRejected": {
|
||||
"message": "Emergency access rejected"
|
||||
},
|
||||
"passwordResetFor": {
|
||||
"message": "Password reset for $USER$. You can now login using the new password.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"personalOwnership": {
|
||||
"message": "Personal Ownership"
|
||||
},
|
||||
"personalOwnershipPolicyDesc": {
|
||||
"message": "Require users to save vault items to an organization by removing the personal ownership option."
|
||||
},
|
||||
"personalOwnershipExemption": {
|
||||
"message": "Organization Owners and Administrators are exempt from this policy's enforcement."
|
||||
},
|
||||
"personalOwnershipSubmitError": {
|
||||
"message": "Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organization and choose from available Collections."
|
||||
},
|
||||
"modifiedPolicyId": {
|
||||
"message": "Modified policy $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "Master Password"
|
||||
}
|
||||
}
|
||||
},
|
||||
"planPrice": {
|
||||
"message": "Plan price"
|
||||
},
|
||||
"estimatedTax": {
|
||||
"message": "Estimated tax"
|
||||
},
|
||||
"custom": {
|
||||
"message": "Custom"
|
||||
},
|
||||
"customDesc": {
|
||||
"message": "Allows more granular control of user permissions for advanced configurations."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "Permissions"
|
||||
},
|
||||
"accessBusinessPortal": {
|
||||
"message": "Access Business Portal"
|
||||
},
|
||||
"accessEventLogs": {
|
||||
"message": "Access Event Logs"
|
||||
},
|
||||
"accessImportExport": {
|
||||
"message": "Access Import/Export"
|
||||
},
|
||||
"accessReports": {
|
||||
"message": "Access Reports"
|
||||
},
|
||||
"manageAllCollections": {
|
||||
"message": "Manage All Collections"
|
||||
},
|
||||
"manageAssignedCollections": {
|
||||
"message": "Manage Assigned Collections"
|
||||
},
|
||||
"manageGroups": {
|
||||
"message": "Manage Groups"
|
||||
},
|
||||
"managePolicies": {
|
||||
"message": "Manage Policies"
|
||||
},
|
||||
"manageSso": {
|
||||
"message": "Manage SSO"
|
||||
},
|
||||
"manageUsers": {
|
||||
"message": "Manage Users"
|
||||
},
|
||||
"disableRequireSsoError": {
|
||||
"message": "You must manually disable the Single Sign-On Authentication policy before this policy can be disabled."
|
||||
},
|
||||
"personalOwnershipPolicyInEffect": {
|
||||
"message": "An organization policy is affecting your ownership options."
|
||||
},
|
||||
"personalOwnershipCheckboxDesc": {
|
||||
"message": "Disable personal ownership for organization users"
|
||||
},
|
||||
"textHiddenByDefault": {
|
||||
"message": "When accessing the Send, hide the text by default",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,6 +347,9 @@
|
||||
"editItem": {
|
||||
"message": "Kirje muutmine"
|
||||
},
|
||||
"viewItem": {
|
||||
"message": "View Item"
|
||||
},
|
||||
"ex": {
|
||||
"message": "nt.",
|
||||
"description": "Short abbreviation for 'example'."
|
||||
@@ -790,9 +793,15 @@
|
||||
"warning": {
|
||||
"message": "Hoiatus"
|
||||
},
|
||||
"confirmVaultExport": {
|
||||
"message": "Confirm Vault Export"
|
||||
},
|
||||
"exportWarningDesc": {
|
||||
"message": "Eksporditav fail on krüpteeringuta ja sisaldab hoidla sisu. Seda faili ei tohiks kaua käidelda ning mitte mingil juhul ebaturvaliselt saata (näiteks e-postiga). Kustuta see koheselt pärast kasutamist."
|
||||
},
|
||||
"encExportWarningDesc": {
|
||||
"message": "This export encrypts your data using your account's encryption key. If you ever rotate your account's encryption key you should export again since you will not be able to decrypt this export file."
|
||||
},
|
||||
"exportMasterPassword": {
|
||||
"message": "Hoidlas olevate andmete eksportimiseks on vajalik ülemparooli sisestamine."
|
||||
},
|
||||
@@ -1659,6 +1668,9 @@
|
||||
"paymentInformation": {
|
||||
"message": "Maksemeetod"
|
||||
},
|
||||
"billingInformation": {
|
||||
"message": "Billing Information"
|
||||
},
|
||||
"creditCard": {
|
||||
"message": "Krediitkaart"
|
||||
},
|
||||
@@ -2483,6 +2495,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"unlinkedSsoUser": {
|
||||
"message": "Unlinked SSO for user $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"device": {
|
||||
"message": "Seade"
|
||||
},
|
||||
@@ -2786,6 +2807,9 @@
|
||||
"updateEncryptionKeyWarning": {
|
||||
"message": "Pärast krüpteerimisvõtme uuendamist pead kõikides seadmetes, kus Bitwardeni rakendust kasutad, oma kontosse uuesti sisse logima (nt nutitelefonis ja brauseris). Välja- ja sisselogimise (mis ühtlasi laadib ka uue krüpteerimisvõtme) nurjumine võib tingida andmete riknemise. Üritame sinu seadmetest ise välja logida, aga see võib võtta natukene aega."
|
||||
},
|
||||
"updateEncryptionKeyExportWarning": {
|
||||
"message": "Any encrypted exports that you have saved will also become invalid."
|
||||
},
|
||||
"subscription": {
|
||||
"message": "Tellimus"
|
||||
},
|
||||
@@ -3194,7 +3218,7 @@
|
||||
"message": "Ettevõtte Single Sign-On"
|
||||
},
|
||||
"ssoHandOff": {
|
||||
"message": "You may now close this tab and continue in the extension."
|
||||
"message": "Võid nüüd selle vahelehe sulgeda ning jätkata brauseri laienduses."
|
||||
},
|
||||
"businessPortal": {
|
||||
"message": "Ärikliendi portaal",
|
||||
@@ -3295,15 +3319,35 @@
|
||||
"deletionDate": {
|
||||
"message": "Kustutamise kuupäev"
|
||||
},
|
||||
"deletionDateDesc": {
|
||||
"message": "The Send will be permanently deleted on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"expirationDate": {
|
||||
"message": "Aegumiskuupäev"
|
||||
},
|
||||
"expirationDateDesc": {
|
||||
"message": "If set, access to this Send will expire on the specified date and time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"maxAccessCount": {
|
||||
"message": "Maksimaalne ligipääsude arv"
|
||||
},
|
||||
"maxAccessCountDesc": {
|
||||
"message": "If set, users will no longer be able to access this send once the maximum access count is reached.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"currentAccessCount": {
|
||||
"message": "Hetkeline ligipääsude arv"
|
||||
},
|
||||
"sendPasswordDesc": {
|
||||
"message": "Optionally require a password for users to access this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendNotesDesc": {
|
||||
"message": "Private notes about this Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"disabled": {
|
||||
"message": "Keelatud"
|
||||
},
|
||||
@@ -3324,9 +3368,22 @@
|
||||
"removePasswordConfirmation": {
|
||||
"message": "Soovid kindlasti selle parooli eemaldada?"
|
||||
},
|
||||
"disableThisSend": {
|
||||
"message": "Disable this Send so that no one can access it.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"allSends": {
|
||||
"message": "Kõik Sendid"
|
||||
},
|
||||
"maxAccessCountReached": {
|
||||
"message": "Max access count reached"
|
||||
},
|
||||
"pendingDeletion": {
|
||||
"message": "Pending deletion"
|
||||
},
|
||||
"expired": {
|
||||
"message": "Expired"
|
||||
},
|
||||
"searchSends": {
|
||||
"message": "Otsi Sende",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
@@ -3346,8 +3403,230 @@
|
||||
"downloadFile": {
|
||||
"message": "Laadi fail alla"
|
||||
},
|
||||
"sendAccessUnavailable": {
|
||||
"message": "The Send you are trying to access does not exist or is no longer available.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"noSendsInList": {
|
||||
"message": "Puuduvad Sendid, mida kuvada.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"emergencyAccess": {
|
||||
"message": "Emergency Access"
|
||||
},
|
||||
"emergencyAccessDesc": {
|
||||
"message": "Grant and manage emergency access for trusted contacts. Trusted contacts may request access to either View or Takeover your account in case of a emergency. Visit our help page for more information and details into how zero knowledge sharing works."
|
||||
},
|
||||
"trustedEmergencyContacts": {
|
||||
"message": "Trusted emergency contacts"
|
||||
},
|
||||
"noTrustedContacts": {
|
||||
"message": "You have not added any emergency contacts yet, invite a trusted contact to get started."
|
||||
},
|
||||
"addEmergencyContact": {
|
||||
"message": "Add emergency contact"
|
||||
},
|
||||
"designatedEmergencyContacts": {
|
||||
"message": "Designated as emergency contact"
|
||||
},
|
||||
"noGrantedAccess": {
|
||||
"message": "You have not been designated as an emergency contact for anyone yet."
|
||||
},
|
||||
"inviteEmergencyContact": {
|
||||
"message": "Invite emergency contact"
|
||||
},
|
||||
"editEmergencyContact": {
|
||||
"message": "Edit emergency contact"
|
||||
},
|
||||
"inviteEmergencyContactDesc": {
|
||||
"message": "Invite a new emergency contact by entering their Bitwarden account email address below. If they do not have a Bitwarden account already, they will be prompted to create a new account."
|
||||
},
|
||||
"emergencyAccessRecoveryInitiated": {
|
||||
"message": "Emergency Access Initiated"
|
||||
},
|
||||
"emergencyAccessRecoveryApproved": {
|
||||
"message": "Emergency Access Approved"
|
||||
},
|
||||
"viewDesc": {
|
||||
"message": "Can view all items in your own vault."
|
||||
},
|
||||
"takeover": {
|
||||
"message": "Takeover"
|
||||
},
|
||||
"takeoverDesc": {
|
||||
"message": "Can reset your account with a new master password."
|
||||
},
|
||||
"waitTime": {
|
||||
"message": "Wait Time"
|
||||
},
|
||||
"waitTimeDesc": {
|
||||
"message": "Time required before automatically granting access."
|
||||
},
|
||||
"oneDay": {
|
||||
"message": "1 day"
|
||||
},
|
||||
"days": {
|
||||
"message": "$DAYS$ days",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"invitedUser": {
|
||||
"message": "Invited user."
|
||||
},
|
||||
"acceptEmergencyAccess": {
|
||||
"message": "You've been invited to become an emergency contact for the user listed above. To accept the invitation, you need to log in or create a new Bitwarden account."
|
||||
},
|
||||
"emergencyInviteAcceptFailed": {
|
||||
"message": "Unable to accept invitation. Ask the user to send a new invitation."
|
||||
},
|
||||
"emergencyInviteAcceptFailedShort": {
|
||||
"message": "Unable to accept invitation. $DESCRIPTION$",
|
||||
"placeholders": {
|
||||
"description": {
|
||||
"content": "$1",
|
||||
"example": "You must enable 2FA on your user account before you can join this organization."
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyInviteAcceptedDesc": {
|
||||
"message": "You can access the emergency options for this user after your identity has been confirmed. We'll send you an email when that happens."
|
||||
},
|
||||
"requestAccess": {
|
||||
"message": "Request Access"
|
||||
},
|
||||
"requestAccessConfirmation": {
|
||||
"message": "Are you sure you want to request emergency access? You will be provided access after $WAITTIME$ day(s) or whenever the user manually approves the request.",
|
||||
"placeholders": {
|
||||
"waittime": {
|
||||
"content": "$1",
|
||||
"example": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requestSent": {
|
||||
"message": "Emergency access requested for $USER$. We'll notify you by email when it's possible to continue.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"approve": {
|
||||
"message": "Approve"
|
||||
},
|
||||
"reject": {
|
||||
"message": "Reject"
|
||||
},
|
||||
"approveAccessConfirmation": {
|
||||
"message": "Are you sure you want to approve emergency access? This will allow $USER$ to $ACTION$ your account.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
},
|
||||
"action": {
|
||||
"content": "$2",
|
||||
"example": "View"
|
||||
}
|
||||
}
|
||||
},
|
||||
"emergencyApproved": {
|
||||
"message": "Emergency access approved."
|
||||
},
|
||||
"emergencyRejected": {
|
||||
"message": "Emergency access rejected"
|
||||
},
|
||||
"passwordResetFor": {
|
||||
"message": "Password reset for $USER$. You can now login using the new password.",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"personalOwnership": {
|
||||
"message": "Personal Ownership"
|
||||
},
|
||||
"personalOwnershipPolicyDesc": {
|
||||
"message": "Require users to save vault items to an organization by removing the personal ownership option."
|
||||
},
|
||||
"personalOwnershipExemption": {
|
||||
"message": "Organization Owners and Administrators are exempt from this policy's enforcement."
|
||||
},
|
||||
"personalOwnershipSubmitError": {
|
||||
"message": "Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organization and choose from available Collections."
|
||||
},
|
||||
"modifiedPolicyId": {
|
||||
"message": "Modified policy $ID$.",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
"example": "Master Password"
|
||||
}
|
||||
}
|
||||
},
|
||||
"planPrice": {
|
||||
"message": "Plan price"
|
||||
},
|
||||
"estimatedTax": {
|
||||
"message": "Estimated tax"
|
||||
},
|
||||
"custom": {
|
||||
"message": "Custom"
|
||||
},
|
||||
"customDesc": {
|
||||
"message": "Allows more granular control of user permissions for advanced configurations."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "Permissions"
|
||||
},
|
||||
"accessBusinessPortal": {
|
||||
"message": "Access Business Portal"
|
||||
},
|
||||
"accessEventLogs": {
|
||||
"message": "Access Event Logs"
|
||||
},
|
||||
"accessImportExport": {
|
||||
"message": "Access Import/Export"
|
||||
},
|
||||
"accessReports": {
|
||||
"message": "Access Reports"
|
||||
},
|
||||
"manageAllCollections": {
|
||||
"message": "Manage All Collections"
|
||||
},
|
||||
"manageAssignedCollections": {
|
||||
"message": "Manage Assigned Collections"
|
||||
},
|
||||
"manageGroups": {
|
||||
"message": "Manage Groups"
|
||||
},
|
||||
"managePolicies": {
|
||||
"message": "Manage Policies"
|
||||
},
|
||||
"manageSso": {
|
||||
"message": "Manage SSO"
|
||||
},
|
||||
"manageUsers": {
|
||||
"message": "Manage Users"
|
||||
},
|
||||
"disableRequireSsoError": {
|
||||
"message": "You must manually disable the Single Sign-On Authentication policy before this policy can be disabled."
|
||||
},
|
||||
"personalOwnershipPolicyInEffect": {
|
||||
"message": "An organization policy is affecting your ownership options."
|
||||
},
|
||||
"personalOwnershipCheckboxDesc": {
|
||||
"message": "Disable personal ownership for organization users"
|
||||
},
|
||||
"textHiddenByDefault": {
|
||||
"message": "When accessing the Send, hide the text by default",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user