Compare commits
36 Commits
Author | SHA1 | Date |
---|---|---|
github-actions[bot] | 4693dfb1b0 | |
Addison Beck | 5b21ab7c6a | |
Oscar Hinton | 3f40678599 | |
Thomas Rittson | 81c6a8b9d7 | |
Addison Beck | fd64b39b6a | |
github-actions[bot] | b01c2d6216 | |
Addison Beck | ede3feb798 | |
Addison Beck | 6d6f6f8743 | |
addison | 54eded8311 | |
Oscar Hinton | 86a4a3e8d5 | |
Matt Gibson | a879a7da7e | |
Joseph Flinn | af5db8c8a1 | |
github-actions[bot] | b9ed2f6d48 | |
Thomas Rittson | 2dce34af68 | |
Thomas Rittson | 5cf26ba1bc | |
Thomas Rittson | 8f90e1317f | |
Joseph Flinn | 45d359353b | |
Joseph Flinn | 730955a101 | |
Micaiah Martin | 276aee98e4 | |
github-actions[bot] | 2a61a87542 | |
Addison Beck | 50aec04b4b | |
Addison Beck | ab4ecff04a | |
Addison Beck | 828fcc1896 | |
Joseph Flinn | 89b167a9a1 | |
Addison Beck | 25b00a1500 | |
Thomas Rittson | 5d9164bce3 | |
Addison Beck | 5bd1eeb3b4 | |
Daniel James Smith | bf3c8cef24 | |
Daniel James Smith | 58709857db | |
Vincent Salucci | 9b7672186f | |
Joseph Flinn | 55296b136e | |
Addison Beck | f44c009837 | |
Addison Beck | 442c8828f5 | |
Daniel James Smith | 3e6989ea91 | |
Vincent Salucci | 4e08ccb4dd | |
Addison Beck | 79aeb102cb |
|
@ -55,9 +55,9 @@ jobs:
|
|||
run: |
|
||||
SAFARI_REF=master
|
||||
|
||||
if [ "$GITHUB_REF" = "hotfix" ]; then
|
||||
if [[ "$GITHUB_REF" == "refs/heads/hotfix" ]]; then
|
||||
SAFARI_REF=hotfix
|
||||
elif [ "$GITHUB_REF" = "rc" ]; then
|
||||
elif [[ "$GITHUB_REF" == "refs/heads/rc" ]]; then
|
||||
SAFARI_REF=rc
|
||||
fi
|
||||
|
||||
|
@ -221,6 +221,22 @@ jobs:
|
|||
npm --version
|
||||
choco --version
|
||||
|
||||
- name: Login to Azure
|
||||
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
|
||||
with:
|
||||
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
||||
|
||||
- name: Retrieve secrets
|
||||
id: retrieve-secrets
|
||||
uses: Azure/get-keyvault-secrets@80ccd3fafe5662407cc2e55f202ee34bfff8c403
|
||||
with:
|
||||
keyvault: "bitwarden-prod-kv"
|
||||
secrets: "code-signing-vault-url,
|
||||
code-signing-client-id,
|
||||
code-signing-tenant-id,
|
||||
code-signing-client-secret,
|
||||
code-signing-cert-name"
|
||||
|
||||
- name: Install Node dependencies
|
||||
run: npm ci
|
||||
|
||||
|
@ -230,11 +246,11 @@ jobs:
|
|||
- name: Build & Sign (dev)
|
||||
env:
|
||||
ELECTRON_BUILDER_SIGN: 1
|
||||
SIGNING_VAULT_URL: ${{ secrets.SIGNING_VAULT_URL }}
|
||||
SIGNING_CLIENT_ID: ${{ secrets.SIGNING_CLIENT_ID }}
|
||||
SIGNING_TENANT_ID: ${{ secrets.SIGNING_TENANT_ID }}
|
||||
SIGNING_CLIENT_SECRET: ${{ secrets.SIGNING_CLIENT_SECRET }}
|
||||
SIGNING_CERT_NAME: ${{ secrets.SIGNING_CERT_NAME }}
|
||||
SIGNING_VAULT_URL: ${{ steps.retrieve-secrets.outputs.code-signing-vault-url }}
|
||||
SIGNING_CLIENT_ID: ${{ steps.retrieve-secrets.outputs.code-signing-client-id }}
|
||||
SIGNING_TENANT_ID: ${{ steps.retrieve-secrets.outputs.code-signing-tenant-id }}
|
||||
SIGNING_CLIENT_SECRET: ${{ steps.retrieve-secrets.outputs.code-signing-client-secret }}
|
||||
SIGNING_CERT_NAME: ${{ steps.retrieve-secrets.outputs.code-signing-cert-name }}
|
||||
run: |
|
||||
npm run build
|
||||
npm run pack:win
|
||||
|
|
|
@ -68,6 +68,8 @@ jobs:
|
|||
branch: ${{ steps.branch.outputs.branch-name }}
|
||||
|
||||
- name: Rename .pkg to .pkg.archive
|
||||
env:
|
||||
PKG_VERSION: ${{ steps.retrieve-version.outputs.package_version }}
|
||||
run: mv Bitwarden-${{ env.PKG_VERSION }}-universal.pkg Bitwarden-${{ env.PKG_VERSION }}-universal.pkg.archive
|
||||
|
||||
- name: Create release
|
||||
|
@ -116,10 +118,22 @@ jobs:
|
|||
- name: Checkout Repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||
|
||||
- name: Login to Azure
|
||||
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
|
||||
with:
|
||||
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
||||
|
||||
- name: Retrieve secrets
|
||||
id: retrieve-secrets
|
||||
uses: Azure/get-keyvault-secrets@80ccd3fafe5662407cc2e55f202ee34bfff8c403
|
||||
with:
|
||||
keyvault: "bitwarden-prod-kv"
|
||||
secrets: "snapcraft-store-token"
|
||||
|
||||
- name: Install Snap
|
||||
uses: samuelmeuli/action-snapcraft@10d7d0a84d9d86098b19f872257df314b0bd8e2d # v1.2.0
|
||||
with:
|
||||
snapcraft_token: ${{ secrets.SNAP_TOKEN }}
|
||||
snapcraft_token: ${{ steps.retrieve-secrets.outputs.snapcraft-store-token }}
|
||||
|
||||
- name: Setup
|
||||
run: mkdir dist
|
||||
|
|
2
jslib
2
jslib
|
@ -1 +1 @@
|
|||
Subproject commit 92a65b7b368a8dbf55350657674c90169b04c30b
|
||||
Subproject commit 1e9c4cacce34b1c21a878bf6b5ca2c0dc3926cf8
|
|
@ -43,7 +43,6 @@
|
|||
"electron-notarize": "^1.1.1",
|
||||
"electron-rebuild": "^3.2.5",
|
||||
"electron-reload": "^1.5.0",
|
||||
"font-awesome": "4.7.0",
|
||||
"html-loader": "^3.0.1",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"husky": "^7.0.4",
|
||||
|
@ -4382,15 +4381,6 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/font-awesome": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz",
|
||||
"integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.3"
|
||||
}
|
||||
},
|
||||
"node_modules/forcefocus": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/forcefocus/-/forcefocus-1.1.0.tgz",
|
||||
|
@ -13406,12 +13396,6 @@
|
|||
"path-exists": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"font-awesome": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz",
|
||||
"integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=",
|
||||
"dev": true
|
||||
},
|
||||
"forcefocus": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/forcefocus/-/forcefocus-1.1.0.tgz",
|
||||
|
|
|
@ -265,7 +265,6 @@
|
|||
"electron-notarize": "^1.1.1",
|
||||
"electron-rebuild": "^3.2.5",
|
||||
"electron-reload": "^1.5.0",
|
||||
"font-awesome": "4.7.0",
|
||||
"html-loader": "^3.0.1",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"husky": "^7.0.4",
|
||||
|
|
|
@ -1,96 +1,98 @@
|
|||
<div class="login-header">
|
||||
<a
|
||||
href="#"
|
||||
appStopClick
|
||||
(click)="settings()"
|
||||
class="environment-urls-settings-icon"
|
||||
attr.aria-label="{{ 'settings' | i18n }}"
|
||||
<div id="login-page">
|
||||
<div class="login-header">
|
||||
<a
|
||||
href="#"
|
||||
appStopClick
|
||||
(click)="settings()"
|
||||
class="environment-urls-settings-icon"
|
||||
attr.aria-label="{{ 'settings' | i18n }}"
|
||||
>
|
||||
<i class="bwi bwi-cog bwi-lg" aria-hidden="true"></i>
|
||||
{{ "settings" | i18n }}
|
||||
</a>
|
||||
</div>
|
||||
<form
|
||||
id="login-page"
|
||||
#form
|
||||
(ngSubmit)="submit()"
|
||||
[appApiAction]="formPromise"
|
||||
attr.aria-hidden="{{ showingModal }}"
|
||||
>
|
||||
<i class="bwi bwi-cog bwi-lg" aria-hidden="true"></i>
|
||||
{{ "settings" | i18n }}
|
||||
</a>
|
||||
</div>
|
||||
<form
|
||||
id="login-page"
|
||||
#form
|
||||
(ngSubmit)="submit()"
|
||||
[appApiAction]="formPromise"
|
||||
attr.aria-hidden="{{ showingModal }}"
|
||||
>
|
||||
<div id="content" class="content">
|
||||
<img class="logo-image" alt="Bitwarden" />
|
||||
<p class="lead">{{ "loginOrCreateNewAccount" | i18n }}</p>
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="email">{{ "emailAddress" | i18n }}</label>
|
||||
<input
|
||||
id="email"
|
||||
type="text"
|
||||
name="Email"
|
||||
[(ngModel)]="email"
|
||||
required
|
||||
appInputVerbatim="false"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
|
||||
<div id="content" class="content">
|
||||
<img class="logo-image" alt="Bitwarden" />
|
||||
<p class="lead">{{ "loginOrCreateNewAccount" | i18n }}</p>
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="email">{{ "emailAddress" | i18n }}</label>
|
||||
<input
|
||||
id="masterPassword"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="MasterPassword"
|
||||
class="monospaced"
|
||||
[(ngModel)]="masterPassword"
|
||||
id="email"
|
||||
type="text"
|
||||
name="Email"
|
||||
[(ngModel)]="email"
|
||||
required
|
||||
appInputVerbatim
|
||||
appInputVerbatim="false"
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="togglePassword()"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
||||
></i>
|
||||
</a>
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
|
||||
<input
|
||||
id="masterPassword"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="MasterPassword"
|
||||
class="monospaced"
|
||||
[(ngModel)]="masterPassword"
|
||||
required
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="togglePassword()"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
||||
></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" [hidden]="!showCaptcha()">
|
||||
<iframe id="hcaptcha_iframe" height="80"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" [hidden]="!showCaptcha()">
|
||||
<iframe id="hcaptcha_iframe" height="80"></iframe>
|
||||
</div>
|
||||
|
||||
<div class="buttons with-rows">
|
||||
<div class="buttons-row">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
||||
<b [hidden]="form.loading"
|
||||
><i class="bwi bwi-sign-in" aria-hidden="true"></i> {{ "logIn" | i18n }}</b
|
||||
>
|
||||
<i class="bwi bwi-spinner bwi-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<a routerLink="/register" class="btn block">
|
||||
<i class="bwi bwi-pencil-square" aria-hidden="true"></i> {{ "createAccount" | i18n }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="buttons-row">
|
||||
<a (click)="launchSsoBrowser('desktop', 'bitwarden://sso-callback')" class="btn block">
|
||||
<i class="bwi bwi-bank" aria-hidden="true"></i> {{ "enterpriseSingleSignOn" | i18n }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="buttons with-rows">
|
||||
<div class="buttons-row">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
||||
<b [hidden]="form.loading"
|
||||
><i class="bwi bwi-sign-in" aria-hidden="true"></i> {{ "logIn" | i18n }}</b
|
||||
>
|
||||
<i class="bwi bwi-spinner bwi-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<a routerLink="/register" class="btn block">
|
||||
<i class="bwi bwi-pencil-square" aria-hidden="true"></i> {{ "createAccount" | i18n }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="buttons-row">
|
||||
<a (click)="launchSsoBrowser('desktop', 'bitwarden://sso-callback')" class="btn block">
|
||||
<i class="bwi bwi-bank" aria-hidden="true"></i> {{ "enterpriseSingleSignOn" | i18n }}
|
||||
</a>
|
||||
<div class="sub-options">
|
||||
<a routerLink="/hint">{{ "getMasterPasswordHint" | i18n }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sub-options">
|
||||
<a routerLink="/hint">{{ "getMasterPasswordHint" | i18n }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
</div>
|
||||
<ng-template #environment></ng-template>
|
||||
|
|
|
@ -2,184 +2,184 @@
|
|||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body form">
|
||||
<ng-container *ngIf="currentUserEmail != null">
|
||||
<div class="box">
|
||||
<label class="settingsTitle">{{ "settingsTitle" | i18n: currentUserEmail }} </label>
|
||||
<div class="box-content box-content-padded">
|
||||
<h2>
|
||||
<button
|
||||
type="button"
|
||||
class="box-header-expandable"
|
||||
(click)="showSecurity = !showSecurity"
|
||||
[attr.aria-expanded]="showSecurity"
|
||||
>
|
||||
{{ "security" | i18n }}
|
||||
<i
|
||||
*ngIf="!showSecurity"
|
||||
class="fa fa-chevron-down fa-sm icon"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<i
|
||||
*ngIf="showSecurity"
|
||||
class="fa fa-chevron-up fa-sm icon"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
</h2>
|
||||
<ng-container *ngIf="showSecurity">
|
||||
<app-vault-timeout-input
|
||||
[vaultTimeouts]="vaultTimeouts"
|
||||
[formControl]="vaultTimeout"
|
||||
ngDefaultControl
|
||||
></app-vault-timeout-input>
|
||||
<div class="form-group">
|
||||
<label>{{ "vaultTimeoutAction" | i18n }}</label>
|
||||
<div class="radio radio-mt-2">
|
||||
<label for="vaultTimeoutActionLock">
|
||||
<input
|
||||
type="radio"
|
||||
name="VaultTimeoutAction"
|
||||
id="vaultTimeoutActionLock"
|
||||
value="lock"
|
||||
[(ngModel)]="vaultTimeoutAction"
|
||||
(change)="saveVaultTimeoutOptions()"
|
||||
/>
|
||||
{{ "lock" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{ "vaultTimeoutActionLockDesc" | i18n }}</small>
|
||||
<div class="radio">
|
||||
<label for="vaultTimeoutActionLogOut">
|
||||
<input
|
||||
type="radio"
|
||||
name="VaultTimeoutAction"
|
||||
id="vaultTimeoutActionLogOut"
|
||||
value="logOut"
|
||||
[(ngModel)]="vaultTimeoutAction"
|
||||
(change)="saveVaultTimeoutOptions()"
|
||||
/>
|
||||
{{ "logOut" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{ "vaultTimeoutActionLogOutDesc" | i18n }}</small>
|
||||
<div class="box">
|
||||
<label class="settingsTitle">{{ "settingsTitle" | i18n: currentUserEmail }} </label>
|
||||
<div class="box-content box-content-padded">
|
||||
<h2>
|
||||
<button
|
||||
id="app-settings"
|
||||
type="button"
|
||||
class="box-header-expandable"
|
||||
(click)="showSecurity = !showSecurity"
|
||||
[attr.aria-expanded]="showSecurity"
|
||||
appAutofocus
|
||||
>
|
||||
{{ "security" | i18n }}
|
||||
<i
|
||||
*ngIf="!showSecurity"
|
||||
class="bwi bwi-angle-down bwi-sm icon"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<i
|
||||
*ngIf="showSecurity"
|
||||
class="bwi bwi-chevron-up bwi-sm icon"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
</h2>
|
||||
<ng-container *ngIf="showSecurity">
|
||||
<app-vault-timeout-input
|
||||
[vaultTimeouts]="vaultTimeouts"
|
||||
[formControl]="vaultTimeout"
|
||||
ngDefaultControl
|
||||
></app-vault-timeout-input>
|
||||
<div class="form-group">
|
||||
<label>{{ "vaultTimeoutAction" | i18n }}</label>
|
||||
<div class="radio radio-mt-2">
|
||||
<label for="vaultTimeoutActionLock">
|
||||
<input
|
||||
type="radio"
|
||||
name="VaultTimeoutAction"
|
||||
id="vaultTimeoutActionLock"
|
||||
value="lock"
|
||||
[(ngModel)]="vaultTimeoutAction"
|
||||
(change)="saveVaultTimeoutOptions()"
|
||||
/>
|
||||
{{ "lock" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="pin">
|
||||
<input
|
||||
id="pin"
|
||||
type="checkbox"
|
||||
name="PIN"
|
||||
[(ngModel)]="pin"
|
||||
(change)="updatePin()"
|
||||
/>
|
||||
{{ "unlockWithPin" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{ "vaultTimeoutActionLockDesc" | i18n }}</small>
|
||||
<div class="radio">
|
||||
<label for="vaultTimeoutActionLogOut">
|
||||
<input
|
||||
type="radio"
|
||||
name="VaultTimeoutAction"
|
||||
id="vaultTimeoutActionLogOut"
|
||||
value="logOut"
|
||||
[(ngModel)]="vaultTimeoutAction"
|
||||
(change)="saveVaultTimeoutOptions()"
|
||||
/>
|
||||
{{ "logOut" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group" *ngIf="supportsBiometric">
|
||||
<div class="checkbox">
|
||||
<label for="biometric">
|
||||
<input
|
||||
id="biometric"
|
||||
type="checkbox"
|
||||
name="biometric"
|
||||
[checked]="biometric"
|
||||
(change)="updateBiometric()"
|
||||
/>
|
||||
{{ biometricText | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{ "vaultTimeoutActionLogOutDesc" | i18n }}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="pin">
|
||||
<input
|
||||
id="pin"
|
||||
type="checkbox"
|
||||
name="PIN"
|
||||
[(ngModel)]="pin"
|
||||
(change)="updatePin()"
|
||||
/>
|
||||
{{ "unlockWithPin" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group" *ngIf="supportsBiometric">
|
||||
<div class="checkbox">
|
||||
<label for="noAutoPromptBiometrics">
|
||||
<input
|
||||
id="noAutoPromptBiometrics"
|
||||
type="checkbox"
|
||||
name="noAutoPromptBiometrics"
|
||||
[(ngModel)]="noAutoPromptBiometrics"
|
||||
[disabled]="!biometric"
|
||||
(change)="updateNoAutoPromptBiometrics()"
|
||||
/>
|
||||
{{ noAutoPromptBiometricsText | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" *ngIf="supportsBiometric">
|
||||
<div class="checkbox">
|
||||
<label for="biometric">
|
||||
<input
|
||||
id="biometric"
|
||||
type="checkbox"
|
||||
name="biometric"
|
||||
[checked]="biometric"
|
||||
(change)="updateBiometric()"
|
||||
/>
|
||||
{{ biometricText | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" *ngIf="supportsBiometric">
|
||||
<div class="checkbox">
|
||||
<label for="noAutoPromptBiometrics">
|
||||
<input
|
||||
id="noAutoPromptBiometrics"
|
||||
type="checkbox"
|
||||
name="noAutoPromptBiometrics"
|
||||
[(ngModel)]="noAutoPromptBiometrics"
|
||||
[disabled]="!biometric"
|
||||
(change)="updateNoAutoPromptBiometrics()"
|
||||
/>
|
||||
{{ noAutoPromptBiometricsText | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content box-content-padded">
|
||||
<h2>
|
||||
<button
|
||||
type="button"
|
||||
class="box-header-expandable"
|
||||
(click)="showAccountPreferences = !showAccountPreferences"
|
||||
[attr.aria-expanded]="showAccountPreferences"
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content box-content-padded">
|
||||
<h2>
|
||||
<button
|
||||
type="button"
|
||||
class="box-header-expandable"
|
||||
(click)="showAccountPreferences = !showAccountPreferences"
|
||||
[attr.aria-expanded]="showAccountPreferences"
|
||||
>
|
||||
{{ "accountPreferences" | i18n }}
|
||||
<i
|
||||
*ngIf="!showAccountPreferences"
|
||||
class="bwi bwi-angle-down bwi-sm icon"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<i
|
||||
*ngIf="showAccountPreferences"
|
||||
class="bwi bwi-chevron-up bwi-sm icon"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
</h2>
|
||||
<ng-container *ngIf="showAccountPreferences">
|
||||
<div class="form-group">
|
||||
<label for="clearClipboard">{{ "clearClipboard" | i18n }}</label>
|
||||
<select
|
||||
id="clearClipboard"
|
||||
name="ClearClipboard"
|
||||
[(ngModel)]="clearClipboard"
|
||||
(change)="saveClearClipboard()"
|
||||
>
|
||||
{{ "accountPreferences" | i18n }}
|
||||
<i
|
||||
*ngIf="!showAccountPreferences"
|
||||
class="fa fa-chevron-down fa-sm icon"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<i
|
||||
*ngIf="showAccountPreferences"
|
||||
class="fa fa-chevron-up fa-sm icon"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
</h2>
|
||||
<ng-container *ngIf="showAccountPreferences">
|
||||
<div class="form-group">
|
||||
<label for="clearClipboard">{{ "clearClipboard" | i18n }}</label>
|
||||
<select
|
||||
id="clearClipboard"
|
||||
name="ClearClipboard"
|
||||
[(ngModel)]="clearClipboard"
|
||||
(change)="saveClearClipboard()"
|
||||
>
|
||||
<option *ngFor="let o of clearClipboardOptions" [ngValue]="o.value">
|
||||
{{ o.name }}
|
||||
</option>
|
||||
</select>
|
||||
<small class="help-block">{{ "clearClipboardDesc" | i18n }}</small>
|
||||
<option *ngFor="let o of clearClipboardOptions" [ngValue]="o.value">
|
||||
{{ o.name }}
|
||||
</option>
|
||||
</select>
|
||||
<small class="help-block">{{ "clearClipboardDesc" | i18n }}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="minimizeOnCopyToClipboard">
|
||||
<input
|
||||
id="minimizeOnCopyToClipboard"
|
||||
type="checkbox"
|
||||
name="MinimizeOnCopyToClipboard"
|
||||
[(ngModel)]="minimizeOnCopyToClipboard"
|
||||
(change)="saveMinOnCopyToClipboard()"
|
||||
/>
|
||||
{{ "minimizeOnCopyToClipboard" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="minimizeOnCopyToClipboard">
|
||||
<input
|
||||
id="minimizeOnCopyToClipboard"
|
||||
type="checkbox"
|
||||
name="MinimizeOnCopyToClipboard"
|
||||
[(ngModel)]="minimizeOnCopyToClipboard"
|
||||
(change)="saveMinOnCopyToClipboard()"
|
||||
/>
|
||||
{{ "minimizeOnCopyToClipboard" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{ "minimizeOnCopyToClipboardDesc" | i18n }}</small>
|
||||
<small class="help-block">{{ "minimizeOnCopyToClipboardDesc" | i18n }}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="disableFavicons">
|
||||
<input
|
||||
id="disableFavicons"
|
||||
type="checkbox"
|
||||
name="DisableFavicons"
|
||||
[(ngModel)]="disableFavicons"
|
||||
(change)="saveFavicons()"
|
||||
/>
|
||||
{{ "disableFavicon" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="disableFavicons">
|
||||
<input
|
||||
id="disableFavicons"
|
||||
type="checkbox"
|
||||
name="DisableFavicons"
|
||||
[(ngModel)]="disableFavicons"
|
||||
(change)="saveFavicons()"
|
||||
/>
|
||||
{{ "disableFavicon" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{ "disableFaviconDesc" | i18n }}</small>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<small class="help-block">{{ "disableFaviconDesc" | i18n }}</small>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content box-content-padded">
|
||||
<h2>
|
||||
|
@ -192,12 +192,12 @@
|
|||
{{ "appPreferences" | i18n }}
|
||||
<i
|
||||
*ngIf="!showAppPreferences"
|
||||
class="fa fa-chevron-down fa-sm icon"
|
||||
class="bwi bwi-angle-down bwi-sm icon"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<i
|
||||
*ngIf="showAppPreferences"
|
||||
class="fa fa-chevron-up fa-sm icon"
|
||||
class="bwi bwi-chevron-up bwi-sm icon"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
name="VaultTimeout"
|
||||
formControlName="vaultTimeout"
|
||||
class="form-control"
|
||||
appAutofocus
|
||||
>
|
||||
<option *ngFor="let o of vaultTimeouts" [ngValue]="o.value">{{ o.name }}</option>
|
||||
</select>
|
||||
|
|
|
@ -53,6 +53,12 @@ const BroadcasterSubscriptionId = "AppComponent";
|
|||
const IdleTimeout = 60000 * 10; // 10 minutes
|
||||
const SyncInterval = 6 * 60 * 60 * 1000; // 6 hours
|
||||
|
||||
const systemTimeoutOptions = {
|
||||
onLock: -2,
|
||||
onSuspend: -3,
|
||||
onIdle: -4,
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: "app-root",
|
||||
styles: [],
|
||||
|
@ -90,6 +96,7 @@ export class AppComponent implements OnInit {
|
|||
private modal: ModalRef = null;
|
||||
private idleTimer: number = null;
|
||||
private isIdle = false;
|
||||
private activeUserId: string = null;
|
||||
|
||||
constructor(
|
||||
private broadcasterService: BroadcasterService,
|
||||
|
@ -122,21 +129,18 @@ export class AppComponent implements OnInit {
|
|||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
let activeUserId: string = null;
|
||||
this.stateService.activeAccount.subscribe((userId) => {
|
||||
activeUserId = userId;
|
||||
this.activeUserId = userId;
|
||||
});
|
||||
this.ngZone.runOutsideAngular(() => {
|
||||
setTimeout(async () => {
|
||||
await this.updateAppMenu();
|
||||
}, 1000);
|
||||
|
||||
if (activeUserId != null) {
|
||||
window.ontouchstart = () => this.recordActivity(activeUserId);
|
||||
window.onmousedown = () => this.recordActivity(activeUserId);
|
||||
window.onscroll = () => this.recordActivity(activeUserId);
|
||||
window.onkeypress = () => this.recordActivity(activeUserId);
|
||||
}
|
||||
window.ontouchstart = () => this.recordActivity();
|
||||
window.onmousedown = () => this.recordActivity();
|
||||
window.onscroll = () => this.recordActivity();
|
||||
window.onkeypress = () => this.recordActivity();
|
||||
});
|
||||
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||
|
@ -161,7 +165,7 @@ export class AppComponent implements OnInit {
|
|||
this.router.navigate(["login"]);
|
||||
break;
|
||||
case "logout":
|
||||
this.loading = true;
|
||||
this.loading = message.userId == null || message.userId === this.activeUserId;
|
||||
await this.logOut(!!message.expired, message.userId);
|
||||
this.loading = false;
|
||||
break;
|
||||
|
@ -339,6 +343,12 @@ export class AppComponent implements OnInit {
|
|||
this.router.navigate(["vault"]);
|
||||
}
|
||||
break;
|
||||
case "systemSuspended":
|
||||
await this.checkForSystemTimeout(systemTimeoutOptions.onSuspend);
|
||||
case "systemLocked":
|
||||
await this.checkForSystemTimeout(systemTimeoutOptions.onLock);
|
||||
case "systemIdle":
|
||||
await this.checkForSystemTimeout(systemTimeoutOptions.onIdle);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -441,28 +451,24 @@ export class AppComponent implements OnInit {
|
|||
}
|
||||
|
||||
private async logOut(expired: boolean, userId?: string) {
|
||||
if (!userId) {
|
||||
userId = await this.stateService.getUserId();
|
||||
}
|
||||
|
||||
const userBeingLoggedOut = await this.stateService.getUserId({ userId: userId });
|
||||
await Promise.all([
|
||||
this.eventService.uploadEvents(userId),
|
||||
this.syncService.setLastSync(new Date(0), userId),
|
||||
this.tokenService.clearToken(userId),
|
||||
this.cryptoService.clearKeys(userId),
|
||||
this.settingsService.clear(userId),
|
||||
this.cipherService.clear(userId),
|
||||
this.folderService.clear(userId),
|
||||
this.collectionService.clear(userId),
|
||||
this.passwordGenerationService.clear(userId),
|
||||
this.vaultTimeoutService.clear(userId),
|
||||
this.policyService.clear(userId),
|
||||
this.eventService.uploadEvents(userBeingLoggedOut),
|
||||
this.syncService.setLastSync(new Date(0), userBeingLoggedOut),
|
||||
this.cryptoService.clearKeys(userBeingLoggedOut),
|
||||
this.settingsService.clear(userBeingLoggedOut),
|
||||
this.cipherService.clear(userBeingLoggedOut),
|
||||
this.folderService.clear(userBeingLoggedOut),
|
||||
this.collectionService.clear(userBeingLoggedOut),
|
||||
this.passwordGenerationService.clear(userBeingLoggedOut),
|
||||
this.vaultTimeoutService.clear(userBeingLoggedOut),
|
||||
this.policyService.clear(userBeingLoggedOut),
|
||||
this.keyConnectorService.clear(),
|
||||
]);
|
||||
|
||||
await this.stateService.setBiometricLocked(true, { userId: userId });
|
||||
await this.stateService.setBiometricLocked(true, { userId: userBeingLoggedOut });
|
||||
|
||||
if (userId == null || userId === (await this.stateService.getUserId())) {
|
||||
if (userBeingLoggedOut === this.activeUserId) {
|
||||
this.searchService.clearIndex();
|
||||
this.authService.logOut(async () => {
|
||||
if (expired) {
|
||||
|
@ -475,25 +481,30 @@ export class AppComponent implements OnInit {
|
|||
});
|
||||
}
|
||||
|
||||
await this.stateService.clean({ userId: userId });
|
||||
const preLogoutActiveUserId = this.activeUserId;
|
||||
await this.stateService.clean({ userId: userBeingLoggedOut });
|
||||
|
||||
if (this.stateService.activeAccount.getValue() == null) {
|
||||
if (this.activeUserId == null) {
|
||||
this.router.navigate(["login"]);
|
||||
} else {
|
||||
} else if (preLogoutActiveUserId !== this.activeUserId) {
|
||||
this.messagingService.send("switchAccount");
|
||||
}
|
||||
|
||||
await this.updateAppMenu();
|
||||
}
|
||||
|
||||
private async recordActivity(userId: string) {
|
||||
private async recordActivity() {
|
||||
if (this.activeUserId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const now = new Date().getTime();
|
||||
if (this.lastActivity != null && now - this.lastActivity < 250) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.lastActivity = now;
|
||||
await this.stateService.setLastActive(now, { userId: userId });
|
||||
await this.stateService.setLastActive(now, { userId: this.activeUserId });
|
||||
|
||||
// Idle states
|
||||
if (this.isIdle) {
|
||||
|
@ -583,4 +594,24 @@ export class AppComponent implements OnInit {
|
|||
}
|
||||
await this.systemService.startProcessReload();
|
||||
}
|
||||
|
||||
private async checkForSystemTimeout(timeout: number): Promise<void> {
|
||||
for (const userId in this.stateService.accounts.getValue()) {
|
||||
if (userId == null) {
|
||||
continue;
|
||||
}
|
||||
const options = await this.getVaultTimeoutOptions(userId);
|
||||
if (options[0] === timeout) {
|
||||
options[1] === "logOut"
|
||||
? this.logOut(false, userId)
|
||||
: await this.vaultTimeoutService.lock(true, userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async getVaultTimeoutOptions(userId: string): Promise<[number, string]> {
|
||||
const timeout = await this.stateService.getVaultTimeout({ userId: userId });
|
||||
const action = await this.stateService.getVaultTimeoutAction({ userId: userId });
|
||||
return [timeout, action];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
<a
|
||||
<button
|
||||
class="account-switcher"
|
||||
(click)="toggle()"
|
||||
cdkOverlayOrigin
|
||||
#trigger="cdkOverlayOrigin"
|
||||
[hidden]="!showSwitcher"
|
||||
aria-haspopup="menu"
|
||||
aria-expanded="isOpen"
|
||||
aria-controls="cdk-overlay-container"
|
||||
>
|
||||
<ng-container *ngIf="activeAccountEmail != null; else noActiveAccount">
|
||||
<app-avatar
|
||||
|
@ -13,6 +16,7 @@
|
|||
[fontSize]="14"
|
||||
[dynamic]="true"
|
||||
*ngIf="activeAccountEmail != null"
|
||||
aria-hidden="true"
|
||||
></app-avatar>
|
||||
<span>{{ activeAccountEmail }}</span>
|
||||
</ng-container>
|
||||
|
@ -24,26 +28,33 @@
|
|||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-angle-down': !isOpen, 'bwi-chevron-up': isOpen }"
|
||||
></i>
|
||||
</a>
|
||||
</button>
|
||||
|
||||
<ng-template
|
||||
cdkConnectedOverlay
|
||||
[cdkConnectedOverlayOrigin]="trigger"
|
||||
[cdkConnectedOverlayHasBackdrop]="true"
|
||||
[cdkConnectedOverlayBackdropClass]="'cdk-overlay-transparent-backdrop'"
|
||||
(backdropClick)="toggle()"
|
||||
(backdropClick)="close()"
|
||||
(detach)="close()"
|
||||
[cdkConnectedOverlayOpen]="showSwitcher && isOpen"
|
||||
[cdkConnectedOverlayPositions]="overlayPostition"
|
||||
cdkConnectedOverlayMinWidth="250px"
|
||||
>
|
||||
<div class="account-switcher-dropdown" [@transformPanel]="'open'">
|
||||
<div
|
||||
class="account-switcher-dropdown"
|
||||
[@transformPanel]="'open'"
|
||||
cdkTrapFocus
|
||||
cdkTrapFocusAutoCapture
|
||||
>
|
||||
<div class="accounts" *ngIf="numberOfAccounts > 0">
|
||||
<a
|
||||
<button
|
||||
*ngFor="let a of accounts | keyvalue"
|
||||
class="account"
|
||||
[ngClass]="{ active: a.value.profile.authenticationStatus == 'active' }"
|
||||
href="#"
|
||||
(mousedown)="switch(a.key)"
|
||||
(click)="switch(a.key)"
|
||||
appA11yTitle="{{ 'loggedInAsOn' | i18n: a.value.profile.email:a.value.serverUrl }}"
|
||||
attr.aria-label="{{ 'switchAccount' | i18n }}"
|
||||
>
|
||||
<app-avatar
|
||||
[data]="a.value.profile.email"
|
||||
|
@ -52,30 +63,33 @@
|
|||
[fontSize]="14"
|
||||
[dynamic]="true"
|
||||
*ngIf="a.value.profile.email != null"
|
||||
aria-hidden="true"
|
||||
></app-avatar>
|
||||
<div class="accountInfo">
|
||||
<span class="email">{{ a.value.profile.email }}</span>
|
||||
<span class="server" *ngIf="a.value.serverUrl != 'bitwarden.com'">{{
|
||||
<span class="email" aria-hidden="true">{{ a.value.profile.email }}</span>
|
||||
<span class="server" aria-hidden="true" *ngIf="a.value.serverUrl != 'bitwarden.com'">{{
|
||||
a.value.serverUrl
|
||||
}}</span>
|
||||
<span class="status">{{ a.value.profile.authenticationStatus }}</span>
|
||||
<span class="status" aria-hidden="true">{{ a.value.profile.authenticationStatus }}</span>
|
||||
</div>
|
||||
<i
|
||||
class="bwi bwi-unlock bwi-2x text-muted"
|
||||
aria-hidden="true"
|
||||
*ngIf="a.value.profile.authenticationStatus == 'unlocked'"
|
||||
></i>
|
||||
<i
|
||||
class="bwi bwi-lock bwi-2x text-muted"
|
||||
aria-hidden="true"
|
||||
*ngIf="a.value.profile.authenticationStatus == 'locked'"
|
||||
></i>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
<ng-container *ngIf="activeAccountEmail != null">
|
||||
<div class="border" *ngIf="numberOfAccounts > 0"></div>
|
||||
<ng-container *ngIf="numberOfAccounts < 4">
|
||||
<a class="add" routerLink="/login" (click)="addAccount()">
|
||||
<button class="add" routerLink="/login" (click)="addAccount()">
|
||||
<i class="bwi bwi-plus" aria-hidden="true"></i> {{ "addAccount" | i18n }}
|
||||
</a>
|
||||
</button>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="numberOfAccounts == 4">
|
||||
<span class="accountLimitReached">{{ "accountSwitcherLimitReached" | i18n }} </span>
|
||||
|
|
|
@ -107,14 +107,18 @@ export class AccountSwitcherComponent implements OnInit {
|
|||
this.isOpen = !this.isOpen;
|
||||
}
|
||||
|
||||
close() {
|
||||
this.isOpen = false;
|
||||
}
|
||||
|
||||
async switch(userId: string) {
|
||||
this.toggle();
|
||||
this.close();
|
||||
|
||||
this.messagingService.send("switchAccount", { userId: userId });
|
||||
}
|
||||
|
||||
async addAccount() {
|
||||
this.toggle();
|
||||
this.close();
|
||||
await this.stateService.setActiveUser(null);
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ import { Account } from "../models/account";
|
|||
import { GlobalState } from "jslib-common/models/domain/globalState";
|
||||
|
||||
import { StateFactory } from "jslib-common/factories/stateFactory";
|
||||
import { StateMigrationService } from "jslib-common/services/stateMigration.service";
|
||||
|
||||
export function initFactory(
|
||||
window: Window,
|
||||
|
@ -201,6 +202,19 @@ export function initFactory(
|
|||
StateMigrationServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: StateMigrationServiceAbstraction,
|
||||
useFactory: (
|
||||
storageService: StorageServiceAbstraction,
|
||||
secureStorageService: StorageServiceAbstraction
|
||||
) =>
|
||||
new StateMigrationService(
|
||||
storageService,
|
||||
secureStorageService,
|
||||
new StateFactory(GlobalState, Account)
|
||||
),
|
||||
deps: [StorageServiceAbstraction, "SECURE_STORAGE"],
|
||||
},
|
||||
],
|
||||
})
|
||||
export class ServicesModule {}
|
||||
|
|
|
@ -426,7 +426,7 @@
|
|||
appA11yTitle="{{ 'toggleOptions' | i18n }}"
|
||||
(click)="toggleUriOptions(u)"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-cog-f" aria-hidden="true"></i>
|
||||
<i class="bwi bwi-lg bwi-cog" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
</li>
|
||||
<li [ngClass]="{ active: selectedFavorites }">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectFavorites()">
|
||||
<i class="bwi bwi-fw bwi-star-f" aria-hidden="true"></i> {{ "favorites" | i18n }}
|
||||
<i class="bwi bwi-fw bwi-star" aria-hidden="true"></i> {{ "favorites" | i18n }}
|
||||
</a>
|
||||
</li>
|
||||
<li [ngClass]="{ active: selectedTrash }">
|
||||
|
@ -64,8 +64,8 @@
|
|||
title="{{ 'toggleCollapse' | i18n }}"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{
|
||||
'bwi-caret-right': isCollapsed(f.node),
|
||||
'bwi-caret-down': !isCollapsed(f.node)
|
||||
'bwi-angle-right': isCollapsed(f.node),
|
||||
'bwi-angle-down': !isCollapsed(f.node)
|
||||
}"
|
||||
(click)="collapse(f.node)"
|
||||
appStopProp
|
||||
|
@ -114,8 +114,8 @@
|
|||
title="{{ 'toggleCollapse' | i18n }}"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{
|
||||
'bwi-caret-right': isCollapsed(c.node),
|
||||
'bwi-caret-down': !isCollapsed(c.node)
|
||||
'bwi-angle-right': isCollapsed(c.node),
|
||||
'bwi-angle-down': !isCollapsed(c.node)
|
||||
}"
|
||||
(click)="collapse(c.node)"
|
||||
appStopProp
|
||||
|
|
|
@ -1,17 +1,4 @@
|
|||
<div id="vault" class="vault" attr.aria-hidden="{{ showingModal }}">
|
||||
<app-vault-groupings
|
||||
id="groupings"
|
||||
class="groupings"
|
||||
(onAllClicked)="clearGroupingFilters()"
|
||||
(onFavoritesClicked)="filterFavorites()"
|
||||
(onCipherTypeClicked)="filterCipherType($event)"
|
||||
(onFolderClicked)="filterFolder($event.id)"
|
||||
(onAddFolder)="addFolder()"
|
||||
(onEditFolder)="editFolder($event.id)"
|
||||
(onCollectionClicked)="filterCollection($event.id)"
|
||||
(onTrashClicked)="filterDeleted()"
|
||||
>
|
||||
</app-vault-groupings>
|
||||
<app-vault-ciphers
|
||||
id="items"
|
||||
class="items"
|
||||
|
@ -64,6 +51,19 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<app-vault-groupings
|
||||
id="groupings"
|
||||
class="groupings"
|
||||
(onAllClicked)="clearGroupingFilters()"
|
||||
(onFavoritesClicked)="filterFavorites()"
|
||||
(onCipherTypeClicked)="filterCipherType($event)"
|
||||
(onFolderClicked)="filterFolder($event.id)"
|
||||
(onAddFolder)="addFolder()"
|
||||
(onEditFolder)="editFolder($event.id)"
|
||||
(onCollectionClicked)="filterCollection($event.id)"
|
||||
(onTrashClicked)="filterDeleted()"
|
||||
>
|
||||
</app-vault-groupings>
|
||||
</div>
|
||||
<ng-template #passwordGenerator></ng-template>
|
||||
<ng-template #attachments></ng-template>
|
||||
|
|
|
@ -1810,5 +1810,8 @@
|
|||
},
|
||||
"switchAccount": {
|
||||
"message": "Switch Account"
|
||||
},
|
||||
"options": {
|
||||
"message": "Options"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -153,9 +153,9 @@ export class Main {
|
|||
this.menuMain.init();
|
||||
await this.trayMain.init("Bitwarden", [
|
||||
{
|
||||
label: this.i18nService.t("lockNow"),
|
||||
label: this.i18nService.t("lockVault"),
|
||||
enabled: false,
|
||||
id: "lockNow",
|
||||
id: "lockVault",
|
||||
click: () => this.messagingService.send("lockVault"),
|
||||
},
|
||||
]);
|
||||
|
|
|
@ -99,10 +99,10 @@ export class MessagingMain {
|
|||
) {
|
||||
return;
|
||||
}
|
||||
const lockNowTrayMenuItem = this.main.trayMain.contextMenu.getMenuItemById("lockNow");
|
||||
const lockVaultTrayMenuItem = this.main.trayMain.contextMenu.getMenuItemById("lockVault");
|
||||
const activeAccount = updateRequest.accounts[updateRequest.activeUserId];
|
||||
if (lockNowTrayMenuItem != null && activeAccount != null) {
|
||||
lockNowTrayMenuItem.enabled = activeAccount.isAuthenticated && !activeAccount.isLocked;
|
||||
if (lockVaultTrayMenuItem != null && activeAccount != null) {
|
||||
lockVaultTrayMenuItem.enabled = activeAccount.isAuthenticated && !activeAccount.isLocked;
|
||||
}
|
||||
this.main.trayMain.updateContextMenu();
|
||||
}
|
||||
|
|
|
@ -17,30 +17,20 @@ export class PowerMonitorMain {
|
|||
// ref: https://github.com/electron/electron/issues/13767
|
||||
if (!isSnapStore()) {
|
||||
// System sleep
|
||||
powerMonitor.on("suspend", async () => {
|
||||
const options = await this.getVaultTimeoutOptions();
|
||||
if (options[0] === -3) {
|
||||
options[1] === "logOut"
|
||||
? this.main.messagingService.send("logout", { expired: false })
|
||||
: this.main.messagingService.send("lockVault");
|
||||
}
|
||||
powerMonitor.on("suspend", () => {
|
||||
this.main.messagingService.send("systemSuspended");
|
||||
});
|
||||
}
|
||||
|
||||
if (process.platform !== "linux") {
|
||||
// System locked
|
||||
powerMonitor.on("lock-screen", async () => {
|
||||
const options = await this.getVaultTimeoutOptions();
|
||||
if (options[0] === -2) {
|
||||
options[1] === "logOut"
|
||||
? this.main.messagingService.send("logout", { expired: false })
|
||||
: this.main.messagingService.send("lockVault");
|
||||
}
|
||||
powerMonitor.on("lock-screen", () => {
|
||||
this.main.messagingService.send("systemLocked");
|
||||
});
|
||||
}
|
||||
|
||||
// System idle
|
||||
global.setInterval(async () => {
|
||||
global.setInterval(() => {
|
||||
const idleSeconds: number = powerMonitor.getSystemIdleTime();
|
||||
const idle = idleSeconds >= IdleLockSeconds;
|
||||
if (idle) {
|
||||
|
@ -48,21 +38,10 @@ export class PowerMonitorMain {
|
|||
return;
|
||||
}
|
||||
|
||||
const options = await this.getVaultTimeoutOptions();
|
||||
if (options[0] === -4) {
|
||||
options[1] === "logOut"
|
||||
? this.main.messagingService.send("logout", { expired: false })
|
||||
: this.main.messagingService.send("lockVault");
|
||||
}
|
||||
this.main.messagingService.send("systemIdle");
|
||||
}
|
||||
|
||||
this.idle = idle;
|
||||
}, IdleCheckInterval);
|
||||
}
|
||||
|
||||
private async getVaultTimeoutOptions(): Promise<[number, string]> {
|
||||
const timeout = await this.main.stateService.getVaultTimeout();
|
||||
const action = await this.main.stateService.getVaultTimeoutAction();
|
||||
return [timeout, action];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"name": "@bitwarden/desktop",
|
||||
"productName": "Bitwarden",
|
||||
"description": "A secure and free password manager for all of your devices.",
|
||||
"version": "1.30.1",
|
||||
"version": "1.31.3",
|
||||
"author": "Bitwarden Inc. <hello@bitwarden.com> (https://bitwarden.com)",
|
||||
"homepage": "https://bitwarden.com",
|
||||
"license": "GPL-3.0",
|
||||
|
|
|
@ -80,6 +80,10 @@
|
|||
height: 100%;
|
||||
user-select: none;
|
||||
|
||||
border: none;
|
||||
background: transparent;
|
||||
width: auto;
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed("accountSwitcherTextColor");
|
||||
}
|
||||
|
@ -114,9 +118,11 @@
|
|||
0 1px 5px 0 rgba(0, 0, 0, 0.2);
|
||||
border-radius: $border-radius;
|
||||
|
||||
a {
|
||||
button {
|
||||
border: none;
|
||||
background: transparent;
|
||||
width: 100%;
|
||||
padding: 5px 10px;
|
||||
display: block;
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed("textColor");
|
||||
|
@ -141,6 +147,7 @@
|
|||
|
||||
.accountInfo {
|
||||
display: grid;
|
||||
text-align: left;
|
||||
|
||||
.email {
|
||||
font-size: $font-size-base;
|
||||
|
@ -173,6 +180,7 @@
|
|||
|
||||
.add {
|
||||
margin: 4px 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.accountLimitReached {
|
||||
|
|
|
@ -139,28 +139,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.login-header {
|
||||
padding: 1em;
|
||||
font-size: 1.2em;
|
||||
.environment-urls-settings-icon {
|
||||
@include themify($themes) {
|
||||
color: themed("mutedColor");
|
||||
}
|
||||
|
||||
span {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
text-decoration: none;
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed("primaryColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#sso-page {
|
||||
.content {
|
||||
width: 300px;
|
||||
|
@ -237,3 +215,31 @@
|
|||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
#login-page {
|
||||
flex-direction: column;
|
||||
|
||||
.login-header {
|
||||
align-self: flex-start;
|
||||
padding: 1em;
|
||||
font-size: 1.2em;
|
||||
.environment-urls-settings-icon {
|
||||
@include themify($themes) {
|
||||
color: themed("mutedColor");
|
||||
}
|
||||
|
||||
span {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
text-decoration: none;
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed("primaryColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,20 @@ app-root {
|
|||
}
|
||||
}
|
||||
|
||||
> .items {
|
||||
order: 2;
|
||||
}
|
||||
|
||||
> .details {
|
||||
order: 3;
|
||||
}
|
||||
|
||||
> .logo {
|
||||
order: 4;
|
||||
}
|
||||
|
||||
> .groupings {
|
||||
order: 1;
|
||||
width: 22%;
|
||||
min-width: 175px;
|
||||
max-width: 250px;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { Injectable } from "@angular/core";
|
||||
import { ipcRenderer } from "electron";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
import Swal from "sweetalert2";
|
||||
|
||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||
|
|
|
@ -4,7 +4,12 @@ import { Account } from "../models/account";
|
|||
|
||||
import { StateService as StateServiceAbstraction } from "jslib-common/abstractions/state.service";
|
||||
|
||||
export class StateService extends BaseStateService<Account> implements StateServiceAbstraction {
|
||||
import { GlobalState } from "jslib-common/models/domain/globalState";
|
||||
|
||||
export class StateService
|
||||
extends BaseStateService<GlobalState, Account>
|
||||
implements StateServiceAbstraction
|
||||
{
|
||||
async addAccount(account: Account) {
|
||||
// Apply desktop overides to default account values
|
||||
account = new Account(account);
|
||||
|
|
|
@ -19,7 +19,7 @@ const common = {
|
|||
},
|
||||
{
|
||||
test: /\.(jpe?g|png|gif|svg)$/i,
|
||||
exclude: /.*(fontawesome-webfont)\.svg/,
|
||||
exclude: /.*(bwi-font)\.svg/,
|
||||
generator: {
|
||||
filename: "images/[name][ext]",
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue